-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added LiquidatorLib and VirtualCpmm tests
- Loading branch information
1 parent
94f6e66
commit bd9e309
Showing
6 changed files
with
300 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
# Calculating Virtual CPMM LP by slippage and swap size | ||
|
||
Let a be the amount of x we are exchanging to get b amount of y. Therefore: | ||
|
||
(x + a) * (y - b) = k | ||
|
||
We want to solve for x. | ||
|
||
We know: | ||
|
||
et = execution price of the trade = b/a | ||
ex = exchange rate before trade = y/x | ||
|
||
Let's now tune the liquidity to determine slippage. Let's say we want 1% slippage. | ||
|
||
We want et/ex = 0.99 | ||
|
||
So we have: | ||
|
||
et = 0.99*ex | ||
|
||
We know the exchange rate and we know b, so we can solve for a: | ||
|
||
b/a = 0.99*ex | ||
|
||
b/(0.99*ex) = a | ||
|
||
We know the exchange rate: | ||
|
||
ex = y/x | ||
|
||
now solve for y | ||
|
||
x*ex = y | ||
|
||
Here is our formula: | ||
|
||
(x + a) * (y - b) = x * y | ||
xy - bx + ay - ab = x * y | ||
-bx + ay - ab = 0 | ||
ay = bx + ab | ||
|
||
solve for x: | ||
ay - ab = bx | ||
x = (ay - ab)/b | ||
x = (ay)/b - a | ||
|
||
x = (a*x*ex)/b - a | ||
|
||
x = (b*x*ex)/(b*0.99*ex) - b/(0.99*ex) | ||
x - (b*x*ex)/(b*0.99*ex) = - b/(0.99*ex) | ||
x(1 - b*ex/(b*0.99*ex)) = - b/(0.99*ex) | ||
|
||
x = (-b / (0.99 * ex)) / (1 - b*ex/(b*0.99*ex)) | ||
x = (b / (0.99 * ex)) / (b*ex/(b*0.99*ex) - 1) | ||
|
||
|
||
if b = 100 | ||
ex = 2 | ||
|
||
Then | ||
|
||
x = (-100 / (0.99*2)) / (1 - (100*2)/(100 *0.99*2)) | ||
x = 5000 | ||
|
||
=> | ||
|
||
2 = y/5000 | ||
|
||
10000 = y | ||
|
||
Let's try it | ||
|
||
x = 5000 | ||
y = 10000 | ||
trading a of x for b of y. | ||
b = 100 | ||
solve for a | ||
|
||
ay = bx + ab | ||
ay - ab = bx | ||
a(y - b) = bx | ||
a = bx / (y - b) | ||
a = 100 * 5000 / (10000 - 100) | ||
a = 50.5 | ||
|
||
Right on point! | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
|
||
pragma solidity 0.8.6; | ||
|
||
import "../../libraries/LiquidatorLib.sol"; | ||
|
||
contract LiquidatorLibHarness { | ||
using LiquidatorLib for LiquidatorLib.State; | ||
using SafeMath for uint256; | ||
using SafeCast for uint256; | ||
using PRBMathSD59x18Typed for PRBMath.SD59x18; | ||
|
||
LiquidatorLib.State state; | ||
|
||
function setState( | ||
int256 exchangeRate, | ||
uint256 lastSaleTime, | ||
int256 deltaRatePerSecond, | ||
int256 maxSlippage | ||
) external { | ||
state = LiquidatorLib.State({ | ||
exchangeRate: PRBMath.SD59x18(exchangeRate), | ||
lastSaleTime: lastSaleTime, | ||
deltaRatePerSecond: PRBMath.SD59x18(deltaRatePerSecond), | ||
maxSlippage: PRBMath.SD59x18(maxSlippage) | ||
}); | ||
} | ||
|
||
function computeExchangeRate(uint256 _currentTime) external view returns (int256) { | ||
return state.computeExchangeRate(_currentTime).value; | ||
} | ||
|
||
function computeExactAmountInAtTime(uint256 availableBalance, uint256 amountOut, uint256 currentTime) external view returns (uint256) { | ||
return state.computeExactAmountInAtTime(availableBalance, amountOut, currentTime); | ||
} | ||
|
||
function computeExactAmountOutAtTime(uint256 availableBalance, uint256 amountIn, uint256 currentTime) external view returns (uint256) { | ||
return state.computeExactAmountOutAtTime(availableBalance, amountIn, currentTime); | ||
} | ||
|
||
function swapExactAmountInAtTime( | ||
uint256 availableBalance, | ||
uint256 amountIn, | ||
uint256 currentTime | ||
) external returns (uint256) { | ||
return state.swapExactAmountInAtTime(availableBalance, amountIn, currentTime); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
|
||
pragma solidity 0.8.6; | ||
|
||
import "@prb/math/contracts/PRBMathSD59x18.sol"; | ||
|
||
import "../../libraries/VirtualCpmmLib.sol"; | ||
|
||
contract VirtualCpmmLibHarness { | ||
using SafeCast for uint256; | ||
|
||
function newCpmm( | ||
int256 maxSlippage, | ||
int256 exchangeRate, | ||
uint256 haveAmount | ||
) external pure returns (VirtualCpmmLib.Cpmm memory) { | ||
return VirtualCpmmLib.newCpmm( | ||
PRBMath.SD59x18(maxSlippage), | ||
PRBMath.SD59x18(exchangeRate), | ||
PRBMathSD59x18Typed.fromInt(haveAmount.toInt256()) | ||
); | ||
} | ||
|
||
// given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset | ||
function getAmountOut(uint amountIn, uint x, uint y) external pure returns (uint amountOut) { | ||
return VirtualCpmmLib.getAmountOut(amountIn, x, y); | ||
} | ||
|
||
// given an output amount of an asset and pair reserves, returns a required input amount of the other asset | ||
function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn) { | ||
return VirtualCpmmLib.getAmountIn(amountOut, reserveIn, reserveOut); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import { expect } from 'chai'; | ||
import { BigNumber, Contract, ContractFactory } from 'ethers'; | ||
import { ethers } from 'hardhat'; | ||
|
||
const { utils } = ethers; | ||
const { parseEther: toWei } = utils; | ||
|
||
describe('LiquidatorLibHarness', () => { | ||
let liquidatorLibHarness: Contract; | ||
let LiquidatorLibHarnessFactory: ContractFactory; | ||
|
||
before(async () => { | ||
LiquidatorLibHarnessFactory = await ethers.getContractFactory('LiquidatorLibHarness'); | ||
liquidatorLibHarness = await LiquidatorLibHarnessFactory.deploy(); | ||
|
||
const exchangeRate = toWei('2') // want:have | ||
const lastSaleTime = '10' | ||
const deltaRatePerSecond = toWei('0.01') // increases by 1% each second | ||
const maxSlippage = toWei('0.01') | ||
|
||
await liquidatorLibHarness.setState( | ||
exchangeRate, | ||
lastSaleTime, | ||
deltaRatePerSecond, | ||
maxSlippage | ||
) | ||
}) | ||
|
||
describe('computeExchangeRate()', () => { | ||
it('should start at the current exchange rate when delta time is zero', async () => { | ||
expect(await liquidatorLibHarness.computeExchangeRate('10')).to.equal(toWei('2')) | ||
}) | ||
|
||
it('should increase the exchange rate by delta time', async () => { | ||
// 10 seconds, 1 percent each second, => Delta exchange rate = 10% x 2 = 0.2 | ||
// = 2 + 0.2 = 2.2 | ||
expect(await liquidatorLibHarness.computeExchangeRate('20')).to.equal(toWei('2.2')) | ||
}) | ||
}) | ||
|
||
describe('computeExactAmountInAtTime()', () => { | ||
it('should compute how much can be purchased at time = 0', async () => { | ||
expect(await liquidatorLibHarness.computeExactAmountInAtTime(toWei('1000'), toWei('100'), '10')).to.equal('50050050050050050049') | ||
}) | ||
|
||
it('should return 0 if available balance is zero', async () => { | ||
expect(await liquidatorLibHarness.computeExactAmountInAtTime('0', toWei('100'), '10')).to.equal('0') | ||
}) | ||
}) | ||
|
||
describe('computeExactAmountOutAtTime()', () => { | ||
it('should compute how much can be purchased at time = 0', async () => { | ||
expect(await liquidatorLibHarness.computeExactAmountOutAtTime(toWei('1000'), toWei('50'), '10')).to.equal('99900099900099900099') | ||
}) | ||
|
||
it('should return 0 if available balance is zero', async () => { | ||
expect(await liquidatorLibHarness.computeExactAmountOutAtTime('0', toWei('100'), '10')).to.equal('0') | ||
}) | ||
}) | ||
|
||
describe('swapExactAmountInAtTime()', () => { | ||
it('should swap correctly', async () => { | ||
await liquidatorLibHarness.swapExactAmountInAtTime(toWei('1000'), toWei('100'), '10') | ||
expect(await liquidatorLibHarness.computeExchangeRate('10')).to.equal('1992023936159616893') | ||
}) | ||
|
||
it('should revert if there is insufficient balance', async () => { | ||
await expect(liquidatorLibHarness.swapExactAmountInAtTime(toWei('50'), toWei('100'), '10')).to.be.revertedWith('Whoops! have exceeds available') | ||
}) | ||
}) | ||
|
||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { expect } from 'chai'; | ||
import { BigNumber, Contract, ContractFactory } from 'ethers'; | ||
import { ethers } from 'hardhat'; | ||
|
||
const { utils } = ethers; | ||
const { parseEther: toWei } = utils; | ||
|
||
describe('VirtualCpmmLibHarness', () => { | ||
let virtualCpmmLibHarness: Contract; | ||
let VirtualCpmmLibHarnessFactory: ContractFactory; | ||
|
||
before(async () => { | ||
VirtualCpmmLibHarnessFactory = await ethers.getContractFactory('VirtualCpmmLibHarness'); | ||
virtualCpmmLibHarness = await VirtualCpmmLibHarnessFactory.deploy(); | ||
}); | ||
|
||
describe("newCpmm()", () => { | ||
it("should have correct LP for one percent slippage", async () => { | ||
|
||
const cpmm = await virtualCpmmLibHarness.newCpmm( | ||
toWei('0.01'), | ||
toWei('2'), | ||
'100' | ||
) | ||
|
||
expect(cpmm.want).to.equal('5000') | ||
expect(cpmm.have).to.equal('10000') | ||
|
||
}) | ||
|
||
it('should have correct LP for ten percent slippage', async () => { | ||
const cpmm = await virtualCpmmLibHarness.newCpmm( | ||
toWei('0.1'), | ||
toWei('2'), | ||
'100' | ||
) | ||
|
||
expect(cpmm.want).to.equal('500') | ||
expect(cpmm.have).to.equal('1000') | ||
}) | ||
}) | ||
|
||
describe("getAmountOut()", () => { | ||
it('should be correct', async () => { | ||
expect(await virtualCpmmLibHarness.getAmountOut(550, 5000, 10000)).to.equal(990) | ||
}) | ||
}) | ||
|
||
describe('getAmountIn()', () => { | ||
it('should be correct', async () => { | ||
expect(await virtualCpmmLibHarness.getAmountIn(990, 5000, 10000)).to.equal(549) | ||
}) | ||
}) | ||
}); |