Skip to content

Commit

Permalink
reduce use of big.Rat in various modules (#1564)
Browse files Browse the repository at this point in the history
  • Loading branch information
BrendanChou authored May 28, 2024
1 parent 5abaea9 commit 6f6ec38
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 141 deletions.
33 changes: 0 additions & 33 deletions protocol/x/clob/types/quantums.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"math/big"

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

Expand Down Expand Up @@ -81,35 +80,3 @@ func GetAveragePriceSubticks(
new(big.Rat).SetInt(bigBaseQuantums),
)
}

// NotionalToCoinAmount returns the coin amount (e.g. `uatom`) that has equal worth to the notional (in quote quantums).
// For example, given price of 9.5 USDC/ATOM, notional of 9_500_000 quote quantums, return 1_000_000 `uatom` (since
// `tokenDenomExp“=-6).
// Note the return value is in coin amount, which is different from base quantums.
//
// Given the below by definitions:
//
// quote_quantums * 10^quote_atomic_resolution = full_quote_coin_amount (e.g. 2_000_000 quote quantums * 10^-6 = 2 USDC)
// coin_amount * 10^denom_exponent = full_coin_amount (e.g. 1_000_000 uatom * 10^-6 = 1 ATOM)
// full_coin_amount * coin_price = full_quote_coin_amount (e.g. 1 ATOM * 9.5 USDC/ATOM = 9.5 USDC)
//
// Therefore:
//
// coin_amount * 10^denom_exponent * coin_price = quote_quantums * 10^quote_atomic_resolution
// coin_amount = quote_quantums * 10^(quote_atomic_resolution - denom_exponent) / coin_price
func NotionalToCoinAmount(
notionalQuoteQuantums *big.Int,
quoteAtomicResolution int32,
denomExp int32,
marketPrice pricestypes.MarketPrice,
) *big.Rat {
fullCoinPrice := lib.BigMulPow10(
new(big.Int).SetUint64(marketPrice.Price),
marketPrice.Exponent,
)
ret := lib.BigMulPow10(notionalQuoteQuantums, quoteAtomicResolution-denomExp)
return ret.Quo(
ret,
fullCoinPrice,
)
}
62 changes: 0 additions & 62 deletions protocol/x/clob/types/quantums_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ import (
"math/big"
"testing"

"github.com/dydxprotocol/v4-chain/protocol/lib"
big_testutil "github.com/dydxprotocol/v4-chain/protocol/testutil/big"
"github.com/dydxprotocol/v4-chain/protocol/x/clob/types"
pricestypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types"
satypes "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -139,63 +137,3 @@ func TestGetAveragePriceSubticks(t *testing.T) {
})
}
}

