Aller au contenu principal

1.0 → 1.5 Migration Guide

Abstract: The Intuition API is being updated to support a number of exciting new features, and developers who are using the API will need to make some adjustments in their queries in order to continue using it. This document serves as a guide for that process.

Current Status: The Base Sepolia 1.5 backend is already live, and can be accessed here: https://prod.base-sepolia-v-1-5.intuition.sh/v1/graphql

Within the next couple weeks, the Mainnet version will come online, and the protocol itself will be upgraded to 1.5. Please ensure your app is working with the Base Sepolia version before that happens.

New Features:

  • Bonding Curve Vaults + Bonding Curve Registry
  • Improved Batch Methods
  • Improved Approval Methods for Deposit / Redeem and Batch Deposit / Batch Redeem
  • Improved Events and Data, such as Price History, P&L, Total Market Cap for Individual Vaults as well as Terms
  • Introduction of ‘Terms’, which refer to the underlying concept of an Atom or the underlying semantics of a Triple / Counter Triple
  • Introduction of multiple Vaults per Term (Pro Rata Vault which existed in 1.0 plus Bonding Curve Vaults)
  • Minor Security Improvements from Consensys Diligence audit
astuce

If you want to understand the changes in greater detail, continue reading on. If you just want to migrate your queries, skip to the Migration section below.

What changed, conceptually:

We have introduced the idea of a “Term”. A “Term” refers to an Atom, Triple or Counter-triple as a concept. The URI or Data of an Atom, or the Atom IDs contained within a Triple or Counter-Triple belong to the “Term”. Any economic state associated with the “Term” is stored in multiple “Vaults” associated with that Term. Each Vault stores a ledger of all shares awarded to various user wallet addresses, along with a totalShares and totalAssets value for the Vault.

Additionally, we have added a total_market_cap value for each Term which is an aggregate market cap of all its constituent Vaults. This total_market_cap is theoretical, in that it does not actually represent the totalAssets or an aggregation of them — it is the arithmetic product (multiplication result) of the totalShares and the currentSharePrice at any given point in time. The reason this is considered theoretical is because if all the shares were liquidated, slippage would cause the share price to change across the movement of shares. Generally, the total_market_cap will be larger than the actual totalAssets.

Terms (atoms, triple and counter-triples) now have multiple Vaults because we have introduced Bonding Curves to our protocol. The Bonding Curves create a much more compelling economic incentive for users to provide valuable signal as it grows in popularity. We will do a deep dive of the first Bonding Curve we added to our Bonding Curve Registry in another article — but for now you should know that our first recommended curve is the Offset Square Root Curve with a Slope of 2 and an Offset of 5e35. Smart contract nerds can view the source code here:

link_preview

Offset Sqrt Curve 2,5e35:

This curve follows a “square root” price function pattern, but utilizes an offset to make the early portion of the curve a bit “less dramatic”, so as not to severely disadvantage intermediate adopters to the curve vault. This means that the earliest stakers will be able to stake at the most advantageous share price, and that the share price will increase supralinearly (gently) as more shares are purchased. Users who purchased shares may redeem them at a much higher price once the share price has increased via the activity of other users.

This contrasts significantly with the 1.0 “Pro-Rata” model, where the share price was always “1”, adjusted by minute changes to the asset pool by collecting small fees on deposits and redeems. In the Pro-Rata model, it would take an incredibly high amount of economic activity for a user to make a profit from their shares, but the downside was minimized to be less than or equal to the fees themselves. In the Bonding Curve model, there is a greater risk of the share price diminishing, as well as a much greater upside of the share price increasing.

Bonding Curve Smart Contract interactions are explained in the Smart Contract section at the end of this article.

Other things that changed: We renamed a few things in the schema to be more accurately descriptive. Namely we renamed all instances of id which referenced an atom or triple to term_id. And we added term -> total_market_cap which is explained above.

Each vault also has a current_share_price which reflects the current cost of buying or selling 1 share, and it has a market_cap, similar to the total_market_cap, except just for the specific vault instead of the aggregation of all vaults in a term.

Migration:

Checklist for Developers:

