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

Use traces to extract the input for settlement::Observer #3233

Merged
merged 8 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from 5 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
11 changes: 9 additions & 2 deletions crates/autopilot/src/domain/eth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,15 +312,20 @@ pub struct TradeEvent {
pub order_uid: domain::OrderUid,
}

/// A trace of a Call type of action.
#[derive(Debug, Clone, Default)]
pub struct TraceCall {
pub to: Address,
pub input: Calldata,
}

/// Any type of on-chain transaction.
#[derive(Debug, Clone, Default)]
pub struct Transaction {
/// The hash of the transaction.
pub hash: TxId,
/// The address of the sender of the transaction.
pub from: Address,
/// The call data of the transaction.
pub input: Calldata,
/// The block number of the block that contains the transaction.
pub block: BlockNo,
/// The timestamp of the block that contains the transaction.
Expand All @@ -329,4 +334,6 @@ pub struct Transaction {
pub gas: Gas,
/// The effective gas price of the transaction.
pub gas_price: EffectiveGasPrice,
/// Traces of all Calls contained in the transaction.
pub trace_calls: Vec<TraceCall>,
}
44 changes: 33 additions & 11 deletions crates/autopilot/src/domain/settlement/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ pub struct Settlement {
gas: eth::Gas,
/// The effective gas price of the settlement transaction.
gas_price: eth::EffectiveGasPrice,
/// The address of the solver that submitted the settlement transaction.
solver: eth::Address,
Copy link
Contributor Author

@sunce86 sunce86 Jan 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not strictly related to the task but removed for completeness, so we don't have to rethink how this field should be populated.

/// The block number of the block that contains the settlement transaction.
block: eth::BlockNo,
/// The associated auction.
Expand Down Expand Up @@ -146,7 +144,6 @@ impl Settlement {
.collect();

Ok(Self {
solver: settled.solver,
block: settled.block,
gas: settled.gas,
gas_price: settled.gas_price,
Expand Down Expand Up @@ -216,10 +213,7 @@ impl From<infra::persistence::DatabaseError> for Error {
#[cfg(test)]
mod tests {
use {
crate::{
domain,
domain::{auction, eth},
},
crate::domain::{self, auction, eth},
hex_literal::hex,
std::collections::{HashMap, HashSet},
};
Expand Down Expand Up @@ -306,12 +300,19 @@ mod tests {
let domain_separator = eth::DomainSeparator(hex!(
"c078f884a2676e1345748b1feace7b0abee5d00ecadb6e574dcdd109a63e8943"
));
let settlement_contract = eth::Address(eth::H160::from_slice(&hex!(
"9008d19f58aabd9ed0d60971565aa8510560ab41"
)));
let transaction = super::transaction::Transaction::new(
&domain::eth::Transaction {
input: calldata.into(),
trace_calls: vec![domain::eth::TraceCall {
to: settlement_contract,
input: calldata.into(),
}],
..Default::default()
},
&domain_separator,
settlement_contract,
)
.unwrap();

Expand Down Expand Up @@ -442,12 +443,19 @@ mod tests {
let domain_separator = eth::DomainSeparator(hex!(
"c078f884a2676e1345748b1feace7b0abee5d00ecadb6e574dcdd109a63e8943"
));
let settlement_contract = eth::Address(eth::H160::from_slice(&hex!(
"9008d19f58aabd9ed0d60971565aa8510560ab41"
)));
let transaction = super::transaction::Transaction::new(
&domain::eth::Transaction {
input: calldata.into(),
trace_calls: vec![domain::eth::TraceCall {
to: settlement_contract,
input: calldata.into(),
}],
..Default::default()
},
&domain_separator,
settlement_contract,
)
.unwrap();

Expand Down Expand Up @@ -610,12 +618,19 @@ mod tests {
let domain_separator = eth::DomainSeparator(hex!(
"c078f884a2676e1345748b1feace7b0abee5d00ecadb6e574dcdd109a63e8943"
));
let settlement_contract = eth::Address(eth::H160::from_slice(&hex!(
"9008d19f58aabd9ed0d60971565aa8510560ab41"
)));
let transaction = super::transaction::Transaction::new(
&domain::eth::Transaction {
input: calldata.into(),
trace_calls: vec![domain::eth::TraceCall {
to: settlement_contract,
input: calldata.into(),
}],
..Default::default()
},
&domain_separator,
settlement_contract,
)
.unwrap();

Expand Down Expand Up @@ -784,12 +799,19 @@ mod tests {
let domain_separator = eth::DomainSeparator(hex!(
"c078f884a2676e1345748b1feace7b0abee5d00ecadb6e574dcdd109a63e8943"
));
let settlement_contract = eth::Address(eth::H160::from_slice(&hex!(
"9008d19f58aabd9ed0d60971565aa8510560ab41"
)));
let transaction = super::transaction::Transaction::new(
&domain::eth::Transaction {
input: calldata.into(),
trace_calls: vec![domain::eth::TraceCall {
to: settlement_contract,
input: calldata.into(),
}],
..Default::default()
},
&domain_separator,
settlement_contract,
)
.unwrap();

Expand Down
3 changes: 2 additions & 1 deletion crates/autopilot/src/domain/settlement/observer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ impl Observer {
let transaction = match self.eth.transaction(event.transaction).await {
Ok(transaction) => {
let separator = self.eth.contracts().settlement_domain_separator();
settlement::Transaction::new(&transaction, separator)
let settlement_contract = self.eth.contracts().settlement().address().into();
settlement::Transaction::new(&transaction, separator, settlement_contract)
}
Err(err) => {
tracing::warn!(hash = ?event.transaction, ?err, "no tx found");
Expand Down
40 changes: 30 additions & 10 deletions crates/autopilot/src/domain/settlement/transaction/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use crate::{
boundary,
domain::{self, auction::order, eth},
use {
crate::{
boundary,
domain::{self, auction::order, eth},
},
ethcontract::common::FunctionExt,
};

mod tokenized;
Expand All @@ -12,8 +15,6 @@ pub struct Transaction {
pub hash: eth::TxId,
/// The associated auction id.
pub auction_id: domain::auction::Id,
/// The address of the solver that submitted the transaction.
pub solver: eth::Address,
/// The block number of the block that contains the transaction.
pub block: eth::BlockNo,
/// The timestamp of the block that contains the transaction.
Expand All @@ -30,23 +31,28 @@ impl Transaction {
pub fn new(
transaction: &eth::Transaction,
domain_separator: &eth::DomainSeparator,
settlement_contract: eth::Address,
) -> Result<Self, Error> {
// find trace call to settlement contract
sunce86 marked this conversation as resolved.
Show resolved Hide resolved
let calldata = transaction
.trace_calls
.iter()
.find(|trace| is_settlement_trace(trace, settlement_contract))
.map(|trace| trace.input.clone())
.ok_or(Error::MissingCallData)?;

/// Number of bytes that may be appended to the calldata to store an
/// auction id.
const META_DATA_LEN: usize = 8;

let (data, metadata) = transaction
.input
.0
.split_at(transaction.input.0.len() - META_DATA_LEN);
let (data, metadata) = calldata.0.split_at(calldata.0.len() - META_DATA_LEN);
let metadata: Option<[u8; META_DATA_LEN]> = metadata.try_into().ok();
sunce86 marked this conversation as resolved.
Show resolved Hide resolved
let auction_id = metadata
.map(crate::domain::auction::Id::from_be_bytes)
.ok_or(Error::MissingAuctionId)?;
Ok(Self {
hash: transaction.hash,
auction_id,
solver: transaction.from,
block: transaction.block,
timestamp: transaction.timestamp,
gas: transaction.gas,
Expand Down Expand Up @@ -116,6 +122,18 @@ impl Transaction {
}
}

fn is_settlement_trace(trace: &eth::TraceCall, settlement_contract: eth::Address) -> bool {
trace.to == settlement_contract
&& trace.input.0.starts_with(
sunce86 marked this conversation as resolved.
Show resolved Hide resolved
&contracts::GPv2Settlement::raw_contract()
.interface
.abi
.function("settle")
.unwrap()
.selector(),
sunce86 marked this conversation as resolved.
Show resolved Hide resolved
)
}

/// Trade containing onchain observable data specific to a settlement
/// transaction.
#[derive(Debug, Clone)]
Expand Down Expand Up @@ -154,6 +172,8 @@ pub struct ClearingPrices {
pub enum Error {
#[error("missing auction id")]
MissingAuctionId,
#[error("missing calldata")]
MissingCallData,
#[error(transparent)]
Decoding(#[from] tokenized::error::Decoding),
#[error("failed to recover order uid {0}")]
Expand Down
20 changes: 16 additions & 4 deletions crates/autopilot/src/infra/blockchain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,10 @@ impl Ethereum {
}

pub async fn transaction(&self, hash: eth::TxId) -> Result<eth::Transaction, Error> {
let (transaction, receipt) = tokio::try_join!(
let (transaction, receipt, traces) = tokio::try_join!(
self.web3.eth().transaction(hash.0.into()),
self.web3.eth().transaction_receipt(hash.0)
self.web3.eth().transaction_receipt(hash.0),
self.web3.trace().transaction(hash.0),
)?;
let transaction = transaction.ok_or(Error::TransactionNotFound)?;
let receipt = receipt.ok_or(Error::TransactionNotFound)?;
Expand All @@ -121,13 +122,15 @@ impl Ethereum {
.block(block_hash.into())
.await?
.ok_or(Error::TransactionNotFound)?;
into_domain(transaction, receipt, block.timestamp).map_err(Error::IncompleteTransactionData)
into_domain(transaction, receipt, traces, block.timestamp)
.map_err(Error::IncompleteTransactionData)
}
}

fn into_domain(
transaction: web3::types::Transaction,
receipt: web3::types::TransactionReceipt,
traces: Vec<web3::types::Trace>,
timestamp: U256,
) -> anyhow::Result<eth::Transaction> {
Ok(eth::Transaction {
Expand All @@ -136,7 +139,6 @@ fn into_domain(
.from
.ok_or(anyhow::anyhow!("missing from"))?
.into(),
input: transaction.input.0.into(),
block: receipt
.block_number
.ok_or(anyhow::anyhow!("missing block_number"))?
Expand All @@ -151,6 +153,16 @@ fn into_domain(
.ok_or(anyhow::anyhow!("missing effective_gas_price"))?
.into(),
timestamp: timestamp.as_u32(),
trace_calls: traces
.into_iter()
.filter_map(|trace| match trace.action {
web3::types::Action::Call(call) => Some(eth::TraceCall {
sunce86 marked this conversation as resolved.
Show resolved Hide resolved
to: call.to.into(),
input: call.input.0.into(),
}),
_ => None,
})
.collect(),
})
}

Expand Down
Loading