func TestNotionalToCoinAmount(t *testing.T) {
tests := map[string]struct {
notionalQuoteQuantums *big.Int
denomExp int32
marketPrice pricestypes.MarketPrice
bigRatExpectedCoinAmount *big.Rat
}{
"$9.5 notional, ATOM price at $9.5, get amount in `uatom` (exp = -6)": {
notionalQuoteQuantums: big.NewInt(9_500_000),
marketPrice: pricestypes.MarketPrice{
Price: 95_000,
Exponent: -4,
},
denomExp: -6,
bigRatExpectedCoinAmount: big.NewRat(1_000_000, 1),
},
"$4.75 notional, ATOM price at $9.5, get amount in `uatom` (exp = -6)": {
notionalQuoteQuantums: big.NewInt(4_750_000),
marketPrice: pricestypes.MarketPrice{
Price: 95_000,
Exponent: -4,
},
denomExp: -6,
bigRatExpectedCoinAmount: big.NewRat(500_000, 1),
},
"$10.5 notional, ETH price at $2000, get amount in `gwei` (exp = -9)": {
notionalQuoteQuantums: big.NewInt(10_500_000),
marketPrice: pricestypes.MarketPrice{
Price: 20_000_000_000,
Exponent: -7,
},
denomExp: -9,
bigRatExpectedCoinAmount: big.NewRat(5_250_000, 1),
},
"$1000 notional, ETH price at $2001.57, get amount in `gwei` (exp = -9)": {
notionalQuoteQuantums: big.NewInt(1_000_000_000),
marketPrice: pricestypes.MarketPrice{
Price: 20_015_700_000,
Exponent: -7,
},
denomExp: -9,
bigRatExpectedCoinAmount: big.NewRat(100_000_000_000_000, 200157), // 499607807.871 Gwei, or 0.499607807871 Eth.
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
bigRatCoinAmount := types.NotionalToCoinAmount(
tc.notionalQuoteQuantums,
lib.QuoteCurrencyAtomicResolution,
tc.denomExp,
tc.marketPrice,
)
require.Equal(t,
tc.bigRatExpectedCoinAmount,
bigRatCoinAmount,
)
})
}
}
8 changes: 3 additions & 5 deletions protocol/x/perpetuals/keeper/perpetual.go
Original file line number Diff line number Diff line change
Expand Up @@ -992,11 +992,9 @@ func GetMarginRequirementsInQuoteQuantums(
big.NewInt(0), // pass in 0 as open interest to get base IMR.
)
// Maintenance margin requirement quote quantums = IM in quote quantums * maintenance fraction PPM.
bigMaintenanceMarginQuoteQuantums = lib.BigRatRound(
lib.BigRatMulPpm(
new(big.Rat).SetInt(bigBaseInitialMarginQuoteQuantums),
liquidityTier.MaintenanceFractionPpm,
),
bigMaintenanceMarginQuoteQuantums = lib.BigMulPpm(
bigBaseInitialMarginQuoteQuantums,
lib.BigU(liquidityTier.MaintenanceFractionPpm),
true,
)

Expand Down
23 changes: 7 additions & 16 deletions protocol/x/rewards/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ import (
"github.com/dydxprotocol/v4-chain/protocol/lib"
"github.com/dydxprotocol/v4-chain/protocol/lib/log"
"github.com/dydxprotocol/v4-chain/protocol/lib/metrics"
assettypes "github.com/dydxprotocol/v4-chain/protocol/x/assets/types"
clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types"
"github.com/dydxprotocol/v4-chain/protocol/x/rewards/types"
)

Expand Down Expand Up @@ -272,10 +270,6 @@ func (k Keeper) ProcessRewardsForBlock(
params := k.GetParams(ctx)

// Calculate value of `F`.
usdcAsset, exists := k.assetsKeeper.GetAsset(ctx, assettypes.AssetUsdc.Id)
if !exists {
return fmt.Errorf("failed to get USDC asset")
}
rewardTokenPrice, err := k.pricesKeeper.GetMarketPrice(ctx, params.GetMarketId())
if err != nil {
return fmt.Errorf("failed to get market price of reward token: %w", err)
Expand All @@ -287,23 +281,20 @@ func (k Keeper) ProcessRewardsForBlock(
types.ModuleName,
metrics.TotalRewardShareWeight,
)
bigRatRewardTokenAmount := clobtypes.NotionalToCoinAmount(
totalRewardWeight,
usdcAsset.AtomicResolution,
totalRewardWeightPpm := new(big.Int).Mul(totalRewardWeight, lib.BigU(params.FeeMultiplierPpm))
rewardTokenAmountPpm := lib.QuoteToBaseQuantums(
totalRewardWeightPpm,
params.DenomExponent,
rewardTokenPrice,
)
bigRatRewardTokenAmount = lib.BigRatMulPpm(
bigRatRewardTokenAmount,
params.FeeMultiplierPpm,
rewardTokenPrice.Price,
rewardTokenPrice.Exponent,
)
bigIntRewardTokenAmount := lib.BigRatRound(bigRatRewardTokenAmount, false)
rewardTokenAmount := new(big.Int).Div(rewardTokenAmountPpm, lib.BigIntOneMillion())

// Calculate value of `T`, the reward tokens balance in the `treasury_account`.
rewardTokenBalance := k.bankKeeper.GetBalance(ctx, types.TreasuryModuleAddress, params.Denom)

// Get tokenToDistribute as the min(F, T).
tokensToDistribute := lib.BigMin(rewardTokenBalance.Amount.BigInt(), bigIntRewardTokenAmount)
tokensToDistribute := lib.BigMin(rewardTokenBalance.Amount.BigInt(), rewardTokenAmount)
// Measure distributed token amount.
telemetry.SetGauge(
metrics.GetMetricValueFromBigInt(tokensToDistribute),
Expand Down
52 changes: 27 additions & 25 deletions protocol/x/vault/keeper/orders.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,36 +180,38 @@ func (k Keeper) GetVaultClobOrders(
marketPrice.GetPrice(),
marketPrice.GetExponent(),
)
leverage := new(big.Rat).Quo(
new(big.Rat).SetInt(openNotional),
new(big.Rat).SetInt(equity),
)
leverage := new(big.Rat).SetFrac(openNotional, equity)

// Get parameters.
params := k.GetParams(ctx)

// Calculate order size (in base quantums).
// order_size = order_size_pct * equity / oracle_price
// = order_size_pct * equity / (price * 10^exponent / 10^quote_atomic_resolution) / 10^base_atomic_resolution
// = order_size_pct * equity / (price * 10^(exponent - quote_atomic_resolution + base_atomic_resolution))
orderSizeBaseQuantums := lib.BigRatMulPpm(
new(big.Rat).SetInt(equity),
params.OrderSizePctPpm,
)
orderSizeBaseQuantums = orderSizeBaseQuantums.Quo(
orderSizeBaseQuantums,
lib.BigMulPow10(
new(big.Int).SetUint64(marketPrice.Price),
marketPrice.Exponent-lib.QuoteCurrencyAtomicResolution+perpetual.Params.AtomicResolution,
),
)
orderSizeBaseQuantumsRounded := lib.BigRatRoundToNearestMultiple(
orderSizeBaseQuantums,
uint32(clobPair.StepBaseQuantums),
false,
orderSizePctPpm := lib.BigU(params.OrderSizePctPpm)
orderSize := lib.QuoteToBaseQuantums(
new(big.Int).Mul(equity, orderSizePctPpm),
perpetual.Params.AtomicResolution,
marketPrice.Price,
marketPrice.Exponent,
)
// If order size is non-positive, return empty orders.
if orderSizeBaseQuantumsRounded <= 0 {
orderSize.Quo(orderSize, lib.BigIntOneMillion())

// Round (towards-zero) order size to the nearest multiple of step size.
stepSize := lib.BigU(clobPair.StepBaseQuantums)
orderSize.Quo(orderSize, stepSize).Mul(orderSize, stepSize)

// If order size is zero, return empty orders.
if orderSize.Sign() == 0 {
return []*clobtypes.Order{}, nil
}

// If order size is not a valid uint64, return error.
if !orderSize.IsUint64() {
return []*clobtypes.Order{}, errorsmod.Wrap(
types.ErrInvalidOrderSize,
fmt.Sprintf("VaultId: %v", vaultId),
)
}

// Calculate spread.
spreadPpm := lib.Max(
params.SpreadMinPpm,
Expand Down Expand Up @@ -298,7 +300,7 @@ func (k Keeper) GetVaultClobOrders(
ClobPairId: clobPair.Id,
},
Side: side,
Quantums: orderSizeBaseQuantumsRounded,
Quantums: orderSize.Uint64(), // Validated to be a uint64 above.
Subticks: lib.BigRatRoundToNearestMultiple(
orderSubticks,
clobPair.SubticksPerTick,
Expand Down
5 changes: 5 additions & 0 deletions protocol/x/vault/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,9 @@ var (
13,
"ActivationThresholdQuoteQuantums must be non-negative",
)
ErrInvalidOrderSize = errorsmod.Register(
ModuleName,
14,
"OrderSize is invalid",
)
)

0 comments on commit 6f6ec38

Please sign in to comment.