Update Core Schema Changes in GraphQL Queries:

  • id → term_id (primary identifier)
  • vault → term (nested object) [if referring to concept]
  • vault → vaults(where: { curveid: { eq: “[X]” } })
    • Where [X] is 1, 2, 3, etc depending on selected Bonding Curve
  • total_shares → total_market_cap (in term object or in specific vault)
    • total_shares should not be compared across vaults with different Bonding Curves, as they have different price functions, and therefore the share value is asymmetrical. So use market_cap to compare vaults, or total_market_cap to compare terms.
  • If using claims, it’s suggested to start using positions instead, as the positions contain the same information and more — and claims are likely to be sunset in the near future.

Examples:

OLD SCHEMA:
query GetEntries($isTypeTypeId: numeric!, $entryTypeId: numeric!, $limit: Int!, $offset: Int!) {
triples {
subject {
id
value {
thing { ... }
}
vault {
total_shares
}
}
}
}

NEW SCHEMA:
query GetEntries($isTypeTypeId: numeric!, $entryTypeId: numeric!, $limit: Int!, $offset: Int!) {
triples {
subject {
term_id
atom_value {
thing { ... }
}
term {
total_market_cap
}
}
}
}
OLD SCHEMA:
query GetEntry($id: numeric!, $typePredicateId: numeric!, $entryTypeId: numeric!) {
atom(id: $id) {
id
value { ... }
}
}

NEW SCHEMA:
query GetEntry($term_id: numeric!, $typePredicateId: numeric!, $entryTypeId: numeric!) {
atom(term_id: $term_id) {
term_id
atom_value { ... }
}
}
OLD SCHEMA:
query SearchEntries {
atoms(where: {
value: { thing: { ... } }
}) {
id
value { ... }
vault { total_shares }
}
}

NEW SCHEMA:
query SearchEntries {
atoms(where: {
atom_value: { thing: { ... } }
}) {
term_id
atom_value { ... }
term { total_market_cap }
}
}
OLD SCHEMA:
query GetVault {
vault(where: {
id: { _eq: "42" }
}) {
total_shares
total_assets
}
}

NEW SCHEMA:
query GetVault {
vaults(where: {
curve_id: { _eq: "1" },
term_id: { _eq: "42" }
}) {
total_shares
total_assets
total_market_cap
}
}
OLD: order_by: [{ vault: { total_shares: desc } }]
NEW: order_by: [{ subject: { term: { total_market_cap: desc } }}]
DEVELOPMENT ENDPOINT
OLD: https://prod.base-sepolia.intuition-api.com/v1/graphql
NEW: https://prod.base-sepolia-v-1-5.intuition.sh/v1/graphql
BONUS 1.5 QUERY EXAMPLES (for brevity):

query GetProRataVaultsFromTriples {
triples(limit: 10, offset: 0) {
term_id
counter_term_id
term {
vaults(where: {curve_id: {_eq: "1"}}) {
current_share_price
position_count
total_shares
}
}
counter_term {
vaults(where: {curve_id: {_eq: "1"}}) {
current_share_price
position_count
total_shares
}
}
}
}

query GetPositionsInVaultWithSharePrice {
positions(
limit: 10
offset: 0
where: {term_id: {_eq: "1"}, curve_id: {_eq: "1"}}
) {
shares
account_id
curve_id
term_id
vault {
current_share_price
}
}
}

query GetAtomVaultTotalsProRata {
atoms {
term {
vaults(where: {curve_id: {_eq: "1"}}) {
market_cap
total_assets
curve_id
term_id
total_shares
}
}
}
}

query GetDepositRedeemHistoryForBondingCurveVault {
deposits(
order_by: {block_timestamp: asc}
limit: 10
offset: 0
where: {term_id: {_eq: "34"}, curve_id: {_eq: "4"}}
) {
curve_id
term_id
shares_for_receiver
sender_assets_after_total_fees
transaction_hash
}
redemptions(
order_by: {block_timestamp: asc},
limit: 10,
offset: 0,
where: {term_id: {_eq: "34"}, curve_id: {_eq: "4"}}
) {
curve_id
term_id
assets_for_receiver
shares_redeemed_by_sender
transaction_hash
}
}

query GetAllTermsData {
terms(order_by: {id: asc}, limit: 10, offset: 0) {
atom {
data
term_id
}
triple {
subject_id
object_id
predicate_id
term_id
}
}
}

query GetAllVaultTotals {
vaults(order_by: {term_id: asc, curve_id: asc}, limit: 10, offset: 0) {
total_assets
total_shares
market_cap
term_id
curve_id
}
}

When in doubt: Go to The Hasura Cloud Dashboard

