Transfers

Operations → Transfers (Composer and Call Forwarder)

Lead type: ComposerCommands.TRANSFERS.

The sub types are defined by the following enum:

TransferIds Enum

Value Name Description
0 TRANSFER_FROM Pull funds from caller
1 SWEEP Send funds from this contract
3 UNWRAP_WNATIVE Unwrap native currency
4 PERMIT2_TRANSFER_FROM Permit2 transfer
5 APPROVE Approve target

SweepType Enum

Value Name Description
0 VALIDATE Check if balanceOf(address(this)) >= amount, if true transfer it, if not revert
1 AMOUNT Transfer 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

Offset Length (bytes) Description
0 20 asset address
20 20 receiver address
40 16 amount 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

Offset Length (bytes) Description
0 20 asset address
20 20 receiver address
40 1 sweepType SweepType
41 16 amount 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

Offset Length (bytes) Description
0 20 wrappedNativeAddress address
20 20 receiver address
40 1 sweepType SweepType
41 16 amount 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

Offset Length (bytes) Description
0 20 asset address
20 20 receiver address
40 16 amount 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

Offset Length (bytes) Description
0 20 token address
20 20 target 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])

results matching ""

    No results matching ""