Skip to content

Commit

Permalink
Test/new exit contribution (#557)
Browse files Browse the repository at this point in the history
### Description

This PR tries to solve the discontinued curve problem that was causing
the same sell amount when executed in two swaps resulting in a larger
amountOut than when sold in one swap.
The idea is to apply the exitContribution on the G$ amount being sold
and burning that amount. In order to not move on the curve by this burn
we increase the reserve ratio in order to keep the price before the
exitContribution burn equal to the price after.

### Other changes

Added requires that ensure the token Supply always stays at a minimum of
1 wei

### Tested

- new tests added ensuring the difference in splitting sells into
multiple swaps stays super small compared to the gas costs

### Related issues

 mento-protocol/mento-general#596
#555


### Backwards compatibility



### Documentation

---------

Co-authored-by: baroooo <[email protected]>
  • Loading branch information
philbow61 and baroooo authored Nov 29, 2024
1 parent 1e78e2c commit 98290ba
Show file tree
Hide file tree
Showing 2 changed files with 191 additions and 85 deletions.
71 changes: 66 additions & 5 deletions contracts/goodDollar/BancorExchangeProvider.sol
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,13 @@ contract BancorExchangeProvider is IExchangeProvider, IBancorExchangeProvider, B
) external view virtual returns (uint256 amountOut) {
PoolExchange memory exchange = getPoolExchange(exchangeId);
uint256 scaledAmountIn = amountIn * tokenPrecisionMultipliers[tokenIn];

if (tokenIn == exchange.tokenAddress) {
require(scaledAmountIn < exchange.tokenSupply, "amountIn is greater than tokenSupply");
// apply exit contribution
scaledAmountIn = (scaledAmountIn * (MAX_WEIGHT - exchange.exitContribution)) / MAX_WEIGHT;
}

uint256 scaledAmountOut = _getScaledAmountOut(exchange, tokenIn, tokenOut, scaledAmountIn);
amountOut = scaledAmountOut / tokenPrecisionMultipliers[tokenOut];
return amountOut;
Expand All @@ -137,6 +144,13 @@ contract BancorExchangeProvider is IExchangeProvider, IBancorExchangeProvider, B
PoolExchange memory exchange = getPoolExchange(exchangeId);
uint256 scaledAmountOut = amountOut * tokenPrecisionMultipliers[tokenOut];
uint256 scaledAmountIn = _getScaledAmountIn(exchange, tokenIn, tokenOut, scaledAmountOut);

if (tokenIn == exchange.tokenAddress) {
// apply exit contribution
scaledAmountIn = (scaledAmountIn * MAX_WEIGHT) / (MAX_WEIGHT - exchange.exitContribution);
require(scaledAmountIn < exchange.tokenSupply, "amountIn is greater than tokenSupply");
}

amountIn = scaledAmountIn / tokenPrecisionMultipliers[tokenIn];
return amountIn;
}
Expand Down Expand Up @@ -196,8 +210,21 @@ contract BancorExchangeProvider is IExchangeProvider, IBancorExchangeProvider, B
) public virtual onlyBroker returns (uint256 amountOut) {
PoolExchange memory exchange = getPoolExchange(exchangeId);
uint256 scaledAmountIn = amountIn * tokenPrecisionMultipliers[tokenIn];
uint256 exitContribution = 0;

if (tokenIn == exchange.tokenAddress) {
require(scaledAmountIn < exchange.tokenSupply, "amountIn is greater than tokenSupply");
// apply exit contribution
exitContribution = (scaledAmountIn * exchange.exitContribution) / MAX_WEIGHT;
scaledAmountIn -= exitContribution;
}

uint256 scaledAmountOut = _getScaledAmountOut(exchange, tokenIn, tokenOut, scaledAmountIn);

executeSwap(exchangeId, tokenIn, scaledAmountIn, scaledAmountOut);
if (exitContribution > 0) {
_accountExitContribution(exchangeId, exitContribution);
}

amountOut = scaledAmountOut / tokenPrecisionMultipliers[tokenOut];
return amountOut;
Expand All @@ -213,9 +240,26 @@ contract BancorExchangeProvider is IExchangeProvider, IBancorExchangeProvider, B
PoolExchange memory exchange = getPoolExchange(exchangeId);
uint256 scaledAmountOut = amountOut * tokenPrecisionMultipliers[tokenOut];
uint256 scaledAmountIn = _getScaledAmountIn(exchange, tokenIn, tokenOut, scaledAmountOut);

uint256 exitContribution = 0;
uint256 scaledAmountInWithExitContribution = scaledAmountIn;

if (tokenIn == exchange.tokenAddress) {
// apply exit contribution
scaledAmountInWithExitContribution = (scaledAmountIn * MAX_WEIGHT) / (MAX_WEIGHT - exchange.exitContribution);
require(
scaledAmountInWithExitContribution < exchange.tokenSupply,
"amountIn required is greater than tokenSupply"
);
exitContribution = scaledAmountInWithExitContribution - scaledAmountIn;
}

executeSwap(exchangeId, tokenIn, scaledAmountIn, scaledAmountOut);
if (exitContribution > 0) {
_accountExitContribution(exchangeId, exitContribution);
}

amountIn = scaledAmountIn / tokenPrecisionMultipliers[tokenIn];
amountIn = scaledAmountInWithExitContribution / tokenPrecisionMultipliers[tokenIn];
return amountIn;
}

