This project implements a system for managing conditional tokens based on user-defined "verses" and their outcomes, inspired by the Multiverse Finance paper by Paradigm and built on the Conditional Tokens Framework by Gnosis.
The main goal is to provide a simple and user-friendly interface for creating and managing conditional tokens, allowing users to focus only on verses and their outcomes, rather than the underlying complexities of the token system.
The MultiverseFinance contract allows users to:
- Deposit a collateral token into a verse, creating conditional tokens for each outcome (or union of outcomes).
- Withdraw collateral from a resolved verse, burning the conditional tokens and returning the collateral.
- Push down ownership of token in a parent verse to a partition of a child verse, allowing for more complex conditional structures.
- Pull up ownership of tokens from a child verse to a parent verse, simplifying the conditional structure.
Additionally, the contract owner can register new verses that can be used for creating conditional tokens, and oracles can resolve verses by providing their actual outcomes.
In the following examples, we'll reference the
MultiverseFinancecontract asmvand use USDC as the collateral token. Also, we'll use pseudo-solidity code for clarity.Complete code examples can be found in the
MultiverseFinance.t.soltest file.
Let's say the owner of the contract wants to register two verses:
- "Will EIP-9999 be implemented before 2026?" with outcomes
["YES", "NO"] - "Who will lead the Ethereum Foundation starting August 2025?" with outcomes
["A", "B", "C"]
For this, they need to call the registerVerse function with the verse, the oracle that will resolve it, and the possible outcomes. For example:
mv.registerVerse(
"Will EIP-9999 be implemented before 2026?",
oracleAddress,
["YES", "NO"]
);Users can deposit collateral (eg. USDC tokens) into a verse by calling one of the two deposit variants.
The first variant allows for minting conditional tokens for each outcome separately:
mv.deposit(
USDC,
"Will EIP-9999 be implemented before 2026?",
100e6 // 100 USDC with 6 decimals
)
// This will mint 100 "YES" and 100 "NO" conditional tokens in the verse.The second variant allows for minting conditional tokens for a union of outcomes:
mv.deposit(
USDC,
"Who will lead the Ethereum Foundation starting August 2025?",
[["A", "B"], ["C"]], // "A or B" and "C" outcomes
100e6 // 100 USDC
)
// This will mint 100 "A or B" and 100 "C" conditional tokens in the verse.Note
All individual outcomes must be specified either as a single outcome or within a union of outcomes. In the example above, we couldn't just mint conditional tokens for "A or B" without minting "C" as well.
Users can push down ownership of tokens from a parent verse to a child verse. Child verses are the intersection of two parent verses, and parent verses can be intersections themselves, allowing for recursive structures.
mv.pushDownOwnership(
USDC,
[("Will EIP-9999 be implemented before 2026?", "YES")], // parent 1 (verse + outcome)
"Who will lead the Ethereum Foundation starting August 2025?", // parent 2 verse
[["A", "B"], ["C"]], // parent 2 outcomes
100e6 // 100 USDC
)The above will burn 100 "YES" tokens and mint 100 "YES and (A or B)" tokens and 100 "YES and C" tokens in the child verse. Note that the child verse intersects outcomes from both parent verses. For instance, the "YES and (A or B)" tokens will be redeemable for collateral only if both conditions are met: "EIP-9999 is implemented before 2026" and "A or B leads the Ethereum Foundation starting August 2025".
This is the reverse operation of the ownership push down described above. Users can pull up ownership of tokens from a child verse to a parent verse.
mv.pullUpOwnership(
USDC,
[("Will EIP-9999 be implemented before 2026?", "YES")],
"Who will lead the Ethereum Foundation starting August 2025?",
[["A", "B"], ["C"]],
100e6
)
// This will burn 100 "YES and (A or B)" and 100 "YES and C" tokens in the child verse,
// and mint 100 "YES" tokens in the parent verse.Oracles can resolve verses by providing a score for each outcome. Each score will be divided by the sum of all scores to determine the final payout distribution.
mv.resolveVerse(
"Will EIP-9999 be implemented before 2026?",
[7, 3]
)
// This will allow the "YES" holders to claim 70% (7 / (7 + 3)) of the collateral while
// "NO" holders will claim 30% (3 / (7 + 3)).If a verse is resolved, users can either pull up ownership to a parent verse or withdraw collateral directly if the resolved verse has no parents.
In the former case, the operation resembles the pullUpOwnership described above, but users do not need to specify the amount to pull up (as it will include all tokens held in the child verse) or the entire partition to pull from (they may select specific outcomes to pull up).
mv.pullUpResolvedVerse(
USDC,
[("Will EIP-9999 be implemented before 2026?", "YES")],
"Who will lead the Ethereum Foundation starting August 2025?", // verse to pull up from
[["C"]], // outcome to pull up from
)
// This will burn the full balance of "YES and C" tokens in the child verse, and mint
// an amount of "YES" tokens in the parent verse proportional to the resolved verse's
// outcome distribution (based on the scores provided during resolution).If the verse has no parents, users can withdraw collateral directly:
mv.withdraw(
USDC,
"Who will lead the Ethereum Foundation starting August 2025?", // verse to pull up from
[["C"]], // outcome to pull up from
)
// This will burn the full balance of "C" tokens in the verse and return the USDC
// collateral to the user, proportional to the resolved verse's outcome distribution.The MultiverseFinance contract simplifies the interaction with conditional tokens by focusing on verses and their outcomes, rather than the underlying complexities of the Gnosis Conditional Tokens Framework. In particular, the following enhancements have been made:
- Simplified Verse Management: Users can manage verses and outcomes directly in plain text, without needing to deal with complex IDs. In Gnosis CTF, users had to manage different types of IDs (collection IDs, condition IDs) and bit arrays for outcome partitions, making it singificantly difficult to understand and use.
- Less Reliance on Offchain Components: The contract does not require offchain storage of verse data, as all necessary information is stored onchain. This helps reduce reliance on external systems and guarantees that all verse data is always available and consistent. In Gnosis CTF, plain-text verses and outcomes never reached the onchain contract and oracle addresses were only emitted in events, requiring users to manage them offchain.
- Convenience Functions: Deposit and withdrawal functions were provided to further simplify the most common operations, providing a more semantic interface for users. In the CTF, these operations were represented as regular push down and pull up operations, respectively.
The main trade-off is that the gas costs for some operations may be higher due to the added layer of abstraction and the need to store more information onchain. Specifically, the push down and pull up operations now have a linear cost with respect to the number of verses and outcomes involved, which may limit the maximum number of verses that can be intersected in a single push down operation. However, this trade-off is deemed acceptable for the increased usability and clarity provided by the MultiverseFinance contract, the low gas costs on L2s, and the unlikely need for very complex verse structures in practice.