Skip to main content
Pre-Release Documentation

This describes a pre-release version of LBAMM. Interfaces and behavior may change.

Verify the exact repository commit before building production integrations.

Submit Signed Orders

This guide shows how to:

  • Accept a maker’s signed permit (EIP-712, PermitC)
  • Execute it using PermitTransferHandler
  • Submit the order via directSwap

This flow assumes:

  • Fill-or-kill permit
  • No cosigner
  • No custom hook validation
  • No fee on top
  • Exchange fee included

For handler internals and full permit semantics, see:

  • Reference → PermitTransferHandler
  • Reference → directSwap

Mental Model

In this flow:

  • Maker signs a PermitC permit authorizing transfer of tokenIn.
  • Executor (taker) submits directSwap.
  • AMM:
    • Pulls tokenIn from maker via PermitTransferHandler.
    • Pulls tokenOut from executor.
    • Transfers tokenOut to maker.
    • Transfers tokenIn to executor.

directSwap does not compute pricing.
All amounts must be supplied explicitly.


directSwap Entry Point

function directSwap(
SwapOrder calldata swapOrder,
DirectSwapParams calldata directSwapParams,
BPSFeeWithRecipient calldata exchangeFee,
FlatFeeWithRecipient calldata feeOnTop,
SwapHooksExtraData calldata swapHooksExtraData,
bytes calldata transferData
) external payable returns (uint256 amountIn, uint256 amountOut);

Construct SwapOrder

SwapOrder memory order = SwapOrder({
deadline: deadline,
recipient: maker,
amountSpecified: int256(amountIn), // >0 => swap-by-input
minAmountSpecified: 0, // not checked in directSwap
limitAmount: minAmountOut, // validated by permit
tokenIn: tokenIn,
tokenOut: tokenOut
});

Notes:

  • Mode is determined by sign of amountSpecified.
  • minAmountSpecified is not enforced in directSwap.
  • limitAmount is part of the permit signature to ensure minimum amount out to permit signer.

Construct DirectSwapParams

directSwap requires the executor to supply the opposite side explicitly.

Mapping rule:

  • If amountSpecified > 0swapAmount = amountOut
  • If amountSpecified < 0swapAmount = amountIn

For swap-by-input example:

DirectSwapParams memory params = DirectSwapParams({
swapAmount: amountOut, // executor provides tokenOut
maxAmountOut: maxExecutorPay, // cap executor payment (after fees)
minAmountIn: minExecutorReceive // minimum executor receives (after fees)
});

Definitions:

  • swapAmount — opposing side of the transaction from SwapOrder.amountSpecified, if amountSpecified is positive for swap by input then swapAmount represents the transaction output amount, if amountSpecified is negative for swap by output then swapAmount represents the transaction input amount.
  • maxAmountOut — maximum executor payment (post-fee).
  • minAmountIn — minimum executor receipt (post-fee).

Include Exchange Fee

BPSFeeWithRecipient memory exchangeFee = BPSFeeWithRecipient({
recipient: exchangeFeeRecipient,
BPS: 30
});

FlatFeeWithRecipient memory feeOnTop = FlatFeeWithRecipient({
recipient: address(0),
amount: 0
});
  • If recipient is zero, fee must be zero.
  • Fee ordering semantics are documented in Fees & Economics.

Create Permit (Raw EIP-712 with Swap additional data)

PermitTransferHandler fill-or-kill permits use an EIP-712 payload of the form:

PermitTransferFromWithAdditionalData(
uint256 tokenType,
address token,
uint256 id,
uint256 amount,
uint256 nonce,
address operator,
uint256 expiration,
uint256 masterNonce,
Swap swapData
)
Swap(
bool partialFill,
address recipient,
int256 amountSpecified,
uint256 limitAmount,
address tokenOut,
address exchangeFeeRecipient,
uint16 exchangeFeeBPS,
address cosigner,
address hook
)

