Skip to content

Commit

Permalink
add megavault withdrawal fee estimation
Browse files Browse the repository at this point in the history
  • Loading branch information
tqin7 committed Sep 12, 2024
1 parent 6db737c commit 32e2eff
Show file tree
Hide file tree
Showing 9 changed files with 801 additions and 47 deletions.
51 changes: 51 additions & 0 deletions protocol/lib/vault/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package vault

import (
"math/big"

"github.com/dydxprotocol/v4-chain/protocol/lib"
pricestypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types"
"github.com/dydxprotocol/v4-chain/protocol/x/vault/types"
)

// SkewAntiderivativePpm returns the antiderivative of skew given a vault's skew
// factor and leverage.
// skew_antiderivative_ppm = skew_factor * leverage^2 + skew_factor^2 * leverage^3 / 3
func SkewAntiderivativePpm(
skewFactorPpm uint32,
leveragePpm *big.Int,
) *big.Int {
bigSkewFactorPpm := new(big.Int).SetUint64(uint64(skewFactorPpm))
bigOneTrillion := lib.BigIntOneTrillion()

// a = skew_factor * leverage^2.
a := new(big.Int).Mul(leveragePpm, leveragePpm)
a.Mul(a, bigSkewFactorPpm)

// b = skew_factor^2 * leverage^3 / 3.
b := new(big.Int).Set(a)
b.Mul(b, leveragePpm)
b.Mul(b, bigSkewFactorPpm)
b = lib.BigDivCeil(b, big.NewInt(3))

// normalize a and b.
a = lib.BigDivCeil(a, bigOneTrillion)
b = lib.BigDivCeil(b, bigOneTrillion)
b = lib.BigDivCeil(b, bigOneTrillion)

// return a + b.
return a.Add(a, b)
}

// SpreadPpm returns the spread that a vault should quote at given its
// quoting params and corresponding market param.
// spread_ppm = max(spread_min_ppm, spread_buffer_ppm + min_price_change_ppm)
func SpreadPpm(
quotingParams *types.QuotingParams,
marketParam *pricestypes.MarketParam,
) uint32 {
return lib.Max(
quotingParams.SpreadMinPpm,
quotingParams.SpreadBufferPpm+marketParam.MinPriceChangePpm,
)
}
118 changes: 118 additions & 0 deletions protocol/lib/vault/utils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package vault_test

import (
"math/big"
"testing"

"github.com/stretchr/testify/require"

"github.com/dydxprotocol/v4-chain/protocol/lib/vault"
pricestypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types"
"github.com/dydxprotocol/v4-chain/protocol/x/vault/types"
)

func TestSkewAntiderivativePpm(t *testing.T) {
tests := map[string]struct {
skewFactorPpm uint32
leveragePpm *big.Int
expected *big.Int
}{
"Zero skew factor and leverage": {
skewFactorPpm: 0,
leveragePpm: big.NewInt(0),
expected: big.NewInt(0),
},
"Non-zero skew factor, zero leverage": {
skewFactorPpm: 1_000_000,
leveragePpm: big.NewInt(0),
expected: big.NewInt(0),
},
"Zero skew factor, non-zero leverage": {
skewFactorPpm: 0,
leveragePpm: big.NewInt(1_000_000),
expected: big.NewInt(0),
},
"Small skew factor and small positive leverage": {
skewFactorPpm: 500_000, // 0.5
leveragePpm: big.NewInt(800_000), // 0.8
// 0.5 * 0.8^2 + 0.5^2 * 0.8^3 / 3 ~= 0.362666
// round up to 0.362667
expected: big.NewInt(362_667),
},
"Small skew factor and small negative leverage": {
skewFactorPpm: 500_000, // 0.5
leveragePpm: big.NewInt(-800_000), // -0.8
// 0.5 * (-0.8)^2 + 0.5^2 * (-0.8)^3 / 3 ~= 0.277333
// round up to 0.277334
expected: big.NewInt(277_334),
},
"Large skew factor and large positive leverage": {
skewFactorPpm: 5_000_000, // 5
leveragePpm: big.NewInt(8_700_000), // 8.7
// 5 * (8.7)^2 + 5^2 * (8.7)^3 / 3 = 5865.975
expected: big.NewInt(5_865_975_000),
},
"Large skew factor and large negative leverage": {
skewFactorPpm: 5_000_000, // 5
leveragePpm: big.NewInt(-8_700_000), // -8.7
// 5 * (-8.7)^2 + 5^2 * (-8.7)^3 / 3 = -5109.075
expected: big.NewInt(-5_109_075_000),
},
}

for name, tc := range tests {
t.Run(name, func(t *testing.T) {
actual := vault.SkewAntiderivativePpm(tc.skewFactorPpm, tc.leveragePpm)
require.Equal(t, tc.expected, actual)
})
}
}

