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

FeePricer: use different multipliers for quote / relay #2663

Merged
merged 7 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
46 changes: 26 additions & 20 deletions services/rfq/relayer/pricer/fee_pricer.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@
// Start starts the fee pricer.
Start(ctx context.Context)
// GetOriginFee returns the total fee for a given chainID and gas limit, denominated in a given token.
GetOriginFee(ctx context.Context, origin, destination uint32, denomToken string, useMultiplier bool) (*big.Int, error)
GetOriginFee(ctx context.Context, origin, destination uint32, denomToken string, isQuote bool) (*big.Int, error)
// GetDestinationFee returns the total fee for a given chainID and gas limit, denominated in a given token.
GetDestinationFee(ctx context.Context, origin, destination uint32, denomToken string, useMultiplier bool) (*big.Int, error)
GetDestinationFee(ctx context.Context, origin, destination uint32, denomToken string, isQuote bool) (*big.Int, error)
// GetTotalFee returns the total fee for a given origin and destination chainID, denominated in a given token.
GetTotalFee(ctx context.Context, origin, destination uint32, denomToken string, useMultiplier bool) (*big.Int, error)
GetTotalFee(ctx context.Context, origin, destination uint32, denomToken string, isQuote bool) (*big.Int, error)
// GetGasPrice returns the gas price for a given chainID in native units.
GetGasPrice(ctx context.Context, chainID uint32) (*big.Int, error)
}
Expand Down Expand Up @@ -81,13 +81,13 @@

var nativeDecimalsFactor = new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(18)), nil)

