Skip to main content
Caplane settles payment with EIP-3009 transferWithAuthorization: the consumer signs a transfer authorization off-chain, and the broker submits it on-chain inside brokerCapability. The binding sets the authorization nonce to the orderHash, so the token’s own signature check enforces the order. This page is the token surface the broker depends on, and the domain you sign under. TestUSDC implements the full EIP-3009; real USDC (Circle’s FiatTokenV2) does too.

transferWithAuthorization

function transferWithAuthorization(
    address from,
    address to,
    uint256 value,
    uint256 validAfter,
    uint256 validBefore,
    bytes32 nonce,
    uint8 v,
    bytes32 r,
    bytes32 s
) external;
ParameterMeaning in Caplane
fromThe order.consumer (payer and signer).
toThe broker (which then distributes funds).
valueThe order.amount.
validAfter / validBeforeThe signed authorization window (unix seconds).
nonceThe orderHash, keccak256(abi.encode(order)). This is the binding.
v / r / sThe consumer’s signature over the typed data below.
The token verifies the signature over (from, to, value, validAfter, validBefore, nonce). Because the nonce is the order hash, changing any signed order field changes the hash, the signature no longer recovers from, and the transfer reverts. See the binding.

authorizationState

function authorizationState(address authorizer, bytes32 nonce) external view returns (bool);
Returns true once a nonce has been used. Nonces are not sequential; an unspent authorization returns false. After a settlement the orderHash nonce is spent, which prevents replay.

The typed data

The consumer signs this EIP-712 struct (the nonce is bytes32, set to the orderHash):
TransferWithAuthorization(
  address from,
  address to,
  uint256 value,
  uint256 validAfter,
  uint256 validBefore,
  bytes32 nonce
)

The EIP-712 domain

The signer and the token must agree on the domain EIP712Domain(string name, string version, uint256 chainId, address verifyingContract). The name, version, and verifyingContract are the same on both testnets; the chainId differs, so the domain separator differs.
FieldValue
nameTest USD Coin
version2
verifyingContract0x125959541Bb486058E7e3b55E49b3B04e49fBa5E
The chainId differs per network, so the domain separator differs; the per-chain separators are on Deployments. The SDK builds this domain with domainFromRegistry(registry); never hard-code it.

See also

  • The binding: why nonce = orderHash makes the token reject a tampered order.
  • Order encoding: how the orderHash is computed.
  • The SDK: signOrder and buildTransferAuthorization.