User Proxy Swaps
UserProxyV4 has two owner-only market-swap entry points. Both call the same settlement path after
the input is in the proxy. executeAsAgent uses that settlement path too, but its authorization
and spender rules are described on agent delegation.
execute
Section titled “execute”execute( address router, address spender, address tokenIn, uint256 amountIn, address tokenOut, uint256 minOut, bytes data) external payable returns (uint256 amountOut)execute is onlyOwner and non-reentrant. For an ERC-20 input, it pulls exactly amountIn from
the owner into the proxy, approves the selected pull target, and calls router with data. For a
native input, the owner supplies the input as msg.value.
The proxy does not maintain a router allowlist. The owner supplies router, spender, and the
opaque route data for this direct path. The spender model determines which address receives the
temporary input approval:
spender |
Pull target and approval behavior |
|---|---|
address(0) |
The pull target is router; the proxy approves router for amountIn. |
| Non-zero address | The pull target is the supplied spender; the proxy approves it for amountIn. |
| Canonical Permit2 | The proxy approves Permit2 for the ERC-20, then gives Permit2 a router-specific allowance. |
The approval is for the current input amount only. After settlement, the proxy revokes the pull approval. A direct call can choose a non-zero spender; the additional restriction for agent calls is described below.
executePermit2In
Section titled “executePermit2In”executePermit2In( IPermit2.PermitTransferFrom permit, bytes permitSig, address router, address spender, address tokenOut, uint256 minOut, bytes data) external returns (uint256 amountOut)This path is onlyOwner and non-reentrant. It passes a plain Permit2 PermitTransferFrom to the
canonical Permit2 contract. It does not use an Intents Protocol Permit2 witness.
The input token is permit.permitted.token, and the input amount is
permit.permitted.amount. Permit2 transfers that amount from the owner to the proxy. The proxy
then uses the same router and spender model as execute, measures the route output, and settles
to the owner.
When the selected pull target is Permit2, the proxy rejects an amount above type(uint160).max
with Permit2AmountOverflow. It approves Permit2 for the ERC-20 and calls
PERMIT2.approve(token, router, amount, uint48(block.timestamp + 1)). The Permit2 allowance expiry
is one second; the router-level Permit2 allowance is cleared during settlement.
Native ETH is not available on executePermit2In: neither the permitted input token nor
tokenOut may be the native sentinel.
Shared settlement path
Section titled “Shared settlement path”After input acquisition and approval, _settleExecuted performs these checks and transfers:
- Revert with
MinOutZerowhenminOut == 0, or withAmountInZerowhenamountIn == 0. - Call
routerwithdataand the call value selected by the entry point. - Measure the proxy’s
tokenOutbalance increase. A failed router call reverts withRouterCallFailed(returnData). - Revert with
SlippageExceeded(received, minOut)when the measured amount is belowminOut. - Transfer the output balance to
owner. - For ERC-20 output, measure the owner’s balance increase. If it is below
minOut, revert withInsufficientOwnerDelivery(delivered, required). - Revoke the pull approval and send leftover
tokenInto the owner.
The output recipient is always owner. The call has no recipient argument, and the router cannot
redirect the settled output through calldata.
Native ETH
Section titled “Native ETH”The native sentinel is:
0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeEFor execute with that value as tokenIn, msg.value must equal amountIn, or the call reverts
with EthValueMismatch(sent, expected). The pull target must be router; a different target
reverts with NativeNotSupportedInOrder.
Sending ETH with an ERC-20 tokenIn reverts with UnexpectedEthValue(sent). Native input is not
available on executePermit2In or in an agent order. Native output is handled by the shared
owner-settlement path; strict native delivery reverts if the owner transfer fails.
tokenIn == tokenOut reverts with SameToken because the input and output balance measurements
would otherwise describe the same asset.
Intents are a different signing flow
Section titled “Intents are a different signing flow”The Intents Protocol signs a Permit2 witness under its own protocol flow. The User Proxy’s
executePermit2In accepts a plain PermitTransferFrom, and its agent path signs an AgentOrder
under the proxy’s own EIP-712 domain. See Intents signing only for comparison,
not as a proxy integration recipe.