func (f *feePricer) GetOriginFee(parentCtx context.Context, origin, destination uint32, denomToken string, useMultiplier bool) (*big.Int, error) {
func (f *feePricer) GetOriginFee(parentCtx context.Context, origin, destination uint32, denomToken string, isQuote bool) (*big.Int, error) {
var err error
ctx, span := f.handler.Tracer().Start(parentCtx, "getOriginFee", trace.WithAttributes(
attribute.Int(metrics.Origin, int(origin)),
attribute.Int(metrics.Destination, int(destination)),
attribute.String("denom_token", denomToken),
attribute.Bool("use_multiplier", useMultiplier),
attribute.Bool("is_quote", isQuote),
))
defer func() {
metrics.EndSpanWithErr(span, err)
Expand All @@ -98,15 +98,15 @@
if err != nil {
return nil, fmt.Errorf("could not get origin gas estimate: %w", err)
}
fee, err := f.getFee(ctx, origin, destination, gasEstimate, denomToken, useMultiplier)
fee, err := f.getFee(ctx, origin, destination, gasEstimate, denomToken, isQuote)
if err != nil {
return nil, err
}

// If specified, calculate and add the L1 fee
l1ChainID, l1GasEstimate, useL1Fee := f.config.GetL1FeeParams(origin, true)
if useL1Fee {
l1Fee, err := f.getFee(ctx, l1ChainID, destination, l1GasEstimate, denomToken, useMultiplier)
l1Fee, err := f.getFee(ctx, l1ChainID, destination, l1GasEstimate, denomToken, isQuote)
if err != nil {
return nil, err
}
Expand All @@ -117,12 +117,12 @@
return fee, nil
}

func (f *feePricer) GetDestinationFee(parentCtx context.Context, _, destination uint32, denomToken string, useMultiplier bool) (*big.Int, error) {
func (f *feePricer) GetDestinationFee(parentCtx context.Context, _, destination uint32, denomToken string, isQuote bool) (*big.Int, error) {
var err error
ctx, span := f.handler.Tracer().Start(parentCtx, "getDestinationFee", trace.WithAttributes(
attribute.Int(metrics.Destination, int(destination)),
attribute.String("denom_token", denomToken),
attribute.Bool("use_multiplier", useMultiplier),
attribute.Bool("is_quote", isQuote),
))
defer func() {
metrics.EndSpanWithErr(span, err)
Expand All @@ -133,15 +133,15 @@
if err != nil {
return nil, fmt.Errorf("could not get dest gas estimate: %w", err)
}
fee, err := f.getFee(ctx, destination, destination, gasEstimate, denomToken, useMultiplier)
fee, err := f.getFee(ctx, destination, destination, gasEstimate, denomToken, isQuote)
if err != nil {
return nil, err
}

// If specified, calculate and add the L1 fee
l1ChainID, l1GasEstimate, useL1Fee := f.config.GetL1FeeParams(destination, false)
if useL1Fee {
l1Fee, err := f.getFee(ctx, l1ChainID, destination, l1GasEstimate, denomToken, useMultiplier)
l1Fee, err := f.getFee(ctx, l1ChainID, destination, l1GasEstimate, denomToken, isQuote)
if err != nil {
return nil, err
}
Expand All @@ -152,26 +152,26 @@
return fee, nil
}

func (f *feePricer) GetTotalFee(parentCtx context.Context, origin, destination uint32, denomToken string, useMultiplier bool) (_ *big.Int, err error) {
func (f *feePricer) GetTotalFee(parentCtx context.Context, origin, destination uint32, denomToken string, isQuote bool) (_ *big.Int, err error) {
ctx, span := f.handler.Tracer().Start(parentCtx, "getTotalFee", trace.WithAttributes(
attribute.Int(metrics.Origin, int(origin)),
attribute.Int(metrics.Destination, int(destination)),
attribute.String("denom_token", denomToken),
attribute.Bool("use_multiplier", useMultiplier),
attribute.Bool("is_quote", isQuote),
))

defer func() {
metrics.EndSpanWithErr(span, err)
}()

originFee, err := f.GetOriginFee(ctx, origin, destination, denomToken, useMultiplier)
originFee, err := f.GetOriginFee(ctx, origin, destination, denomToken, isQuote)
if err != nil {
span.AddEvent("could not get origin fee", trace.WithAttributes(
attribute.String("error", err.Error()),
))
return nil, err
}
destFee, err := f.GetDestinationFee(ctx, origin, destination, denomToken, useMultiplier)
destFee, err := f.GetDestinationFee(ctx, origin, destination, denomToken, isQuote)
if err != nil {
span.AddEvent("could not get destination fee", trace.WithAttributes(
attribute.String("error", err.Error()),
Expand All @@ -187,7 +187,7 @@
return totalFee, nil
}

func (f *feePricer) getFee(parentCtx context.Context, gasChain, denomChain uint32, gasEstimate int, denomToken string, useMultiplier bool) (_ *big.Int, err error) {
func (f *feePricer) getFee(parentCtx context.Context, gasChain, denomChain uint32, gasEstimate int, denomToken string, isQuote bool) (_ *big.Int, err error) {
ctx, span := f.handler.Tracer().Start(parentCtx, "getFee", trace.WithAttributes(
attribute.Int("gas_chain", int(gasChain)),
attribute.Int("denom_chain", int(denomChain)),
Expand Down Expand Up @@ -241,11 +241,16 @@
)
}

multiplier := 1.
if useMultiplier {
multiplier, err = f.config.GetFixedFeeMultiplier(int(gasChain))
var multiplier float64
if isQuote {
multiplier, err = f.config.GetQuoteFixedFeeMultiplier(int(gasChain))
if err != nil {
return nil, fmt.Errorf("could not get fixed fee multiplier: %w", err)
return nil, fmt.Errorf("could not get quote fixed fee multiplier: %w", err)
}

Check warning on line 249 in services/rfq/relayer/pricer/fee_pricer.go

View check run for this annotation

Codecov / codecov/patch

services/rfq/relayer/pricer/fee_pricer.go#L248-L249

Added lines #L248 - L249 were not covered by tests
} else {
multiplier, err = f.config.GetRelayFixedFeeMultiplier(int(gasChain))
if err != nil {
return nil, fmt.Errorf("could not get relay fixed fee multiplier: %w", err)

Check warning on line 253 in services/rfq/relayer/pricer/fee_pricer.go

View check run for this annotation

Codecov / codecov/patch

services/rfq/relayer/pricer/fee_pricer.go#L253

Added line #L253 was not covered by tests
}
}

Expand All @@ -257,6 +262,7 @@
attribute.String("gas_price", gasPrice.String()),
attribute.Float64("native_token_price", nativeTokenPrice),
attribute.Float64("denom_token_price", denomTokenPrice),
attribute.Float64("multplier", multiplier),
attribute.Int("denom_token_decimals", int(denomTokenDecimals)),
attribute.String("fee_wei", feeWei.String()),
attribute.String("fee_denom", feeDenom.String()),
Expand Down
17 changes: 13 additions & 4 deletions services/rfq/relayer/pricer/fee_pricer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,8 @@ func (s *PricerSuite) TestGetGasPrice() {

func (s *PricerSuite) TestGetTotalFeeWithMultiplier() {
// Override the fixed fee multiplier to greater than 1.
s.config.BaseChainConfig.FixedFeeMultiplier = 2
s.config.BaseChainConfig.QuoteFixedFeeMultiplier = 2
s.config.BaseChainConfig.RelayFixedFeeMultiplier = 4

// Build a new FeePricer with a mocked client for fetching gas price.
clientFetcher := new(fetcherMocks.ClientFetcher)
Expand All @@ -277,16 +278,24 @@ func (s *PricerSuite) TestGetTotalFeeWithMultiplier() {
feePricer := pricer.NewFeePricer(s.config, clientFetcher, priceFetcher, metrics.NewNullHandler())
go func() { feePricer.Start(s.GetTestContext()) }()

// Calculate the total fee.
// Calculate the total fee [quote].
fee, err := feePricer.GetTotalFee(s.GetTestContext(), s.origin, s.destination, "USDC", true)
s.NoError(err)

// The expected fee should be the sum of the Origin and Destination fees, i.e. 200_500_000.
expectedFee := big.NewInt(200_500_000) // 200.50 usd
s.Equal(expectedFee, fee)

// Calculate the total fee [relay].
fee, err = feePricer.GetTotalFee(s.GetTestContext(), s.origin, s.destination, "USDC", false)
s.NoError(err)

// The expected fee should be the sum of the Origin and Destination fees, i.e. 401_000_000.
expectedFee = big.NewInt(401_000_000) // 401 usd
s.Equal(expectedFee, fee)

// Override the fixed fee multiplier to less than 1; should default to 1.
s.config.BaseChainConfig.FixedFeeMultiplier = -1
s.config.BaseChainConfig.QuoteFixedFeeMultiplier = -1

// Build a new FeePricer with a mocked client for fetching gas price.
clientOrigin.On(testsuite.GetFunctionName(clientOrigin.SuggestGasPrice), mock.Anything).Once().Return(headerOrigin, nil)
Expand All @@ -305,7 +314,7 @@ func (s *PricerSuite) TestGetTotalFeeWithMultiplier() {
s.Equal(expectedFee, fee)

// Reset the fixed fee multiplier to zero; should default to 1
s.config.BaseChainConfig.FixedFeeMultiplier = 0
s.config.BaseChainConfig.QuoteFixedFeeMultiplier = 0

// Build a new FeePricer with a mocked client for fetching gas price.
clientOrigin.On(testsuite.GetFunctionName(clientOrigin.SuggestGasPrice), mock.Anything).Once().Return(headerOrigin, nil)
Expand Down
6 changes: 4 additions & 2 deletions services/rfq/relayer/relconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,10 @@ type ChainConfig struct {
// QuoteWidthBps is the number of basis points to deduct from the dest amount.
// Note that this parameter is applied on a chain level and must be positive.
QuoteWidthBps float64 `yaml:"quote_width_bps"`
// FixedFeeMultiplier is the multiplier for the fixed fee.
FixedFeeMultiplier float64 `yaml:"fixed_fee_multiplier"`
// QuoteFixedFeeMultiplier is the multiplier for the fixed fee, applied when generating quotes.
QuoteFixedFeeMultiplier float64 `yaml:"quote_fixed_fee_multiplier"`
// RelayFixedFeeMultiplier is the multiplier for the fixed fee, applied when relaying.
RelayFixedFeeMultiplier float64 `yaml:"relay_fixed_fee_multiplier"`
// CCTP start block is the block at which the chain listener will listen for CCTP events.
CCTPStartBlock uint64 `yaml:"cctp_start_block"`
}
Expand Down
134 changes: 67 additions & 67 deletions services/rfq/relayer/relconfig/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,59 +18,59 @@ func TestChainGetters(t *testing.T) {
cfgWithBase := relconfig.Config{
Chains: map[int]relconfig.ChainConfig{
chainID: {
RFQAddress: "0x123",
SynapseCCTPAddress: "0x456",
TokenMessengerAddress: "0x789",
Confirmations: 1,
NativeToken: "MATIC",
DeadlineBufferSeconds: 10,
OriginGasEstimate: 10000,
DestGasEstimate: 20000,
L1FeeChainID: 10,
L1FeeOriginGasEstimate: 30000,
L1FeeDestGasEstimate: 40000,
MinGasToken: "1000",
QuotePct: 50,
QuoteWidthBps: 10,
FixedFeeMultiplier: 1.1,
RFQAddress: "0x123",
SynapseCCTPAddress: "0x456",
TokenMessengerAddress: "0x789",
Confirmations: 1,
NativeToken: "MATIC",
DeadlineBufferSeconds: 10,
OriginGasEstimate: 10000,
DestGasEstimate: 20000,
L1FeeChainID: 10,
L1FeeOriginGasEstimate: 30000,
L1FeeDestGasEstimate: 40000,
MinGasToken: "1000",
QuotePct: 50,
QuoteWidthBps: 10,
QuoteFixedFeeMultiplier: 1.1,
},
},
BaseChainConfig: relconfig.ChainConfig{
RFQAddress: "0x1234",
SynapseCCTPAddress: "0x456",
TokenMessengerAddress: "0x789",
Confirmations: 2,
NativeToken: "ARB",
DeadlineBufferSeconds: 11,
OriginGasEstimate: 10001,
DestGasEstimate: 20001,
L1FeeChainID: 11,
L1FeeOriginGasEstimate: 30001,
L1FeeDestGasEstimate: 40001,
MinGasToken: "1001",
QuotePct: 51,
QuoteWidthBps: 11,
FixedFeeMultiplier: 1.2,
RFQAddress: "0x1234",
SynapseCCTPAddress: "0x456",
TokenMessengerAddress: "0x789",
Confirmations: 2,
NativeToken: "ARB",
DeadlineBufferSeconds: 11,
OriginGasEstimate: 10001,
DestGasEstimate: 20001,
L1FeeChainID: 11,
L1FeeOriginGasEstimate: 30001,
L1FeeDestGasEstimate: 40001,
MinGasToken: "1001",
QuotePct: 51,
QuoteWidthBps: 11,
QuoteFixedFeeMultiplier: 1.2,
},
}
cfg := relconfig.Config{
Chains: map[int]relconfig.ChainConfig{
chainID: {
RFQAddress: "0x123",
SynapseCCTPAddress: "0x456",
TokenMessengerAddress: "0x789",
Confirmations: 1,
NativeToken: "MATIC",
DeadlineBufferSeconds: 10,
OriginGasEstimate: 10000,
DestGasEstimate: 20000,
L1FeeChainID: 10,
L1FeeOriginGasEstimate: 30000,
L1FeeDestGasEstimate: 40000,
MinGasToken: "1000",
QuotePct: 50,
QuoteWidthBps: 10,
FixedFeeMultiplier: 1.1,
RFQAddress: "0x123",
SynapseCCTPAddress: "0x456",
TokenMessengerAddress: "0x789",
Confirmations: 1,
NativeToken: "MATIC",
DeadlineBufferSeconds: 10,
OriginGasEstimate: 10000,
DestGasEstimate: 20000,
L1FeeChainID: 10,
L1FeeOriginGasEstimate: 30000,
L1FeeDestGasEstimate: 40000,
MinGasToken: "1000",
QuotePct: 50,
QuoteWidthBps: 10,
QuoteFixedFeeMultiplier: 1.1,
Tokens: map[string]relconfig.TokenConfig{
"USDC": {
Address: usdcAddr,
Expand Down Expand Up @@ -278,18 +278,18 @@ func TestChainGetters(t *testing.T) {
assert.Equal(t, chainVal, cfgWithBase.Chains[chainID].QuoteWidthBps)
})

t.Run("GetFixedFeeMultiplier", func(t *testing.T) {
defaultVal, err := cfg.GetFixedFeeMultiplier(badChainID)
t.Run("GetQuoteFixedFeeMultiplier", func(t *testing.T) {
defaultVal, err := cfg.GetQuoteFixedFeeMultiplier(badChainID)
assert.NoError(t, err)
assert.Equal(t, defaultVal, relconfig.DefaultChainConfig.FixedFeeMultiplier)
assert.Equal(t, defaultVal, relconfig.DefaultChainConfig.QuoteFixedFeeMultiplier)

baseVal, err := cfgWithBase.GetFixedFeeMultiplier(badChainID)
baseVal, err := cfgWithBase.GetQuoteFixedFeeMultiplier(badChainID)
assert.NoError(t, err)
assert.Equal(t, baseVal, cfgWithBase.BaseChainConfig.FixedFeeMultiplier)
assert.Equal(t, baseVal, cfgWithBase.BaseChainConfig.QuoteFixedFeeMultiplier)

chainVal, err := cfgWithBase.GetFixedFeeMultiplier(chainID)
chainVal, err := cfgWithBase.GetQuoteFixedFeeMultiplier(chainID)
assert.NoError(t, err)
assert.Equal(t, chainVal, cfgWithBase.Chains[chainID].FixedFeeMultiplier)
assert.Equal(t, chainVal, cfgWithBase.Chains[chainID].QuoteFixedFeeMultiplier)
})

t.Run("GetMaxRebalanceAmount", func(t *testing.T) {
Expand All @@ -307,21 +307,21 @@ func TestGetQuoteOffset(t *testing.T) {
cfg := relconfig.Config{
Chains: map[int]relconfig.ChainConfig{
chainID: {
RFQAddress: "0x123",
SynapseCCTPAddress: "0x456",
TokenMessengerAddress: "0x789",
Confirmations: 1,
NativeToken: "MATIC",
DeadlineBufferSeconds: 10,
OriginGasEstimate: 10000,
DestGasEstimate: 20000,
L1FeeChainID: 10,
L1FeeOriginGasEstimate: 30000,
L1FeeDestGasEstimate: 40000,
MinGasToken: "1000",
QuotePct: 50,
QuoteWidthBps: 10,
FixedFeeMultiplier: 1.1,
RFQAddress: "0x123",
SynapseCCTPAddress: "0x456",
TokenMessengerAddress: "0x789",
Confirmations: 1,
NativeToken: "MATIC",
DeadlineBufferSeconds: 10,
OriginGasEstimate: 10000,
DestGasEstimate: 20000,
L1FeeChainID: 10,
L1FeeOriginGasEstimate: 30000,
L1FeeDestGasEstimate: 40000,
MinGasToken: "1000",
QuotePct: 50,
QuoteWidthBps: 10,
QuoteFixedFeeMultiplier: 1.1,
Tokens: map[string]relconfig.TokenConfig{
"USDC": {
Address: usdcAddr,
Expand Down
Loading
Loading