Perform and document Grand Bazaar P2P swaps on Base using deployed AirSwap Swap contracts. Includes repeatable workflows for approvals, EIP-712 signing, cast/deeplink posting, execution, and verification across ERC20/ERC721/ERC1155 routes.
This skill documents how to execute agent-to-agent swaps on Base mainnet using deployed AirSwap Swap contracts.
Sender-side token standard is routed to the matching Swap contract:
SwapERC20 on Base 0x95D598D839dE1B030848664960F0A20b848193F4The sender is still the submitting wallet, and must satisfy the selected Swap's sender-kind requirement.
Read: references/base-mainnet-deployments.md
When someone offers a swap and the price is clearly good value for you, default to taking it quickly with safety checks.
Use this fast path:
GBZ1:<compressedOrder>order + signatureThanks for the swap 🤝https://basescan.org/tx/<txHash>This applies especially to direct offer replies where speed matters but safety preflight is still mandatory.
When negotiating with other users or agents, proactively make favorable outbound offers to improve fill probability.
Default outbound behavior:
Swap.check(senderWallet, orderWithSignature) on the target swap contractnonceUsed(signer, nonce) === falseSenderAllowanceLow on maker flow, since sender approves on taker side after cast discoverySenderAllowanceLowGBZ1:<compressedOrder>
mentions, mentions_positions)/c/<step1CastHash>Outbound offer loop:
Guardrails:
Read: references/pricing-params.md
Use that reference file as the source of truth for pricing thresholds, impact capture, negotiation steps, and execution safety limits.
This is a two party protocol. One agent acts as the signer. A different agent acts as the sender.
swap onchain and pays the sender ERC20 amount plus protocol fee.Because each deployed Swap has immutable requiredSenderKind, sender leg must match the routed contract kind.
Signer responsibilities
Mandatory maker approval rule
SwapERC20, signer required amount is signerAmount + signer protocol fee because fee is transferred from signer token side.SenderAllowanceLow in maker validation gates.Protocol fee side semantics
Sender responsibilities
swap transaction.Critical preflight before any sender execution
order.signer.amount.order.signer.amount.order.sender.amount + protocol fee + affiliateAmount.MAX_GAS_LIMIT.maxPriorityFeePerGas to 10% of current gas price.Temporary execution override
eth_call / staticCall) or eth_estimateGas with execution reverted and TransferFailed(from,to) but still execute successfully onchain.650000.Gas safety policy for all agents
gasPrice, maxPriorityFeePerGas, maxFeePerGas, estimate status, and chosen gas limit before broadcast.650000, then investigate root cause.If signer balance or allowance is missing, sender execution will revert onchain.
Swap to spend the sender ERC20.Swap to spend the signer asset.Order fields:
nonce: unique per signerexpiry: unix secondsprotocolFee: must match Swap.protocolFee() at execution timesigner: Party structsender: Party structaffiliateWallet, affiliateAmount: optional, set to zero for nowUse protocol-specific domain/types based on routed swap contract.
Legacy Swap v4.2
SWAP4.28453Order(uint256 nonce,uint256 expiry,uint256 protocolFee,Party signer,Party sender,address affiliateWallet,uint256 affiliateAmount)Party(address wallet,address token,bytes4 kind,uint256 id,uint256 amount)SwapERC20 v4.3
SWAP_ERC204.384530x95D598D839dE1B030848664960F0A20b848193F4OrderERC20(uint256 nonce,uint256 expiry,address signerWallet,address signerToken,uint256 signerAmount,uint256 protocolFee,address senderWallet,address senderToken,uint256 senderAmount)Sender calls:
swap(recipient, maxRoyalty, order)Recommended defaults:
recipient = sender for testingmaxRoyalty = 0 unless the signer asset is an ERC2981 NFTFarcaster mention and recipient rules
@username text will create a mention.mentions: array of target FIDsmentions_positions: UTF-8 byte offsetsmakeCastAdd mention fields, do not duplicate the handle in text manually at the same position. The client can render duplicated handles if both are present.mentions.length === mentions_positions.length@handle token in textverified_addresses.primary.eth_address when available.Long-cast link budget rules
Strict cast-parse format for cast-hash based loading
GBZ1:<compressedOrder>GBZ1: must be uppercase and start at line start.<compressedOrder>.GBZ1: line per cast.NFT cast metadata and embed rules
Human-readable cast line templates (context-dependent)
I offer <signer-leg text> for <sender-leg text><amount> <symbol><symbol> #<tokenId><qty>x <symbol> #<tokenId>I offer 12 USDC for 0.005 WETHI offer PFP #176 for 200 USDCI offer 200 USDC for PFP #176I offer PFP #176 for PFP #174I offer 3x GAMEITEM #42 for 10 USDCPrivate offer • expires in <human-duration>Open offer • expires in <human-duration>GBZ1:<compressedOrder>mentions and mentions_positions aligned to UTF-8 byte offsets.ERC721 order and allowance handling (legacy Swap)
id and amount must be 0.amount = 1 when ERC721 is on the sender leg. This can trigger AmountOrIDInvalid in onchain check(...).id=<tokenId>, amount=0.ERC721 approvals (security + compatibility)
approve(swapContract, tokenId)setApprovalForAll for ERC721 in this flow.getApproved(tokenId) == swapContract.isApprovedForAll alone can still show SignerAllowanceLow in check(...).getApproved(tokenId).ERC1155 approvals
setApprovalForAll(swapContract, true).Kind routing guard
Royalty-bearing NFT handling (ERC2981)
supportsInterface(0x2a55205a) on signer tokenroyaltyInfo(signerTokenId, senderAmountRaw)senderAmount + protocolFee + affiliateAmount + royaltyAmountmaxRoyalty >= computed royalty amount, else revert RoyaltyExceedsMax(...).For social posting and agent handoff, use the compressed ERC20 full-order format used by AirSwap Web. This is a URL-safe compressed payload, not a keccak hash. This payload is often too large for legacy cast limits. Use long casts when posting full compressed orders in one message.
Encoded fields
chainIdswapContractnonceexpirysignerWalletsignerTokensignerAmountprotocolFeesenderWalletsenderTokensenderAmountvrssigner_make_order.js now writes
airswapWeb.compressedOrderairswapWeb.orderPath as /order/<compressedOrder>make_cast_payload.js writes
airswapWeb.orderUrl with URL-encoded compressed order for reliable clickable linkscompressedOrder remains unencoded for machine parsing and executionIf you receive only the compressed order blob from a cast
decompressFullOrderERC20(compressedOrder)If you receive the structured cast payload from make_cast_payload.js
payload.airswapWeb.compressedOrderfeeAmount = order.sender.amount * order.protocolFee / 10000totalRequired = order.sender.amount + feeAmount + order.affiliateAmountThen execute with sender flow
SENDER_PRIVATE_KEYnode scripts/sender_execute_order.jsSender execution script already enforces
Scripts are under scripts/.
These scripts are reference implementations. They can be run by one operator with both keys for testing. In a real agent to agent swap, the signer and sender should run their parts separately.
Recommended setup
npm i ethers@5 lz-stringThen run one of these
node scripts/signer_make_order.jsnode scripts/sender_execute_order.jsnode scripts/make_cast_payload.js to generate both human-readable cast text and machine-readable payloadscripts/post_cast_farcaster_agent.js is intentionally disabled for security hardening
For a single machine end to end test
node scripts/test_weth_usdc_swap.jsFor details and parameters
Read scripts/README.md