func TestSpreadPpm(t *testing.T) {
tests := map[string]struct {
quotingParams *types.QuotingParams
marketParam *pricestypes.MarketParam
expected uint32
}{
"SpreadMinPpm > SpreadBufferPpm + MinPriceChangePpm": {
quotingParams: &types.QuotingParams{
SpreadMinPpm: 1000,
SpreadBufferPpm: 200,
},
marketParam: &pricestypes.MarketParam{
MinPriceChangePpm: 500,
},
expected: 1000,
},
"SpreadMinPpm < SpreadBufferPpm + MinPriceChangePpm": {
quotingParams: &types.QuotingParams{
SpreadMinPpm: 1000,
SpreadBufferPpm: 600,
},
marketParam: &pricestypes.MarketParam{
MinPriceChangePpm: 500,
},
expected: 1100,
},
"SpreadMinPpm = SpreadBufferPpm + MinPriceChangePpm": {
quotingParams: &types.QuotingParams{
SpreadMinPpm: 1000,
SpreadBufferPpm: 400,
},
marketParam: &pricestypes.MarketParam{
MinPriceChangePpm: 600,
},
expected: 1000,
},
}

for name, tc := range tests {
t.Run(name, func(t *testing.T) {
require.Equal(
t,
tc.expected,
vault.SpreadPpm(tc.quotingParams, tc.marketParam),
)
})
}
}
30 changes: 29 additions & 1 deletion protocol/mocks/PerpetualsKeeper.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

54 changes: 9 additions & 45 deletions protocol/x/vault/keeper/orders.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package keeper

import (
"errors"
"fmt"
"math"
"math/big"
Expand All @@ -11,6 +12,7 @@ import (
"github.com/dydxprotocol/v4-chain/protocol/lib"
"github.com/dydxprotocol/v4-chain/protocol/lib/log"
"github.com/dydxprotocol/v4-chain/protocol/lib/metrics"
"github.com/dydxprotocol/v4-chain/protocol/lib/vault"
clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types"
"github.com/dydxprotocol/v4-chain/protocol/x/vault/types"
)
Expand Down Expand Up @@ -154,31 +156,11 @@ func (k Keeper) GetVaultClobOrders(
vaultId types.VaultId,
) (orders []*clobtypes.Order, err error) {
// Get clob pair, perpetual, market parameter, and market price that correspond to this vault.
clobPair, exists := k.clobKeeper.GetClobPair(ctx, clobtypes.ClobPairId(vaultId.Number))
if !exists || clobPair.Status == clobtypes.ClobPair_STATUS_FINAL_SETTLEMENT {
clobPair, perpetual, marketParam, marketPrice, err := k.GetVaultClobPerpAndMarket(ctx, vaultId)
if errors.Is(err, types.ErrClobPairNotFound) || clobPair.Status == clobtypes.ClobPair_STATUS_FINAL_SETTLEMENT {
return []*clobtypes.Order{}, nil
}
perpId := clobPair.Metadata.(*clobtypes.ClobPair_PerpetualClobMetadata).PerpetualClobMetadata.PerpetualId
perpetual, err := k.perpetualsKeeper.GetPerpetual(ctx, perpId)
if err != nil {
return orders, errorsmod.Wrap(
err,
fmt.Sprintf("VaultId: %v", vaultId),
)
}
marketParam, exists := k.pricesKeeper.GetMarketParam(ctx, perpetual.Params.MarketId)
if !exists {
return orders, errorsmod.Wrap(
types.ErrMarketParamNotFound,
fmt.Sprintf("VaultId: %v", vaultId),
)
}
marketPrice, err := k.pricesKeeper.GetMarketPrice(ctx, perpetual.Params.MarketId)
if err != nil {
return orders, errorsmod.Wrap(
err,
fmt.Sprintf("VaultId: %v", vaultId),
)
} else if err != nil {
return orders, err
} else if marketPrice.Price == 0 {
// Market price can be zero upon market initialization or due to invalid exchange config.
return orders, errorsmod.Wrap(
Expand All @@ -187,26 +169,11 @@ func (k Keeper) GetVaultClobOrders(
)
}

// Calculate leverage = open notional / equity.
equity, err := k.GetVaultEquity(ctx, vaultId)
// Get vault leverage and equity.
leveragePpm, equity, err := k.GetVaultLeverageAndEquity(ctx, vaultId, perpetual, marketPrice)
if err != nil {
return orders, err
}
if equity.Sign() <= 0 {
return orders, errorsmod.Wrap(
types.ErrNonPositiveEquity,
fmt.Sprintf("VaultId: %v", vaultId),
)
}
inventory := k.GetVaultInventoryInPerpetual(ctx, vaultId, perpId)
openNotional := lib.BaseToQuoteQuantums(
inventory,
perpetual.Params.AtomicResolution,
marketPrice.GetPrice(),
marketPrice.GetExponent(),
)
leveragePpm := new(big.Int).Mul(openNotional, lib.BigIntOneMillion())
leveragePpm.Quo(leveragePpm, equity)

// Get vault parameters.
quotingParams, exists := k.GetVaultQuotingParams(ctx, vaultId)
Expand Down Expand Up @@ -247,10 +214,7 @@ func (k Keeper) GetVaultClobOrders(
}

// Calculate spread.
spreadPpm := lib.BigU(lib.Max(
quotingParams.SpreadMinPpm,
quotingParams.SpreadBufferPpm+marketParam.MinPriceChangePpm,
))
spreadPpm := lib.BigU(vault.SpreadPpm(&quotingParams, &marketParam))
// Get oracle price in subticks.
oracleSubticks := clobtypes.PriceToSubticks(
marketPrice,
Expand Down
Loading

0 comments on commit 32e2eff

Please sign in to comment.