diff --git a/pkg/source/saddle/pool_simulator.go b/pkg/source/saddle/pool_simulator.go index fd8cbe60c..e24c59909 100644 --- a/pkg/source/saddle/pool_simulator.go +++ b/pkg/source/saddle/pool_simulator.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/goccy/go-json" + "github.com/samber/lo" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" @@ -193,6 +194,15 @@ func (t *PoolSimulator) CalcAmountOut(param pool.CalcAmountOutParams) (*pool.Cal return &pool.CalcAmountOutResult{}, errors.New("i'm dead here") } +func (t *PoolSimulator) CloneState() pool.IPoolSimulator { + cloned := *t + cloned.LpSupply = new(big.Int).Set(t.LpSupply) + cloned.Info.Reserves = lo.Map(t.Info.Reserves, func(v *big.Int, i int) *big.Int { + return new(big.Int).Set(v) + }) + return &cloned +} + func (t *PoolSimulator) UpdateBalance(params pool.UpdateBalanceParams) { input, output, fee := params.TokenAmountIn, params.TokenAmountOut, params.Fee var inputAmount = input.Amount diff --git a/pkg/source/saddle/pool_simulator_test.go b/pkg/source/saddle/pool_simulator_test.go index 8d51768bd..0016aa798 100644 --- a/pkg/source/saddle/pool_simulator_test.go +++ b/pkg/source/saddle/pool_simulator_test.go @@ -10,6 +10,7 @@ import ( "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/bignumber" utils "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/bignumber" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/testutil" ) @@ -37,7 +38,8 @@ func TestCalcAmountOut_Saddle(t *testing.T) { p, err := NewPoolSimulator(entity.Pool{ Exchange: "", Type: "", - Reserves: entity.PoolReserves{"64752405287155128155", "426593278742302082683", "66589357932477536907", "553429429583268691085"}, + Reserves: entity.PoolReserves{"64752405287155128155", "426593278742302082683", "66589357932477536907", + "553429429583268691085"}, Tokens: []*entity.PoolToken{{Address: "A"}, {Address: "B"}, {Address: "C"}}, Extra: "{\"initialA\":\"48000\",\"futureA\":\"92000\",\"initialATime\":1652287436,\"futureATime\":1653655053,\"swapFee\":\"4000000\",\"adminFee\":\"5000000000\"}", StaticExtra: "{\"lpToken\":\"LP\",\"precisionMultipliers\":[\"1\",\"1\",\"1\"]}", @@ -128,7 +130,8 @@ func TestCalcAmountOut_OneSwap(t *testing.T) { p, err := NewPoolSimulator(entity.Pool{ Exchange: "", Type: "", - Reserves: entity.PoolReserves{"339028421564024338437", "347684462442560871352", "423798212946198474118", "315249216225911580289", "1404290718401538825321"}, + Reserves: entity.PoolReserves{"339028421564024338437", "347684462442560871352", "423798212946198474118", + "315249216225911580289", "1404290718401538825321"}, Tokens: []*entity.PoolToken{{Address: "A"}, {Address: "B"}, {Address: "C"}, {Address: "D"}}, Extra: "{\"initialA\":\"60000\",\"futureA\":\"60000\",\"initialATime\":0,\"futureATime\":0,\"swapFee\":\"1000000\",\"adminFee\":\"10000000000\",\"defaultWithdrawFee\":\"5000000\"}", StaticExtra: "{\"lpToken\":\"LP\",\"precisionMultipliers\":[\"1\",\"1\",\"1\",\"1\"]}", @@ -175,7 +178,8 @@ func TestCalcAmountOut_IronStable(t *testing.T) { p, err := NewPoolSimulator(entity.Pool{ Exchange: "", Type: "", - Reserves: entity.PoolReserves{"233518765839", "198509040315", "228986742536043517345011", "654251953025609178732174"}, + Reserves: entity.PoolReserves{"233518765839", "198509040315", "228986742536043517345011", + "654251953025609178732174"}, Tokens: []*entity.PoolToken{{Address: "A"}, {Address: "B"}, {Address: "C"}}, Extra: "{\"initialA\":\"18000\",\"futureA\":\"120000\",\"initialATime\":1627094541,\"futureATime\":1627699238,\"swapFee\":\"2000000\",\"adminFee\":\"10000000000\", \"defaultWithdrawFee\":\"5000000\"}", StaticExtra: "{\"lpToken\":\"LP\",\"precisionMultipliers\":[\"1000000000000\",\"1000000000000\",\"1\"]}", @@ -207,13 +211,18 @@ func TestUpdateBalance_Saddle(t *testing.T) { // test data from https://etherscan.io/address/0xa6018520eaacc06c30ff2e1b3ee2c7c22e64196a#readContract testcases := []struct { in string - inAmount int64 + inAmount string out string expectedBalances []string }{ - {"A", 10000000, "B", []string{"64752405287165128155", "426593278742291992582", "66589357932477536907", "553429429583268691085"}}, - {"A", 10000000, "C", []string{"64752405287175128155", "426593278742291992582", "66589357932467535940", "553429429583268691085"}}, - {"B", 10000000, "A", []string{"64752405287165221417", "426593278742301992582", "66589357932467535940", "553429429583268691085"}}, + {"A", "10000000", "B", + []string{"64752405287165128155", "426593278742291992582", "66589357932477536907", "553429429583268691085"}}, + {"A", "10000000", "C", + []string{"64752405287175128155", "426593278742291992582", "66589357932467535940", "553429429583268691085"}}, + {"B", "10000000", "A", + []string{"64752405287165221417", "426593278742301992582", "66589357932467535940", "553429429583268691085"}}, + {"C", "9500000000000000000", "B", + []string{"64752405287165221417", "417021399572301197888", "76089357932467535940", "553429429583268691085"}}, // cannot test these case because we haven't accounted for token fee when adding/removing liq yet // {"A", 10000000, "LP", []string{"64752405287175220754", "426593278742301992004", "66589357932467535849", "553429429583278677755"}}, @@ -222,7 +231,8 @@ func TestUpdateBalance_Saddle(t *testing.T) { p, err := NewPoolSimulator(entity.Pool{ Exchange: "", Type: "", - Reserves: entity.PoolReserves{"64752405287155128155", "426593278742302082683", "66589357932477536907", "553429429583268691085"}, + Reserves: entity.PoolReserves{"64752405287155128155", "426593278742302082683", "66589357932477536907", + "553429429583268691085"}, Tokens: []*entity.PoolToken{{Address: "A"}, {Address: "B"}, {Address: "C"}}, Extra: "{\"initialA\":\"48000\",\"futureA\":\"92000\",\"initialATime\":1652287436,\"futureATime\":1653655053,\"swapFee\":\"4000000\",\"adminFee\":\"5000000000\"}", StaticExtra: "{\"lpToken\":\"LP\",\"precisionMultipliers\":[\"1\",\"1\",\"1\"]}", @@ -231,7 +241,8 @@ func TestUpdateBalance_Saddle(t *testing.T) { for idx, tc := range testcases { t.Run(fmt.Sprintf("test %d", idx), func(t *testing.T) { - amountIn := pool.TokenAmount{Token: tc.in, Amount: big.NewInt(tc.inAmount)} + cloned := p.CloneState() + amountIn := pool.TokenAmount{Token: tc.in, Amount: bignumber.NewBig10(tc.inAmount)} out, err := testutil.MustConcurrentSafe[*pool.CalcAmountOutResult](t, func() (any, error) { return p.CalcAmountOut(pool.CalcAmountOutParams{ TokenAmountIn: amountIn, @@ -251,6 +262,13 @@ func TestUpdateBalance_Saddle(t *testing.T) { assert.Equal(t, utils.NewBig10(tc.expectedBalances[i]), balance) } assert.Equal(t, utils.NewBig10(tc.expectedBalances[len(p.Info.Reserves)]), p.LpSupply) + + clonedRes, err := cloned.CalcAmountOut(pool.CalcAmountOutParams{ + TokenAmountIn: amountIn, + TokenOut: tc.out, + }) + require.Nil(t, err) + assert.Equal(t, clonedRes.TokenAmountOut, out.TokenAmountOut) }) } }