Grand Bazaar Swap
by @agriimony
Perform and document Grand Bazaar P2P swaps on Base using deployed AirSwap Swap contracts. Includes repeatable workflows for approvals, EIP-712 signing, cast...
clawhub install grand-bazaar-swap📖 About This Skill
name: grand-bazaar-swap description: 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.
Grand Bazaar P2P Swap
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.
Deployed contracts
Read: references/base-mainnet-deployments.md
Safety rules
Workflow
Favorable inbound offer rule
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:order + signatureThanks for the swap 🤝
- https://basescan.org/tx/
- Post as a reply to the parent offer cast (cast hash origin)This applies especially to direct offer replies where speed matters but safety preflight is still mandatory.
Favorable outbound negotiation rule
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 contract
- require nonceUsed(signer, nonce) === false
- ignore SenderAllowanceLow on maker flow, since sender approves on taker side after cast discovery
- block posting if there are any other check errors besides SenderAllowanceLow
GBZ1:
- for private offers, include counterparty mention via API fields (mentions, mentions_positions)
2) immediately post a follow-up embed cast that deeplinks to Grand Bazaar /c/
Outbound offer loop: 1. Propose a favorable draft quote 2. If counterparty hesitates, improve terms in small controlled steps 3. Re-sign and repost updated order with fresh nonce/expiry 4. Stop if value or risk limits are breached
Guardrails:
Explicit size-aware pricing parameters
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.
0) Pick roles
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.
0.1) What each party does
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.1) Ensure balances
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.
2) Approve
Swap to spend the sender ERC20.Swap to spend the signer asset.3) Create order
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 now4) Sign EIP-712
Use protocol-specific domain/types based on routed swap contract.
Legacy Swap v4.2
SWAP
- version: 4.2
- chainId: 8453
- verifyingContract: routed legacy Swap address
Order(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_ERC20
- version: 4.3
- chainId: 8453
- verifyingContract: 0x95D598D839dE1B030848664960F0A20b848193F4
OrderERC20(uint256 nonce,uint256 expiry,address signerWallet,address signerToken,uint256 signerAmount,uint256 protocolFee,address senderWallet,address senderToken,uint256 senderAmount)5) Execute
Sender calls:
swap(recipient, maxRoyalty, order)Recommended defaults:
recipient = sender for testingmaxRoyalty = 0 unless the signer asset is an ERC2981 NFT6) Confirm
7) Share orders in AirSwap Web compatible compressed format
Farcaster mention and recipient rules
@username text will create a mention.mentions: array of target FIDs
- mentions_positions: UTF-8 byte offsets
makeCastAdd 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
- each position points to the beginning of the exact @handle token in text
verified_addresses.primary.eth_address when available.Long-cast link budget rules
Strict cast-parse format for cast-hash based loading
GBZ1:
GBZ1: must be uppercase and start at line start..GBZ1: line per cast.NFT cast metadata and embed rules
Human-readable cast line templates (context-dependent)
I offer for
- ERC721: #
- ERC1155: x #
I offer 12 USDC for 0.005 WETH
- ERC721 -> ERC20: I offer PFP #176 for 200 USDC
- ERC20 -> ERC721: I offer 200 USDC for PFP #176
- ERC721 -> ERC721: I offer PFP #176 for PFP #174
- ERC1155 -> ERC20: I offer 3x GAMEITEM #42 for 10 USDC
Private offer • expires in
- Public order: Open offer • expires in
GBZ1:
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=, 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 token
- royaltyInfo(signerTokenId, senderAmountRaw)
senderAmount + protocolFee + affiliateAmount + royaltyAmount
maxRoyalty >= 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/make_cast_payload.js writes
airswapWeb.orderUrl with URL-encoded compressed order for reliable clickable linkscompressedOrder remains unencoded for machine parsing and execution8) Decompress and take a posted order
If 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 / 10000
- totalRequired = order.sender.amount + feeAmount + order.affiliateAmountThen execute with sender flow
SENDER_PRIVATE_KEYnode scripts/sender_execute_order.jsSender execution script already enforces
Scripts
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 hardeningFor a single machine end to end test
node scripts/test_weth_usdc_swap.jsFor details and parameters
Read scripts/README.md