Abstract
The Flow ecosystem is experiencing continued growth and with this expansion comes a need to expand the DeFi ecosystem on the Flow blockchain. Expansion of DeFi on Flow requires new liquidity - typically through cross-chain bridges - as well as straightforward integration for wallets and developers to provide a simple and intuitive user experience. While the macro topic of liquidity is beyond the scope of this discussion, the initial focus of the nascent standard aims to enable ease of interoperability with, and composability of, DEXs which are a cornerstone concept serving as gateways to any DeFi ecosystem.
To that end we would like to commence the discussion focusing on a pool-based DEX standard on the Flow blockchain. We intentionally defer discussion on order-book based DEXs and how that may relate to the standard for a subsequent iteration.
The Flow blockchain already has several DEXs live on mainnet, namely Increment.Fi and Blocto swap, whose feedback on a future standard will help define what makes sense for the community.
Rationale
Drawing from observations in other ecosystems, itâs typical to leverage wrapper contracts or adapters to achieve interoperability across different DEXs in smart contract protocols, particularly in lending/borrowing protocols. The reasons for this are partly a lack of common standards and the gradual evolution of DeFi, as well as the influence of the address-centric security model in the resulting design thinking. The complexity this creates for developers when integrating DEXs into applications is a key motivating reason to avoid the same fragmentation on Flow from a lack of standards. It is our belief that establishing a minimal standard interface to simplify integration with DEXs provides the best experience for developers and the users, while also offering true composability without the complexity and compromises observed in other DeFi ecosystems.
Requirements and key user scenarios
The following requirements have been identified following initial discussions, high level prototyping, and analysis of existing DeFi protocols within the FLOW ecosystem, such as Increment.Fi and Blocto Swap.
DEX Swap
Requirements -
- [ ] Alice as a user can easily swap token A from token B.
- [ ] Alice as a user can perform quick swaps where funds are provided in the form of vault.
- [ ] Alice as a user can provide minimum expected amount from a swap to safeguard herself from high slippage.
- [ ] Trades should expire after a certain period of time.
An early draft swap interface version -
pub resource interface ImmediateSwap {
/// @notice It will Swap the source token for to target token
///
/// If the user wants to swap USDC to FLOW then the
/// sourceToTargetTokenPath is [Type<FLOW>] and
/// USDC would be the source token
///
/// Necessary constraints
/// - For the given source vault balance, Swapped target token amount should be
/// greater than or equal to minimumTargetTokenOut, otherwise swap would fail
/// - If the swap settlement time i.e getCurrentBlock().timestamp is less than or
/// equal to the provided expiry then the swap would fail
/// - Provided `recipient` capability should be valid otherwise the swap would fail
/// - If the provided path doesnât exists then the swap would fail.
pub fun swapExactSourceToTargetTokenUsingPath(
sourceToTargetTokenPath: Type[],
sourceVault: @FungibleToken.Vault,
minimumTargetTokenOut: UFix64,
expiry: UInt64,
recipient: Capability<&{FungibleToken.Receiver}>
)
/// @notice It will Swap the source token for to target token and return the target token vault
///
/// If the user wants to swap USDC to FLOW then the
/// sourceToTargetTokenPath is [Type<FLOW>] and
/// USDC would be the source token
///
/// Necessary constraints
/// - For the given source vault balance, Swapped target token amount should be
/// greater than or equal to minimumTargetTokenOut, otherwise swap would fail
/// - If the swap settlement time i.e getCurrentBlock().timestamp is less than or equal to the provided expiry then the swap would fail
/// - If the provided path doesnât exists then the swap would fail.
pub fun swapAndReturnExactSourceToTargetTokenUsingPath(
sourceToTargetTokenPath: Type[],
sourceVault: @FungibleToken.Vault,
minimumTargetTokenOut: UFix64,
expiry: UInt64
): @FungibleToken.Vault
/// @notice It will Swap the source token for to target token
///
/// Necessary constraints
/// - For the given source vault balance, Swapped target token amount should be
/// greater than or equal to minimumTargetTokenOut, otherwise swap would fail
/// - If the swap settlement time i.e getCurrentBlock().timestamp is less than or
/// equal to the provided expiry then the swap would fail
/// - Provided `recipient` capability should be valid otherwise the swap would fail
/// - If the provided path doesnât exists then the swap would fail.
pub fun swapExactSourceToTargetToken(
targetToken: Type,
sourceVault: @FungibleToken.Vault,
minimumTargetTokenOut: UFix64,
expiry: UInt64,
recipient: Capability<&{FungibleToken.Receiver}>
)
/// @notice It will Swap the source token for to target token and return the target token vault
///
/// Necessary constraints
/// - For the given source vault balance, Swapped target token amount should be
/// greater than or equal to minimumTargetTokenOut, otherwise swap would fail
/// - If the swap settlement time i.e getCurrentBlock().timestamp is less than or equal to the provided expiry then the swap would fail
/// - If the provided path doesnât exists then the swap would fail.
pub fun swapAndReturnExactSourceToTargetToken(
targetToken: Type,
sourceVault: @FungibleToken.Vault,
minimumTargetTokenOut: UFix64,
expiry: UInt64
): @FungibleToken.Vault
}
Fetch Token Prices
Requirements -
- [ ] Alice as a user needs to know how much amount of target token she gets if she provide x amount of source token.
- [ ] Alice as a user needs to know how much amount of source token she needs to get x amount of target token.
- [ ] For the DEX developers it is very important to have optimal routing to provide best quotation for its users, It is not possible to do so without providing the optimal path that gets computed off-chain because of various factors like liquidity is scattered and not concentrated to different pool, direct liquidity pair doesnât exists etc.
- Challenges - It is not possible to compute the optimal route onchain efficiently.
Assumptions -
- End user doesnât care about the fees, As it only needs to know how much it get if it provides x amount of source token.
- Calculated quotation is valid only at the time of calculation, There is no guarantee that user would get the same result after submission of the same values because there are chances of having change in pool values after the quotation calculation.
Initial DEX standard proposal draft -
pub resource interface ImmediateSwapQuotation {
/// @notice Provides the quotation of the target token amount for the
/// corresponding provided sell amount i.e amount of source tokens.
///
/// If the source to target token path doesn't exists then below function
/// would return `nil`.
/// Below function would return the quoted amount after deduction of the fees.
///
/// If the sourceToTargetTokenPath is [Type<FLOW>, Type<BLOCTO>].
/// Where sourceToTargetTokenPath[0] is the source token while
/// sourceToTargetTokenPath[sourceToTargetTokenPath.length -1] is
/// target token. i.e. FLOW and BLOCTO respectively.
///
/// @param sourceToTargetTokenPath Offchain computed optimal path from
/// source token to target token.
/// @param sellAmount Amount of source token user wants to sell to buy target token.
/// @return Amount of target token user would get after selling `sellAmount`.
///
pub fun getExactSellQuoteUsingPath(
sourceToTargetTokenPath: Type[],
sellAmount: UFix64
): UFix64?
/// @notice Provides the quotation of the source token amount if user wants to
/// buy provided buyAmount, i.e. amount of target token.
///
/// If the source to target token path doesn't exists then below function
/// would return `nil`.
/// Below function would return the quoted amount after deduction of the fees.
///
/// If the sourceToTargetTokenPath is [Type<FLOW>, Type<BLOCTO>].
/// Where sourceToTargetTokenPath[0] is the source token while
/// sourceToTargetTokenPath[sourceToTargetTokenPath.length -1] is
/// target token. i.e. FLOW and BLOCTO respectively.
///
/// @param sourceToTargetTokenPath Offchain computed optimal path from
/// source token to target token.
/// @param buyAmount Amount of target token user wants to buy.
/// @return Amount of source token user has to pay to buy provided `buyAmount` of target token.
///
pub run getExactBuyQuoteUsingPath(
sourceToTargetTokenPath: Type[],
buyAmount: UFix64
): UFix64?
/// @notice It provides the quotation for selling the source token i.e. `sellAmount` for the corresponding target token.
///
/// If the source to target token path doesn't exists then below function
/// would return `nil`.
/// Below function would return the quoted amount after deduction of the fees.
///
/// @param sourceToken Type of token that user wants to sell.
/// @param targetToken Type of token that user would get in return after selling source token.
/// @param sellAmount Amount of source token user is willing to sell.
/// @return Returns the amount of target token user would get after selling provided source token i.e. `sellAmount`
///
pub fun getExactSellQuote(
sourceToken: Type,
targetToken: Type,
sellAmount: UFix64
): UFix64?
/// @notice It provides the quotation for buying the target token i.e. `buyAmount` for the /// corresponding source token.
///
/// If the source to target token path doesn't exists then below function
/// would return `nil`.
/// Below function would return the quoted amount after deduction of the fees.
///
/// @param sourceToken Type of token that user wants to provide to buy ///target token.
/// @param targetToken Type of token that user wants to buy.
/// @param buyAmount Amount of target token user is willing to buy.
/// @return Returns the amount of source token user would have to pay to /// buy provided target token amount i.e. buyAmount.
///
pub fun getExactBuyQuote(
sourceToken: Type,
targetToken: Type,
buyAmount: UFix64
): UFix64?
Community input feedback is requested and appreciated!
As always, the above draft proposals are presented to stimulate discussion and debate and are likely some ways from being finalized. We invite any interested community members to share any feedback, thoughts, questions or concerns in the thread. Together with your help weâre excited for these discussions to shake out the remaining details and enable the a FLIP to be proposed!