Skip to content

Rename MoiraiDelegate → TransferSettingsPolicy, scope to token transfers only#31

Open
hannahjin wants to merge 1 commit intomainfrom
moirai-delegate-v4
Open

Rename MoiraiDelegate → TransferSettingsPolicy, scope to token transfers only#31
hannahjin wants to merge 1 commit intomainfrom
moirai-delegate-v4

Conversation

@hannahjin
Copy link
Copy Markdown
Collaborator

Summary

Addresses audit feedback from Alexis Williams: MoiraiDelegate allowed arbitrary (target, value, callData) execution which in the worst case enables full SCW takeover (e.g. calling removeOwner). Since the only real use-case is ERC20/ETH transfers, scope it down — the contract now hard-codes the transfer selector and constructs calldata internally.

  • Replace MoiraiConfig { target, value, callData } with TransferConfig { recipient, amount, tokenContract }tokenContract = address(0) for native ETH, ERC20 address otherwise
  • Add ZeroRecipient / ZeroAmount validation at install time
  • Add UnexpectedActionData guard — executor actionData must be empty 0x
  • Add _onPostExecute balance check to catch ERC20 tokens that return false without reverting (e.g. USDT-mainnet) → reverts with ERC20TransferFailed
  • Explicit Unauthorized guard for delay-only configs in the executor uninstall path
  • Use abi.encodeCall throughout for compile-time type safety
  • EIP-712 domain name: "Transfer Settings Policy" (was "Moirai Delegate")
  • Rename deploy script to DeployTransferSettingsPolicy.s.sol

Test plan

  • forge test -vv — 331 tests, 0 failures
  • New install reverts: ZeroRecipient, ZeroAmount, NoConditionSpecified
  • New execute paths: native ETH transfer, ERC20 transfer (MintableERC20)
  • FalseReturningERC20 mock + test_reverts_whenERC20TransferReturnsFalse — verifies _onPostExecute balance check
  • test_reverts_withNonEmptyActionDataUnexpectedActionData guard
  • Pre-install disable path: success, delay-only rejection, expired deadline
  • Dry-run deploy on Base Sepolia: simulated contract at 0x999E14Ac9261F4d32Fe964666305aEC67D90215b, estimated gas 3,716,827 (~0.000041 ETH)

🤖 Generated with Claude Code

…fers only

Audit feedback: MoiraiDelegate allowed arbitrary (target, value, callData) which
in the worst case enables full SCW takeover. Scope it down to native ETH and ERC20
transfers only — the contract now constructs transfer calldata internally.

Key changes:
- Replace MoiraiConfig {target, value, callData} with TransferConfig {recipient,
  amount, tokenContract} — tokenContract=address(0) for ETH, ERC20 addr otherwise
- Add ZeroRecipient/ZeroAmount validation at install time
- Add UnexpectedActionData guard (actionData must be empty)
- Add _onPostExecute balance check to catch ERC20 tokens that return false
  without reverting (e.g. USDT-mainnet) — reverts with ERC20TransferFailed
- Add explicit Unauthorized guard for delay-only configs in executor uninstall path
- Use abi.encodeCall throughout for compile-time type safety
- EIP-712 domain: "Transfer Settings Policy" (was "Moirai Delegate")
- Rename deploy script to DeployTransferSettingsPolicy.s.sol

Tests (331 passing):
- New: ZeroRecipient, ZeroAmount revert cases
- New: ERC20 transfer happy path (MintableERC20 mock)
- New: FalseReturningERC20 mock + test for silent-false ERC20 guard
- New: UnexpectedActionData revert
- New: pre-install disable path tests (success, delay-only rejection, expired deadline)
- Replace executesContractCall with executesERC20Transfer

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant