Skip to content

Commit 8d3be58

Browse files
committed
fix: prevent AMM's from being created outside of range on capped future markets
1 parent 8031219 commit 8d3be58

File tree

3 files changed

+121
-19
lines changed

3 files changed

+121
-19
lines changed

core/execution/common/errors.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,5 @@ var (
9090
ErrIsolatedMarginFullyCollateralised = errors.New("isolated margin not permitted on fully collateralised markets")
9191
// ErrSettlementDataOutOfRange is returned when a capped future receives settlement data that is outside of the acceptable range (either > max price, or neither 0 nor max for binary settlements).
9292
ErrSettlementDataOutOfRange = errors.New("settlement data is outside of the price cap")
93+
ErrAMMBoundsOutsidePriceCap = errors.New("an AMM bound is outside of the price cap")
9394
)

core/execution/future/market.go

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5437,20 +5437,34 @@ func (m *Market) needsRebase(fairPrice *num.Uint) (bool, types.Side, *num.Uint)
54375437
return false, types.SideUnspecified, nil
54385438
}
54395439

5440-
func VerifyAMMBounds(baseParam *num.Uint, lowerParam *num.Uint, upperParam *num.Uint, priceFactor num.Decimal) error {
5441-
base, _ := num.UintFromDecimal(baseParam.ToDecimal().Mul(priceFactor))
5442-
if lowerParam != nil {
5443-
lower, _ := num.UintFromDecimal(lowerParam.ToDecimal().Mul(priceFactor))
5440+
func VerifyAMMBounds(params *types.ConcentratedLiquidityParameters, cap *num.Uint, priceFactor num.Decimal) error {
5441+
base, _ := num.UintFromDecimal(params.Base.ToDecimal().Mul(priceFactor))
5442+
if cap != nil && base.GTE(cap) {
5443+
return common.ErrAMMBoundsOutsidePriceCap
5444+
}
5445+
5446+
if params.LowerBound != nil {
5447+
lower, _ := num.UintFromDecimal(params.LowerBound.ToDecimal().Mul(priceFactor))
54445448
if lower.GTE(base) {
5445-
return fmt.Errorf(fmt.Sprintf("base (%s) as factored by market and asset decimals must be greater than lower bound (%s)", base.String(), lower.String()))
5449+
return fmt.Errorf("base (%s) as factored by market and asset decimals must be greater than lower bound (%s)", base.String(), lower.String())
5450+
}
5451+
5452+
if cap != nil && lower.GTE(cap) {
5453+
return common.ErrAMMBoundsOutsidePriceCap
54465454
}
54475455
}
5448-
if upperParam != nil {
5449-
upper, _ := num.UintFromDecimal(upperParam.ToDecimal().Mul(priceFactor))
5456+
5457+
if params.UpperBound != nil {
5458+
upper, _ := num.UintFromDecimal(params.UpperBound.ToDecimal().Mul(priceFactor))
54505459
if base.GTE(upper) {
5451-
return fmt.Errorf(fmt.Sprintf("upper bound (%s) as factored by market and asset decimals must be greater than base (%s)", upper.String(), base.String()))
5460+
return fmt.Errorf("upper bound (%s) as factored by market and asset decimals must be greater than base (%s)", upper.String(), base.String())
5461+
}
5462+
5463+
if cap != nil && upper.GTE(cap) {
5464+
return common.ErrAMMBoundsOutsidePriceCap
54525465
}
54535466
}
5467+
54545468
return nil
54555469
}
54565470

@@ -5464,7 +5478,7 @@ func (m *Market) SubmitAMM(ctx context.Context, submit *types.SubmitAMM, determi
54645478

54655479
// create the AMM curves but do not confirm it with the engine
54665480
var order *types.Order
5467-
if err := VerifyAMMBounds(submit.Parameters.Base, submit.Parameters.LowerBound, submit.Parameters.UpperBound, m.priceFactor); err != nil {
5481+
if err := VerifyAMMBounds(submit.Parameters, m.capMax, m.priceFactor); err != nil {
54685482
return err
54695483
}
54705484

@@ -5545,7 +5559,7 @@ func (m *Market) AmendAMM(ctx context.Context, amend *types.AmendAMM, determinis
55455559
defer func() { m.idgen = nil }()
55465560

55475561
if amend.Parameters != nil {
5548-
if err := VerifyAMMBounds(amend.Parameters.Base, amend.Parameters.LowerBound, amend.Parameters.UpperBound, m.priceFactor); err != nil {
5562+
if err := VerifyAMMBounds(amend.Parameters, m.capMax, m.priceFactor); err != nil {
55495563
return err
55505564
}
55515565
}

core/execution/future/market_test.go

Lines changed: 96 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6722,13 +6722,100 @@ func TestLiquidityFeeSettingsConstantFee(t *testing.T) {
67226722
}
67236723

67246724
func TestVerifyAMMBounds(t *testing.T) {
6725-
require.Equal(t, "base (8) as factored by market and asset decimals must be greater than lower bound (8)", future.VerifyAMMBounds(num.NewUint(85), num.NewUint(82), num.NewUint(88), num.NewDecimalFromFloat(0.1)).Error())
6726-
require.Equal(t, "upper bound (8) as factored by market and asset decimals must be greater than base (8)", future.VerifyAMMBounds(num.NewUint(85), num.NewUint(78), num.NewUint(88), num.NewDecimalFromFloat(0.1)).Error())
6727-
require.Equal(t, "base (8) as factored by market and asset decimals must be greater than lower bound (8)", future.VerifyAMMBounds(num.NewUint(85), num.NewUint(80), num.NewUint(90), num.NewDecimalFromFloat(0.1)).Error())
6728-
require.NoError(t, future.VerifyAMMBounds(num.NewUint(85), num.NewUint(78), num.NewUint(90), num.NewDecimalFromFloat(0.1)))
6729-
6730-
require.NoError(t, future.VerifyAMMBounds(num.NewUint(85), num.NewUint(82), num.NewUint(88), num.NewDecimalFromFloat(1.1)))
6731-
require.NoError(t, future.VerifyAMMBounds(num.NewUint(85), num.NewUint(78), num.NewUint(88), num.NewDecimalFromFloat(1.1)))
6732-
require.NoError(t, future.VerifyAMMBounds(num.NewUint(85), num.NewUint(80), num.NewUint(90), num.NewDecimalFromFloat(1.1)))
6733-
require.NoError(t, future.VerifyAMMBounds(num.NewUint(85), num.NewUint(78), num.NewUint(90), num.NewDecimalFromFloat(1.1)))
6725+
tests := []struct {
6726+
name string
6727+
params *types.ConcentratedLiquidityParameters
6728+
maxCap *num.Uint
6729+
priceFactor num.Decimal
6730+
expectedErr error
6731+
}{
6732+
{
6733+
name: "normal valid bounds",
6734+
params: &types.ConcentratedLiquidityParameters{
6735+
LowerBound: num.NewUint(82),
6736+
Base: num.NewUint(85),
6737+
UpperBound: num.NewUint(88),
6738+
},
6739+
priceFactor: num.NewDecimalFromFloat(1.1),
6740+
},
6741+
{
6742+
name: "lower greater than base with fewer decimals",
6743+
params: &types.ConcentratedLiquidityParameters{
6744+
LowerBound: num.NewUint(80),
6745+
Base: num.NewUint(85),
6746+
UpperBound: num.NewUint(90),
6747+
},
6748+
priceFactor: num.NewDecimalFromFloat(0.1),
6749+
expectedErr: fmt.Errorf("base (8) as factored by market and asset decimals must be greater than lower bound (8)"),
6750+
},
6751+
{
6752+
name: "base greater than base with fewer decimals",
6753+
params: &types.ConcentratedLiquidityParameters{
6754+
LowerBound: num.NewUint(80),
6755+
Base: num.NewUint(85),
6756+
UpperBound: num.NewUint(88),
6757+
},
6758+
priceFactor: num.NewDecimalFromFloat(0.1),
6759+
expectedErr: fmt.Errorf("base (8) as factored by market and asset decimals must be greater than lower bound (8)"),
6760+
},
6761+
{
6762+
name: "both bounds too close with fewer decimals",
6763+
params: &types.ConcentratedLiquidityParameters{
6764+
LowerBound: num.NewUint(82),
6765+
Base: num.NewUint(85),
6766+
UpperBound: num.NewUint(88),
6767+
},
6768+
priceFactor: num.NewDecimalFromFloat(0.1),
6769+
expectedErr: fmt.Errorf("base (8) as factored by market and asset decimals must be greater than lower bound (8)"),
6770+
},
6771+
{
6772+
name: "upper bound higher than cap",
6773+
params: &types.ConcentratedLiquidityParameters{
6774+
LowerBound: num.NewUint(82),
6775+
Base: num.NewUint(85),
6776+
UpperBound: num.NewUint(88),
6777+
},
6778+
priceFactor: num.NewDecimalFromFloat(1),
6779+
maxCap: num.NewUint(86),
6780+
expectedErr: common.ErrAMMBoundsOutsidePriceCap,
6781+
},
6782+
{
6783+
name: "upper bound equal cap",
6784+
params: &types.ConcentratedLiquidityParameters{
6785+
LowerBound: num.NewUint(82),
6786+
Base: num.NewUint(85),
6787+
UpperBound: num.NewUint(88),
6788+
},
6789+
priceFactor: num.NewDecimalFromFloat(1),
6790+
maxCap: num.NewUint(88),
6791+
expectedErr: common.ErrAMMBoundsOutsidePriceCap,
6792+
},
6793+
{
6794+
name: "base higher than cap",
6795+
params: &types.ConcentratedLiquidityParameters{
6796+
LowerBound: num.NewUint(82),
6797+
Base: num.NewUint(100),
6798+
},
6799+
priceFactor: num.NewDecimalFromFloat(1),
6800+
maxCap: num.NewUint(86),
6801+
expectedErr: common.ErrAMMBoundsOutsidePriceCap,
6802+
},
6803+
{
6804+
name: "base equal cap",
6805+
params: &types.ConcentratedLiquidityParameters{
6806+
LowerBound: num.NewUint(82),
6807+
Base: num.NewUint(88),
6808+
},
6809+
priceFactor: num.NewDecimalFromFloat(1),
6810+
maxCap: num.NewUint(88),
6811+
expectedErr: common.ErrAMMBoundsOutsidePriceCap,
6812+
},
6813+
}
6814+
6815+
for _, tt := range tests {
6816+
t.Run(tt.name, func(t *testing.T) {
6817+
err := future.VerifyAMMBounds(tt.params, tt.maxCap, tt.priceFactor)
6818+
assert.Equal(t, tt.expectedErr, err)
6819+
})
6820+
}
67346821
}

0 commit comments

Comments
 (0)