Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/curve stable meta ng #387

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 105 additions & 48 deletions pkg/liquidity-source/curve/plain/math.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,10 +311,13 @@ func (t *PoolSimulator) getYD(
tokenIndex int,
xp []uint256.Int,
d *uint256.Int,
) (*uint256.Int, error) {

//output
y *uint256.Int,
) error {
var numTokens = len(xp)
if tokenIndex >= numTokens {
return nil, ErrTokenNotFound
return ErrTokenNotFound
}
var c, s uint256.Int
c.Set(d)
Expand All @@ -330,7 +333,7 @@ func (t *PoolSimulator) getYD(
}
}
if nA.IsZero() {
return nil, ErrZero
return ErrZero
}
c.Div(
number.Mul(number.Mul(&c, d), t.staticExtra.APrecision),
Expand All @@ -340,80 +343,85 @@ func (t *PoolSimulator) getYD(
&s,
number.Div(number.Mul(d, t.staticExtra.APrecision), nA),
)
var y, yPrev uint256.Int
var yPrev uint256.Int
y.Set(d)
for i := 0; i < MaxLoopLimit; i++ {
yPrev.Set(&y)
yPrev.Set(y)
y.Div(
number.Add(
number.Mul(&y, &y),
number.Mul(y, y),
&c,
),
number.Sub(
number.Add(
number.Add(&y, &y),
number.Add(y, y),
b,
),
d,
),
)
if withinDelta(&y, &yPrev, 1) {
return number.Set(&y), nil
if withinDelta(y, &yPrev, 1) {
return nil
}
}
return nil, ErrAmountOutNotConverge
return ErrAmountOutNotConverge
}

// need to keep big.Int for interface method, will be removed later
func (t *PoolSimulator) CalculateWithdrawOneCoin(
tokenAmount *big.Int,
i int,
) (*big.Int, *big.Int, error) {
dy, diff, err := t.CalculateWithdrawOneCoinU256(number.SetFromBig(tokenAmount), i)
var dy, dyFee uint256.Int
err := t.CalculateWithdrawOneCoinU256(number.SetFromBig(tokenAmount), i, &dy, &dyFee)
if err != nil {
return nil, nil, err
}
return dy.ToBig(), diff.ToBig(), nil
return dy.ToBig(), dyFee.ToBig(), nil
}

func (t *PoolSimulator) CalculateWithdrawOneCoinU256(
tokenAmount *uint256.Int,
i int,
) (*uint256.Int, *uint256.Int, error) {

// output
dy *uint256.Int, dyFee *uint256.Int,
) error {
var amp = t._A()
var xp = xpMem(t.extra.RateMultipliers, t.reserves)
var D0 uint256.Int
var D0, newY, newYD uint256.Int
err := t.getD(xp, amp, &D0)
if err != nil {
return nil, nil, err
return err
}
var totalSupply = &t.LpSupply
var D1 = number.Sub(&D0, number.Div(number.Mul(tokenAmount, &D0), totalSupply))
newY, err := t.getYD(amp, i, xp, D1)
err = t.getYD(amp, i, xp, D1, &newY)
if err != nil {
return nil, nil, err
return err
}
var nCoins = len(t.Info.Reserves)
var xpReduced = make([]uint256.Int, nCoins)
var xpReduced [shared.MaxTokenCount]uint256.Int
var nCoinBI = number.SetUint64(uint64(nCoins))
var fee = number.Div(number.Mul(t.extra.SwapFee, nCoinBI), number.Mul(uint256.NewInt(4), number.SubUint64(nCoinBI, 1)))
for j := 0; j < nCoins; j += 1 {
var dxExpected uint256.Int
if j == i {
dxExpected.Sub(number.Div(number.Mul(&xp[j], D1), &D0), newY)
dxExpected.Sub(number.Div(number.Mul(&xp[j], D1), &D0), &newY)
} else {
dxExpected.Sub(&xp[j], number.Div(number.Mul(&xp[j], D1), &D0))
}
xpReduced[j].Sub(&xp[j], number.Div(number.Mul(fee, &dxExpected), FeeDenominator))
}
newYD, err := t.getYD(amp, i, xpReduced, D1)
err = t.getYD(amp, i, xpReduced[:nCoins], D1, &newYD)
if err != nil {
return nil, nil, err
return err
}
var dy = number.Sub(&xpReduced[i], newYD)
dy.Sub(&xpReduced[i], &newYD)
dy.Div(number.SubUint64(dy, 1), &t.precisionMultipliers[i])
var dy0 = number.Div(number.Sub(&xp[i], newY), &t.precisionMultipliers[i])
return dy, number.Sub(dy0, dy), nil
var dy0 = number.Div(number.Sub(&xp[i], &newY), &t.precisionMultipliers[i])
dyFee.Sub(dy0, dy)
return nil
}

// need to keep big.Int for interface method, will be removed later
Expand All @@ -425,47 +433,75 @@ func (t *PoolSimulator) CalculateTokenAmount(
for i, amount := range amounts {
amountsU256[i].SetFromBig(amount)
}
res, err := t.CalculateTokenAmountU256(amountsU256, deposit)
var mintAmount uint256.Int
var feeAmounts [shared.MaxTokenCount]uint256.Int
err := t.CalculateTokenAmountU256(amountsU256, deposit, &mintAmount, feeAmounts[:t.numTokens])
if err != nil {
return nil, err
}
return res.ToBig(), nil
return mintAmount.ToBig(), nil
}

func (t *PoolSimulator) CalculateTokenAmountU256(
amounts []uint256.Int,
deposit bool,
) (*uint256.Int, error) {

// output
mintAmount *uint256.Int,
feeAmounts []uint256.Int,
) error {
var numTokens = len(t.Info.Tokens)
var a = t._A()
var d0, d1 uint256.Int
var d0, d1, d2 uint256.Int
err := t.get_D_mem(t.extra.RateMultipliers, t.reserves, a, &d0)
if err != nil {
return nil, err
return err
}
var balances1 = make([]uint256.Int, numTokens)
var balances1 [shared.MaxTokenCount]uint256.Int
for i := 0; i < numTokens; i++ {
if deposit {
balances1[i].Add(&t.reserves[i], &amounts[i])
} else {
if t.reserves[i].Cmp(&amounts[i]) < 0 {
return nil, ErrWithdrawMoreThanAvailable
return ErrWithdrawMoreThanAvailable
}
balances1[i].Sub(&t.reserves[i], &amounts[i])
}
}
err = t.get_D_mem(t.extra.RateMultipliers, balances1, a, &d1)
err = t.get_D_mem(t.extra.RateMultipliers, balances1[:numTokens], a, &d1)
if err != nil {
return nil, err
return err
}

// in SC, this method won't take fee into account, so the result is different than the actual add_liquidity method
// we'll copy that code here

// We need to recalculate the invariant accounting for fees
// to calculate fair user's share
var totalSupply = t.LpSupply
var diff uint256.Int
if deposit {
diff.Sub(&d1, &d0)
var difference uint256.Int
if !totalSupply.IsZero() {
var _fee = number.Div(number.Mul(t.extra.SwapFee, &t.numTokensU256),
number.Mul(number.Number_4, uint256.NewInt(uint64(t.numTokens-1))))
var _admin_fee = t.extra.AdminFee
for i := 0; i < t.numTokens; i += 1 {
var ideal_balance = number.Div(number.Mul(&d1, &t.reserves[i]), &d0)
if ideal_balance.Cmp(&balances1[i]) > 0 {
difference.Sub(ideal_balance, &balances1[i])
} else {
difference.Sub(&balances1[i], ideal_balance)
}
var fee = number.Div(number.Mul(_fee, &difference), FeeDenominator)
feeAmounts[i].Set(number.Div(number.Mul(fee, _admin_fee), FeeDenominator))
balances1[i].Sub(&balances1[i], fee)
}
_ = t.get_D_mem(t.extra.RateMultipliers, balances1[:t.numTokens], a, &d2)
mintAmount.Div(number.Mul(&totalSupply, number.Sub(&d2, &d0)), &d0)
} else {
diff.Sub(&d0, &d1)
mintAmount.Set(&d1)
}
return number.Div(number.Mul(&diff, &totalSupply), &d0), nil

return nil
}

// need to keep big.Int for interface method, will be removed later
Expand Down Expand Up @@ -537,6 +573,18 @@ func (t *PoolSimulator) AddLiquidityU256(amounts []uint256.Int) (*uint256.Int, e
return &mint_amount, nil
}

func (t *PoolSimulator) ApplyAddLiquidity(amounts, feeAmounts []uint256.Int, mintAmount *uint256.Int) error {
for i := 0; i < t.numTokens; i++ {
number.SafeAddZ(&t.reserves[i], &amounts[i], &t.reserves[i])
number.SafeSubZ(&t.reserves[i], &feeAmounts[i], &t.reserves[i])
number.FillBig(&t.reserves[i], t.Info.Reserves[i]) // always sync back update to t.Info, will be removed later
}

t.LpSupply.Add(&t.LpSupply, mintAmount)

return nil
}

// need to keep big.Int for interface method, will be removed later
func (t *PoolSimulator) RemoveLiquidityOneCoin(tokenAmount *big.Int, i int) (*big.Int, error) {
dy, err := t.RemoveLiquidityOneCoinU256(number.SetFromBig(tokenAmount), i)
Expand All @@ -547,35 +595,44 @@ func (t *PoolSimulator) RemoveLiquidityOneCoin(tokenAmount *big.Int, i int) (*bi
}

func (t *PoolSimulator) RemoveLiquidityOneCoinU256(tokenAmount *uint256.Int, i int) (*uint256.Int, error) {
var dy, dy_fee, err = t.CalculateWithdrawOneCoinU256(tokenAmount, i)
var dy, dyFee uint256.Int
var err = t.CalculateWithdrawOneCoinU256(tokenAmount, i, &dy, &dyFee)
if err != nil {
return nil, err
}
t.reserves[i].Sub(&t.reserves[i], number.Add(dy, number.Div(number.Mul(dy_fee, t.extra.AdminFee), FeeDenominator)))
t.reserves[i].Sub(&t.reserves[i], number.Add(&dy, number.Div(number.Mul(&dyFee, t.extra.AdminFee), FeeDenominator)))
number.FillBig(&t.reserves[i], t.Info.Reserves[i]) // always sync back update to t.Info, will be removed later
t.LpSupply.Sub(&t.LpSupply, tokenAmount)
return &dy, nil
}

func (t *PoolSimulator) ApplyRemoveLiquidityOneCoinU256(i int, tokenAmount, dy, dyFee *uint256.Int) error {
t.reserves[i].Sub(&t.reserves[i], number.Add(dy, number.Div(number.Mul(dyFee, t.extra.AdminFee), FeeDenominator)))
number.FillBig(&t.reserves[i], t.Info.Reserves[i]) // always sync back update to t.Info, will be removed later
t.LpSupply.Sub(&t.LpSupply, tokenAmount)
return dy, nil
return nil
}

// need to keep big.Int for interface method, will be removed later
func (t *PoolSimulator) GetVirtualPrice() (*big.Int, *big.Int, error) {
vPrice, d, err := t.GetVirtualPriceU256()
var vPrice, d uint256.Int
err := t.GetVirtualPriceU256(&vPrice, &d)
if err != nil {
return nil, nil, err
}
return vPrice.ToBig(), d.ToBig(), err
}

func (t *PoolSimulator) GetVirtualPriceU256() (*uint256.Int, *uint256.Int, error) {
func (t *PoolSimulator) GetVirtualPriceU256(vPrice, D *uint256.Int) error {
var xp = xpMem(t.extra.RateMultipliers, t.reserves)
var A = t._A()
var D uint256.Int
var err = t.getD(xp, A, &D)
var err = t.getD(xp, A, D)
if err != nil {
return nil, nil, err
return err
}
if t.LpSupply.IsZero() {
return nil, nil, ErrDenominatorZero
return ErrDenominatorZero
}
return number.Div(number.Mul(&D, Precision), &t.LpSupply), &D, nil
vPrice.Div(number.Mul(D, Precision), &t.LpSupply)
return nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ func TestCalcAmountOutAsBasePool(t *testing.T) {
{"A", 1000000000000000, "B", 999},
{"A", 1000000000000000, "C", 1000},

{"B", 3, "Am", 92475148432038},
{"B", 3, "Am", 92474249943422}, // get_dy_underlying return 92475148432038, but the actual swap is different
{"B", 1, "A", 999909687790},
{"B", 100, "C", 100},

{"C", 2, "Am", 61628215439376},
{"C", 2, "Am", 61627616659617}, // get_dy_underlying return 61628215439376, but the actual swap is different
{"C", 3, "A", 2998664269827},
{"C", 30, "B", 29},
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/liquidity-source/curve/shared/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ type (
Implementation string
LpTokenAddress string
IsMetaPool bool

// for meta pool
BasePoolAddress string
UnderlyingCoins []CurveCoin
}

GetPoolsResult struct {
Expand Down
Loading
Loading