Skip to content

Commit 5447c2f

Browse files
authored
fix: curve-aave should expose pool tokens, not underlying (#1144)
* fix: curve-aave should expose pool tokens, not underlying * ft: disable aave underlying swap if DepositFrozen
1 parent 0d5feca commit 5447c2f

File tree

6 files changed

+138
-58
lines changed

6 files changed

+138
-58
lines changed

pkg/source/curve/aave/constant.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
package aave
22

33
import (
4+
"time"
5+
46
"github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/curve"
57
"github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/bignumber"
68
)
79

810
var (
911
DefaultGas = curve.Gas{Exchange: 495000}
1012
Precision = bignumber.NewBig10("1000000000000000000")
13+
14+
DepositFrozen = time.Now().Unix() > 1750939722 // https://vote.onaave.com/proposal/?proposalId=333
1115
)

pkg/source/curve/aave/error.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import "errors"
55
var (
66
ErrZero = errors.New("zero")
77
ErrBalancesMustMatchMultipliers = errors.New("balances must match multipliers")
8+
ErrOverflow = errors.New("overflow")
89
ErrDDoesNotConverge = errors.New("d does not converge")
910
ErrTokenFromEqualsTokenTo = errors.New("can't compare token to itself")
1011
ErrTokenIndexesOutOfRange = errors.New("token index out of range")
@@ -13,4 +14,5 @@ var (
1314
ErrWithdrawMoreThanAvailable = errors.New("cannot withdraw more than available")
1415
ErrD1LowerThanD0 = errors.New("d1 <= d0")
1516
ErrDenominatorZero = errors.New("denominator should not be 0")
17+
ErrInvalidAmountOut = errors.New("invalid amount out")
1618
)

pkg/source/curve/aave/math.go

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -97,40 +97,44 @@ func _getAPrecise(
9797
*/
9898
func getD(xp []*big.Int, a *big.Int) (*big.Int, error) {
9999
var numTokens = len(xp)
100-
var s = new(big.Int).SetInt64(0)
100+
var s = new(big.Int)
101101
for i := 0; i < numTokens; i++ {
102-
s = new(big.Int).Add(s, xp[i])
102+
s = s.Add(s, xp[i])
103103
}
104104
if s.Sign() == 0 {
105105
return s, nil
106106
}
107107
var numTokensBI = big.NewInt(int64(numTokens))
108-
var prevD *big.Int
108+
var prevD big.Int
109109
var d = new(big.Int).Set(s)
110110
var nA = new(big.Int).Mul(a, numTokensBI)
111+
var tmp, tmp2, mul big.Int
111112
for i := 0; i < MaxLoopLimit; i++ {
112113
var dP = new(big.Int).Set(d)
113114
for j := 0; j < numTokens; j++ {
114-
dP = new(big.Int).Div(
115-
new(big.Int).Mul(dP, d),
116-
new(big.Int).Add(new(big.Int).Mul(xp[j], numTokensBI), bignumber.One), // +1 is to prevent /0 (https://github.com/curvefi/curve-contract/blob/d4e8589/contracts/pools/aave/StableSwapAave.vy#L299)
115+
dP = dP.Div(
116+
dP.Mul(dP, d),
117+
tmp.Add(tmp.Mul(xp[j], numTokensBI), bignumber.One), // +1 is to prevent /0 (https://github.com/curvefi/curve-contract/blob/d4e8589/contracts/pools/aave/StableSwapAave.vy#L299)
117118
)
118119
}
119-
prevD = d
120-
d = new(big.Int).Div(
121-
new(big.Int).Mul(
122-
new(big.Int).Add(
123-
new(big.Int).Div(new(big.Int).Mul(nA, s), APrecision),
124-
new(big.Int).Mul(dP, numTokensBI),
125-
),
126-
d,
120+
prevD.Set(d)
121+
if mul.Mul(
122+
tmp.Add(
123+
tmp.Div(tmp.Mul(nA, s), APrecision),
124+
tmp2.Mul(dP, numTokensBI),
127125
),
128-
new(big.Int).Add(
129-
new(big.Int).Div(new(big.Int).Mul(new(big.Int).Sub(nA, APrecision), d), APrecision),
130-
new(big.Int).Mul(dP, big.NewInt(int64(numTokens+1))),
126+
d,
127+
).Cmp(bignumber.MAX_UINT_256) > 0 {
128+
return nil, ErrOverflow
129+
}
130+
d = d.Div(
131+
&mul,
132+
tmp.Add(
133+
tmp.Div(tmp.Mul(tmp.Sub(nA, APrecision), d), APrecision),
134+
tmp2.Mul(dP, big.NewInt(int64(numTokens+1))),
131135
),
132136
)
133-
if new(big.Int).Sub(d, prevD).CmpAbs(big.NewInt(1)) <= 0 {
137+
if tmp.Sub(d, &prevD).CmpAbs(bignumber.One) <= 0 {
134138
return d, nil
135139
}
136140
}

pkg/source/curve/aave/pool_simulator.go

Lines changed: 102 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ package aave
33
import (
44
"fmt"
55
"math/big"
6+
"slices"
67
"strings"
78

89
"github.com/goccy/go-json"
10+
"github.com/samber/lo"
911

1012
"github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity"
1113
"github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/curve"
@@ -15,7 +17,9 @@ import (
1517

1618
type PoolSimulator struct {
1719
pool.Pool
18-
Multipliers []*big.Int
20+
LpToken string
21+
UnderlyingTokens []string
22+
Multipliers []*big.Int
1923
// extra fields
2024
InitialA *big.Int
2125
FutureA *big.Int
@@ -46,7 +50,7 @@ func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) {
4650
var reserves = make([]*big.Int, numTokens)
4751
var multipliers = make([]*big.Int, numTokens)
4852
for i := 0; i < numTokens; i += 1 {
49-
tokens[i] = staticExtra.UnderlyingTokens[i]
53+
tokens[i] = entityPool.Tokens[i].Address
5054
reserves[i] = bignumber.NewBig10(entityPool.Reserves[i])
5155
multipliers[i] = bignumber.NewBig10(staticExtra.PrecisionMultipliers[i])
5256
}
@@ -67,6 +71,8 @@ func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) {
6771
Reserves: reserves,
6872
},
6973
},
74+
LpToken: staticExtra.LpToken,
75+
UnderlyingTokens: staticExtra.UnderlyingTokens,
7076
Multipliers: multipliers,
7177
InitialA: bignumber.NewBig10(extra.InitialA),
7278
FutureA: bignumber.NewBig10(extra.FutureA),
@@ -80,28 +86,23 @@ func NewPoolSimulator(entityPool entity.Pool) (*PoolSimulator, error) {
8086
}
8187

8288
func (t *PoolSimulator) CalcAmountOut(param pool.CalcAmountOutParams) (*pool.CalcAmountOutResult, error) {
83-
tokenAmountIn := param.TokenAmountIn
84-
tokenOut := param.TokenOut
85-
var tokenIndexFrom = t.GetTokenIndex(tokenAmountIn.Token)
86-
var tokenIndexTo = t.GetTokenIndex(tokenOut)
87-
if tokenIndexFrom >= 0 && tokenIndexTo >= 0 {
88-
amountOut, fee, err := GetDyUnderlying(
89-
t.Info.Reserves,
90-
t.Multipliers,
91-
t.FutureATime,
92-
t.FutureA,
93-
t.InitialATime,
94-
t.InitialA,
95-
t.Info.SwapFee,
96-
t.OffpegFeeMultiplier,
97-
tokenIndexFrom,
98-
tokenIndexTo,
89+
tokenAmountIn, tokenOut := param.TokenAmountIn, param.TokenOut
90+
idxIn, idxOut := t.GetTokenIndex(tokenAmountIn.Token), t.GetTokenIndex(tokenOut)
91+
if idxIn >= 0 && idxOut >= 0 {
92+
A := _getAPrecise(t.FutureATime, t.FutureA, t.InitialATime, t.InitialA)
93+
D, err := t.getDPrecision(t.Info.Reserves, A)
94+
if err != nil {
95+
return nil, err
96+
}
97+
amountOut, fee, err := t.GetDy(
98+
idxIn,
99+
idxOut,
99100
tokenAmountIn.Amount,
101+
D,
100102
)
101103
if err != nil {
102104
return nil, err
103-
}
104-
if err == nil && amountOut.Cmp(bignumber.ZeroBI) > 0 {
105+
} else if amountOut.Sign() > 0 {
105106
return &pool.CalcAmountOutResult{
106107
TokenAmountOut: &pool.TokenAmount{
107108
Token: tokenOut,
@@ -114,8 +115,44 @@ func (t *PoolSimulator) CalcAmountOut(param pool.CalcAmountOutParams) (*pool.Cal
114115
Gas: t.gas.Exchange,
115116
}, nil
116117
}
118+
return nil, ErrInvalidAmountOut
119+
} else if DepositFrozen {
120+
return nil, ErrTokenNotFound
117121
}
118-
return &pool.CalcAmountOutResult{}, fmt.Errorf("tokenIndexFrom or tokenIndexTo is not correct: tokenIndexFrom: %v, tokenIndexTo: %v", tokenIndexFrom, tokenIndexTo)
122+
123+
idxIn, idxOut = slices.Index(t.UnderlyingTokens, tokenAmountIn.Token), slices.Index(t.UnderlyingTokens, tokenOut)
124+
if idxIn < 0 || idxOut < 0 {
125+
return nil, ErrTokenNotFound
126+
}
127+
amountOut, fee, err := GetDyUnderlying(
128+
t.Info.Reserves,
129+
t.Multipliers,
130+
t.FutureATime,
131+
t.FutureA,
132+
t.InitialATime,
133+
t.InitialA,
134+
t.Info.SwapFee,
135+
t.OffpegFeeMultiplier,
136+
idxIn,
137+
idxOut,
138+
tokenAmountIn.Amount,
139+
)
140+
if err != nil {
141+
return nil, err
142+
} else if amountOut.Sign() > 0 {
143+
return &pool.CalcAmountOutResult{
144+
TokenAmountOut: &pool.TokenAmount{
145+
Token: tokenOut,
146+
Amount: amountOut,
147+
},
148+
Fee: &pool.TokenAmount{
149+
Token: tokenOut,
150+
Amount: fee,
151+
},
152+
Gas: t.gas.Exchange,
153+
}, nil
154+
}
155+
return nil, ErrInvalidAmountOut
119156
}
120157

121158
func (t *PoolSimulator) UpdateBalance(params pool.UpdateBalanceParams) {
@@ -144,17 +181,49 @@ func (t *PoolSimulator) UpdateBalance(params pool.UpdateBalanceParams) {
144181
}
145182
}
146183

184+
func (t *PoolSimulator) CanSwapTo(address string) []string {
185+
if t.GetTokenIndex(address) >= 0 {
186+
// exchange
187+
return lo.Filter(t.Info.Tokens, func(item string, _ int) bool {
188+
return item != address
189+
})
190+
}
191+
// check from underlying
192+
if DepositFrozen || slices.Index(t.UnderlyingTokens, address) < 0 {
193+
return nil
194+
}
195+
// exchange_underlying can swap underlying token to other underlying tokens
196+
return lo.Filter(t.UnderlyingTokens, func(item string, _ int) bool {
197+
return item != address
198+
})
199+
}
200+
201+
func (t *PoolSimulator) CanSwapFrom(address string) []string { return t.CanSwapTo(address) }
202+
203+
func (t *PoolSimulator) GetTokens() []string {
204+
if DepositFrozen {
205+
return t.Info.Tokens
206+
}
207+
result := make([]string, 0, 2*len(t.UnderlyingTokens))
208+
result = append(result, t.Info.Tokens...)
209+
result = append(result, t.UnderlyingTokens...)
210+
return result
211+
}
212+
147213
func (t *PoolSimulator) GetLpToken() string {
148-
return ""
214+
return t.LpToken
149215
}
150216

151-
func (t *PoolSimulator) GetMetaInfo(tokenIn string, tokenOut string) interface{} {
152-
var fromId = t.GetTokenIndex(tokenIn)
153-
var toId = t.GetTokenIndex(tokenOut)
217+
func (t *PoolSimulator) GetMetaInfo(tokenIn string, tokenOut string) any {
218+
idxIn, idxOut, underlying := t.GetTokenIndex(tokenIn), t.GetTokenIndex(tokenOut), false
219+
if idxIn < 0 || idxOut < 0 {
220+
idxIn, idxOut, underlying = slices.Index(t.UnderlyingTokens, tokenIn), slices.Index(t.UnderlyingTokens,
221+
tokenOut), true
222+
}
154223
return curve.Meta{
155-
TokenInIndex: fromId,
156-
TokenOutIndex: toId,
157-
Underlying: true,
224+
TokenInIndex: idxIn,
225+
TokenOutIndex: idxOut,
226+
Underlying: underlying,
158227
}
159228
}
160229

@@ -192,7 +261,7 @@ func (t *PoolSimulator) AddLiquidity(amounts []*big.Int) (*big.Int, error) {
192261
return nil, ErrD1LowerThanD0
193262
}
194263
var mint_amount *big.Int
195-
if token_supply.Cmp(bignumber.ZeroBI) > 0 {
264+
if token_supply.Sign() > 0 {
196265
ys := new(big.Int).Div(new(big.Int).Add(D0, D1), nCoinsBi)
197266
var _fee = new(big.Int).Div(new(big.Int).Mul(t.Info.SwapFee, nCoinsBi),
198267
new(big.Int).Mul(bignumber.Four, big.NewInt(int64(nCoins-1))))
@@ -210,7 +279,8 @@ func (t *PoolSimulator) AddLiquidity(amounts []*big.Int) (*big.Int, error) {
210279
var fee = new(big.Int).Div(new(big.Int).Mul(_dynamicFee(xs, ys, _fee, _feemul), difference), FeeDenominator)
211280
new_balances[i] = new(big.Int).Sub(new_balances[i], fee)
212281
}
213-
D2, _ := t.getDPrecision(new_balances, amp)
282+
D2, err := t.getDPrecision(new_balances, amp)
283+
fmt.Println(err)
214284
mint_amount = new(big.Int).Div(new(big.Int).Mul(token_supply, new(big.Int).Sub(D2, D0)), D0)
215285
} else {
216286
for i := 0; i < nCoins; i += 1 {
@@ -297,12 +367,12 @@ func (t *PoolSimulator) GetDy(i int, j int, dx *big.Int, dCached *big.Int) (*big
297367
}
298368

299369
func (t *PoolSimulator) GetVirtualPrice() (*big.Int, *big.Int, error) {
300-
var A = _getAPrecise(t.FutureATime, t.FutureA, t.InitialATime, t.InitialA)
370+
A := _getAPrecise(t.FutureATime, t.FutureA, t.InitialATime, t.InitialA)
301371
D, err := t.getDPrecision(t.Info.Reserves, A)
302372
if err != nil {
303373
return nil, nil, err
304374
}
305-
if t.LpSupply.Cmp(bignumber.ZeroBI) == 0 {
375+
if t.LpSupply.Sign() == 0 {
306376
return nil, nil, ErrDenominatorZero
307377
}
308378
return new(big.Int).Div(new(big.Int).Mul(D, Precision), t.LpSupply), D, nil

pkg/source/curve/aave/pool_simulator_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ func TestCalcAmountOut(t *testing.T) {
2626
out string
2727
expectedOutAmount int64
2828
}{
29-
{"Cu", 100000000, "Bu", 99989535},
30-
{"Cu", 1, "Au", 999897685887},
29+
{"C", 100000000, "B", 99989535},
30+
{"C", 1, "A", 999897685887},
3131
}
3232
p, err := NewPoolSimulator(entity.Pool{
3333
Exchange: "",
@@ -45,10 +45,10 @@ func TestCalcAmountOut(t *testing.T) {
4545
})
4646
require.Nil(t, err)
4747

48-
assert.Equal(t, []string{"Au", "Bu"}, p.CanSwapTo("Cu"))
49-
assert.Equal(t, []string{"Au", "Cu"}, p.CanSwapTo("Bu"))
50-
assert.Equal(t, []string{"Bu", "Cu"}, p.CanSwapTo("Au"))
51-
assert.Equal(t, 0, len(p.CanSwapTo("LP")))
48+
assert.Empty(t, p.CanSwapTo("Cu"))
49+
assert.Empty(t, p.CanSwapTo("Bu"))
50+
assert.Empty(t, p.CanSwapTo("Au"))
51+
assert.Empty(t, p.CanSwapTo("LP"))
5252

5353
for idx, tc := range testcases {
5454
t.Run(fmt.Sprintf("test %d", idx), func(t *testing.T) {

pkg/source/curve/meta/pool_simulator.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,8 +233,6 @@ func (t *PoolSimulator) UpdateBalance(params pool.UpdateBalanceParams) {
233233
}
234234
}
235235

236-
func (t *PoolSimulator) CanSwapFrom(address string) []string { return t.CanSwapTo(address) }
237-
238236
func (t *PoolSimulator) CanSwapTo(address string) []string {
239237
var ret = make([]string, 0)
240238
var tokenIndex = t.GetTokenIndex(address)
@@ -266,6 +264,8 @@ func (t *PoolSimulator) CanSwapTo(address string) []string {
266264
return ret
267265
}
268266

267+
func (t *PoolSimulator) CanSwapFrom(address string) []string { return t.CanSwapTo(address) }
268+
269269
func (t *PoolSimulator) GetMetaInfo(tokenIn string, tokenOut string) interface{} {
270270
fromId, toId := t.GetTokenIndex(tokenIn), t.GetTokenIndex(tokenOut)
271271
if fromId >= 0 && toId >= 0 {

0 commit comments

Comments
 (0)