Expand Down Expand Up @@ -289,6 +333,27 @@ contract BancorExchangeProvider is IExchangeProvider, IBancorExchangeProvider, B
exchanges[exchangeId].tokenSupply = exchange.tokenSupply;
}

/**
* @notice Accounting of exit contribution on a swap.
* @dev Accounting of exit contribution without changing the current price of an exchange.
* this is done by updating the reserve ratio and subtracting the exit contribution from the token supply.
* Formula: newRatio = (Supply * oldRatio) / (Supply - exitContribution)
* @param exchangeId The ID of the pool
* @param exitContribution The amount of the token to be removed from the pool, scaled to 18 decimals
*/
function _accountExitContribution(bytes32 exchangeId, uint256 exitContribution) internal {
PoolExchange memory exchange = getPoolExchange(exchangeId);
uint256 scaledReserveRatio = uint256(exchange.reserveRatio) * 1e10;
UD60x18 nominator = wrap(exchange.tokenSupply).mul(wrap(scaledReserveRatio));
UD60x18 denominator = wrap(exchange.tokenSupply - exitContribution);
UD60x18 newRatioScaled = nominator.div(denominator);

uint256 newRatio = unwrap(newRatioScaled) / 1e10;

exchanges[exchangeId].reserveRatio = uint32(newRatio);
exchanges[exchangeId].tokenSupply -= exitContribution;
}

/**
* @notice Calculate the scaledAmountIn of tokenIn for a given scaledAmountOut of tokenOut
* @param exchange The pool exchange to operate on
Expand All @@ -306,8 +371,6 @@ contract BancorExchangeProvider is IExchangeProvider, IBancorExchangeProvider, B
if (tokenIn == exchange.reserveAsset) {
scaledAmountIn = fundCost(exchange.tokenSupply, exchange.reserveBalance, exchange.reserveRatio, scaledAmountOut);
} else {
// apply exit contribution
scaledAmountOut = (scaledAmountOut * MAX_WEIGHT) / (MAX_WEIGHT - exchange.exitContribution);
scaledAmountIn = saleCost(exchange.tokenSupply, exchange.reserveBalance, exchange.reserveRatio, scaledAmountOut);
}
}
Expand Down Expand Up @@ -340,8 +403,6 @@ contract BancorExchangeProvider is IExchangeProvider, IBancorExchangeProvider, B
exchange.reserveRatio,
scaledAmountIn
);
// apply exit contribution
scaledAmountOut = (scaledAmountOut * (MAX_WEIGHT - exchange.exitContribution)) / MAX_WEIGHT;
}
}

Expand Down
Loading

0 comments on commit 98290ba

Please sign in to comment.