Skip to main content

Common flows

End-to-end examples showing how the contracts work together.

Funding Holdings

Deposit into your own Holdings balance
1. usdc.approve(holdings, amount)
2. holdings.deposit(user, usdc, amount)
Deposit into another user’s Holdings balance
1. usdc.approve(holdings, amount)
2. holdings.deposit(recipient, usdc, amount)
Deposit via Permit2
1. usdc.approve(permit2, amount)  // one-time Permit2 approval
2. Sign PermitTransferFrom with spender = holdings
3. holdings.depositWithPermit(depositor, usdc, amount, nonce, deadline, signature)

Approving Settlement as operator

// Direct call:
holdings.setOperator(settlement, true)

// Or via signature:
1. Sign OperatorApproval(user, operator, approved, nonce, deadline)
2. holdings.setOperatorBySig(user, settlement, true, nonce, deadline, signature)

Acquiring outcome tokens

Mint complete sets directly to a wallet
1. usdc.approve(marketFactory, amount)
2. marketFactory.mintCompleteSets(marketId, amount, recipient)
Mint complete sets from Holdings collateral
// Prerequisite: collateral already in Holdings and Settlement approved as operator
settlement.mintCompleteSetsFromHoldings(marketId, amount)

Trading

Sign a limit order (Settlement V2)
domain: { name: "Settlement", version: "1", chainId, verifyingContract: settlement }
types: SignedOrder(bytes32 marketId, address trader, uint256 maxShares, uint256 minSharesOut, uint256 maxCollateralIn, uint256 minCollateralOut, uint8 outcomeIndex, uint8 orderKind, bytes32 nonce, uint256 expiry, uint256 maxFee, uint8 timeInForce, uint8 makerRoleConstraint)
Execute matched fills
settlement.settleFill(fillData, fillSignature)
// or
settlement.settleFillBatch(fillsArray, fillSignaturesArray)

Moving and exiting Holdings balances

Transfer balance internally
holdings.transfer(recipient, token, amount)
Withdraw to an external address
holdings.withdraw(recipient, token, amount)
Withdraw via signature
1. Sign Withdraw(from, to, token, amount, nonce, deadline)
2. Relayer calls holdings.withdrawBySig(...)

Exiting positions

Burn complete sets from Holdings
settlement.burnCompleteSetsFromHoldings(marketId, amount, recipient, creditInternal)
// creditInternal = true  -> collateral goes back into Holdings
// creditInternal = false -> collateral is sent externally to recipient
Redeem resolved outcomes to Holdings
settlement.redeemOutcomesToHoldings(marketId, amounts)
Withdraw redeemed collateral from Holdings
holdings.withdraw(recipient, token, amount)

Negative-risk conversion

For a multi-binary market with M child questions, converting N NO positions yields:
N NO -> (M - N) YES + (N - 1) collateral
Wallet-based conversion
marketFactory.convertPositions(parentMarketId, indexSet, amount, expectedQuestionCount)
Holdings-based conversion
settlement.convertMultiBinaryPositionsFromHoldings(parentMarketId, indexSet, amount, expectedQuestionCount)
indexSet is a bitmask where bit i means the NO token for child question i is being converted.

Permit2 reference

Used for gasless token pulls in Holdings.depositWithPermit and Holdings.batchDepositWithPermit. Domain separator calculation:
permit2Address = 0x000000000022D473030F116dDEE9F6B43aC78BA3
domainTypeHash = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)")
nameHash = keccak256("Permit2")
DOMAIN_SEPARATOR = keccak256(abi.encode(domainTypeHash, nameHash, chainId, permit2Address))
PermitTransferFrom structs:
struct TokenPermissions {
    address token;
    uint256 amount;
}

struct PermitTransferFrom {
    TokenPermissions permitted;
    uint256 nonce;
    uint256 deadline;
}
The Permit2 spender in the signed hash is address(holdings).