Look at the schema in the explorer and try to re-create the query which is failing in the application. After reading this guide it should become pretty clear how things have been renamed. Once you’ve re-created your query using the explorer, you can update the query in your code to match.

If there are any queries in your application which you are uncertain about or unsure how to migrate, reach out to us in Discord and we will help out as quickly as we can.

Section 2: If you are making RPC Calls

We have added quite a bit of stuff to the 1.5 EthMultiVault contract. Bonding curves, batch methods, a more nuanced approval mechanism for both deposit and redeem. All of the old methods from 1.0 still exist, so there should be no breaking changes here — except if you are using the entire ABI.JSON file to interface with the contract, you will need the latest one:

EthMultiVault.json

file

If you prefer to use an ABI which is defined in Typescript, you can use the abi.ts file attached above. Or you can skip this altogether and just use ABI fragments for the methods you need to interact with.

You will notice that all Bonding Curve variants of the methods in the 1.5 EthMultiVault have the same name as their non-bonding-curve predecessors, except the word ‘curve’ is typically appended at the end. To interact with the Pro-Rata vaults which were already used in 1.0, use the same functions as before. To interact with the curves, invoke the curve functions and specify a curve ID. The Offset Sqrt 2,5e35 curve ID is “4”.

The 1.5 EthMultiVault will live at the same contract address as the 1.0 EthMultiVault, once the proxy upgrade is performed on the protocol. There is currently a “dummy” version of it deployed to Base Sepolia here for testing: 0x63B90A9c109fF8f137916026876171ffeEdEe714

The new Batch methods should be fairly self explanatory — you can deposit or redeem in multiple terms in a single transaction. It’s worth noting, however, that using the new Approval mechanism, and approved caller can now Deposit, Redeem, or Both on a users behalf. This unlocks a lot of new UX where the contract interactions are abstracted away from the user.

New Bonding Curve Mutators

/// @notice deposit eth into an atom vault and grant ownership of 'shares' to 'reciever'    
/// *payable msg.value amount of eth to deposit
/// @dev assets parameter is omitted in favor of msg.value, unlike in ERC4626 ///
/// @param receiver the address to receive the shares
/// @param atomId the vault ID of the atom
/// @param curveId the vault ID of the curve
///
/// @return shares the amount of shares minted
/// NOTE: this function will revert if the minimum deposit amount of eth is not met and
/// if the vault ID does not exist/is not an atom.
function depositAtomCurve(address receiver, uint256 atomId, uint256 curveId) external payable returns (uint256);

/// @notice redeem shares from a bonding curve atom vault for assets
///
/// @param shares the amount of shares to redeem
/// @param receiver the address to receiver the assets
/// @param atomId the vault ID of the atom
/// @param curveId the vault ID of the curve
///
/// @return assets the amount of assets/eth withdrawn
/// NOTE: Emergency redemptions without any fees being charged are always possible, even if the contract is paused
/// See `getRedeemAssetsAndFees` for more details on the fees charged
function redeemAtomCurve(uint256 shares, address receiver, uint256 atomId, uint256 curveId) external returns (uint256);

/// @notice deposit eth into a bonding curve triple vault and grant ownership of 'shares' to 'receiver'
/// *payable msg.value amount of eth to deposit
/// @dev assets parameter is omitted in favor of msg.value, unlike in ERC4626 ///
/// @param receiver the address to receive the shares
/// @param tripleId the vault ID of the triple
/// @param curveId the vault ID of the curve
///
/// @return shares the amount of shares minted
/// NOTE: this function will revert if the minimum deposit amount of eth is not met and
/// if the vault ID does not exist/is not a triple.
function depositTripleCurve(address receiver, uint256 tripleId, uint256 curveId) external payable returns (uint256);

/// @notice redeem shares from a bonding curve triple vault for assets
///
/// @param shares the amount of shares to redeem
/// @param receiver the address to receiver the assets
/// @param tripleId the vault ID of the triple
/// @param curveId the vault ID of the curve
///
/// @return assets the amount of assets/eth withdrawn
/// NOTE: Emergency redemptions without any fees being charged are always possible, even if the contract is paused
/// See `getRedeemAssetsAndFees` for more details on the fees charged
function redeemTripleCurve(uint256 shares, address receiver, uint256 tripleId, uint256 curveId) external returns (uint256);

New Bonding Curve Accessors

