diff --git a/rust/main/agents/scraper/migration/src/m20230309_000004_create_table_gas_payment.rs b/rust/main/agents/scraper/migration/src/m20230309_000004_create_table_gas_payment.rs index 7f3da9a78..90e2e9fa2 100644 --- a/rust/main/agents/scraper/migration/src/m20230309_000004_create_table_gas_payment.rs +++ b/rust/main/agents/scraper/migration/src/m20230309_000004_create_table_gas_payment.rs @@ -1,3 +1,5 @@ +use std::borrow::BorrowMut as _; + use sea_orm::ConnectionTrait; use sea_orm_migration::prelude::*; @@ -39,6 +41,13 @@ impl MigrationTrait for Migration { .big_unsigned() .not_null(), ) + .col(ColumnDef::new(GasPayment::Origin).unsigned()) + .col(ColumnDef::new(GasPayment::Destination).unsigned()) + .col( + ColumnDef::new_with_type(GasPayment::InterchainGasPaymaster, Address) + .borrow_mut(), + ) + .col(ColumnDef::new(GasPayment::Sequence).big_integer()) .foreign_key( ForeignKey::create() .from_col(GasPayment::TxId) @@ -49,6 +58,16 @@ impl MigrationTrait for Migration { .from_col(GasPayment::Domain) .to(Domain::Table, Domain::Id), ) + .foreign_key( + ForeignKey::create() + .from_col(GasPayment::Origin) + .to(Domain::Table, Domain::Id), + ) + .foreign_key( + ForeignKey::create() + .from_col(GasPayment::Destination) + .to(Domain::Table, Domain::Id), + ) .index( Index::create() // don't need domain because TxId includes it @@ -136,6 +155,16 @@ pub enum GasPayment { /// Used to disambiguate duplicate payments from multiple payments made in /// same transaction. LogIndex, + /// Domain ID of the chain the payment was made on; technically duplicating + /// field Domain, but Domain becomes ambiguous as we add Destination domain as well. + Origin, + /// Domain ID of the chain the payment was made for. + Destination, + /// Interchain Gas Paymaster contract address + InterchainGasPaymaster, + /// Sequence of this payment for indexing by agent. It can be null if agent + /// does not use sequence-aware indexing. + Sequence, } #[derive(Iden)] diff --git a/rust/main/agents/scraper/src/agent.rs b/rust/main/agents/scraper/src/agent.rs index 443d1c7fa..95da9aef5 100644 --- a/rust/main/agents/scraper/src/agent.rs +++ b/rust/main/agents/scraper/src/agent.rs @@ -62,8 +62,9 @@ impl BaseAgent for Scraper { let chain_setup = settings.chain_setup(domain).expect("Missing chain config"); let store = HyperlaneDbStore::new( db.clone(), - chain_setup.addresses.mailbox, domain.clone(), + chain_setup.addresses.mailbox, + chain_setup.addresses.interchain_gas_paymaster, settings .build_provider(domain, &metrics.clone()) .await? diff --git a/rust/main/agents/scraper/src/db/generated/gas_payment.rs b/rust/main/agents/scraper/src/db/generated/gas_payment.rs index d4e3d53ac..0ec04960b 100644 --- a/rust/main/agents/scraper/src/db/generated/gas_payment.rs +++ b/rust/main/agents/scraper/src/db/generated/gas_payment.rs @@ -21,6 +21,10 @@ pub struct Model { pub gas_amount: BigDecimal, pub tx_id: i64, pub log_index: i64, + pub origin: Option, + pub destination: Option, + pub interchain_gas_paymaster: Option>, + pub sequence: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] @@ -33,6 +37,10 @@ pub enum Column { GasAmount, TxId, LogIndex, + Origin, + Destination, + InterchainGasPaymaster, + Sequence, } #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] @@ -49,7 +57,9 @@ impl PrimaryKeyTrait for PrimaryKey { #[derive(Copy, Clone, Debug, EnumIter)] pub enum Relation { + Destination, Domain, + Origin, Transaction, } @@ -65,6 +75,14 @@ impl ColumnTrait for Column { Self::GasAmount => ColumnType::Decimal(Some((78u32, 0u32))).def(), Self::TxId => ColumnType::BigInteger.def(), Self::LogIndex => ColumnType::BigInteger.def(), + Self::Origin => ColumnType::Integer.def().null(), + Self::Destination => ColumnType::Integer.def().null(), + Self::InterchainGasPaymaster => { + ColumnType::Binary(sea_orm::sea_query::BlobSize::Blob(None)) + .def() + .null() + } + Self::Sequence => ColumnType::BigInteger.def().null(), } } } @@ -72,10 +90,18 @@ impl ColumnTrait for Column { impl RelationTrait for Relation { fn def(&self) -> RelationDef { match self { + Self::Destination => Entity::belongs_to(super::domain::Entity) + .from(Column::Destination) + .to(super::domain::Column::Id) + .into(), Self::Domain => Entity::belongs_to(super::domain::Entity) .from(Column::Domain) .to(super::domain::Column::Id) .into(), + Self::Origin => Entity::belongs_to(super::domain::Entity) + .from(Column::Origin) + .to(super::domain::Column::Id) + .into(), Self::Transaction => Entity::belongs_to(super::transaction::Entity) .from(Column::TxId) .to(super::transaction::Column::Id) diff --git a/rust/main/agents/scraper/src/db/payment.rs b/rust/main/agents/scraper/src/db/payment.rs index 498128c34..9e5a4a8b5 100644 --- a/rust/main/agents/scraper/src/db/payment.rs +++ b/rust/main/agents/scraper/src/db/payment.rs @@ -3,7 +3,7 @@ use itertools::Itertools; use sea_orm::{prelude::*, ActiveValue::*, Insert, QuerySelect}; use tracing::{debug, instrument, trace}; -use hyperlane_core::{h256_to_bytes, InterchainGasPayment, LogMeta}; +use hyperlane_core::{address_to_bytes, h256_to_bytes, InterchainGasPayment, LogMeta, H256}; use migration::OnConflict; use crate::conversions::u256_to_decimal; @@ -14,6 +14,7 @@ use super::generated::gas_payment; pub struct StorablePayment<'a> { pub payment: &'a InterchainGasPayment, + pub sequence: Option, pub meta: &'a LogMeta, /// The database id of the transaction the payment was made in pub txn_id: i64, @@ -24,9 +25,11 @@ impl ScraperDb { pub async fn store_payments( &self, domain: u32, + interchain_gas_paymaster: &H256, payments: impl Iterator>, ) -> Result { let latest_id_before = self.latest_payment_id(domain).await?; + let interchain_gas_paymaster = address_to_bytes(interchain_gas_paymaster); // we have a race condition where a message may not have been scraped yet even let models = payments @@ -39,6 +42,10 @@ impl ScraperDb { gas_amount: Set(u256_to_decimal(storable.payment.gas_amount)), tx_id: Unchanged(storable.txn_id), log_index: Unchanged(storable.meta.log_index.as_u64() as i64), + origin: Set(Some(domain as i32)), + destination: Set(Some(storable.payment.destination as i32)), + interchain_gas_paymaster: Set(Some(interchain_gas_paymaster.clone())), + sequence: Set(storable.sequence), }) .collect_vec(); @@ -61,6 +68,10 @@ impl ScraperDb { gas_payment::Column::TimeCreated, gas_payment::Column::Payment, gas_payment::Column::GasAmount, + gas_payment::Column::Origin, + gas_payment::Column::Destination, + gas_payment::Column::InterchainGasPaymaster, + gas_payment::Column::Sequence, ]) .to_owned(), ) diff --git a/rust/main/agents/scraper/src/store/payments.rs b/rust/main/agents/scraper/src/store/payments.rs index 4c5ee465f..09d19cb2a 100644 --- a/rust/main/agents/scraper/src/store/payments.rs +++ b/rust/main/agents/scraper/src/store/payments.rs @@ -28,16 +28,30 @@ impl HyperlaneLogStore for HyperlaneDbStore { let storable = payments .iter() .filter_map(|(payment, meta)| { - txns.get(&meta.transaction_id) - .map(|txn| (payment.inner(), meta, txn.id)) + txns.get(&meta.transaction_id).map(|txn| { + ( + payment.inner(), + payment.sequence.map(|v| v as i64), + meta, + txn.id, + ) + }) }) - .map(|(payment, meta, txn_id)| StorablePayment { + .map(|(payment, sequence, meta, txn_id)| StorablePayment { payment, + sequence, meta, txn_id, }); - let stored = self.db.store_payments(self.domain.id(), storable).await?; + let stored = self + .db + .store_payments( + self.domain.id(), + &self.interchain_gas_paymaster_address, + storable, + ) + .await?; Ok(stored as u32) } } diff --git a/rust/main/agents/scraper/src/store/storage.rs b/rust/main/agents/scraper/src/store/storage.rs index 5dcde4ed3..e73d07bd5 100644 --- a/rust/main/agents/scraper/src/store/storage.rs +++ b/rust/main/agents/scraper/src/store/storage.rs @@ -28,9 +28,10 @@ const CHUNK_SIZE: usize = 50; /// connections needed to scrape the contracts on a single blockchain. #[derive(Clone, Debug)] pub struct HyperlaneDbStore { - pub(crate) mailbox_address: H256, - pub(crate) domain: HyperlaneDomain, pub(crate) db: ScraperDb, + pub(crate) domain: HyperlaneDomain, + pub(crate) mailbox_address: H256, + pub(crate) interchain_gas_paymaster_address: H256, provider: Arc, cursor: Arc, } @@ -39,8 +40,9 @@ pub struct HyperlaneDbStore { impl HyperlaneDbStore { pub async fn new( db: ScraperDb, - mailbox_address: H256, domain: HyperlaneDomain, + mailbox_address: H256, + interchain_gas_paymaster_address: H256, provider: Arc, index_settings: &IndexSettings, ) -> Result { @@ -51,8 +53,9 @@ impl HyperlaneDbStore { Ok(Self { db, domain, - provider, mailbox_address, + interchain_gas_paymaster_address, + provider, cursor, }) }