Futarchy market

In futarchy markets, participants trade on how a specific decision (e.g. a DAO proposal) will affect the price of two tokens. The market asks a single yes/no question (e.g. β€œWill proposal GIP-120 be accepted by February 1 2025?”). There are four outcome tokens: Yes/Token1, No/Token1, Yes/Token2, No/Token2. After resolution, either the two β€œYes” outcomes or the two β€œNo” outcomes are redeemable for the two collateral tokens.

Creation uses FutarchyFactory.createProposal. Split, merge and redeem use FutarchyRouter, which is like the standard Router but works with two collateral tokens and a FutarchyProposal (proposal address) instead of a Market. Resolution uses FutarchyRealityProxy.resolve(proposal) (or proposal.resolve()).


Create a futarchy proposal

Call FutarchyFactory.createProposal with a CreateProposalParams struct. Outcomes and token names are derived from the two collateral token symbols (e.g. Yes-GNO, No-GNO, Yes-wstETH, No-wstETH).

struct CreateProposalParams {
    string marketName;
    IERC20 collateralToken1;
    IERC20 collateralToken2;
    string category;
    string lang;
    uint256 minBond;
    uint32 openingTime;
}
  • marketName: The Reality.eth question (e.g. β€œWill proposal X be accepted by date Y?”).

  • collateralToken1 / collateralToken2: The two ERC20 tokens used as collateral (e.g. GNO and wstETH).

  • category / lang: Reality question category and language.

  • minBond: Minimum bond for answering on Reality.eth.

  • openingTime: Unix timestamp when the question can be answered.

On success, the factory returns the new proposal contract address (you can also read it from the NewProposal event).


Split, merge and redeem

Use FutarchyRouter for all three. The argument order differs from the standard Router: the proposal (FutarchyProposal address) comes first, then the collateral token, then the amount.

Split

You choose one of the two collateral tokens and split that token into the two outcome tokens for that collateral (Yes-Token and No-Token). So one split gives you only the pair for token1 or the pair for token2. To get all four outcome tokens you split twice (once per collateral).

  1. Approve collateralToken (token1 or token2) to the FutarchyRouter.

  2. Call futarchyRouter.splitPosition(proposal, collateralToken, amount).

  3. You receive the two ERC20 outcome tokens for that collateral (e.g. Yes-GNO and No-GNO).

Merge

Hold one full set of outcome tokens for one collateral (i.e. equal amounts of Yes-Token and No-Token for that collateral). Merge returns that collateral to you.

Approve both outcome tokens (for that collateral) to the router, then call mergePositions(proposal, collateralToken, amount). You receive collateralToken back.

Redeem (after resolution)

After the proposal is resolved (FutarchyRealityProxy has reported payouts), only the winning side (Yes or No) is redeemable. You can redeem per collateral or both in one call.

Option A – Redeem one collateral

Approve the winning outcome token for one collateral (e.g. Yes-GNO if the proposal was accepted). Call redeemPositions(proposal, collateralToken, amount). You receive collateralToken.

Option B – Redeem both collaterals in one tx

Approve the winning outcome tokens for both collaterals (e.g. Yes-GNO and Yes-wstETH). Call redeemProposal(proposal, amount1, amount2). You receive collateralToken1 and collateralToken2 (amount1 and amount2 of each).


Resolve a futarchy proposal

Same idea as Resolve a market: the question must be answered and finalized on Reality.eth. Then call either:

  • FutarchyRealityProxy.resolve(proposal), or

  • proposal.resolve() (the proposal forwards to the reality proxy).

After resolution, YES (outcome 0) or NO (outcome 1) is the winning side; only the corresponding outcome tokens are redeemable.


Viem examples

We use the Viem setup: getPublicClient(chain) and getWalletClient(chain, process.env.PRIVATE_KEY). Addresses come from SEER_CONTRACTS[chain.id]. Futarchy contracts are deployed on Gnosis (chain 100); on that chain use addresses.FutarchyFactory and addresses.FutarchyRouter.

Shared: addresses


1. Create a futarchy proposal

Build the params tuple and call createProposal. The factory derives outcomes and token names from the collateral symbols.


2. Split (one collateral)

Choose one of the two collateral tokens, approve it, then call splitPosition(proposal, collateralToken, amount). Argument order: proposal first, then collateral, then amount.

To get outcome tokens for the second collateral as well, repeat with wstETH_ADDRESS (and approve wstETH to the router).


3. Merge (one collateral)

Hold equal amounts of Yes-Token and No-Token for one collateral. Approve both outcome tokens (Yes and No for that collateral) to the FutarchyRouter, then call mergePositions(proposal, collateralToken, amount).


4. Redeem – one collateral (redeemPositions)

After the proposal is resolved, approve the winning outcome token (e.g. Yes-GNO if accepted) to the FutarchyRouter. Then call redeemPositions(proposal, collateralToken, amount) to receive that collateral.


5. Redeem – both collaterals (redeemProposal)

Approve the winning outcome tokens for both collaterals. Then call redeemProposal(proposal, amount1, amount2) to receive both collateral tokens in one tx.


6. Resolve the proposal

Once the Reality.eth question is answered and finalized, call proposal.resolve() (or FutarchyRealityProxy.resolve(proposal)).


7. Simulate before sending

To avoid reverts (e.g. insufficient allowance or balance):


Summary

Action
Contract
Function
Notes

Create

FutarchyFactory

createProposal(params)

params: marketName, collateralToken1, collateralToken2, category, lang, minBond, openingTime

Split

FutarchyRouter

splitPosition(proposal, collateralToken, amount)

Approve collateral first; do once per collateral to get all four outcome tokens

Merge

FutarchyRouter

mergePositions(proposal, collateralToken, amount)

Need equal amounts of Yes + No for that collateral

Redeem (one collateral)

FutarchyRouter

redeemPositions(proposal, collateralToken, amount)

Approve winning outcome token; after resolve

Redeem (both)

FutarchyRouter

redeemProposal(proposal, amount1, amount2)

Approve both winning outcome tokens

Resolve

FutarchyProposal

resolve()

Or FutarchyRealityProxy.resolve(proposal); question must be settled on Reality

Last updated