Using Native Pools and Pool Units for Intuitive Liquidity Provider UX | The Radix Blog | Radix DLT
YOU ARE: A Scrypto component developer
YOU’RE BUILDING: A DeFi dApp or blueprint that includes user-contributed liquidity or other pools of tokens
THE PROBLEM TO SOLVE: How to give contributors of liquidity a token that represents their contribution that shows its true, guaranteed value to the user right in their wallet? How to avoid your users thinking “I have this LP token, but I have to visit a dApp website to know what it’s redeemable for and I have to trust the dApp that I can redeem it”?
THE TOOL TO USE: The Radix Network’s native Pool package and associated Pool Unit resources
A common and powerful pattern in DeFi is the “liquidity pool”. Application areas like exchange, lending, and more require capital to function, and a liquidity pool is a way for individual users to contribute that capital – and typically share in the benefits of what that pool of capital enables. Basically, the pool creates the basis of a two-sided market, with liquidity providers on one side contributing capital to the pool and users on the other side (traders, borrowers, etc.) using that capital.
For example, a typical DEX has a liquidity pool of two different tokens. Liquidity providers contribute tokens to that pool, and when a trader wishes to conduct a trade, the DEX logic automatically conducts the trade using the tokens in the pool. It might charge a fee for the trade, and deposit that fee back into the pool to increase the overall value of the pool for liquidity providers.
The basic concept of a pool is this: When somebody contributes tokens to the pool, in return they receive the right to redeem tokens back from the pool at a later time, in proportion to what they contributed (compared to other contributors). The dApp’s logic uses the capital in the pool (and perhaps deposits fees back into it), and as a result the amount of tokens a contributor can redeem from the pool changes – but the pool ensures that redemption always remains in proportion to the other contributors to the pool. The pool’s logic must also ensure that contributions to the pool, and redemptions from the pool, are always in proportion to what is in the pool (if it is more than one kind of token).
To make this work, contributors need to receive a special token that represents the proportional size of their contribution for later redemption. This is often called an “LP token”, and Radix generalizes the concept into what it calls a “pool unit”. A pool unit is a fungible token where the quantity of the pool unit a user holds represents the proportion of the pool that it may be redeemed for. This is convenient because the pool unit itself is fungible (each pool unit of a pool is worth exactly the same as any other) and liquid (it may be sold, traded, and used in various DeFi applications).
For example, a pool in action:
Let’s say there is a DEX trading pool of 2 tokens, currently 95 ZOMBO/190 GUMBLE. The collective contributors to the pool currently hold 9500 “Cool Pool ZMB-GMB” pool units.
- Alice contributes 5 ZOMBO/10 GUMBLE (bringing it to a total of 100 ZOMBO/200 GUMBLE). That’s worth exactly 5% of the pool (the pool logic required Alice to contribute the same % of both ZOMBO and GUMBLE). The pool logic mints 500 Cool Pool ZMB-GMB pool units for Alice – 5% of the current supply of pool units at this moment.
- If Alice turned around and redeemed those pool units right now, she would get exactly her 5 ZOMBO/10 GUMBLE back – 5% of the current pool. But instead she holds onto them.
- Immediately after Alice, Bob contributes 100 ZOMBO/200 GUMBLE to the pool – doubling its size to 200 ZOMBO/400 GUMBLE. Bob of course receives a newly-minted 10,000 pool units representing 50% of the total supply of pool units which is now 20,000.
Because of the new contribution by Bob, Alice now holds only 2.5% of the pool units. However, if she were to redeem them all right now, she would still receive her 5 ZOMBO/10 GUMBLE back – because the pool logic enforces the proportionality of pool units (and of contributions/redemptions between the pools).
Now let’s return to the pool a little later, when a combination of trading, fees, and contributions/redemptions by other liquidity providers has changed the state of the pool.
There are now, say, 2582 ZOMBO/9101 GUMBLE with an outstanding supply of 41,225 pool units (who knows why – lots of things have happened with the pool according to the DEX’s unique logic).
- Alice still holds her 500 pool units. She chooses to redeem 100 of them.
- 100 is 0.24% of the current pool units, so Alice receives 0.24% of the pool contents: roughly 6 ZOMBO/22 GUMBLE.
Hey, she made a profit on her original contribution of 5 ZOMBO/10 GUMBLE! And those redeemed pool units are burned by the pool, keeping the value of other contributor’s pool units unchanged at that moment – both the size of the pool and the quantity of outstanding pool units went down in the same proportion.
If you’re Alice in the example above, holding some Cool Pool ZMB/GMB pool unit tokens, those tokens have a very important meaning to you: These pool units are worth some amount of ZOMBO and GUMBLE right now. Right after contribution, they’re worth exactly what you contributed. But over time, you want to see how that value changes according to the dApp’s internal logic.
The problem is that Alice’s wallet can’t naturally tell what the pool units are worth – in fact, it can’t be sure that they are even pool units, or are actually redeemable! This is the problem with LP tokens on every other DeFi network today, and it basically means that users don’t feel very confident about participating in liquidity pools.
To solve this, we somehow need to make it so that the Radix Wallet (or other client user interfaces) can look at a given token and objectively tell the user with absolute certainty:
- This is in fact a pool unit, corresponding to a given pool
- Here are what these pool units are worth right now (for however many tokens are in the pool)
- You can in fact redeem these pool units for their value
Of course the user still has to trust what your dApp is going to do with the liquidity in the pool – that’s the nature of the relationship between liquidity provider and dApp. But if the wallet can do the above, users get the transparency and intuitive feel of LP tokens being “real” and valuable, giving them the confidence to participate in contributing liquidity.
The Radix Tool for the Job
The Radix Network includes a native package called a “Pool”, which contains blueprints for different types of Pool components that create their own pool unit fungible resources. The Pool package implements just the absolute core, common logic of the minting and burning of pool units in proportion – and ensures that pool units are objectively redeemable for current pool contents. This means that the Radix Wallet can give holders of pool units from your dApp the good UX described above.
A pool component, instantiated from this package’s blueprints, really only has 4 main simple functions:
- Allows users to contribute tokens to the pool. It ensures that the contribution matches the ratio between pools (for pools with more than 1 resource), and mints and returns a quantity of pool unit tokens in proportion to the contribution.
- Allows users to redeem tokens from the pool. It burns the pool units redeemed, and returns the right proportion of pool tokens to the user.
- Allows a defined role to deposit tokens into the pool, in any quantity or ratio, according to dApp logic (effectively increasing the value of outstanding pool units).
- Allows a defined role to withdraw tokens from the pool, in any quantity or ratio, according to dApp logic (effectively decreasing the value of outstanding pool units).
The Recommended Use Pattern
You can freely instantiate your own Pool components, either via transaction or within Scrypto logic. When instantiated, you choose what resources that pool will contain – with blueprints to cover three pool variations for 1, 2, or “n” number of resources.
When instantiating a Pool, you decide who can contribute, deposit, and withdraw.
Deposit and withdraw are typically reserved for your dApp’s own logic (and they should never be public). You can set these methods to require a badge held by your component, or a more complex auth structure.
Contribute may sometimes be public, but sometimes the dApp may want to require the user to “go through the front door” of its own component with its own rules about who may contribute and how. To do this, you once again may set this method to require a badge held by your component, so that it makes the contribution call rather than the user – typically returning the resulting pool units it receives back to the user.
Redeem is always public. The reason is that the only way the Radix Wallet can truly guarantee that a quantity of pool units is truly redeemable for a given value is if it knows that the user can call the redeem method, regardless of any other dApp logic.
You’ll notice that there is no DEX bonding curve, no lending logic, nothing application-specific here. A Pool is designed to be integrated with your own Scrypto component that implements all of your core logic. The Pool can just be treated as a simple repository of resources, where you can trust that pool units will be correctly minted/burned. Your own Scrypto logic will be conducting all deposits and withdrawals with the Pool, according to the design of your application.
You will also have the ability to set up metadata on both Pool component and Pool Unit resource, so that Pool Units are branded in the user’s wallet, and Pool interactions are associated with your dApp Definition.
The native implementation of Pools and Pool Units may not serve all possible liquidity pool designs; its purpose is to provide an option for the most common, simple form of pool. Hopefully you will find that many types of pool are served by it, by wrapping your own component logic around the Pool component’s simple interface and capabilities. However there are certainly some places where a fungible pool unit simply doesn’t conceptually make sense.
In particular, some applications need to consider additional factors beyond just contribution amount. Each contribution may have an important notion of time, special parameters about that contribution, or other reasons why each contribution is unique. That means that fundamentally fungible “pool units” can’t fully represent that contribution.
In these circumstances, a non-fungible token may provide a better pattern, offering a sort of “pool claim”, rather than a quantity of fungible “pool units”. Each non-fungible can include a data struct that can represent the unique parameters for each contribution – along with a description of the value of the contribution that can be used later for redemption. Using the NFT of course comes at the cost of less natural liquidity and tradeability, but this may be a natural and necessary compromise for these applications.
While the initial implementation of the Pool does not have an NFT option, an extension to include it is under consideration and a survey will be conducted with the Radix developer community on a possible implementation that covers the widest possible set of use cases.
Links to Get Started
- Native Pool and Pool Unit documentation
- Example of Pool usage in the Radix docs
- Metadata standards for wallet display
- Metadata standards for verification