Support On Chain Fee Quoting (#1658)
### Description * New gas enforcement policy that uses the on-chain-fee-quoting values. * Change gas enforcement policies to return the gas limit that should be used to submit the transaction. * Change gelato submitter to use the gas limit provided by the gas enforcement policy. * Now indexes the gas amounts for the payments A couple of notes: * We might want to test this with the e2e test. * I took a look into the retying provider and it looks like it already does not retry on failed transaction submissions so I don't think anything needs to be done there after all. ### Drive-by changes ### Related issues - Fixes #1535 ### Backward compatibility _Are these changes backward compatible?_ Yes _Are there any infrastructure implications, e.g. changes that would prohibit deploying older commits using this infra tooling?_ Yes - changes the internal database format for message payments ### Testing _What kind of testing have these changes undergone?_ Unit Tests --------- Co-authored-by: Trevor Porter <trkporter@ucdavis.edu>pull/1912/head
parent
59a90b1bb6
commit
0049cca1ba
@ -1,7 +1,9 @@ |
||||
mod meets_estimated_cost; |
||||
mod minimum; |
||||
mod none; |
||||
mod on_chain_fee_quoting; |
||||
|
||||
pub(crate) use meets_estimated_cost::GasPaymentPolicyMeetsEstimatedCost; |
||||
pub(crate) use minimum::GasPaymentPolicyMinimum; |
||||
pub(crate) use none::GasPaymentPolicyNone; |
||||
pub(crate) use on_chain_fee_quoting::GasPaymentPolicyOnChainFeeQuoting; |
||||
|
@ -0,0 +1,195 @@ |
||||
use async_trait::async_trait; |
||||
use eyre::Result; |
||||
|
||||
use hyperlane_core::{ |
||||
HyperlaneMessage, InterchainGasExpenditure, InterchainGasPayment, TxCostEstimate, U256, |
||||
}; |
||||
|
||||
use crate::msg::gas_payment::GasPaymentPolicy; |
||||
|
||||
#[derive(Debug)] |
||||
pub struct GasPaymentPolicyOnChainFeeQuoting { |
||||
/// Numerator value to modify the estimated gas by. The estimated gas value
|
||||
/// is multiplied by this value.
|
||||
fractional_numerator: u64, |
||||
/// Denominator value to modify the estimated gas by. The estimated gas
|
||||
/// value is divided by this value.
|
||||
fractional_denominator: u64, |
||||
} |
||||
|
||||
impl GasPaymentPolicyOnChainFeeQuoting { |
||||
pub fn new(fractional_numerator: u64, fractional_denominator: u64) -> Self { |
||||
Self { |
||||
fractional_numerator, |
||||
fractional_denominator, |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl Default for GasPaymentPolicyOnChainFeeQuoting { |
||||
fn default() -> Self { |
||||
// default to requiring they have paid 1/2 the estimated gas.
|
||||
Self { |
||||
fractional_numerator: 1, |
||||
fractional_denominator: 2, |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[async_trait] |
||||
impl GasPaymentPolicy for GasPaymentPolicyOnChainFeeQuoting { |
||||
async fn message_meets_gas_payment_requirement( |
||||
&self, |
||||
_message: &HyperlaneMessage, |
||||
current_payment: &InterchainGasPayment, |
||||
current_expenditure: &InterchainGasExpenditure, |
||||
tx_cost_estimate: &TxCostEstimate, |
||||
) -> Result<Option<U256>> { |
||||
let fractional_gas_estimate = |
||||
(tx_cost_estimate.gas_limit * self.fractional_numerator) / self.fractional_denominator; |
||||
let gas_amount = current_payment |
||||
.gas_amount |
||||
.saturating_sub(current_expenditure.gas_used); |
||||
// We might want to migrate later to a solution which is a little more
|
||||
// sophisticated. See https://github.com/hyperlane-xyz/hyperlane-monorepo/pull/1658#discussion_r1093243358
|
||||
if gas_amount >= fractional_gas_estimate { |
||||
Ok(Some(tx_cost_estimate.gas_limit.max(gas_amount))) |
||||
} else { |
||||
Ok(None) |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[cfg(test)] |
||||
mod test { |
||||
use hyperlane_core::H256; |
||||
|
||||
use super::*; |
||||
|
||||
fn current_payment(gas_amount: impl Into<U256>) -> InterchainGasPayment { |
||||
InterchainGasPayment { |
||||
message_id: H256::zero(), |
||||
payment: U256::zero(), |
||||
gas_amount: gas_amount.into(), |
||||
} |
||||
} |
||||
|
||||
fn current_expenditure(gas_used: impl Into<U256>) -> InterchainGasExpenditure { |
||||
InterchainGasExpenditure { |
||||
message_id: H256::zero(), |
||||
gas_used: gas_used.into(), |
||||
tokens_used: U256::zero(), |
||||
} |
||||
} |
||||
|
||||
const MIN: U256 = U256([1000, 0, 0, 0]); |
||||
const COST_ESTIMATE: TxCostEstimate = TxCostEstimate { |
||||
gas_limit: U256([2000, 0, 0, 0]), // MIN * 2
|
||||
gas_price: U256([100001, 0, 0, 0]), |
||||
}; |
||||
|
||||
#[test] |
||||
fn ensure_little_endian() { |
||||
assert_eq!(MIN, U256::from(1000u32)); |
||||
} |
||||
|
||||
#[tokio::test] |
||||
async fn test_payment_less_than_min() { |
||||
let policy = GasPaymentPolicyOnChainFeeQuoting::default(); |
||||
let message = HyperlaneMessage::default(); |
||||
|
||||
// If the payment is less than the minimum, returns None
|
||||
assert_eq!( |
||||
policy |
||||
.message_meets_gas_payment_requirement( |
||||
&message, |
||||
¤t_payment(MIN - 1), |
||||
¤t_expenditure(0), |
||||
&COST_ESTIMATE, |
||||
) |
||||
.await |
||||
.unwrap(), |
||||
None |
||||
); |
||||
} |
||||
|
||||
#[tokio::test] |
||||
async fn test_payment_at_least_min() { |
||||
let policy = GasPaymentPolicyOnChainFeeQuoting::default(); |
||||
let message = HyperlaneMessage::default(); |
||||
|
||||
// If the payment is at least the minimum, returns the correct gas amount to use
|
||||
assert_eq!( |
||||
policy |
||||
.message_meets_gas_payment_requirement( |
||||
&message, |
||||
¤t_payment(MIN), |
||||
¤t_expenditure(0), |
||||
&COST_ESTIMATE, |
||||
) |
||||
.await |
||||
.unwrap(), |
||||
Some(COST_ESTIMATE.gas_limit) |
||||
); |
||||
} |
||||
|
||||
#[tokio::test] |
||||
async fn test_uses_full_paid_amount() { |
||||
let policy = GasPaymentPolicyOnChainFeeQuoting::default(); |
||||
let message = HyperlaneMessage::default(); |
||||
|
||||
// Uses the full paid gas amount when it is sufficient
|
||||
assert_eq!( |
||||
policy |
||||
.message_meets_gas_payment_requirement( |
||||
&message, |
||||
¤t_payment(MIN * 2 + 300), |
||||
¤t_expenditure(0), |
||||
&COST_ESTIMATE, |
||||
) |
||||
.await |
||||
.unwrap(), |
||||
Some(MIN * 2 + 300) |
||||
); |
||||
} |
||||
|
||||
#[tokio::test] |
||||
async fn test_accounts_for_expenditure() { |
||||
let policy = GasPaymentPolicyOnChainFeeQuoting::default(); |
||||
let message = HyperlaneMessage::default(); |
||||
|
||||
// Accounts for gas that has already been spent
|
||||
assert_eq!( |
||||
policy |
||||
.message_meets_gas_payment_requirement( |
||||
&message, |
||||
¤t_payment(MIN + 300), |
||||
¤t_expenditure(301), |
||||
&COST_ESTIMATE |
||||
) |
||||
.await |
||||
.unwrap(), |
||||
None |
||||
) |
||||
} |
||||
|
||||
#[tokio::test] |
||||
async fn test_accounts_for_expenditure_when_giving_full_amount() { |
||||
let policy = GasPaymentPolicyOnChainFeeQuoting::default(); |
||||
let message = HyperlaneMessage::default(); |
||||
|
||||
// Accounts for gas that has already been spent
|
||||
assert_eq!( |
||||
policy |
||||
.message_meets_gas_payment_requirement( |
||||
&message, |
||||
¤t_payment(MIN * 2 + 300), |
||||
¤t_expenditure(50), |
||||
&COST_ESTIMATE |
||||
) |
||||
.await |
||||
.unwrap(), |
||||
Some(MIN * 2 + 250) |
||||
) |
||||
} |
||||
} |
@ -0,0 +1,122 @@ |
||||
use std::io::{Read, Write}; |
||||
|
||||
use crate::{ |
||||
Decode, Encode, HyperlaneProtocolError, InterchainGasExpenditure, InterchainGasPayment, H256, |
||||
U256, |
||||
}; |
||||
|
||||
/// Subset of `InterchainGasPayment` excluding the message id which is stored in
|
||||
/// the key.
|
||||
#[derive(Debug, Copy, Clone)] |
||||
pub(super) struct InterchainGasPaymentData { |
||||
pub payment: U256, |
||||
pub gas_amount: U256, |
||||
} |
||||
|
||||
/// Subset of `InterchainGasExpenditure` excluding the message id which is
|
||||
/// stored in the key.
|
||||
#[derive(Debug, Copy, Clone)] |
||||
pub(super) struct InterchainGasExpenditureData { |
||||
pub tokens_used: U256, |
||||
pub gas_used: U256, |
||||
} |
||||
|
||||
impl Default for InterchainGasPaymentData { |
||||
fn default() -> Self { |
||||
Self { |
||||
payment: U256::zero(), |
||||
gas_amount: U256::zero(), |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl InterchainGasPaymentData { |
||||
pub fn complete(self, message_id: H256) -> InterchainGasPayment { |
||||
InterchainGasPayment { |
||||
message_id, |
||||
payment: self.payment, |
||||
gas_amount: self.gas_amount, |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl From<InterchainGasPayment> for InterchainGasPaymentData { |
||||
fn from(p: InterchainGasPayment) -> Self { |
||||
Self { |
||||
payment: p.payment, |
||||
gas_amount: p.gas_amount, |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl Encode for InterchainGasPaymentData { |
||||
fn write_to<W>(&self, writer: &mut W) -> std::io::Result<usize> |
||||
where |
||||
W: Write, |
||||
{ |
||||
Ok(self.payment.write_to(writer)? + self.gas_amount.write_to(writer)?) |
||||
} |
||||
} |
||||
|
||||
impl Decode for InterchainGasPaymentData { |
||||
fn read_from<R>(reader: &mut R) -> Result<Self, HyperlaneProtocolError> |
||||
where |
||||
R: Read, |
||||
Self: Sized, |
||||
{ |
||||
Ok(Self { |
||||
payment: U256::read_from(reader)?, |
||||
gas_amount: U256::read_from(reader)?, |
||||
}) |
||||
} |
||||
} |
||||
|
||||
impl Default for InterchainGasExpenditureData { |
||||
fn default() -> Self { |
||||
Self { |
||||
tokens_used: U256::zero(), |
||||
gas_used: U256::zero(), |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl InterchainGasExpenditureData { |
||||
pub fn complete(self, message_id: H256) -> InterchainGasExpenditure { |
||||
InterchainGasExpenditure { |
||||
message_id, |
||||
tokens_used: self.tokens_used, |
||||
gas_used: self.gas_used, |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl From<InterchainGasExpenditure> for InterchainGasExpenditureData { |
||||
fn from(p: InterchainGasExpenditure) -> Self { |
||||
Self { |
||||
tokens_used: p.tokens_used, |
||||
gas_used: p.gas_used, |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl Encode for InterchainGasExpenditureData { |
||||
fn write_to<W>(&self, writer: &mut W) -> std::io::Result<usize> |
||||
where |
||||
W: Write, |
||||
{ |
||||
Ok(self.tokens_used.write_to(writer)? + self.gas_used.write_to(writer)?) |
||||
} |
||||
} |
||||
|
||||
impl Decode for InterchainGasExpenditureData { |
||||
fn read_from<R>(reader: &mut R) -> Result<Self, HyperlaneProtocolError> |
||||
where |
||||
R: Read, |
||||
Self: Sized, |
||||
{ |
||||
Ok(Self { |
||||
tokens_used: U256::read_from(reader)?, |
||||
gas_used: U256::read_from(reader)?, |
||||
}) |
||||
} |
||||
} |
Loading…
Reference in new issue