/// @notice returns the number of shares and assets (less fees) user has in the vault for a specific curve    
///
/// @param vaultId vault id of the vault
/// @param curveId curve id of the curve
/// @param receiver address of the receiver
///
/// @return shares number of shares user has in the vault
function getVaultStateForUserCurve(uint256 vaultId, uint256 curveId, address receiver) external view returns (uint256, uint256);

/// @notice returns the shares for recipient and other important values when depositing 'assets' into a bonding curve vault
///
/// @param assets amount of `assets` to calculate fees on (should always be msg.value - protocolFee)
/// @param vaultId vault id to get corresponding fees for
/// @param curveId curve id to get corresponding fees for
///
/// @return totalAssetsDelta changes in vault's total assets
/// @return sharesForReceiver changes in vault's total shares (shares owed to receiver)
/// @return userAssetsAfterTotalFees amount of assets that goes towards minting shares for the receiver
/// @return entryFee amount of assets that would be charged for the entry fee
function getDepositSharesAndFeesCurve(uint256 assets, uint256 vaultId, uint256 curveId) external view returns (uint256, uint256, uint256, uint256);

/// @notice returns the assets for receiver and other important values when redeeming 'shares' from a bonding curve vault
///
/// @param shares amount of `shares` to calculate fees on
/// @param vaultId vault id to get corresponding fees for
/// @param curveId curve id to get corresponding fees for
///
/// @return totalUserAssets total amount of assets user would receive if redeeming 'shares', not including fees
/// @return assetsForReceiver amount of assets that is redeemable by the receiver
/// @return protocolFee amount of assets that would be sent to the protocol multisig
/// @return exitFee amount of assets that would be charged for the exit fee
function getRedeemAssetsAndFeesCurve(uint256 shares, uint256 vaultId, uint256 curveId) external view returns (uint256, uint256, uint256, uint256);

/// @notice returns the current share price for the given vault id and curve id
/// @param vaultId vault id to get corresponding share price for
/// @param curveId curve id to get corresponding share price for
/// @return price current share price for the given vault id and curve id
function currentSharePriceCurve(uint256 vaultId, uint256 curveId) external view returns (uint256);

/// @notice returns max amount of assets that can be deposited into the vault through a deposit call for a specific curve
///
/// @param curveId curve id to get corresponding max deposit for
///
/// @return maxAssets amount of assets that can be deposited into the vault through a deposit call
function maxDepositCurve(uint256 curveId) external view returns (uint256);

/// @notice returns max amount of shares that can be redeemed from the 'owner' balance through a redeem call for a specific curve
///
/// @param owner address of the account to get max redeemable shares for
/// @param vaultId vault id to get corresponding shares for
/// @param curveId curve id to get corresponding shares for
///
/// @return shares amount of shares that can be redeemed from the 'owner' balance through a redeem call
function maxRedeemCurve(address owner, uint256 vaultId, uint256 curveId) external view returns (uint256);

/// @notice returns amount of shares that would be exchanged by vault given amount of 'assets' provided for a specific curve
///
/// @param assets amount of assets to calculate shares on
/// @param vaultId vault id to get corresponding shares for
/// @param curveId curve id to get corresponding shares for
///
/// @return shares amount of shares that would be exchanged by vault given amount of 'assets' provided
function convertToSharesCurve(uint256 assets, uint256 vaultId, uint256 curveId) external view returns (uint256);

/// @notice returns amount of assets that would be exchanged by vault given amount of 'shares' provided for a specific curve
///
/// @param shares amount of shares to calculate assets on
/// @param vaultId vault id to get corresponding assets for
/// @param curveId curve id to get corresponding assets for
///
/// @return assets amount of assets that would be exchanged by vault given amount of 'shares' provided
function convertToAssetsCurve(uint256 shares, uint256 vaultId, uint256 curveId) external view returns (uint256);

/// @notice simulates the effects of the deposited amount of 'assets' and returns the estimated
/// amount of shares that would be minted from the deposit of `assets` for a specific curve
///
/// @param assets amount of assets to calculate shares on
/// @param vaultId vault id to get corresponding shares for
/// @param curveId curve id to get corresponding shares for
///
/// @return shares amount of shares that would be minted from the deposit of `assets`
/// NOTE: this function pessimistically estimates the amount of shares that would be minted from the
/// input amount of assets so if the vault is empty before the deposit the caller receives more
/// shares than returned by this function, reference internal _depositIntoVault logic for details
function previewDepositCurve(uint256 assets, uint256 vaultId, uint256 curveId) external view returns (uint256);

