Integrating
This guide covers the client side: a RAILGUN wallet fork acting for a maker, and a filler bot or relayer acting for the other leg.
As a maker: submitting an order
The maker is entering the pool. They escrow public tokens and describe the shielded note they want back.
- Choose the note recipient key.
mpkis the pool master public key that must be able to spend the delivered note. - Approve the contract to move
takingAmountoftakingToken. - Call
submitOrder, which takes custody of the escrow immediately and emitsOrderCreated.
function submitOrder(
bytes32 mpk,
TokenData calldata makingToken, // token wanted as a shielded note
uint120 makingAmount, // note value wanted
TokenData calldata takingToken, // public token escrowed as payment
uint256 takingAmount, // amount escrowed
uint256 expiry, // must be in the future
bytes32 listProviderPPoIKey // recorded on the order and emitted; not read on-chain today
) external;The ratio of takingAmount to makingAmount is the limit price. TokenData is RAILGUN's
(uint8 tokenType, address tokenAddress, uint256 tokenSubID).
listProviderPPoIKey has no default and no on-chain effect. Pass bytes32(0) unless you are
coordinating with a list provider out of band and want the key recorded on the order for consumers
of the OrderCreated event.
As a filler: discovering orders
Query the subgraph for the order book. The subgraph cannot compare against wall-clock time, so filter for live orders client-side:
query OpenOrders($now: BigInt!) {
orders(
where: { status: OPEN, remainingAmount_gt: 0, expiry_gt: $now }
orderBy: createdAtTimestamp
orderDirection: desc
) {
id
orderId
mpk
makingTokenAddress
makingAmount
takingTokenAddress
takingAmount
remainingAmount
expiry
}
}See the subgraph reference for the full schema.
As a filler: filling an order
A fill delivers a shielded note to the maker and claims a pro-rata share of the escrow.
- Pick a blinding. Choose
randomand compute the note the maker asked for:npk = poseidon2(mpk, random), thencommitment = poseidon3(npk, tokenID, value). - Build the RAILGUN transaction that creates that commitment, spending your own shielded notes.
- Bind the bundle. Set
adaptContractto the ZenoLOP address andadaptParamstokeccak256(abi.encode(nullifiers, transactions.length, fills, recipient)) % SNARK_SCALAR_FIELD, then generate the shielded proof over those bound params. - Prove the note requirements. Produce the Noir proof whose public signals are
token,value,mpk, and the resulting commitment. - Submit both together:
function fillOrder(
Transaction[] calldata _transactions, // RAILGUN transactions creating the notes
Fill[] calldata _fills, // one per delivered note
address _recipient // where the payout goes on a successful claim
) external;Each Fill carries { orderId, amount, commitment, proof }. The call reverts atomically if any
fill fails, including on the shielded side.
Retain the random you chose: it is required to claim.
As a filler: claiming the payout
fillOrder escrows the payout rather than releasing it. Watch for your OrderFilled event to learn
the payoutId and deadline, then claim before the deadline:
function claimPayout(uint256 _pendingPayoutId, bytes32 _random, bytes calldata _proof) external;The contract recomputes the commitment from the order's mpk, the delivered note amount, and your
random, and requires it to match the note delivered.
On success the escrow is released to the recipient minus the taker fee, and PayoutExecuted is
emitted, revealing random publicly.
If the deadline passes without a claim, the maker can call reclaimPayout and recover that escrow.
Related
- Contract reference - every function, event, and revert.
- Subgraph reference - entities and queries.