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

[stableswap]: Add checks for invalid cfmm inputs #2695

Merged
merged 2 commits into from
Sep 14, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
24 changes: 20 additions & 4 deletions x/gamm/pool-models/stableswap/amm.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ var (

// solidly CFMM is xy(x^2 + y^2) = k
func cfmmConstant(xReserve, yReserve sdk.Dec) sdk.Dec {
if !xReserve.IsPositive() || !yReserve.IsPositive() {
panic("invalid input: reserves must be positive")
}
xy := xReserve.Mul(yReserve)
x2 := xReserve.Mul(xReserve)
y2 := yReserve.Mul(yReserve)
Expand All @@ -28,6 +31,10 @@ func cfmmConstant(xReserve, yReserve sdk.Dec) sdk.Dec {
// of their squares (e.g. v = w^2 + z^2).
// When u = 1 and v = 0, this is equivalent to solidly's CFMM
func cfmmConstantMulti(xReserve, yReserve, uReserve, vSumSquares sdk.Dec) sdk.Dec {
if !xReserve.IsPositive() || !yReserve.IsPositive() || !uReserve.IsPositive() || vSumSquares.IsNegative() {
panic("invalid input: reserves must be positive")
}

xyu := xReserve.Mul(yReserve.Mul(uReserve))
x2 := xReserve.Mul(xReserve)
y2 := yReserve.Mul(yReserve)
Expand All @@ -40,8 +47,8 @@ func cfmmConstantMulti(xReserve, yReserve, uReserve, vSumSquares sdk.Dec) sdk.De
// So we solve the following expression for `a`
// xy(x^2 + y^2) = (x - a)(y + b)((x - a)^2 + (y + b)^2)
func solveCfmm(xReserve, yReserve, yIn sdk.Dec) sdk.Dec {
if !yReserve.Add(yIn).IsPositive() {
panic("invalid yReserve, yIn combo")
if !xReserve.IsPositive() || !yReserve.IsPositive() || !yIn.IsPositive() {
panic("invalid input: reserves and input must be positive")
}

// use the following wolfram alpha link to solve the equation
Expand Down Expand Up @@ -151,6 +158,11 @@ func solveCfmm(xReserve, yReserve, yIn sdk.Dec) sdk.Dec {
term3 := term3Numerator.Quo(bpy)

a := term1.Sub(term2).Add(term3)

if a.GTE(xReserve) {
panic("invalid output: greater than full pool reserves")
}

return a
}

Expand All @@ -160,8 +172,8 @@ func solveCfmm(xReserve, yReserve, yIn sdk.Dec) sdk.Dec {
// So we solve the following expression for `a`
// xyz(x^2 + y^2 + w) = (x - a)(y + b)z((x - a)^2 + (y + b)^2 + w)
func solveCfmmMulti(xReserve, yReserve, wSumSquares, yIn sdk.Dec) sdk.Dec {
if !yReserve.Add(yIn).IsPositive() {
panic("invalid yReserve, yIn combo")
if !xReserve.IsPositive() || !yReserve.IsPositive() || !yIn.IsPositive() {
panic("invalid input: reserves and input must be positive")
}

// Use the following wolfram alpha link to solve the equation
Expand Down Expand Up @@ -256,6 +268,10 @@ func solveCfmmMulti(xReserve, yReserve, wSumSquares, yIn sdk.Dec) sdk.Dec {

a := term1.Sub(term2).Add(term3)

if a.GTE(xReserve) {
panic("invalid output: greater than full pool reserves")
}

return a
}

Expand Down
128 changes: 105 additions & 23 deletions x/gamm/pool-models/stableswap/amm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,42 +19,70 @@ func TestCFMMInvariantTwoAssets(t *testing.T) {
kErrTolerance := sdk.OneDec()

tests := []struct {
xReserve sdk.Dec
yReserve sdk.Dec
yIn sdk.Dec
xReserve sdk.Dec
yReserve sdk.Dec
yIn sdk.Dec
expectPanic bool
}{
{
sdk.NewDec(100),
sdk.NewDec(100),
sdk.NewDec(1),
false,
},
{
sdk.NewDec(100),
sdk.NewDec(100),
sdk.NewDec(1000),
false,
},
// {
// sdk.NewDec(100000),
// sdk.NewDec(100000),
// sdk.NewDec(10000),
// },

// panic catching
{ // xReserve negative
AlpinYukseloglu marked this conversation as resolved.
Show resolved Hide resolved
sdk.NewDec(-100),
sdk.NewDec(100),
sdk.NewDec(1),
true,
},
{ // yReserve negative
sdk.NewDec(100),
sdk.NewDec(-100),
sdk.NewDec(1),
true,
},
{ // yIn negative
sdk.NewDec(100),
sdk.NewDec(100),
sdk.NewDec(-1),
true,
},
}

for _, test := range tests {
// using two-asset cfmm
k0 := cfmmConstant(test.xReserve, test.yReserve)
xOut := solveCfmm(test.xReserve, test.yReserve, test.yIn)

k1 := cfmmConstant(test.xReserve.Sub(xOut), test.yReserve.Add(test.yIn))
osmoassert.DecApproxEq(t, k0, k1, kErrTolerance)

// using multi-asset cfmm (should be equivalent with u = 1, w = 0)
k2 := cfmmConstantMulti(test.xReserve, test.yReserve, sdk.OneDec(), sdk.ZeroDec())
osmoassert.DecApproxEq(t, k2, k0, kErrTolerance)
xOut2 := solveCfmmMulti(test.xReserve, test.yReserve, sdk.ZeroDec(), test.yIn)
fmt.Println(xOut2)
k3 := cfmmConstantMulti(test.xReserve.Sub(xOut2), test.yReserve.Add(test.yIn), sdk.OneDec(), sdk.ZeroDec())
osmoassert.DecApproxEq(t, k2, k3, kErrTolerance)
// system under test
sut := func() {
AlpinYukseloglu marked this conversation as resolved.
Show resolved Hide resolved
// using two-asset cfmm
k0 := cfmmConstant(test.xReserve, test.yReserve)
xOut := solveCfmm(test.xReserve, test.yReserve, test.yIn)

k1 := cfmmConstant(test.xReserve.Sub(xOut), test.yReserve.Add(test.yIn))
osmoassert.DecApproxEq(t, k0, k1, kErrTolerance)

// using multi-asset cfmm (should be equivalent with u = 1, w = 0)
k2 := cfmmConstantMulti(test.xReserve, test.yReserve, sdk.OneDec(), sdk.ZeroDec())
osmoassert.DecApproxEq(t, k2, k0, kErrTolerance)
xOut2 := solveCfmmMulti(test.xReserve, test.yReserve, sdk.ZeroDec(), test.yIn)
fmt.Println(xOut2)
AlpinYukseloglu marked this conversation as resolved.
Show resolved Hide resolved
k3 := cfmmConstantMulti(test.xReserve.Sub(xOut2), test.yReserve.Add(test.yIn), sdk.OneDec(), sdk.ZeroDec())
osmoassert.DecApproxEq(t, k2, k3, kErrTolerance)
}

osmoassert.ConditionalPanic(t, test.expectPanic, sut)
}
}

Expand All @@ -67,6 +95,7 @@ func TestCFMMInvariantMultiAssets(t *testing.T) {
uReserve sdk.Dec
wSumSquares sdk.Dec
yIn sdk.Dec
expectPanic bool
}{
{
sdk.NewDec(100),
Expand All @@ -75,28 +104,81 @@ func TestCFMMInvariantMultiAssets(t *testing.T) {
sdk.NewDec(200),
sdk.NewDec(20000),
sdk.NewDec(1),
false,
},
{
sdk.NewDec(100),
sdk.NewDec(100),
sdk.NewDec(200),
sdk.NewDec(20000),
sdk.NewDec(1000),
false,
},
// {
// sdk.NewDec(100000),
// sdk.NewDec(100000),
// sdk.NewDec(10000),
// },

// panic catching
{ // negative xReserve
sdk.NewDec(-100),
sdk.NewDec(100),
// represents a 4-asset pool with 100 in each reserve
sdk.NewDec(200),
sdk.NewDec(20000),
sdk.NewDec(1),
true,
},
{ // negative yReserve
sdk.NewDec(100),
sdk.NewDec(-100),
// represents a 4-asset pool with 100 in each reserve
sdk.NewDec(200),
sdk.NewDec(20000),
sdk.NewDec(1),
true,
},
{ // negative uReserve
sdk.NewDec(100),
sdk.NewDec(100),
// represents a 4-asset pool with 100 in each reserve
sdk.NewDec(-200),
sdk.NewDec(20000),
sdk.NewDec(1),
true,
},
{ // negative sumSquares
sdk.NewDec(100),
sdk.NewDec(100),
// represents a 4-asset pool with 100 in each reserve
sdk.NewDec(200),
sdk.NewDec(-20000),
sdk.NewDec(1),
true,
},
{ // negative yIn
sdk.NewDec(100),
sdk.NewDec(100),
// represents a 4-asset pool with 100 in each reserve
sdk.NewDec(200),
sdk.NewDec(-20000),
sdk.NewDec(1),
true,
},
}

for _, test := range tests {
// using multi-asset cfmm
k2 := cfmmConstantMulti(test.xReserve, test.yReserve, test.uReserve, test.wSumSquares)
xOut2 := solveCfmmMulti(test.xReserve, test.yReserve, test.wSumSquares, test.yIn)
fmt.Println(xOut2)
k3 := cfmmConstantMulti(test.xReserve.Sub(xOut2), test.yReserve.Add(test.yIn), test.uReserve, test.wSumSquares)
osmoassert.DecApproxEq(t, k2, k3, kErrTolerance)
// system under test
AlpinYukseloglu marked this conversation as resolved.
Show resolved Hide resolved
sut := func() {
// using multi-asset cfmm
k2 := cfmmConstantMulti(test.xReserve, test.yReserve, test.uReserve, test.wSumSquares)
xOut2 := solveCfmmMulti(test.xReserve, test.yReserve, test.wSumSquares, test.yIn)
k3 := cfmmConstantMulti(test.xReserve.Sub(xOut2), test.yReserve.Add(test.yIn), test.uReserve, test.wSumSquares)
osmoassert.DecApproxEq(t, k2, k3, kErrTolerance)
}

osmoassert.ConditionalPanic(t, test.expectPanic, sut)
}
}

Expand Down