Simple liquidity provisoning strategy and pooling contracts

Since it is possible in dfusion to place orders without having a balance it allows for some interesting concepts that can be described as “a trade-strategy”, “simple market-making” or “programmable orders”.

A simple example of this is a strategy to market make on stable coins. A user can select a bunch of stable coins they trust (trust to maintain a value of e.g. 1$) and create a bunch of orders that trade any of those stable coins against any other as long as they make a spread on the trade. It is sufficient to provide any of the stable coins into this strategy. As soon as stable coin A gets exchanged into stable coin B this will “activate” all orders from “B”.

In this strategy, the full amount that is given is used at one price level. A strategy might be price sensitive. A simple example is the x*y=k formula, made popular by unisawp. Simplified it means, the more the price of token A drops the more to buy of token A with token B.
This can be achieved by dividing up capital into different accounts. Those different accounts would each trade A and B against each other at a specific price level. They would have a spread - so trading A to B and B back to A would result in a profit of 2 times the spread.
Uniswap describes a continuous supply and demand curve - with this strategy the curve can be approximated by a step function. The big advantage is that ANY curve can be used and even just parts of a curve.

If those orders would be placed per trader it would lead to many orders. If a trader want to market make $/ETH in the range from $100 to $200 with 1% spread it would result in 100 different accounts that each creates an order to e.g. buy ETH for $145 and sell ETH for at $146. So in aggregate, it would be 100 accounts that each place 2 orders. If the trader spends 1k in total each account would be funded with $10. Since the system can only handle 25 orders per batch small orders will be excluded if the system runs at capacity. The system can scale much bigger if the same order types are aggregated into larger orders. So we could have one larger order (or accounts with multiple orders) owned by a pool of traders. This ownership can be expressed as tokens (like pool shares in uniswap).

Implementing pool contracts:
Let’s consider a simple pool that is selling all DAI for USDc with 0.5% spread and selling all USDc for 0.5% spread to DAI as well.
A smart contract would just place to standing order with unlimited volume that is valid forever. A trader deposit into the pool. The pool deposits into dfusion. Implicitly this deposit will increase the order size. At the moment of deposit, the trader needs to get allocated pool shares. At the moment of deposit, the contract can calculate the ratio of outstanding pool shares and the current balance in the contract. The user will get an allocation of pool shares at this rate (minus the spread to avoid effectively trading with the pool by deposit and withdrawal). A small issue might be that at the moment of the deposit the pool contract did already engaged in a trade, but the solver has not submitted the trade yet. Now you could deposit into a pool that you know did a profitable trade in the previous batch. But you can still join under the conditions before this trade happened.

Potentially a bigger issue - a solver might have submitted a solution (potentially with unrealistic prices) that will get reverted. In this case, you join the pool under the assumption that it just did a very profitable trade but the trade will get reverted. Or alternatively, you can join under the assumption that the pool just did a horrible trade and you will get much better conditions than you should since the trade gets reverted.

One way to solve that issue would be to only allow joining the pool in minutes 4-5 when the previous batch is final. In practice, it might be hard enough to pull off this attack to just accept the risk. In can be eliminated by the user defining a max price they are willing to pay for pool shares.

A withdrawal can be implemented as follows: a user can lock their pool shares to withdrawal a token or a combination of tokens they want. The issue here is that at the moment of withdrawal (unless in minute 4-5) it will not be in any case clear what tokens the pool has (since they may be traded away in the current trade).

Example - the user had 100 shares representing 100 tokens of A and B. The user could decide to withdraw 50 and 50. At the moment of withdrawal, the account might even have that balance. Right after the trade, the solver submits a solution that converts all A to B. Now in the next batch the user will only be able to withdrawal B. There needs to be a way to convert the remaining A withdrawal into B.

Additionally, we also need to charge a fee for withdraws, as otherwise people might exploit the pool as an stable token exchange.

@alex this is why I suggest do reduce that amount of pool shares that are minted at deposit by the spread.

If people provide liquidity into a stable coin pooling strategy their primary objective is to earn a return. Ideally interest earning tokens can be used like CHAI or cDAI or cUSDC.

A sensible strategy for such a pool would be:
Holds funds in CHAI, cDAI or cUSDC. Have open orders between CHAI, cDAI, cUSDC, DAI and USDC. (no sell orders for USDC and DAI) Every time someone interacts with the pool (deposit or withdrawal) a maintenance function would be called and do the following steps:

  1. Withdrawal all DAI and USDC and convert it to c-tokens. (there is a unlimited withdrawal request so this can be done directly) Deposit back in.
  2. Update prices of the standing orders according to the current rates at which DAI and USDc can be converted to the interest earning tokens.

Ideally there would be even more liquidity on the interest earning tokens. In this case it would make sense to build a clever UI that would allow to trade DAI to USDc but under the hood convert DAI to e.g. cDAI trade it against cUSDc and convert back to USDc.

1 Like

Sorry, I must have read over the deposit spread.

I guess I would favour a withdraw-fee over a deposit fee. Deposit fees benefit first comers too much in my point of view. Imagine 10 people are joining the pool. The pool is not trading at at. Then the first depositor can already withdraw with some winnings, as he has already earned portions all the deposit fees from the others.
Instead, if we would charge the fee during withdraws, then we would encourage staying in the pool longer, as the party staying the longest in the pool, earns the most fees from the others.

1 Like

I think it won’t take too much longer and we will see that compound adds all the other stable coins as well. And then, yeah, we should encourage trading only in the cTokens. I like the idea of “conversion under the hood a lot”.

1 Like

In this case, you join the pool under the assumption that it just did a very profitable trade but the trade will get reverted.

The result of this would be that depositor gets less pool shares that he actually would get. But if we allow to specify a minimum pool shares, this attack can be mitigated.

Or alternatively, you can join under the assumption that the pool just did a horrible trade

Pools can under normal circumstances only make good trades, right? In both pools, in the bracket-strategy pool and the stable coin pool, the pools will always sell an asset for a higher price than the price they were buying it.

for the withdrawals, you write

One could open a withdrawal request on several stable coins and then just do the withdrawal with one coin. While this reduces the liquidity of the pool for one batch, I think that it is still a feasible approach. Especially, if we limit the amount that can be withdrawn at once.

I think that for the bracket-liquidity pooling, one attack vector is that a withdrawer puts a market order in the dfusion exchange and then matches it very quickly with the pool. Now the pool is under the assumption that it has more funds as it actually has and he can redeem his liquidity tokens for more tokens. If then later, the first solution for the batch gets reverted, the pool might have made a loss.
But I think if we also restrict here there total amount of pool shares, which can be redeemed, then this is also a pretty unrealistic attack.