This proposal introduces a standard for embedding Bonding Curve-like liquidity into
Non-Fungible Tokens (NFTs) without modifying the ERC-721 standard.
The proposed standard allows the attachment of an embedded liquidity contract, referred to as Tradable Shares,
to an ERC-721 NFT. Tradable Shares leverage a Bonding Curve-like approach to attract liquidity, enabling trading of
shares based on the bonding curve price formula.
Motivation
The ERC-721 standard lacks a specific mechanism for embedding bonding curve-based liquidity, limiting the creative
possibilities for NFT-based projects. This EIP addresses the need for a standardized approach to integrate bonding curve
contracts seamlessly into ERC-721 NFTs, allowing for diverse and innovative implementations without modifying the
ERC-721 standard.
The proposed standard focuses on enhancing the ERC-721 standard by introducing a framework for embedding bonding
curve-based liquidity into NFTs. This approach provides creators with a flexible and customizable tool to attract
liquidity through bonding curve mechanisms, while ensuring creators receive guaranteed fees for their contributions.
The bonding curve-embedded liquidity for NFTs standard finds compelling use cases across diverse industries, offering a
dynamic solution for embedding Bonding Curve-like liquidity into NFTs. One prominent use case revolves around the
intersection of AI services, where NFTs model AI models, GPU resource pools, and storage resource pools. Let’s explore
two specific use cases within this domain:
AI Model Marketplace:
NFTs representing AI models leverage the embedded liquidity standard to embed Bonding Curve-like liquidity.
AI model providers attach Tradable Shares contracts to their NFTs, enabling a seamless integration of liquidity
features without modifying the ERC-721 standard.
The Bonding Curve mechanism allows the pricing of shares (or keys) based on the AI model’s supply and demand.
As AI models gain popularity or demonstrate superior performance, liquidity providers are incentivized to buy and
sell shares, fostering a competitive marketplace.
Creators can customize bonding curve parameters, such as slope and intercept, tailoring the liquidity mechanism to
match the evolving nature of AI models. This ensures a fair and adaptive marketplace where liquidity providers are
attracted to promising AI models, thereby creating a symbiotic relationship between liquidity and AI innovation.
Decentralized GPU and Storage Resource Allocation:
In a decentralized ecosystem, GPU and storage resource pools are represented as NFTs with embedded Tradable Shares
contracts. This enables resource providers to attract liquidity and compete for resource allocations based on the
Bonding Curve mechanism.
The Bonding Curve determines the price of shares associated with GPU and storage resources, reflecting the current
supply and demand. Providers can customize bonding curve parameters to optimize their resource pool’s
attractiveness, taking into account factors like available resources, performance metrics, and historical usage.
Guaranteed creative fees incentivize resource providers to continually enhance and optimize their services.
As the demand for GPU and storage resources evolves, the embedded liquidity standard ensures that providers
receive fair compensation for their contributions, maintaining a competitive and responsive marketplace.
In both use cases, the standard serves as a powerful incentive for providers to attract and retain liquidity.
The dynamic nature of the Bonding Curve-like mechanism aligns with the evolving landscape of AI models and resource
pools, fostering innovation, competition, and liquidity-driven growth within the decentralized AI services domain.
Specification
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”,
“NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119 and RFC 8174.
An embedded bonding curve-based liquidity SHOULD be attached to the NFT via a separate contract.
An embedded bonding curve-based liquidity MUST NOT be embedded into or modify the ERC-721 standard.
The bonding curve contract MUST manage the liquidity of the associated NFT through a bonding curve mechanism.
Bonding Curve Mechanism:
The bonding curve determines the price of the NFT “keys” (sometimes also referred to as “shares”) in relation to
its supply, encouraging liquidity providers to buy and sell NFT shares based on the curve’s formula.
Implementation MAY allow the creators to customize the bonding curve parameters, such as slope, intercept,
or any other relevant parameters.
Implementation MAY allow the creators to customize the shape of the bonding curve (the curve’s formula).
Guaranteed Creative Fees:
The implementation MUST include the mechanisms that guarantee creative fees for NFT creators, that is
it MUST guarantee the creators receive a percentage of transaction fees generated by the embedded liquidity
contract during buy and sell operations.
The implementation MAY allow the creators to defne the transaction fees.
Payment Mechanisms:
The embedded liquidity contract MUST support either ERC-20 tokens or native ETH as a payment,
it MAY support both.
BondingCurve Interface
/**
* @title Bonding Curve
*
* @notice A bonding curve definition
*
* @notice Bonding curve defines the price of the smallest unit of the asset as a function
* of the asset supply
*/
interface BondingCurve {
/**
* @notice Bonding curve function definition. The function calculating the price
* of the `amount` of shares given the current total supply `supply`
*
* @param supply total shares supply
* @param amount number of shares to buy/sell
* @return the price of the shares (all `amount` amount)
*/
function getPrice(uint256 supply, uint256 amount) external pure returns(uint256);
}
/**
* @title Tradeable Shares
*
* @notice Tradeable shares is a non-transferable, but buyable/sellable fungible token-like asset,
* which is sold/bought solely by the shares contract at the predefined by
* the bonding curve function price
*
* @notice The shares is bound to its "subject" – an NFT; the NFT owner gets the subject fee
* emerging in every buy/sell operation
*/
interface TradeableShares is BondingCurve {
/**
* @notice Shares subject is an NFT defined by its ERC-721 contract address and NFT ID
* Shares subject is an NFT the liquidity is embedded to
*/
struct SharesSubject {
/// @dev ERC-721 contract address
address tokenAddress;
/// @dev NFT ID
uint256 tokenId;
}
/**
* @dev Fired in `buyShares` and `sellShares` functions, this event logs
* the entire trading activity happening on the curve
*
* @dev Trader, that is the buyer or seller, depending on the operation type is the transaction sender
*
* @param beneficiary the address which receives shares or funds, usually this is the trader itself
* @param issuer subject issuer, usually an owner of the NFT defined by the subject
* @param isBuy true if the event comes from the `buyShares` and represents the buy operation,
* false if the event comes from the `sellShares` and represents the sell operation
* @param sharesAmount amount of the shares bought or sold (see `isBuy`)
* @param paidAmount amount of ETH spent or gained by the buyer or seller;
* this is implementation dependent and can represent an amount of ERC-20 payment token
* @param feeAmount amount of all the fees paid, if any
* @param supply total shares supply after the operation
*/
event Trade(
address indexed beneficiary,
address indexed issuer,
bool indexed isBuy,
uint256 sharesAmount,
uint256 paidAmount,
uint256 feeAmount,
uint256 supply
);
/**
* @notice Shares subject, usually defined as NFT (ERC-721 contract address + NFT ID)
*
* @dev Immutable, client applications may cache this value
*
* @return Shares subject as a SharesSubject struct, this is an NFT if all currently known implementations
*/
function getSharesSubject() external view returns(SharesSubject calldata);
/**
* @notice Cumulative fee percent, applied to all the buy and sell operations;
* the fee percent is defined with the 18 decimals, 10^18 corresponds to 100%
*
* @notice The fee can be combined from multiple fees which are sent to the various destinations
*
* @dev Immutable, client applications may cache this value
*
* @return protocol fee percent with the 18 decimals (10^18 is 100%)
*/
function getFeePercent() external view returns(uint256);
/**
* @notice Shares issuer, the receiver of the shares fees
*
* @dev Mutable, changes (potentially frequently and unpredictably) when the NFT owner changes;
* subject to the front-run attacks, off-chain client applications must not rely on this address
* in anyway
*
* @return nftOwner subject issuer, the owner of the NFT
*/
function getSharesIssuer() external view returns(address nftOwner);
/**
* @notice Shares balance of the given holder; this function is similar to ERC20.balanceOf()
*
* @param holder the address to check the balance for
*
* @return balance number of shares the holder has
*/
function getSharesBalance(address holder) external view returns(uint256 balance);
/**
* @notice Total amount of the shares in existence, the sum of all individual shares balances;
* this function is similar to ERC20.totalSupply()
*
* @return supply total shares supply
*/
function getSharesSupply() external view returns(uint256 supply);
/**
* @notice The price of the `amount` of shares to buy calculated based on
* the specified total shares supply
*
* @param supply total shares supply
* @param amount number of shares to buy
* @return the price of the shares to buy
*/
function getBuyPrice(uint256 supply, uint256 amount) external pure returns(uint256);
/**
* @notice The price of the `amount` of shares to sell calculated based on
* the specified total shares supply
*
* @param supply total shares supply
* @param amount number of shares to sell
* @return the price of the shares to sell
*/
function getSellPrice(uint256 supply, uint256 amount) external pure returns(uint256);
/**
* @notice The price of the `amount` of shares to buy, including all fees;
* calculated based on the specified total shares supply and fees percentages
*
* @param supply total shares supply
* @param amount number of shares to buy
* @param protocolFeePercent protocol fee percent
* @param holdersFeePercent shares holders fee percent
* @param subjectFeePercent protocol fee percent
* @return the price of the shares to buy
*/
function getBuyPriceAfterFee(
uint256 supply,
uint256 amount,
uint256 protocolFeePercent,
uint256 holdersFeePercent,
uint256 subjectFeePercent
) external pure returns(uint256);
/**
* @notice The price of the `amount` of shares to sell, including all fees;
* calculated based on the specified total shares supply and fees percentages
*
* @param supply total shares supply
* @param amount number of shares to sell
* @param protocolFeePercent protocol fee percent
* @param holdersFeePercent shares holders fee percent
* @param subjectFeePercent protocol fee percent
* @return the price of the shares to sell
*/
function getSellPriceAfterFee(
uint256 supply,
uint256 amount,
uint256 protocolFeePercent,
uint256 holdersFeePercent,
uint256 subjectFeePercent
) external pure returns(uint256);
/**
* @notice Current price of the `amount` of shares to buy; calculated based on
* the current total shares supply
*
* @param amount number of shares to buy
* @return the price of the shares to buy
*/
function getBuyPrice(uint256 amount) external view returns(uint256);
/**
* @notice Current price of the `amount` of shares to sell; calculated based on
* the current total shares supply
*
* @param amount number of shares to sell
* @return the price of the shares to sell
*/
function getSellPrice(uint256 amount) external view returns(uint256);
/**
* @notice Current price of the `amount` of shares to buy, including all fees;
* calculated based on the current total shares supply and fees percentages
*
* @param amount number of shares to buy
* @return the price of the shares to buy
*/
function getBuyPriceAfterFee(uint256 amount) external view returns(uint256);
/**
* @notice Current price of the `amount` of shares to sell, including all fees;
* calculated based on the current total shares supply and fees percentages
*
* @param amount number of shares to sell
* @return the price of the shares to sell
*/
function getSellPriceAfterFee(uint256 amount) external view returns(uint256);
/**
* @notice Buy `amount` of shares. Sender has to supply `getBuyPriceAfterFee(amount)` ETH.
* First share can be bought only by current subject issuer.
*
* @dev Depending on the implementation, ERC-20 token payment may be required instead of ETH.
* In such a case, implementation must through if ETH is sent, effectively overriding
* the function definition as non-payable
*
* @param amount amount of the shares to buy
*/
function buyShares(uint256 amount) external payable;
/**
* @notice Buy `amount` of shares in the favor of the address specified (beneficiary).
* Sender has to supply `getBuyPriceAfterFee(amount)` ETH.
* First share can be bought only by current subject issuer.
*
* @dev Depending on the implementation, ERC-20 token payment may be required instead of ETH.
* In such a case, implementation must through if ETH is sent, effectively overriding
* the function definition as non-payable
*
* @param amount amount of the shares to buy
* @param beneficiary an address receiving the shares
*/
function buySharesTo(uint256 amount, address beneficiary) external payable;
/**
* @notice Sell `amount` of shares. Sender gets `getSellPriceAfterFee(amount)` of ETH.
* Last share cannot be sold.
*
* @dev Depending on the implementation, ERC-20 token may be payed instead of ETH.
*
* @param amount amount of the shares to sell
*/
function sellShares(uint256 amount) external;
/**
* @notice Sell `amount` of shares in the favor of the address specified (beneficiary).
* The beneficiary gets `getSellPriceAfterFee(amount)` of ETH.
* Last share cannot be sold.
*
* @dev Depending on the implementation, ERC-20 token may be payed instead of ETH.
*
* @param amount amount of the shares to sell
* @param beneficiary an address receiving the funds from the sale
*/
function sellSharesTo(uint256 amount, address payable beneficiary) external;
/**
* @notice Cumulative value of all trades; allows to derive cumulative fees paid
*
* @dev This value cannot decrease over time; it can increase or remain constant
* if no trades are happening
*
* @return Sum of the modulo of all trading operations
*/
function getTradeVolume() external view returns(uint256);
Rationale
The rationale behind the design choices for the embedded liquidity standard is deeply rooted in providing a robust and
versatile framework for embedding Bonding Curve-like liquidity into NFTs. The following key considerations have
influenced the technical decisions:
Seamless Integration: The decision to allow an embedded bonding curve-based liquidity contract to be attached
to an NFT without altering the ERC-721 standard stems from the desire for seamless integration.
This approach ensures that NFT developers can enhance their creations with liquidity mechanisms without
introducing complexities or requiring modifications to the widely adopted ERC-721 standard.
Liquidity Management: The bonding curve contract’s role in managing liquidity through the bonding curve
mechanism is essential. This design choice facilitates a dynamic and automated pricing model based on supply
and demand, contributing to the overall liquidity and tradability of NFT shares.
Bonding Curve Mechanism:
Dynamic Pricing: The adoption of a bonding curve mechanism to determine the price of Tradable Shares aligns
with the goal of encouraging liquidity providers to engage in buying and selling NFT shares.
The dynamic pricing, influenced by the curve’s formula, ensures that the market for Tradable Shares remains
responsive to changing conditions.
Customization for Creators: The decision to allow creators to customize bonding curve parameters, such as
slope and intercept, empowers them to tailor the liquidity mechanism to the unique needs and characteristics of
their projects. This customization fosters creativity and innovation within the NFT space.
Guaranteed Creative Fees:
Creator Incentives: The emphasis on guaranteeing creative fees for NFT creators is foundational to sustaining
a thriving ecosystem. By enabling creators to specify and receive a percentage of transaction fees, the standard
aligns incentives and rewards creators for their contributions, fostering a sustainable and creator-friendly
environment.
Payment Mechanisms:
Developer Freedom: The standard’s implementation-agnostic approach is motivated by the desire to provide
developers with the freedom to choose and design the most suitable liquidity mechanism for their NFT projects.
Whether interacting with ERC-20 tokens or native ETH, this independence ensures that developers can make
informed choices based on the specific requirements of their projects.
The rationale behind these design choices is to create a Tradable Shares standard that is not only technically sound but
also flexible, adaptable, and supportive of diverse and creative implementations within the ERC-721 ecosystem.
See also: Bonded Fungible Tokens (1671)
Security Considerations
Smart Contract Security: Implementations of smart contracts should undergo thorough security audits to ensure
resistance against vulnerabilities and attacks.
Creative Fee Handling: Mechanisms for handling and distributing creative fees should be secure and transparent to
prevent any malicious activities.
Compatibility: Developers should ensure compatibility with existing ERC-721 implementations, allowing for a smooth
integration of the embedded liquidity standard.
User Experience: Considerations should be made to maintain a positive user experience, avoiding complexities that
may hinder the adoption of NFT projects utilizing embedded liquidity.
This security considerations section reflects the importance of anticipating and addressing potential security
challenges in the implementation, ensuring its robustness, compatibility, and user-friendly nature.