/// @notice simulates the effects of the redemption of `shares` and returns the estimated
/// amount of assets estimated to be returned to the receiver of the redeem for a specific curve
///
/// @param shares amount of shares to calculate assets on
/// @param vaultId vault id to get corresponding assets for
/// @param curveId curve id to get corresponding assets for
///
/// @return assets amount of assets estimated to be returned to the receiver
function previewRedeemCurve(uint256 shares, uint256 vaultId, uint256 curveId) external view returns (uint256);

Updated Approval Method

/// @notice Set the approval type for a sender to act on behalf of the receiver  /// @param sender address to set approval for    
/// @param approvalType type of approval to grant (NONE = 0, DEPOSIT = 1, REDEMPTION = 2, BOTH = 3)
function approve(address sender, ApprovalTypes approvalType) external;

Batch Methods

/// @notice Batch create atoms and return their vault ids    
/// @param atomUris atom data array to create atoms with
/// @return ids vault ids array of the atoms
/// NOTE: This function will revert if called with less than `getAtomCost()` * `atomUris.length` in `msg.value`
function batchCreateAtom(bytes[] calldata atomUris) external payable returns (uint256[] memory);

/// @notice batch create triples and return their vault ids
/// @param subjectIds vault ids array of subject atoms
/// @param predicateIds vault ids array of predicate atoms
/// @param objectIds vault ids array of object atoms
/// NOTE: This function will revert if called with less than `getTripleCost()` * `array.length` in `msg.value`.
/// This function will revert if any of the atoms do not exist or if any ids are triple vaults.
function batchCreateTriple(uint256[] calldata subjectIds, uint256[] calldata predicateIds, uint256[] calldata objectIds) external payable returns (uint256[] memory);

/// @notice deposit eth into multiple terms and grant ownership of 'shares' to 'reciever'
/// *payable msg.value amount of eth to deposit
/// works with atoms, triples, and counter-triples
///
/// @param receiver the address to receive the shares
/// @param termIds the IDs of the terms (atoms, triples, or counter-triples) to deposit into
/// @param amounts array of the amount to deposit in each vault
///
/// @return shares the amount of shares minted for each atom
function batchDeposit(address receiver, uint256[] calldata termIds, uint256[] calldata amounts) external payable returns (uint256[] memory);

/// @notice deposit eth into an atom vault and grant ownership of 'shares' to 'reciever'
/// *payable msg.value amount of eth to deposit
///
/// @param receiver the address to receive the shares
/// @param termIds array of the vault IDs of the terms (atoms, triples, or counter-triples)
/// @param curveIds array of the vault IDs of the curves
/// @param amounts array of the amount to deposit in each vault
///
/// @return shares array of the amount of shares minted in the specified vaults
function batchDepositCurve(address receiver, uint256[] calldata termIds, uint256[] calldata curveIds, uint256[] calldata amounts) external payable returns (uint256[] memory);

/// @notice redeem shares from an atom vault for assets -- works for atoms, triples and counter-triples
///
/// @param percentage the percentage of shares to redeem from each vault (i.e. 50% -> 50, 100% -> 100)
/// @param receiver the address to receiver the assets
/// @param ids array of IDs of the term (atom, triple or counter-triple) to redeem from
///
/// @return assets the amount of assets/eth withdrawn
function batchRedeem(uint256 percentage, address receiver, uint256[] calldata ids) external returns (uint256[] memory);

/// @notice redeem shares from bonding curve atom vaults for assets
///
/// @param percentage the percentage of shares to redeem from the vaults
/// @param receiver the address to receiver the assets
/// @param termIds array of the IDs of the terms (atoms, triples, or counter-triples)
/// @param curveIds array of the IDs of the curves for each term
///
/// @return assets array of the amounts of assets/eth withdrawn
function batchRedeemCurve(uint256 percentage, address receiver, uint256[] calldata termIds, uint256[] calldata curveIds) external returns (uint256[] memory);
attention

It’s worth noting that the Dummy 1.5 contract does not contain the same dataset as the 1.0 contract on Base Sepolia. This means that if your app relies on specific data, you may need to add it to the Dummy 1.5 contract for testing.

Once the contract upgrade is completed, the 1.5 contract will have the same data set as the 1.0 version — and will live at the same contract address as the 1.0 version.