Skip to main content

Transfers

Operations → Transfers (Composer and Call Forwarder)

Lead type: ComposerCommands.TRANSFERS.

The sub types are defined by the following enum:

TransferIds Enum

ValueNameDescription
0TRANSFER_FROMPull funds from caller
1SWEEPSend funds from this contract
3UNWRAP_WNATIVEUnwrap native currency
4PERMIT2_TRANSFER_FROMPermit2 transfer
5APPROVEApprove target

SweepType Enum

ValueNameDescription
0VALIDATECheck if balanceOf(address(this)) >= amount, if true transfer it, if not revert
1AMOUNTTransfer the amount without validation

To encode an operation, the caller has to append

abi.encodePacked(
uint8(ComposerCommands.TRANSFERS),
uint8(TransferIds.[Operation]),
...params
);

to the array of composer commands.

Transfer From Operation

Pull funds from the caller to a receiver address.

Parameters

OffsetLength (bytes)Description
020asset address
2020receiver address
4016amount uint128

Notes

If amount is 0, we pull asset.balanceOf(caller).

Encoding Example

// Solidity
bytes memory transferFromOp = abi.encodePacked(
uint8(ComposerCommands.TRANSFERS),
uint8(TransferIds.TRANSFER_FROM),
assetAddress,
receiverAddress,
uint128(amount)
);
// TypeScript
const transferFromOp = encodePacked(
["uint8", "uint8", "address", "address", "uint128"],
[TRANSFERS_COMMAND, TransferIds.TRANSFER_FROM, assetAddress, receiverAddress, amount]
)

Sweep Operation

Pull funds from this contract to a provided address.

Parameters

OffsetLength (bytes)Description
020asset address
2020receiver address
401sweepType SweepType
4116amount uint128

Enums Used

Notes

Note that asset can be the zero address for native currency.

Encoding Examples

// Solidity - Basic sweep
bytes memory sweepOp = abi.encodePacked(
uint8(ComposerCommands.TRANSFERS),
uint8(TransferIds.SWEEP),
assetAddress,
receiverAddress,
uint8(SweepType.AMOUNT),
uint128(amount)
);
// TypeScript - Basic sweep
const sweepOp = encodePacked(
["uint8", "uint8", "address", "address", "uint8", "uint128"],
[TRANSFERS_COMMAND, TransferIds.SWEEP, assetAddress, receiverAddress, SweepType.AMOUNT, amount]
)

Sweeping Full Balance

To sweep the entire balance of an asset without knowing the exact amount, set amount = 0 and use SweepType.VALIDATE:

// Sweep entire token balance
function encodeSweepFullBalance(asset: string, receiver: string) {
return abi.encodePacked(
uint8(ComposerCommands.TRANSFERS),
uint8(TransferIds.SWEEP),
asset,
receiver,
uint8(SweepType.VALIDATE), // fetch actual balance and validate
uint128(0) // amount = 0 triggers balanceOf lookup
)
}

This works because:

  • SweepType.VALIDATE fetches the actual balance using balanceOf(address(this))
  • amount = 0 ensures the validation always passes (balance >= 0)
  • The full balance is transferred to the receiver

Important: if the balance is zero and amount = 0, the validation passes and the contract skips the empty transfer execution - this is ideal for refunding dust.

Wrapping Native Currency

To wrap the native currency to wrapped native, you can just use asset=address(0),receiver=wrappedNativeAddress.

// Wrap native currency using SWEEP operation
function encodeWrap(amount: bigint, wrapTarget: string) {
return abi.encodePacked(
uint8(ComposerCommands.TRANSFERS),
uint8(TransferIds.SWEEP),
address(0), // signals native asset
wrapTarget, // wrapped native contract address
uint8(SweepType.AMOUNT),
uint128(amount)
)
}

This works because:

  • asset = address(0) tells the system to handle native currency
  • The wrapped native contract's receive() or fallback() function automatically wraps incoming native currency

Unwrap WNative Operation

Unwrap native asset and transfer if needed.

Parameters

OffsetLength (bytes)Description
020wrappedNativeAddress address
2020receiver address
401sweepType SweepType
4116amount uint128

Enums Used

Notes

Note that we do not hard-code the wrapped native address as some chains have duplicate wnative implementations.

Encoding Example

// Solidity
bytes memory unwrapOp = abi.encodePacked(
uint8(ComposerCommands.TRANSFERS),
uint8(TransferIds.UNWRAP_WNATIVE),
wrappedNativeAddress,
receiverAddress,
uint8(SweepType.AMOUNT),
uint128(amount)
);
// TypeScript
const unwrapOp = encodePacked(
["uint8", "uint8", "address", "address", "uint8", "uint128"],
[TRANSFERS_COMMAND, TransferIds.UNWRAP_WNATIVE, wrappedNativeAddress, receiverAddress, SweepType.AMOUNT, amount]
)

Permit2 Transfer From Operation

Execute a transfer via permit2. We only support the canonical Uniswap permit2 at address 0x000000000022D473030F116dDEE9F6B43aC78BA3.

Parameters

OffsetLength (bytes)Description
020asset address
2020receiver address
4016amount uint128

Notes

If amount is 0, we pull asset.balanceOf(caller).

Encoding Example

// Solidity
bytes memory permit2TransferOp = abi.encodePacked(
uint8(ComposerCommands.TRANSFERS),
uint8(TransferIds.PERMIT2_TRANSFER_FROM),
assetAddress,
receiverAddress,
uint128(amount)
);
// TypeScript
const permit2TransferOp = encodePacked(
["uint8", "uint8", "address", "address", "uint128"],
[TRANSFERS_COMMAND, TransferIds.PERMIT2_TRANSFER_FROM, assetAddress, receiverAddress, amount]
)

Approve Operation

Approve an asset - typically for a lender operation or external call.

Parameters

OffsetLength (bytes)Description
020token address
2020target address

Notes

ERC20-Approve the target via token, always uses infinite approve, the approval is stored and skipped if already done in the past.

Encoding Example

// Solidity
bytes memory approveOp = abi.encodePacked(
uint8(ComposerCommands.TRANSFERS),
uint8(TransferIds.APPROVE),
tokenAddress,
targetAddress
);
// TypeScript
const approveOp = encodePacked(["uint8", "uint8", "address", "address"], [TRANSFERS_COMMAND, TransferIds.APPROVE, tokenAddress, targetAddress])