What must be bound by the maker signature

For the integration flow in this guide (fill-or-kill, no cosigner, no hook):

  • swapData.partialFill = false
  • swapData.recipient = maker
  • swapData.amountSpecified = SwapOrder.amountSpecified
  • swapData.limitAmount = SwapOrder.limitAmount
  • swapData.tokenOut = SwapOrder.tokenOut
  • swapData.exchangeFeeRecipient + swapData.exchangeFeeBPS must match the exchangeFee passed to directSwap
  • swapData.cosigner = address(0)
  • swapData.hook = address(0)

TypeScript example: EIP-712 typed data to sign

This is a minimal example showing the nested structs. You must use the PermitC instance as the EIP-712 verifying contract.

const domain = {
name: "PermitC",
version: "1",
chainId: executionChainId,
verifyingContract: permitProcessor, // canonical PermitC instance
};

const types = {
Swap: [
{ name: "partialFill", type: "bool" },
{ name: "recipient", type: "address" },
{ name: "amountSpecified", type: "int256" },
{ name: "limitAmount", type: "uint256" },
{ name: "tokenOut", type: "address" },
{ name: "exchangeFeeRecipient", type: "address" },
{ name: "exchangeFeeBPS", type: "uint16" },
{ name: "cosigner", type: "address" },
{ name: "hook", type: "address" },
],
PermitTransferFromWithAdditionalData: [
{ name: "tokenType", type: "uint256" },
{ name: "token", type: "address" },
{ name: "id", type: "uint256" },
{ name: "amount", type: "uint256" },
{ name: "nonce", type: "uint256" },
{ name: "operator", type: "address" },
{ name: "expiration", type: "uint256" },
{ name: "masterNonce", type: "uint256" },
{ name: "swapData", type: "Swap" },
],
};

const message = {
20, // tokenType ERC20
token: tokenIn,
0, // tokenId is 0 for ERC20
amount: amountIn,
nonce,
operator: permitTransferHandler,
expiration: permitExpiration,
masterNonce,
swapData: {
partialFill: false,
recipient: maker,
amountSpecified: amountSpecified, // int256 (match SwapOrder.amountSpecified)
limitAmount: limitAmount, // match SwapOrder.limitAmount
tokenOut: tokenOut, // match SwapOrder.tokenOut
exchangeFeeRecipient,
exchangeFeeBPS, // uint16
cosigner: zeroAddress,
hook: zeroAddress,
},
};

const signature = await wallet.signTypedData({
domain,
types,
primaryType: "PermitTransferFromWithAdditionalData",
message,
});

Encode transferData

Solidity example:

bytes memory transferData = bytes.concat(
abi.encode(permitTransferHandler), // first 32 bytes
bytes1(0), // discriminator (fill-or-kill)
abi.encode(permitStruct) // ABI-encoded permit
);

For full helper implementations, see PermitTransferHandler reference.


Call directSwap

SwapHooksExtraData memory hookData = SwapHooksExtraData({
tokenInHook: "",
tokenOutHook: "",
poolHook: "",
poolType: ""
});

(uint256 amountIn, uint256 amountOut) =
amm.directSwap(
order,
params,
exchangeFee,
feeOnTop,
hookData,
transferData
);

Return values:

  • amountIn — total tokenIn moved (including fees)
  • amountOut — total tokenOut moved

Executor Requirements

Executor must:

  • Hold sufficient tokenOut
  • Approve LBAMM for tokenOut
  • Ensure signed permit matches swap parameters
  • Ensure recipient == permit.from

Common failures:

  • Signature invalid
  • Permit expired
  • Nonce reused
  • Exchange fee mismatch (if bound in signed intent)
  • Executor payment exceeds maxAmountOut

Limit Break

TwitterLimitBreak.comMedium

© 2026 Limit Break International, Inc. All rights reserved.

Privacy PolicyTerms of ServiceCookie PolicyDo Not Sell My Info