Quip Network Documentation quip.network ↗

Integrating

This page covers reading QuipSwap from outside the order book: the contract interface, the events that record every swap, and the TypeScript SDK that wraps them. Addresses are on Contracts and fees.

Reading offers

Open listings are stored off chain by the order book and are read through the order book app at swap.quip.network. A listing reaches the chain once the maker accepts a request and commits. From then on, the swap is readable from the contract and its Commitment events, as shown below.

The TypeScript SDK

@quip.network/quip-swap-sdk wraps the contract with Solidity-exact hashing, terms and asset builders, schedule validation, a typed client and event decoders. It is a prerelease published under the beta dist-tag, so install it with the tag:

npm install @quip.network/quip-swap-sdk@beta viem

viem 2.x is a peer dependency. The package is ESM only and licensed AGPL-3.0-or-later. A plain install without @beta resolves an older build.

The SDK never holds keys, never signs and never stores the secret. For a Quip account it prepares the exact call (prepareCommit returns {to, value, data}) and the account’s own tooling signs and executes it.

Minimal example: read the fee and recent settlements

This script reads the current commitment fee on Base and lists the swaps settled in the last 2,000 blocks. It sends no transactions and needs no key. It uses top-level await, so it must run as an ES module: save it as example.mts, or set "type": "module" in package.json (with "module": "nodenext" in tsconfig.json if you typecheck it). The SDK being ESM-only does not make your file a module.

import { createPublicClient, http, formatEther } from 'viem'
import {
  createOmnibusClient,
  resolveDeployment,
  getClaimEvents,
} from '@quip.network/quip-swap-sdk'

const publicClient = createPublicClient({ transport: http('https://mainnet.base.org') })

// The SDK ships the deployment registry, so the address resolves by chain ID.
const { omnibusAddress } = resolveDeployment(8453n)
const omnibus = createOmnibusClient({ address: omnibusAddress, publicClient })

const fee = await omnibus.getCommitmentFee()
console.log('commitmentFee', formatEther(fee), 'ETH')

// Public RPCs cap eth_getLogs ranges; mainnet.base.org allows 2,000 blocks.
const latest = await publicClient.getBlockNumber()
const claims = await getClaimEvents({
  publicClient,
  address: omnibusAddress,
  fromBlock: latest - 1999n,
  toBlock: latest,
})
for (const c of claims) console.log(c.lock, c.recipient, c.secret)

For a full history scan, start at the Base deployment block, 50545260, as fromBlock, and page through in ranges your RPC allows.

To follow one swap, compute its ID with the SDK’s swapId(lock, party, counterparty) and read omnibus.getLockState(id). Watch for new reveals with watchClaimEvents.

Contract reference

All signatures below are from src/interfaces/IOmnibus.sol. Terms and Asset are defined in src/TermsLib.sol and described on How a swap works.

Swap functions

function commit(bytes32 lock, Terms calldata party, Terms calldata counterparty, CommitAuth calldata auth) external payable;
function claim(bytes32 secret, Terms calldata party, Terms calldata counterparty) external;
function reclaim(bytes32 lock, Terms calldata party, Terms calldata counterparty) external;

In each call, party is the side acting and counterparty is the other side. The contract derives the proposer from the deadlines, so passing the sides in the other order cannot select a different window.

Views

function swapId(bytes32 lock, Terms calldata party, Terms calldata counterparty) external pure returns (bytes32 id);
function lockState(bytes32 swapId) external view returns (LockState state);
function termsHash(bytes32 swapId) external view returns (bytes32 canonicalDigest);
function posted(bytes32 swapId, bytes32 termsDigest) external view returns (bool isPosted);
function reclaimed(bytes32 swapId, bytes32 sideDigest) external view returns (bool isReclaimed);
function commitDigest(bytes32 lock, Terms calldata party, Terms calldata counterparty, bytes32 signerHash) external view returns (bytes32 digest);
function commitmentFee() external view returns (uint256);
function accruedFees() external view returns (uint256 fees);
function MAX_FEE() external view returns (uint256);
function verifierActive(address verifier) external view returns (bool active);
function WALLET_FACTORY() external view returns (IWalletFactory factory);
function DOMAIN_SEPARATOR() external view returns (bytes32 separator);
function COMMIT_DIGEST_TAG() external view returns (bytes32 tag);

State is keyed by swap ID, not by the bare lock. lockState returns Unused (0), Free (1), Committed (2) or Spent (3). termsHash is non-zero once the pairing has been committed on this chain. commitDigest returns the exact 32-byte digest a keyed-route signer must sign; signerHash is the keccak256 of the whole signer blob.

Omnibus also exposes VERSION(), which returns the deployment’s salt label as a right-padded bytes32, and the standard owner() and pendingOwner().

Owner functions

function setCommitmentFee(uint256 newFee) external;           // reverts above MAX_FEE
function withdrawFees(address to) external;                    // sends accruedFees only
function setVerifierActivity(address verifier, bool active) external;

Events

event Commitment(bytes32 indexed lock, Terms party, bytes signer, address indexed sender);
event Claim(bytes32 indexed lock, address indexed recipient, bytes32 secret);
event Reclaimed(address caller, bytes32 indexed lock, address indexed recipient);
event FeeUpdated(uint256 oldFee, uint256 newFee);
event FeesWithdrawn(address indexed to, uint256 amount);
event VerifierActivityUpdated(address indexed verifier, bool active);
  • Commitment fires once per commit. sender is the address that submitted and funded it. signer is attribution only on the wallet route.
  • Claim fires once per leg paid out: once for a cross-chain claim, twice for a same-chain claim. It carries the secret, which is public from that block on. A cross-chain taker reads the secret from this event on the other chain.
  • Reclaimed fires once per reclaim, for the caller’s own side.

When you decode receipts, filter logs by the Omnibus address. A settlement receipt also contains the token contracts’ logs, and a raw decoder that matches by event signature alone can be fooled by another contract emitting the same signature. The SDK’s client.waitForReceipt and getClaimEvents scope decoding to the Omnibus address. extractSecretFromClaims additionally checks that each revealed secret hashes back to the lock, so read secrets through it.

Errors

ErrorRaised when
TermsMismatchThe terms passed do not match the digest stored at the first commit
InvalidLockStateThe swap is not in the state the call requires
InvalidLockAdvancementThe call would make a state transition the contract does not allow
IncorrectNativeAmountReceivedmsg.value is not exactly the leg’s native total plus the fee
TermsAlreadyPostedA second same-chain commit tries to post the side already posted
TermsNotPostedSettlement would pay out a side that was never committed
AlreadyReclaimedA side tries to reclaim its leg a second time
MalformedSignerThe signer blob is 20 bytes or shorter
NotQuipWalletWallet route, and msg.sender is not a Quip account
VerifierNotActiveKeyed route, and the named verifier is not active
InvalidCommitSignatureKeyed route, and the verifier rejected the signature
NativeNotAcceptedETH was sent to the contract outside commit
FeeExceedsMax, ZeroFeeRecipient, ZeroVerifier, RenounceDisabledOwner calls with invalid arguments, or an attempt to renounce ownership

Validation errors from TermsLib include WindowViolated, DisordedDeadlines, ChainIdMismatch, EmptyAssets, TooManyAssets, ZeroAmount, ZeroAddressRecipient, UnexpectedAmountReceived and the per-type Invalid...Asset errors.