From fcf7fd7209a1e68137c33e7044d0ed844296f242 Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Wed, 27 Apr 2022 11:51:24 -0700 Subject: [PATCH 01/41] prog(gelato): starting gelato relay sdk --- Cargo.lock | 19 ++++++++++++++++++- Cargo.toml | 1 + gelato-relay/Cargo.toml | 17 +++++++++++++++++ gelato-relay/src/lib.rs | 31 +++++++++++++++++++++++++++++++ gelato-relay/src/types.rs | 14 ++++++++++++++ nomad-base/Cargo.toml | 1 - 6 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 gelato-relay/Cargo.toml create mode 100644 gelato-relay/src/lib.rs create mode 100644 gelato-relay/src/types.rs diff --git a/Cargo.lock b/Cargo.lock index 0be539d9..da94ee00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1542,6 +1542,21 @@ dependencies = [ "slab", ] +[[package]] +name = "gelato-relay" +version = "0.1.0" +dependencies = [ + "async-trait", + "color-eyre 0.5.11", + "ethers", + "futures-util", + "reqwest", + "serde 1.0.136", + "serde_json", + "thiserror", + "tokio", +] + [[package]] name = "generic-array" version = "0.12.4" @@ -2421,7 +2436,6 @@ dependencies = [ "affix", "async-trait", "color-eyre 0.5.11", - "config 0.10.1", "ethers", "futures-util", "lazy_static", @@ -3419,11 +3433,13 @@ dependencies = [ "http-body", "hyper", "hyper-rustls", + "hyper-tls", "ipnet", "js-sys", "lazy_static", "log", "mime", + "native-tls", "percent-encoding", "pin-project-lite", "rustls", @@ -3432,6 +3448,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "tokio", + "tokio-native-tls", "tokio-rustls", "url", "wasm-bindgen", diff --git a/Cargo.toml b/Cargo.toml index 9c705f91..cc06b158 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,4 +17,5 @@ members = [ "tools/kms-cli", "tools/nomad-cli", "tools/balance-exporter", + "gelato-relay", ] diff --git a/gelato-relay/Cargo.toml b/gelato-relay/Cargo.toml new file mode 100644 index 00000000..3b0eccd6 --- /dev/null +++ b/gelato-relay/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "gelato-relay" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tokio = { version = "1.0.1", features = ["rt", "macros"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = { version = "1.0", default-features = false } +ethers = { git = "https://github.com/gakonst/ethers-rs", branch = "master" } +thiserror = { version = "1.0.22", default-features = false } +async-trait = { version = "0.1.42", default-features = false } +futures-util = "0.3.12" +color-eyre = "0.5.0" +reqwest = { version = "0.11.10", features = ["json"]} \ No newline at end of file diff --git a/gelato-relay/src/lib.rs b/gelato-relay/src/lib.rs new file mode 100644 index 00000000..f3be035b --- /dev/null +++ b/gelato-relay/src/lib.rs @@ -0,0 +1,31 @@ +mod types; +use types::*; + +use color_eyre::eyre::Result; + +const RELAY_URL: &str = "https://relay.gelato.digital"; + +pub async fn send_relay_transaction( + chain_id: usize, + dest: String, + data: String, + token: String, + relayer_fee: String, +) -> Result { + let params = RelayRequest { + dest, + data, + token, + relayer_fee, + }; + + let client = reqwest::Client::new(); + let url = format!("{}/relays/{}", RELAY_URL, chain_id); + let res = client + .post(url) + .json(¶ms) + .send() + .await?; + + res.json().await +} \ No newline at end of file diff --git a/gelato-relay/src/types.rs b/gelato-relay/src/types.rs new file mode 100644 index 00000000..600a2305 --- /dev/null +++ b/gelato-relay/src/types.rs @@ -0,0 +1,14 @@ +use serde::{Serialize, Deserialize}; + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RelayRequest { + pub dest: String, + pub data: String, + pub token: String, + pub relayer_fee: String, +} + +pub struct RelayTransaction { + pub task_id: String +} \ No newline at end of file diff --git a/nomad-base/Cargo.toml b/nomad-base/Cargo.toml index b24686a6..4c50eb46 100644 --- a/nomad-base/Cargo.toml +++ b/nomad-base/Cargo.toml @@ -8,7 +8,6 @@ edition = "2021" [dependencies] # Main block tokio = { version = "1.0.1", features = ["rt", "macros"] } -config = "0.10" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", default-features = false } ethers = { git = "https://github.com/gakonst/ethers-rs", branch = "master" } From f8fb39be9d07d27f82ab9ea5281465ee0af33459 Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Thu, 28 Apr 2022 13:52:28 -0700 Subject: [PATCH 02/41] feat(gelato): finish gelato bindings for sending relay, getting supported chains, and task status --- gelato-relay/Cargo.toml | 6 ++- gelato-relay/bin/test.rs | 14 +++++++ gelato-relay/src/lib.rs | 40 +++++++++++++------ gelato-relay/src/types.rs | 82 +++++++++++++++++++++++++++++++++++++-- 4 files changed, 125 insertions(+), 17 deletions(-) create mode 100644 gelato-relay/bin/test.rs diff --git a/gelato-relay/Cargo.toml b/gelato-relay/Cargo.toml index 3b0eccd6..9ef8c8f2 100644 --- a/gelato-relay/Cargo.toml +++ b/gelato-relay/Cargo.toml @@ -14,4 +14,8 @@ thiserror = { version = "1.0.22", default-features = false } async-trait = { version = "0.1.42", default-features = false } futures-util = "0.3.12" color-eyre = "0.5.0" -reqwest = { version = "0.11.10", features = ["json"]} \ No newline at end of file +reqwest = { version = "0.11.10", features = ["json"]} + +[[bin]] +name = "test" +path = "bin/test.rs" \ No newline at end of file diff --git a/gelato-relay/bin/test.rs b/gelato-relay/bin/test.rs new file mode 100644 index 00000000..8f1070a8 --- /dev/null +++ b/gelato-relay/bin/test.rs @@ -0,0 +1,14 @@ +use gelato_relay::*; + +#[tokio::main(flavor = "current_thread")] +async fn main() -> Result<(), reqwest::Error> { + let chains = get_gelato_relay_chains().await?; + println!("Relay chains: {:?}", chains); + + let task_status = + get_task_status("0xeefc20b15402c30ead95d572034532c7097488726a6582d3d6674971e9d97879") + .await?; + println!("Task status: {:?}", task_status); + + Ok(()) +} diff --git a/gelato-relay/src/lib.rs b/gelato-relay/src/lib.rs index f3be035b..74886886 100644 --- a/gelato-relay/src/lib.rs +++ b/gelato-relay/src/lib.rs @@ -7,25 +7,41 @@ const RELAY_URL: &str = "https://relay.gelato.digital"; pub async fn send_relay_transaction( chain_id: usize, - dest: String, - data: String, - token: String, - relayer_fee: String, -) -> Result { + dest: &str, + data: &str, + token: &str, + relayer_fee: &str, +) -> Result { let params = RelayRequest { - dest, - data, - token, - relayer_fee, + dest: dest.to_owned(), + data: data.to_owned(), + token: token.to_owned(), + relayer_fee: relayer_fee.to_owned(), }; - let client = reqwest::Client::new(); let url = format!("{}/relays/{}", RELAY_URL, chain_id); - let res = client + let res = reqwest::Client::new() .post(url) .json(¶ms) .send() .await?; res.json().await -} \ No newline at end of file +} + +pub async fn is_chain_supported(chain_id: usize) -> Result { + let supported_chains = get_gelato_relay_chains().await?; + Ok(supported_chains.contains(&chain_id.to_string())) +} + +pub async fn get_gelato_relay_chains() -> Result, reqwest::Error> { + let url = format!("{}/relays", RELAY_URL); + let res = reqwest::get(url).await?; + Ok(res.json::().await?.relays) +} + +pub async fn get_task_status(task_id: &str) -> Result { + let url = format!("{}/tasks/{}", RELAY_URL, task_id); + let res = reqwest::get(url).await?; + Ok(res.json().await?) +} diff --git a/gelato-relay/src/types.rs b/gelato-relay/src/types.rs index 600a2305..1cb6090c 100644 --- a/gelato-relay/src/types.rs +++ b/gelato-relay/src/types.rs @@ -1,4 +1,4 @@ -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -9,6 +9,80 @@ pub struct RelayRequest { pub relayer_fee: String, } -pub struct RelayTransaction { - pub task_id: String -} \ No newline at end of file +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RelayResponse { + pub task_id: String, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RelayChainsResponse { + pub relays: Vec, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TaskStatusResponse { + data: Vec, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TaskStatus { + pub service: String, + pub chain: String, + pub task_id: String, + pub task_state: TaskState, + #[serde(rename = "created_at")] + pub created_at: String, // date + pub last_check: Option, + pub execution: Option, + pub last_execution: String, // date +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Execution { + pub status: String, + pub transaction_hash: String, + pub block_number: usize, + pub created_at: String, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Check { + pub task_state: TaskState, + pub message: Option, + #[serde(rename = "created_at")] + pub created_at: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Payload { + pub to: String, + pub data: String, + pub fee_data: FeeData, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct FeeData { + pub gas_price: usize, + pub max_fee_per_gas: usize, + pub max_priority_fee_per_gas: usize, +} + +#[derive(Debug, Serialize, Deserialize)] +pub enum TaskState { + CheckPending, + ExecPending, + ExecSuccess, + ExecReverted, + WaitingForConfirmation, + Blacklisted, + Cancelled, + NotFound, +} From 5f78657c6c8ee072dc369f08c77ddc3b9dc549b5 Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Thu, 28 Apr 2022 15:45:23 -0700 Subject: [PATCH 03/41] chore(maintenance): add changelog and readme --- gelato-relay/CHANGELOG.md | 5 +++++ gelato-relay/README.md | 3 +++ 2 files changed, 8 insertions(+) create mode 100644 gelato-relay/CHANGELOG.md create mode 100644 gelato-relay/README.md diff --git a/gelato-relay/CHANGELOG.md b/gelato-relay/CHANGELOG.md new file mode 100644 index 00000000..c015f513 --- /dev/null +++ b/gelato-relay/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +# Unreleased + +- adds bindings for sending relay txs, getting supported chains, and fetching task status diff --git a/gelato-relay/README.md b/gelato-relay/README.md new file mode 100644 index 00000000..0006942e --- /dev/null +++ b/gelato-relay/README.md @@ -0,0 +1,3 @@ +## Gelato Relay SDK Bindings + +This crate provides Rust bindings for interacting with [Gelato's Relay SDK](https://github.com/gelatodigital/relay-sdks-monorepo/tree/07b8cec8b8370547e5632b3a6c33362df2f80125/packages/relay-sdk). It simply wraps Gelato Relay requests and responses to/from Gelato endpoints with Rust types and methods. From 088e20efa86732a26980f6adc291f3b29573af8c Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Thu, 28 Apr 2022 15:58:26 -0700 Subject: [PATCH 04/41] refactor(gelato): make client object --- gelato-relay/bin/test.rs | 10 +++-- gelato-relay/src/lib.rs | 92 ++++++++++++++++++++++++---------------- 2 files changed, 62 insertions(+), 40 deletions(-) diff --git a/gelato-relay/bin/test.rs b/gelato-relay/bin/test.rs index 8f1070a8..26e1d54b 100644 --- a/gelato-relay/bin/test.rs +++ b/gelato-relay/bin/test.rs @@ -2,12 +2,14 @@ use gelato_relay::*; #[tokio::main(flavor = "current_thread")] async fn main() -> Result<(), reqwest::Error> { - let chains = get_gelato_relay_chains().await?; + let gelato = GelatoClient::default(); + + let chains = gelato.get_gelato_relay_chains().await?; println!("Relay chains: {:?}", chains); - let task_status = - get_task_status("0xeefc20b15402c30ead95d572034532c7097488726a6582d3d6674971e9d97879") - .await?; + let task_status = gelato + .get_task_status("0xeefc20b15402c30ead95d572034532c7097488726a6582d3d6674971e9d97879") + .await?; println!("Task status: {:?}", task_status); Ok(()) diff --git a/gelato-relay/src/lib.rs b/gelato-relay/src/lib.rs index 74886886..d113a755 100644 --- a/gelato-relay/src/lib.rs +++ b/gelato-relay/src/lib.rs @@ -3,45 +3,65 @@ use types::*; use color_eyre::eyre::Result; -const RELAY_URL: &str = "https://relay.gelato.digital"; - -pub async fn send_relay_transaction( - chain_id: usize, - dest: &str, - data: &str, - token: &str, - relayer_fee: &str, -) -> Result { - let params = RelayRequest { - dest: dest.to_owned(), - data: data.to_owned(), - token: token.to_owned(), - relayer_fee: relayer_fee.to_owned(), - }; - - let url = format!("{}/relays/{}", RELAY_URL, chain_id); - let res = reqwest::Client::new() - .post(url) - .json(¶ms) - .send() - .await?; - - res.json().await -} +const DEFAULT_URL: &str = "https://relay.gelato.digital"; -pub async fn is_chain_supported(chain_id: usize) -> Result { - let supported_chains = get_gelato_relay_chains().await?; - Ok(supported_chains.contains(&chain_id.to_string())) +pub struct GelatoClient { + url: String, } -pub async fn get_gelato_relay_chains() -> Result, reqwest::Error> { - let url = format!("{}/relays", RELAY_URL); - let res = reqwest::get(url).await?; - Ok(res.json::().await?.relays) +impl Default for GelatoClient { + fn default() -> Self { + Self::new(DEFAULT_URL.to_owned()) + } } -pub async fn get_task_status(task_id: &str) -> Result { - let url = format!("{}/tasks/{}", RELAY_URL, task_id); - let res = reqwest::get(url).await?; - Ok(res.json().await?) +impl GelatoClient { + pub fn new(url: String) -> Self { + Self { url } + } + + pub async fn send_relay_transaction( + &self, + chain_id: usize, + dest: &str, + data: &str, + token: &str, + relayer_fee: &str, + ) -> Result { + let params = RelayRequest { + dest: dest.to_owned(), + data: data.to_owned(), + token: token.to_owned(), + relayer_fee: relayer_fee.to_owned(), + }; + + let url = format!("{}/relays/{}", &self.url, chain_id); + let res = reqwest::Client::new() + .post(url) + .json(¶ms) + .send() + .await?; + + res.json().await + } + + pub async fn is_chain_supported(&self, chain_id: usize) -> Result { + let supported_chains = self.get_gelato_relay_chains().await?; + Ok(supported_chains.contains(&chain_id.to_string())) + } + + pub async fn get_gelato_relay_chains(&self) -> Result, reqwest::Error> { + let url = format!("{}/relays", &self.url); + let res = reqwest::get(url).await?; + Ok(res.json::().await?.relays) + } + + pub async fn get_task_status( + &self, + task_id: &str, + ) -> Result { + let url = format!("{}/tasks/{}", &self.url, task_id); + let res = reqwest::get(url).await?; + Ok(res.json().await?) + } } From 4db1ce56e5e82a4c05a48561bd13a86027ae6265 Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Thu, 28 Apr 2022 16:31:45 -0700 Subject: [PATCH 05/41] prog(contract/gelato): starts draft ChainSubmitter --- Cargo.lock | 1 + chains/nomad-ethereum/Cargo.toml | 9 +++--- chains/nomad-ethereum/src/chain_submitter.rs | 30 ++++++++++++++++++++ chains/nomad-ethereum/src/lib.rs | 4 +++ 4 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 chains/nomad-ethereum/src/chain_submitter.rs diff --git a/Cargo.lock b/Cargo.lock index da94ee00..5a6b2804 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2526,6 +2526,7 @@ dependencies = [ "ethers-providers", "ethers-signers", "futures-util", + "gelato-relay", "hex", "nomad-core", "nomad-types", diff --git a/chains/nomad-ethereum/Cargo.toml b/chains/nomad-ethereum/Cargo.toml index fd727270..0d112deb 100644 --- a/chains/nomad-ethereum/Cargo.toml +++ b/chains/nomad-ethereum/Cargo.toml @@ -20,10 +20,6 @@ tracing = "0.1.22" color-eyre = "0.5.0" anyhow = "1" num = "0.4" - -nomad-xyz-configuration = { path = "../../configuration" } -nomad-types = { path = "../../nomad-types" } -nomad-core = { path = "../../nomad-core" } tokio = "1.7.1" hex = "0.4.3" prometheus = "0.12" @@ -33,5 +29,10 @@ tracing-futures = "0.2.5" url = "2.2.2" thiserror = "1.0.30" +nomad-xyz-configuration = { path = "../../configuration" } +nomad-types = { path = "../../nomad-types" } +nomad-core = { path = "../../nomad-core" } +gelato-relay = { path = "../../gelato-relay" } + [build-dependencies] ethers = {git = "https://github.com/gakonst/ethers-rs", branch = "master", features = ["abigen"]} diff --git a/chains/nomad-ethereum/src/chain_submitter.rs b/chains/nomad-ethereum/src/chain_submitter.rs new file mode 100644 index 00000000..17134876 --- /dev/null +++ b/chains/nomad-ethereum/src/chain_submitter.rs @@ -0,0 +1,30 @@ +use gelato_relay::GelatoClient; +use ethers::types::transaction::eip2718::TypedTransaction; +use ethers::providers::Middleware; +use crate::report_tx; + +use std::sync::Arc; + +pub enum ChainSubmitter { + Local(Arc), + Gelato(GelatoClient) +} + +impl ChainSubmitter { + pub async fn submit_to_chain(&self, tx: ContractCall) -> Result<(), ChainCommunicationError> { + match self { + ChainSubmitter::Local(client) => { + report_tx!(tx, client).try_into() + } + ChainSubmitter::Gelato(client) => { + client.send_relay_transaction( + chain_id, // translate util + dest, // contract address + data, // TypedTransaction + token, // configurable fee token + relayer_fee // configurable fee amount + ).await? + } + } + } +} \ No newline at end of file diff --git a/chains/nomad-ethereum/src/lib.rs b/chains/nomad-ethereum/src/lib.rs index 2b093e6e..7bfb32d8 100644 --- a/chains/nomad-ethereum/src/lib.rs +++ b/chains/nomad-ethereum/src/lib.rs @@ -20,6 +20,10 @@ mod macros; mod retrying; pub use retrying::{RetryingProvider, RetryingProviderError}; +/// Chain submitter +mod chain_submitter; +pub use chain_submitter::*; + /// Contract binding #[cfg(not(doctest))] pub(crate) mod bindings; From 6995cf984f6a97d6723f85b87d08ab04dd5a096c Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Fri, 29 Apr 2022 00:03:49 -0700 Subject: [PATCH 06/41] prog(contract/gelato): completes local half of ChainSubmitter --- chains/nomad-ethereum/src/chain_submitter.rs | 79 ++++++++++++++------ chains/nomad-ethereum/src/macros.rs | 50 +++++++++++++ 2 files changed, 108 insertions(+), 21 deletions(-) diff --git a/chains/nomad-ethereum/src/chain_submitter.rs b/chains/nomad-ethereum/src/chain_submitter.rs index 17134876..af7232a0 100644 --- a/chains/nomad-ethereum/src/chain_submitter.rs +++ b/chains/nomad-ethereum/src/chain_submitter.rs @@ -1,30 +1,67 @@ -use gelato_relay::GelatoClient; use ethers::types::transaction::eip2718::TypedTransaction; -use ethers::providers::Middleware; -use crate::report_tx; +use nomad_core::Signers; +use nomad_xyz_configuration::{agent::SignerConf, ethereum::Connection}; +use crate::http_signer_middleware; +use std::{collections::HashMap, sync::Arc}; +use color_eyre::Result; +use ethers::prelude::*; -use std::sync::Arc; +/// Configuration for creating an ethers::SignerMiddleware +pub struct SigningProviderConfig { + /// Signer configuration + pub signer: SignerConf, + /// Connection configuration + pub connection: Connection, +} -pub enum ChainSubmitter { - Local(Arc), - Gelato(GelatoClient) +/// Component responsible for submitting transactions to the chain. Can +/// sign/submit locally or use a transaction relay service. +pub enum EthChainSubmitter { + /// Sign/submit txs locally + Local(HashMap), } -impl ChainSubmitter { - pub async fn submit_to_chain(&self, tx: ContractCall) -> Result<(), ChainCommunicationError> { +impl EthChainSubmitter { + /// Submit transaction to chain + pub async fn submit_to_chain( + &self, + domain: u32, + tx: impl Into, + ) -> Result<()> { + let tx: TypedTransaction = tx.into(); + match self { - ChainSubmitter::Local(client) => { - report_tx!(tx, client).try_into() - } - ChainSubmitter::Gelato(client) => { - client.send_relay_transaction( - chain_id, // translate util - dest, // contract address - data, // TypedTransaction - token, // configurable fee token - relayer_fee // configurable fee amount - ).await? + EthChainSubmitter::Local(client_map) => { + let client_config = client_map.get(&domain).expect("!eth client"); + let signer = Signers::try_from_signer_conf(&client_config.signer).await?; + + let client = match &client_config.connection { + Connection::Http { url } => http_signer_middleware!(url, signer), + Connection::Ws { url: _ } => panic!("not supporting ws"), + }; + + let dispatched = client.send_transaction(tx, None).await?; + let tx_hash: ethers::core::types::H256 = *dispatched; + let result = dispatched + .await? + .ok_or_else(|| nomad_core::ChainCommunicationError::DroppedError(tx_hash))?; + + tracing::info!( + "confirmed transaction with tx_hash {:?}", + result.transaction_hash + ); + + Ok(()) } + // ChainSubmitter::Gelato(client) => { + // client.send_relay_transaction( + // chain_id, // translate util + // dest, // contract address + // data, // TypedTransaction + // token, // configurable fee token + // relayer_fee // configurable fee amount + // ).await? + // } } } -} \ No newline at end of file +} diff --git a/chains/nomad-ethereum/src/macros.rs b/chains/nomad-ethereum/src/macros.rs index 96d39231..9a2120a2 100644 --- a/chains/nomad-ethereum/src/macros.rs +++ b/chains/nomad-ethereum/src/macros.rs @@ -106,6 +106,56 @@ macro_rules! boxed_indexer { }; } +/// Create ethers::SignerMiddleware from http connection +#[macro_export] +macro_rules! http_signer_middleware { + ($url:expr, $signer:ident) => {{ + let http: crate::retrying::RetryingProvider = $url.parse()?; + let provider = Arc::new(ethers::providers::Provider::new(http)); + + // First set the chain ID locally + let provider_chain_id = provider.get_chainid().await?; + let signer = ethers::signers::Signer::with_chain_id($signer, provider_chain_id.as_u64()); + + // Manage the nonce locally + let address = ethers::prelude::Signer::address(&signer); + let provider = + ethers::middleware::nonce_manager::NonceManagerMiddleware::new(provider, address); + + // Kludge. Increase the gas by multiplication of every estimated gas by + // 2, except the gas for chain id 1 (Ethereum Mainnet) + let provider = crate::gas::GasAdjusterMiddleware::with_default_policy(provider, provider_chain_id.as_u64()); + + // Manage signing locally + Arc::new(ethers::middleware::SignerMiddleware::new(provider, signer)) + }}; +} + +/// Create ethers::SignerMiddleware from websockets connection +#[macro_export] +macro_rules! ws_signer_middleware { + ($url:expr, $signer:ident) => {{ + let ws = ethers::providers::Ws::connect($url).await?; + let provider = Arc::new(ethers::providers::Provider::new(ws)); + + // First set the chain ID locally + let provider_chain_id = provider.get_chainid().await?; + let signer = ethers::signers::Signer::with_chain_id($signer, provider_chain_id.as_u64()); + + // Manage the nonce locally + let address = ethers::prelude::Signer::address(&signer); + let provider = + ethers::middleware::nonce_manager::NonceManagerMiddleware::new(provider, address); + + // Kludge. Increase the gas by multiplication of every estimated gas by + // 2, except the gas for chain id 1 (Ethereum Mainnet) + let provider = crate::gas::GasAdjusterMiddleware::with_default_policy(provider, provider_chain_id.as_u64()); + + // Manage signing locally + Arc::new(ethers::middleware::SignerMiddleware::new(provider, signer)) + }}; +} + macro_rules! boxed_contract { (@timelag $provider:expr, $abi:ident, $timelag:ident, $($tail:tt)*) => {{ let write_provider: Arc<_> = $provider.clone(); From 5ff361b9e63c9121b3009f012af51a89bb08460b Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Fri, 29 Apr 2022 12:42:17 -0700 Subject: [PATCH 07/41] prog(gelato): adds get fee estimate hook to gelato bindings --- chains/nomad-ethereum/src/chain_submitter.rs | 55 ++++++++-------- chains/nomad-ethereum/src/macros.rs | 66 +++++++++++--------- configuration/src/agent/signer.rs | 2 +- gelato-relay/bin/test.rs | 11 ++++ gelato-relay/src/client.rs | 35 +++++++++++ gelato-relay/src/lib.rs | 36 ++++++++++- gelato-relay/src/types.rs | 14 +++++ 7 files changed, 161 insertions(+), 58 deletions(-) create mode 100644 gelato-relay/src/client.rs diff --git a/chains/nomad-ethereum/src/chain_submitter.rs b/chains/nomad-ethereum/src/chain_submitter.rs index af7232a0..5010f1bc 100644 --- a/chains/nomad-ethereum/src/chain_submitter.rs +++ b/chains/nomad-ethereum/src/chain_submitter.rs @@ -1,41 +1,47 @@ -use ethers::types::transaction::eip2718::TypedTransaction; -use nomad_core::Signers; -use nomad_xyz_configuration::{agent::SignerConf, ethereum::Connection}; use crate::http_signer_middleware; -use std::{collections::HashMap, sync::Arc}; use color_eyre::Result; use ethers::prelude::*; +use ethers::types::transaction::eip2718::TypedTransaction; +use nomad_core::Signers; +use nomad_xyz_configuration::ethereum::Connection; +use std::sync::Arc; /// Configuration for creating an ethers::SignerMiddleware pub struct SigningProviderConfig { /// Signer configuration - pub signer: SignerConf, + pub signer: Signers, /// Connection configuration pub connection: Connection, } -/// Component responsible for submitting transactions to the chain. Can +impl SigningProviderConfig { + /// Instantiate new signing provider config + pub fn new(signer: Signers, connection: Connection) -> Self { + Self { signer, connection } + } +} + +/// Component responsible for submitting transactions to the chain. Can /// sign/submit locally or use a transaction relay service. -pub enum EthChainSubmitter { +pub enum ChainSubmitter { /// Sign/submit txs locally - Local(HashMap), + Local(SigningProviderConfig), } -impl EthChainSubmitter { +impl ChainSubmitter { /// Submit transaction to chain pub async fn submit_to_chain( &self, - domain: u32, + _domain: u32, + _contract_address: Address, tx: impl Into, ) -> Result<()> { let tx: TypedTransaction = tx.into(); match self { - EthChainSubmitter::Local(client_map) => { - let client_config = client_map.get(&domain).expect("!eth client"); - let signer = Signers::try_from_signer_conf(&client_config.signer).await?; - - let client = match &client_config.connection { + ChainSubmitter::Local(config) => { + let signer = config.signer.clone(); + let client = match &config.connection { Connection::Http { url } => http_signer_middleware!(url, signer), Connection::Ws { url: _ } => panic!("not supporting ws"), }; @@ -52,16 +58,15 @@ impl EthChainSubmitter { ); Ok(()) - } - // ChainSubmitter::Gelato(client) => { - // client.send_relay_transaction( - // chain_id, // translate util - // dest, // contract address - // data, // TypedTransaction - // token, // configurable fee token - // relayer_fee // configurable fee amount - // ).await? - // } + } // ChainSubmitter::Gelato(client) => { + // client.send_relay_transaction( + // chain_id, // translate util + // dest, // contract address + // data, // TypedTransaction + // token, // configurable fee token + // relayer_fee // configurable fee amount + // ).await? + // } } } } diff --git a/chains/nomad-ethereum/src/macros.rs b/chains/nomad-ethereum/src/macros.rs index 9a2120a2..68afd2fb 100644 --- a/chains/nomad-ethereum/src/macros.rs +++ b/chains/nomad-ethereum/src/macros.rs @@ -113,21 +113,24 @@ macro_rules! http_signer_middleware { let http: crate::retrying::RetryingProvider = $url.parse()?; let provider = Arc::new(ethers::providers::Provider::new(http)); - // First set the chain ID locally - let provider_chain_id = provider.get_chainid().await?; - let signer = ethers::signers::Signer::with_chain_id($signer, provider_chain_id.as_u64()); - - // Manage the nonce locally - let address = ethers::prelude::Signer::address(&signer); - let provider = - ethers::middleware::nonce_manager::NonceManagerMiddleware::new(provider, address); - - // Kludge. Increase the gas by multiplication of every estimated gas by - // 2, except the gas for chain id 1 (Ethereum Mainnet) - let provider = crate::gas::GasAdjusterMiddleware::with_default_policy(provider, provider_chain_id.as_u64()); - - // Manage signing locally - Arc::new(ethers::middleware::SignerMiddleware::new(provider, signer)) + // First set the chain ID locally + let provider_chain_id = provider.get_chainid().await?; + let signer = ethers::signers::Signer::with_chain_id($signer, provider_chain_id.as_u64()); + + // Manage the nonce locally + let address = ethers::prelude::Signer::address(&signer); + let provider = + ethers::middleware::nonce_manager::NonceManagerMiddleware::new(provider, address); + + // Kludge. Increase the gas by multiplication of every estimated gas by + // 2, except the gas for chain id 1 (Ethereum Mainnet) + let provider = crate::gas::GasAdjusterMiddleware::with_default_policy( + provider, + provider_chain_id.as_u64(), + ); + + // Manage signing locally + Arc::new(ethers::middleware::SignerMiddleware::new(provider, signer)) }}; } @@ -138,21 +141,24 @@ macro_rules! ws_signer_middleware { let ws = ethers::providers::Ws::connect($url).await?; let provider = Arc::new(ethers::providers::Provider::new(ws)); - // First set the chain ID locally - let provider_chain_id = provider.get_chainid().await?; - let signer = ethers::signers::Signer::with_chain_id($signer, provider_chain_id.as_u64()); - - // Manage the nonce locally - let address = ethers::prelude::Signer::address(&signer); - let provider = - ethers::middleware::nonce_manager::NonceManagerMiddleware::new(provider, address); - - // Kludge. Increase the gas by multiplication of every estimated gas by - // 2, except the gas for chain id 1 (Ethereum Mainnet) - let provider = crate::gas::GasAdjusterMiddleware::with_default_policy(provider, provider_chain_id.as_u64()); - - // Manage signing locally - Arc::new(ethers::middleware::SignerMiddleware::new(provider, signer)) + // First set the chain ID locally + let provider_chain_id = provider.get_chainid().await?; + let signer = ethers::signers::Signer::with_chain_id($signer, provider_chain_id.as_u64()); + + // Manage the nonce locally + let address = ethers::prelude::Signer::address(&signer); + let provider = + ethers::middleware::nonce_manager::NonceManagerMiddleware::new(provider, address); + + // Kludge. Increase the gas by multiplication of every estimated gas by + // 2, except the gas for chain id 1 (Ethereum Mainnet) + let provider = crate::gas::GasAdjusterMiddleware::with_default_policy( + provider, + provider_chain_id.as_u64(), + ); + + // Manage signing locally + Arc::new(ethers::middleware::SignerMiddleware::new(provider, signer)) }}; } diff --git a/configuration/src/agent/signer.rs b/configuration/src/agent/signer.rs index 36aa1728..848108a5 100644 --- a/configuration/src/agent/signer.rs +++ b/configuration/src/agent/signer.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use crate::FromEnv; use nomad_types::HexString; -/// Ethereum signer types +/// Signer types #[derive(Debug, Clone, PartialEq, serde::Deserialize)] #[serde(untagged, rename_all = "camelCase")] pub enum SignerConf { diff --git a/gelato-relay/bin/test.rs b/gelato-relay/bin/test.rs index 26e1d54b..6f60d84e 100644 --- a/gelato-relay/bin/test.rs +++ b/gelato-relay/bin/test.rs @@ -12,5 +12,16 @@ async fn main() -> Result<(), reqwest::Error> { .await?; println!("Task status: {:?}", task_status); + let mainnet: usize = chains[0].parse().unwrap(); + let estimated_fee = gelato + .get_estimated_fee( + mainnet, + "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + 100_000, + true, + ) + .await?; + println!("Estimated fee: {:?}", estimated_fee); + Ok(()) } diff --git a/gelato-relay/src/client.rs b/gelato-relay/src/client.rs new file mode 100644 index 00000000..e7ccf058 --- /dev/null +++ b/gelato-relay/src/client.rs @@ -0,0 +1,35 @@ +use crate::{GelatoClient, RelayResponse}; + +/// Gelato client for submitting txs to single chain +pub struct SingleChainGelatoClient { + pub client: GelatoClient, + pub chain_id: usize, + pub payment_token: String, +} + +impl SingleChainGelatoClient { + /// Instantiate single chain client with default Gelato url + pub fn with_default_url(chain_id: usize, payment_token: String) -> Self { + Self { + client: GelatoClient::default(), + chain_id, + payment_token, + } + } + + /// Send relay transaction + pub async fn send_relay_transaction( + &self, + dest: &str, + data: &str, + ) -> Result { + let relayer_fee = self + .client + .get_estimated_fee(self.chain_id, &self.payment_token, 100_000, true) + .await?; + + self.client + .send_relay_transaction(self.chain_id, dest, data, &self.payment_token, relayer_fee) + .await + } +} diff --git a/gelato-relay/src/lib.rs b/gelato-relay/src/lib.rs index d113a755..fcfc29e2 100644 --- a/gelato-relay/src/lib.rs +++ b/gelato-relay/src/lib.rs @@ -1,7 +1,11 @@ mod types; use types::*; +mod client; +use client::*; + use color_eyre::eyre::Result; +use std::collections::HashMap; const DEFAULT_URL: &str = "https://relay.gelato.digital"; @@ -26,13 +30,13 @@ impl GelatoClient { dest: &str, data: &str, token: &str, - relayer_fee: &str, + relayer_fee: usize, ) -> Result { let params = RelayRequest { dest: dest.to_owned(), data: data.to_owned(), token: token.to_owned(), - relayer_fee: relayer_fee.to_owned(), + relayer_fee: relayer_fee.to_string(), }; let url = format!("{}/relays/{}", &self.url, chain_id); @@ -56,6 +60,34 @@ impl GelatoClient { Ok(res.json::().await?.relays) } + pub async fn get_estimated_fee( + &self, + chain_id: usize, + payment_token: &str, + gas_limit: usize, + is_high_priority: bool, + ) -> Result { + let payment_token = payment_token.to_string(); + let gas_limit = gas_limit.to_string(); + let is_high_priority = is_high_priority.to_string(); + let params = HashMap::from([ + ("paymentToken", payment_token), + ("gasLimit", gas_limit), + ("isHighPriority", is_high_priority), + ]); + + let base_url = format!("{}/oracles/{}/estimate", &self.url, chain_id); + let url = reqwest::Url::parse_with_params(&base_url, params).expect("!url"); + let res = reqwest::get(url).await?; + + Ok(res + .json::() + .await? + .estimated_fee + .parse() + .expect("!string to int")) + } + pub async fn get_task_status( &self, task_id: &str, diff --git a/gelato-relay/src/types.rs b/gelato-relay/src/types.rs index 1cb6090c..4a89bce7 100644 --- a/gelato-relay/src/types.rs +++ b/gelato-relay/src/types.rs @@ -9,12 +9,26 @@ pub struct RelayRequest { pub relayer_fee: String, } +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct EstimatedFeeRequest { + pub payment_token: String, + pub gas_limit: usize, + pub is_high_priority: bool, +} + #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RelayResponse { pub task_id: String, } +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct EstimatedFeeResponse { + pub estimated_fee: String, +} + #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RelayChainsResponse { From 6c0e1fd698857b2627f31ccb25724eeb975d152e Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Sat, 30 Apr 2022 12:15:57 -0700 Subject: [PATCH 08/41] prog(chain-submitter): adds MetaTx type and function to spawn receiving submitter --- chains/nomad-ethereum/src/lib.rs | 4 +- .../src/{chain_submitter.rs => submitter.rs} | 64 ++++++++++++++----- nomad-base/src/lib.rs | 3 + nomad-base/src/submitter.rs | 1 + 4 files changed, 55 insertions(+), 17 deletions(-) rename chains/nomad-ethereum/src/{chain_submitter.rs => submitter.rs} (56%) create mode 100644 nomad-base/src/submitter.rs diff --git a/chains/nomad-ethereum/src/lib.rs b/chains/nomad-ethereum/src/lib.rs index 7bfb32d8..b3e3bbf7 100644 --- a/chains/nomad-ethereum/src/lib.rs +++ b/chains/nomad-ethereum/src/lib.rs @@ -21,8 +21,8 @@ mod retrying; pub use retrying::{RetryingProvider, RetryingProviderError}; /// Chain submitter -mod chain_submitter; -pub use chain_submitter::*; +mod submitter; +pub use submitter::*; /// Contract binding #[cfg(not(doctest))] diff --git a/chains/nomad-ethereum/src/chain_submitter.rs b/chains/nomad-ethereum/src/submitter.rs similarity index 56% rename from chains/nomad-ethereum/src/chain_submitter.rs rename to chains/nomad-ethereum/src/submitter.rs index 5010f1bc..9748de8a 100644 --- a/chains/nomad-ethereum/src/chain_submitter.rs +++ b/chains/nomad-ethereum/src/submitter.rs @@ -1,12 +1,14 @@ use crate::http_signer_middleware; -use color_eyre::Result; +use color_eyre::{eyre::bail, Result}; use ethers::prelude::*; use ethers::types::transaction::eip2718::TypedTransaction; use nomad_core::Signers; use nomad_xyz_configuration::ethereum::Connection; use std::sync::Arc; +use tokio::{sync::mpsc, task::JoinHandle}; -/// Configuration for creating an ethers::SignerMiddleware +/// Configuration for a ethers signing provider +#[derive(Debug, Clone)] pub struct SigningProviderConfig { /// Signer configuration pub signer: Signers, @@ -21,16 +23,34 @@ impl SigningProviderConfig { } } +/// Unsigned transaction and metadata +#[derive(Debug, Clone)] +pub struct MetaTx { + domain: u32, + contract_address: Address, + tx: TypedTransaction, +} + /// Component responsible for submitting transactions to the chain. Can /// sign/submit locally or use a transaction relay service. -pub enum ChainSubmitter { +#[derive(Debug, Clone)] +pub enum Submitter { /// Sign/submit txs locally Local(SigningProviderConfig), } +/// Receives meta txs and submits them to chain +#[derive(Debug)] +pub struct ChainSubmitter { + /// Tx submitter + pub submitter: Submitter, + // /// Meta tx receiver + // pub rx: mpsc::Receiver, +} + impl ChainSubmitter { /// Submit transaction to chain - pub async fn submit_to_chain( + pub async fn submit( &self, _domain: u32, _contract_address: Address, @@ -38,8 +58,8 @@ impl ChainSubmitter { ) -> Result<()> { let tx: TypedTransaction = tx.into(); - match self { - ChainSubmitter::Local(config) => { + match &self.submitter { + Submitter::Local(config) => { let signer = config.signer.clone(); let client = match &config.connection { Connection::Http { url } => http_signer_middleware!(url, signer), @@ -58,15 +78,29 @@ impl ChainSubmitter { ); Ok(()) - } // ChainSubmitter::Gelato(client) => { - // client.send_relay_transaction( - // chain_id, // translate util - // dest, // contract address - // data, // TypedTransaction - // token, // configurable fee token - // relayer_fee // configurable fee amount - // ).await? - // } + } } } + + // /// Spawn ChainSubmitter task. Receives meta txs and submits in loop. + // #[tracing::instrument] + // pub async fn spawn(mut self) -> JoinHandle> { + // tokio::spawn(async move { + // loop { + // let tx = self.rx.recv().await; + + // if tx.is_none() { + // bail!("Eth ChainSubmitter channel closed.") + // } + + // let MetaTx { + // domain, + // contract_address, + // tx, + // } = tx.unwrap(); + + // self.submit(domain, contract_address, tx).await?; + // } + // }) + // } } diff --git a/nomad-base/src/lib.rs b/nomad-base/src/lib.rs index 264b96cd..86a86b2e 100644 --- a/nomad-base/src/lib.rs +++ b/nomad-base/src/lib.rs @@ -50,3 +50,6 @@ pub use contract_sync::*; mod indexer; pub use indexer::*; + +mod submitter; +pub use submitter::*; diff --git a/nomad-base/src/submitter.rs b/nomad-base/src/submitter.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/nomad-base/src/submitter.rs @@ -0,0 +1 @@ + From 194d6d66b78b1f884c846b3aefcf717d9e9b2cf2 Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Sat, 30 Apr 2022 14:17:42 -0700 Subject: [PATCH 09/41] prog(chain-submitter): add gelato variant skeleton --- chains/nomad-ethereum/src/submitter.rs | 72 +++++++++++++++++++------- gelato-relay/src/client.rs | 14 ++++- gelato-relay/src/lib.rs | 10 ++-- gelato-relay/src/types.rs | 26 +++++----- 4 files changed, 83 insertions(+), 39 deletions(-) diff --git a/chains/nomad-ethereum/src/submitter.rs b/chains/nomad-ethereum/src/submitter.rs index 9748de8a..124ae1ea 100644 --- a/chains/nomad-ethereum/src/submitter.rs +++ b/chains/nomad-ethereum/src/submitter.rs @@ -1,11 +1,11 @@ -use crate::http_signer_middleware; -use color_eyre::{eyre::bail, Result}; +use color_eyre::Result; use ethers::prelude::*; use ethers::types::transaction::eip2718::TypedTransaction; +use gelato_relay::{RelayResponse, SingleChainGelatoClient}; use nomad_core::Signers; use nomad_xyz_configuration::ethereum::Connection; use std::sync::Arc; -use tokio::{sync::mpsc, task::JoinHandle}; +use tracing::info; /// Configuration for a ethers signing provider #[derive(Debug, Clone)] @@ -34,56 +34,88 @@ pub struct MetaTx { /// Component responsible for submitting transactions to the chain. Can /// sign/submit locally or use a transaction relay service. #[derive(Debug, Clone)] -pub enum Submitter { +pub enum Submitter { /// Sign/submit txs locally - Local(SigningProviderConfig), + Local(Arc), + /// Pass meta txs to Gelato relay service + Gelato(SingleChainGelatoClient), } /// Receives meta txs and submits them to chain #[derive(Debug)] -pub struct ChainSubmitter { +pub struct ChainSubmitter { /// Tx submitter - pub submitter: Submitter, + pub submitter: Submitter, // /// Meta tx receiver // pub rx: mpsc::Receiver, } -impl ChainSubmitter { +impl ChainSubmitter { /// Submit transaction to chain pub async fn submit( &self, - _domain: u32, - _contract_address: Address, + domain: u32, + contract_address: Address, tx: impl Into, ) -> Result<()> { let tx: TypedTransaction = tx.into(); match &self.submitter { - Submitter::Local(config) => { - let signer = config.signer.clone(); - let client = match &config.connection { - Connection::Http { url } => http_signer_middleware!(url, signer), - Connection::Ws { url: _ } => panic!("not supporting ws"), - }; - + Submitter::Local(client) => { let dispatched = client.send_transaction(tx, None).await?; let tx_hash: ethers::core::types::H256 = *dispatched; + info!("dispatched transaction with tx_hash {:?}", tx_hash); + let result = dispatched .await? .ok_or_else(|| nomad_core::ChainCommunicationError::DroppedError(tx_hash))?; - tracing::info!( + info!( "confirmed transaction with tx_hash {:?}", result.transaction_hash ); + } + Submitter::Gelato(client) => { + let tx_data = tx.data().expect("!tx data"); + let data = format!("{:x}", tx_data); + let address = format!("{:x}", contract_address); + + info!( + domain = domain, + contract_address = ?address, + "Dispatching tx to Gelato relay." + ); - Ok(()) + let RelayResponse { task_id } = + client.send_relay_transaction(&address, &data).await?; + info!(task_id = ?task_id, "Submitted tx to Gelato relay."); + + loop { + let status = client + .client() + .get_task_status(&task_id) + .await? + .expect("!task status"); + + if let Some(execution) = &status.execution { + info!( + chain = ?status.chain, + task_id = ?status.task_id, + execution = ?execution, + "Gelato relay executed tx." + ); + + break; + } + } } } + + Ok(()) } // /// Spawn ChainSubmitter task. Receives meta txs and submits in loop. - // #[tracing::instrument] + // #[instrument] // pub async fn spawn(mut self) -> JoinHandle> { // tokio::spawn(async move { // loop { diff --git a/gelato-relay/src/client.rs b/gelato-relay/src/client.rs index e7ccf058..af86aaa1 100644 --- a/gelato-relay/src/client.rs +++ b/gelato-relay/src/client.rs @@ -1,19 +1,29 @@ use crate::{GelatoClient, RelayResponse}; +use ethers::providers::Middleware; +use std::marker::PhantomData; /// Gelato client for submitting txs to single chain -pub struct SingleChainGelatoClient { +#[derive(Debug, Clone)] +pub struct SingleChainGelatoClient { pub client: GelatoClient, pub chain_id: usize, pub payment_token: String, + _middleware: PhantomData, } -impl SingleChainGelatoClient { +impl SingleChainGelatoClient { + /// Get reference to base client + pub fn client(&self) -> &GelatoClient { + &self.client + } + /// Instantiate single chain client with default Gelato url pub fn with_default_url(chain_id: usize, payment_token: String) -> Self { Self { client: GelatoClient::default(), chain_id, payment_token, + _middleware: Default::default(), } } diff --git a/gelato-relay/src/lib.rs b/gelato-relay/src/lib.rs index fcfc29e2..f499fd8e 100644 --- a/gelato-relay/src/lib.rs +++ b/gelato-relay/src/lib.rs @@ -1,14 +1,15 @@ mod types; -use types::*; +pub use types::*; mod client; -use client::*; +pub use client::*; use color_eyre::eyre::Result; use std::collections::HashMap; const DEFAULT_URL: &str = "https://relay.gelato.digital"; +#[derive(Debug, Clone)] pub struct GelatoClient { url: String, } @@ -91,9 +92,10 @@ impl GelatoClient { pub async fn get_task_status( &self, task_id: &str, - ) -> Result { + ) -> Result, reqwest::Error> { let url = format!("{}/tasks/{}", &self.url, task_id); let res = reqwest::get(url).await?; - Ok(res.json().await?) + let task_status: TaskStatusResponse = res.json().await?; + Ok(task_status.data.first().cloned()) } } diff --git a/gelato-relay/src/types.rs b/gelato-relay/src/types.rs index 4a89bce7..66e2cf02 100644 --- a/gelato-relay/src/types.rs +++ b/gelato-relay/src/types.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RelayRequest { pub dest: String, @@ -9,7 +9,7 @@ pub struct RelayRequest { pub relayer_fee: String, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct EstimatedFeeRequest { pub payment_token: String, @@ -17,31 +17,31 @@ pub struct EstimatedFeeRequest { pub is_high_priority: bool, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RelayResponse { pub task_id: String, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct EstimatedFeeResponse { pub estimated_fee: String, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RelayChainsResponse { pub relays: Vec, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TaskStatusResponse { - data: Vec, + pub data: Vec, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct TaskStatus { pub service: String, @@ -55,7 +55,7 @@ pub struct TaskStatus { pub last_execution: String, // date } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Execution { pub status: String, @@ -64,7 +64,7 @@ pub struct Execution { pub created_at: String, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Check { pub task_state: TaskState, @@ -73,7 +73,7 @@ pub struct Check { pub created_at: Option, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Payload { pub to: String, @@ -81,7 +81,7 @@ pub struct Payload { pub fee_data: FeeData, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct FeeData { pub gas_price: usize, @@ -89,7 +89,7 @@ pub struct FeeData { pub max_priority_fee_per_gas: usize, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum TaskState { CheckPending, ExecPending, From af1f5421539d5a519a1831c3bad813ea5f17cb01 Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Sat, 30 Apr 2022 14:42:52 -0700 Subject: [PATCH 10/41] prog(chain-submitter): impl From traits --- chains/nomad-ethereum/src/submitter.rs | 66 ++++++-------------------- 1 file changed, 14 insertions(+), 52 deletions(-) diff --git a/chains/nomad-ethereum/src/submitter.rs b/chains/nomad-ethereum/src/submitter.rs index 124ae1ea..5565c95c 100644 --- a/chains/nomad-ethereum/src/submitter.rs +++ b/chains/nomad-ethereum/src/submitter.rs @@ -2,52 +2,36 @@ use color_eyre::Result; use ethers::prelude::*; use ethers::types::transaction::eip2718::TypedTransaction; use gelato_relay::{RelayResponse, SingleChainGelatoClient}; -use nomad_core::Signers; -use nomad_xyz_configuration::ethereum::Connection; use std::sync::Arc; use tracing::info; -/// Configuration for a ethers signing provider -#[derive(Debug, Clone)] -pub struct SigningProviderConfig { - /// Signer configuration - pub signer: Signers, - /// Connection configuration - pub connection: Connection, -} - -impl SigningProviderConfig { - /// Instantiate new signing provider config - pub fn new(signer: Signers, connection: Connection) -> Self { - Self { signer, connection } - } -} - -/// Unsigned transaction and metadata -#[derive(Debug, Clone)] -pub struct MetaTx { - domain: u32, - contract_address: Address, - tx: TypedTransaction, -} - /// Component responsible for submitting transactions to the chain. Can /// sign/submit locally or use a transaction relay service. #[derive(Debug, Clone)] -pub enum Submitter { +pub enum Submitter { /// Sign/submit txs locally Local(Arc), /// Pass meta txs to Gelato relay service Gelato(SingleChainGelatoClient), } +impl From> for Submitter { + fn from(client: Arc) -> Self { + Self::Local(client) + } +} + +impl From> for Submitter { + fn from(client: SingleChainGelatoClient) -> Self { + Self::Gelato(client) + } +} + /// Receives meta txs and submits them to chain #[derive(Debug)] -pub struct ChainSubmitter { +pub struct ChainSubmitter { /// Tx submitter pub submitter: Submitter, - // /// Meta tx receiver - // pub rx: mpsc::Receiver, } impl ChainSubmitter { @@ -113,26 +97,4 @@ impl ChainSubmitter { Ok(()) } - - // /// Spawn ChainSubmitter task. Receives meta txs and submits in loop. - // #[instrument] - // pub async fn spawn(mut self) -> JoinHandle> { - // tokio::spawn(async move { - // loop { - // let tx = self.rx.recv().await; - - // if tx.is_none() { - // bail!("Eth ChainSubmitter channel closed.") - // } - - // let MetaTx { - // domain, - // contract_address, - // tx, - // } = tx.unwrap(); - - // self.submit(domain, contract_address, tx).await?; - // } - // }) - // } } From 5b214a5aa148e2ad0e11afcfb6ff2594ada9cf6a Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Sat, 30 Apr 2022 15:02:54 -0700 Subject: [PATCH 11/41] refactor(gelato): move client to nomad-ethereum --- Cargo.lock | 1 + chains/nomad-ethereum/Cargo.toml | 1 + .../nomad-ethereum/src/gelato.rs | 35 +++++++++++++++---- chains/nomad-ethereum/src/lib.rs | 4 +++ chains/nomad-ethereum/src/submitter.rs | 3 +- gelato-relay/src/lib.rs | 3 -- 6 files changed, 37 insertions(+), 10 deletions(-) rename gelato-relay/src/client.rs => chains/nomad-ethereum/src/gelato.rs (55%) diff --git a/Cargo.lock b/Cargo.lock index 5a6b2804..2aed8200 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2533,6 +2533,7 @@ dependencies = [ "nomad-xyz-configuration", "num", "prometheus", + "reqwest", "rocksdb", "serde 1.0.136", "serde_json", diff --git a/chains/nomad-ethereum/Cargo.toml b/chains/nomad-ethereum/Cargo.toml index 0d112deb..8016fa52 100644 --- a/chains/nomad-ethereum/Cargo.toml +++ b/chains/nomad-ethereum/Cargo.toml @@ -28,6 +28,7 @@ futures-util = "0.3.12" tracing-futures = "0.2.5" url = "2.2.2" thiserror = "1.0.30" +reqwest = { version = "0.11.10", features = ["json"]} nomad-xyz-configuration = { path = "../../configuration" } nomad-types = { path = "../../nomad-types" } diff --git a/gelato-relay/src/client.rs b/chains/nomad-ethereum/src/gelato.rs similarity index 55% rename from gelato-relay/src/client.rs rename to chains/nomad-ethereum/src/gelato.rs index af86aaa1..3562506b 100644 --- a/gelato-relay/src/client.rs +++ b/chains/nomad-ethereum/src/gelato.rs @@ -1,13 +1,35 @@ -use crate::{GelatoClient, RelayResponse}; use ethers::providers::Middleware; +use gelato_relay::{GelatoClient, RelayResponse}; +use nomad_core::Signers; use std::marker::PhantomData; +/* +{ + chainId: number; + target: string; ** contract address? + data: BytesLike; + feeToken: string; + paymentType: number; ** some kind of enum for gas tank vs. legacy? + maxFee: string; ** just call get_estimated_fee? + sponsor: string; + sponsorChainId: number; + nonce: number; ** does this enforce ordering too? + sponsorSignature: BytesLike; +} + */ + /// Gelato client for submitting txs to single chain #[derive(Debug, Clone)] pub struct SingleChainGelatoClient { + /// Base client pub client: GelatoClient, + /// Sponsor signer + pub sponsor: Signers, + /// Chain id pub chain_id: usize, - pub payment_token: String, + /// Fee token + pub fee_token: String, + /// Unused _middleware: PhantomData, } @@ -18,11 +40,12 @@ impl SingleChainGelatoClient { } /// Instantiate single chain client with default Gelato url - pub fn with_default_url(chain_id: usize, payment_token: String) -> Self { + pub fn with_default_url(sponsor: Signers, chain_id: usize, fee_token: String) -> Self { Self { client: GelatoClient::default(), + sponsor, chain_id, - payment_token, + fee_token, _middleware: Default::default(), } } @@ -35,11 +58,11 @@ impl SingleChainGelatoClient { ) -> Result { let relayer_fee = self .client - .get_estimated_fee(self.chain_id, &self.payment_token, 100_000, true) + .get_estimated_fee(self.chain_id, &self.fee_token, 100_000, true) .await?; self.client - .send_relay_transaction(self.chain_id, dest, data, &self.payment_token, relayer_fee) + .send_relay_transaction(self.chain_id, dest, data, &self.fee_token, relayer_fee) .await } } diff --git a/chains/nomad-ethereum/src/lib.rs b/chains/nomad-ethereum/src/lib.rs index b3e3bbf7..cd0a9bc2 100644 --- a/chains/nomad-ethereum/src/lib.rs +++ b/chains/nomad-ethereum/src/lib.rs @@ -20,6 +20,10 @@ mod macros; mod retrying; pub use retrying::{RetryingProvider, RetryingProviderError}; +/// Gelato client types +mod gelato; +pub use gelato::*; + /// Chain submitter mod submitter; pub use submitter::*; diff --git a/chains/nomad-ethereum/src/submitter.rs b/chains/nomad-ethereum/src/submitter.rs index 5565c95c..afc1d0f8 100644 --- a/chains/nomad-ethereum/src/submitter.rs +++ b/chains/nomad-ethereum/src/submitter.rs @@ -1,7 +1,8 @@ +use crate::SingleChainGelatoClient; use color_eyre::Result; use ethers::prelude::*; use ethers::types::transaction::eip2718::TypedTransaction; -use gelato_relay::{RelayResponse, SingleChainGelatoClient}; +use gelato_relay::RelayResponse; use std::sync::Arc; use tracing::info; diff --git a/gelato-relay/src/lib.rs b/gelato-relay/src/lib.rs index f499fd8e..d01b8baa 100644 --- a/gelato-relay/src/lib.rs +++ b/gelato-relay/src/lib.rs @@ -1,9 +1,6 @@ mod types; pub use types::*; -mod client; -pub use client::*; - use color_eyre::eyre::Result; use std::collections::HashMap; From 5d07c829c83ae1f86f53fbf0768a5771f6e1f0df Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Sat, 30 Apr 2022 17:32:25 -0700 Subject: [PATCH 12/41] prog(tx-submission-config): struggling --- configuration/src/chains/ethereum/gelato.rs | 54 +++++++++++++++++++ configuration/src/chains/ethereum/mod.rs | 59 +++++++++++++++++++++ configuration/src/chains/mod.rs | 24 +++++++++ configuration/src/secrets.rs | 45 ++++++++++++++-- fixtures/test_secrets.json | 23 ++++++-- nomad-base/src/settings/mod.rs | 16 +++--- 6 files changed, 207 insertions(+), 14 deletions(-) create mode 100644 configuration/src/chains/ethereum/gelato.rs create mode 100644 configuration/src/chains/ethereum/mod.rs diff --git a/configuration/src/chains/ethereum/gelato.rs b/configuration/src/chains/ethereum/gelato.rs new file mode 100644 index 00000000..e513530b --- /dev/null +++ b/configuration/src/chains/ethereum/gelato.rs @@ -0,0 +1,54 @@ +use crate::{agent::SignerConf, FromEnv}; + +/// Configuration for tx submission through Gelato relay +#[derive(Debug, Clone, PartialEq, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GelatoConf { + /// Sponsor signer configuration + pub signer: SignerConf, + /// Address of fee token + pub fee_token: String, +} + +/* + { + "transactionSubmission": { + "ethereum": { + "type": "gelato", + "sponsorSigner": { + ... + }, + "feeToken": "0xabc" + }, + "moonbeam": { + "type": "local", + "sponsorSigner": { + ... + }, + } + } + } + + + TXSUBMISSION_KOVAN_TYPE=local + TRANSACTIONSIGNERS_KOVAN_TYPE=hexKey + TRANSACTIONSIGNERS_KOVAN_KEY=0x0000001111111111111111111111111111111111111111111111111111ce1002 + + TXSUBMISSION_KOVAN_TYPE=gelato + SPONSORSIGNER_KOVAN_TYPE=hexKey + SPONSORSIGNER_KOVAN_KEY=0x0000001111111111111111111111111111111111111111111111111111ce1002 + + TXSUBMISSION_KOVAN_TYPE=gelato + SPONSORSIGNER_KOVAN_TYPE=aws + SPONSORSIGNER_KOVAN_ID=... + SPONSORSIGNER_KOVAN_REGION=... +*/ + +impl FromEnv for GelatoConf { + fn from_env(network: &str) -> Option { + let signer = SignerConf::from_env(&format!("SPONSORSIGNER_{}", network))?; + let fee_token = std::env::var(&format!("FEETOKEN_{}", network)).ok()?; + + Some(Self { signer, fee_token }) + } +} diff --git a/configuration/src/chains/ethereum/mod.rs b/configuration/src/chains/ethereum/mod.rs new file mode 100644 index 00000000..4d943d3f --- /dev/null +++ b/configuration/src/chains/ethereum/mod.rs @@ -0,0 +1,59 @@ +//! Ethereum/EVM configuration types + +use crate::{agent::SignerConf, FromEnv}; + +mod gelato; +pub use gelato::*; + +/// Ethereum connection configuration +#[derive(Debug, serde::Deserialize, Clone, PartialEq)] +#[serde(tag = "type", rename_all = "camelCase")] +pub enum Connection { + /// HTTP connection details + Http { + /// Fully qualified string to connect to + url: String, + }, + /// Websocket connection details + Ws { + /// Fully qualified string to connect to + url: String, + }, +} + +impl Default for Connection { + fn default() -> Self { + Self::Http { + url: Default::default(), + } + } +} + +/// Local or relay-based transaction submission +#[derive(Debug, Clone, PartialEq, serde::Deserialize)] +#[serde(tag = "submitterType", rename_all = "camelCase")] +pub enum TransactionSubmitter { + /// Signer configuration for local signer + Local(SignerConf), + /// Gelato configuration for Gelato relay + Gelato(GelatoConf), +} + +impl FromEnv for TransactionSubmitter { + fn from_env(network: &str) -> Option { + let submission_type = + std::env::var(&format!("TRANSACTIONSUBMITTER_{}_TYPE", network)).ok()?; + + match submission_type.as_ref() { + "local" => { + let signer_conf = SignerConf::from_env(&format!("TRANSACTIONSIGNERS_{}", network))?; + Some(Self::Local(signer_conf)) + } + "gelato" => { + let gelato_conf = GelatoConf::from_env(network)?; + Some(Self::Gelato(gelato_conf)) + } + _ => panic!("Unknown tx submission type: {}", submission_type), + } + } +} diff --git a/configuration/src/chains/mod.rs b/configuration/src/chains/mod.rs index e6175b81..8972c4f2 100644 --- a/configuration/src/chains/mod.rs +++ b/configuration/src/chains/mod.rs @@ -38,3 +38,27 @@ impl FromEnv for ChainConf { ) } } + +/// Transaction submssion configuration for some chain. +#[derive(Clone, Debug, serde::Deserialize, PartialEq)] +#[serde( + tag = "rpcStyle", + content = "transactionSubmission", + rename_all = "camelCase" +)] +pub enum TransactionSubmitter { + /// Ethereum configuration + Ethereum(ethereum::TransactionSubmitter), +} + +impl FromEnv for TransactionSubmitter { + fn from_env(network: &str) -> Option { + let rpc_style = std::env::var(&format!("{}_RPCSTYLE", network)).ok()?; + match rpc_style.as_ref() { + "ethereum" => Some(Self::Ethereum(ethereum::TransactionSubmitter::from_env( + network, + )?)), + _ => panic!("Unknown transaction submission rpc style: {}", rpc_style), + } + } +} diff --git a/configuration/src/secrets.rs b/configuration/src/secrets.rs index 7c515b43..6b34f82e 100644 --- a/configuration/src/secrets.rs +++ b/configuration/src/secrets.rs @@ -3,7 +3,7 @@ //! This struct built from environment variables. It is used alongside a //! NomadConfig to build an agents `Settings` block (see settings/mod.rs). -use crate::{agent::SignerConf, chains::ethereum, ChainConf, FromEnv}; +use crate::{agent::SignerConf, chains::ethereum, ChainConf, TransactionSubmitter, FromEnv}; use eyre::Result; use serde::Deserialize; use std::collections::{HashMap, HashSet}; @@ -15,8 +15,8 @@ use std::{fs::File, io::BufReader, path::Path}; pub struct AgentSecrets { /// RPC endpoints pub rpcs: HashMap, - /// Transaction signers - pub transaction_signers: HashMap, + /// Transaction submission variants + pub transaction_submitters: HashMap, /// Attestation signers pub attestation_signer: Option, } @@ -107,6 +107,45 @@ impl AgentSecrets { } } +impl FromEnv for AgentSecrets { + fn from_env(_prefix: &str) -> Option { + let env = std::env::var("RUN_ENV").ok()?; + let home = std::env::var("AGENT_HOME").ok()?; + + let config = crate::get_builtin(&env) + .expect("couldn't retrieve config!") + .to_owned(); + + let mut networks = config + .protocol() + .networks + .get(&home) + .expect("!networks") + .connections + .to_owned(); + networks.insert(home); + + let mut secrets = AgentSecrets::default(); + + for network in networks.iter() { + let network_upper = network.to_uppercase(); + let chain_conf = ChainConf::from_env(&format!("RPCS_{}", network_upper))?; + let transaction_submitter = + TransactionSubmitter::from_env(network)?; + + secrets.rpcs.insert(network.to_owned(), chain_conf); + secrets + .transaction_submitters + .insert(network.to_owned(), transaction_submitter); + } + + let attestation_signer = SignerConf::from_env("ATTESTATION_SIGNER"); + secrets.attestation_signer = attestation_signer; + + Some(secrets) + } +} + #[cfg(test)] mod test { use super::*; diff --git a/fixtures/test_secrets.json b/fixtures/test_secrets.json index 57bbb0c3..42ead945 100644 --- a/fixtures/test_secrets.json +++ b/fixtures/test_secrets.json @@ -10,9 +10,26 @@ "connection": "wss://rpc.api.moonbeam.network" } }, - "transactionSigners": { - "ethereum": "0x1111111111111111111111111111111111111111111111111111111111111112", - "moonbeam": "1111111111111111111111111111111111111111111111111111111111111112" + "transactionSubmission": { + "ethereum": { + "rpcStyle": "ethereum", + "transactionSubmission": { + "submissionType": "local", + "key": "0x1111111111111111111111111111111111111111111111111111111111111111", + "type": "hexKey" + } + }, + "moonbeam": { + "rpcStyle": "ethereum", + "transactionSubmission": { + "submissionType": "gelato", + "signer": { + "key": "0x1111111111111111111111111111111111111111111111111111111111111111", + "type": "hexKey" + }, + "feeToken": "0xabc" + } + } }, "attestationSigner": { "id": "hello", diff --git a/nomad-base/src/settings/mod.rs b/nomad-base/src/settings/mod.rs index cb5502ba..ed981c0d 100644 --- a/nomad-base/src/settings/mod.rs +++ b/nomad-base/src/settings/mod.rs @@ -17,7 +17,7 @@ use crate::{ use color_eyre::{eyre::bail, Result}; use nomad_core::{db::DB, Common, ContractLocator, Signers}; use nomad_ethereum::{make_home_indexer, make_replica_indexer}; -use nomad_xyz_configuration::{agent::SignerConf, AgentSecrets}; +use nomad_xyz_configuration::{agent::SignerConf, AgentSecrets, TransactionSubmitter}; use nomad_xyz_configuration::{contracts::CoreContracts, ChainConf, NomadConfig, NomadGasConfig}; use serde::Deserialize; use std::collections::HashSet; @@ -161,7 +161,7 @@ pub struct Settings { /// The tracing configuration pub logging: LogConfig, /// Transaction signers - pub signers: HashMap, + pub submitters: HashMap, /// Optional attestation signer pub attestation_signer: Option, } @@ -178,7 +178,7 @@ impl Settings { managers: self.managers.clone(), gas: self.gas.clone(), logging: self.logging, - signers: self.signers.clone(), + submitters: self.submitters.clone(), attestation_signer: self.attestation_signer.clone(), } } @@ -187,7 +187,7 @@ impl Settings { impl Settings { /// Try to get a signer instance by name pub async fn get_signer(&self, name: &str) -> Option> { - let conf = self.signers.get(name); + let conf = self.submitters.get(name); if let Some(conf) = conf { Some(Signers::try_from_signer_conf(conf).await) } else { @@ -504,7 +504,7 @@ impl Settings { gas, index, logging: agent.logging, - signers: secrets.transaction_signers.clone(), + submitters: secrets.transaction_submitters.clone(), attestation_signer: secrets.attestation_signer.clone(), } } @@ -585,9 +585,9 @@ impl Settings { assert_eq!(&replica_setup.chain, replica_chain_conf); } - for (network, signer) in self.signers.iter() { - let secret_signer = secrets.transaction_signers.get(network).unwrap(); - assert_eq!(signer, secret_signer); + for (network, signer) in self.submitters.iter() { + let secret_submitter = secrets.transaction_submitters.get(network).unwrap(); + assert_eq!(signer, secret_submitter); } Ok(()) From f191e7c59441061c9140f62fb830e82be4554cc8 Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Sat, 30 Apr 2022 22:58:11 -0700 Subject: [PATCH 13/41] prog(configuration): working TransactionSubmitterConf in configuration --- configuration/src/chains/ethereum/gelato.rs | 6 ++--- configuration/src/chains/ethereum/mod.rs | 19 +++++++-------- configuration/src/chains/mod.rs | 27 +++++++++++---------- configuration/src/secrets.rs | 9 +++---- fixtures/env.test | 9 ++++++- fixtures/test_secrets.json | 14 +++++------ nomad-base/src/settings/chains.rs | 4 +-- nomad-base/src/settings/mod.rs | 4 +-- 8 files changed, 49 insertions(+), 43 deletions(-) diff --git a/configuration/src/chains/ethereum/gelato.rs b/configuration/src/chains/ethereum/gelato.rs index e513530b..c883d8af 100644 --- a/configuration/src/chains/ethereum/gelato.rs +++ b/configuration/src/chains/ethereum/gelato.rs @@ -45,9 +45,9 @@ pub struct GelatoConf { */ impl FromEnv for GelatoConf { - fn from_env(network: &str) -> Option { - let signer = SignerConf::from_env(&format!("SPONSORSIGNER_{}", network))?; - let fee_token = std::env::var(&format!("FEETOKEN_{}", network)).ok()?; + fn from_env(prefix: &str) -> Option { + let signer = SignerConf::from_env(&format!("{}_SIGNER", prefix))?; + let fee_token = std::env::var(&format!("{}_FEETOKEN", prefix)).ok()?; Some(Self { signer, fee_token }) } diff --git a/configuration/src/chains/ethereum/mod.rs b/configuration/src/chains/ethereum/mod.rs index 4d943d3f..70996a60 100644 --- a/configuration/src/chains/ethereum/mod.rs +++ b/configuration/src/chains/ethereum/mod.rs @@ -31,29 +31,28 @@ impl Default for Connection { /// Local or relay-based transaction submission #[derive(Debug, Clone, PartialEq, serde::Deserialize)] -#[serde(tag = "submitterType", rename_all = "camelCase")] -pub enum TransactionSubmitter { +#[serde(tag = "submitterType", content = "submitter", rename_all = "camelCase")] +pub enum TransactionSubmitterConf { /// Signer configuration for local signer Local(SignerConf), /// Gelato configuration for Gelato relay Gelato(GelatoConf), } -impl FromEnv for TransactionSubmitter { - fn from_env(network: &str) -> Option { - let submission_type = - std::env::var(&format!("TRANSACTIONSUBMITTER_{}_TYPE", network)).ok()?; +impl FromEnv for TransactionSubmitterConf { + fn from_env(prefix: &str) -> Option { + let submitter_type = std::env::var(&format!("{}_SUBMITTERTYPE", prefix)).ok()?; - match submission_type.as_ref() { + match submitter_type.as_ref() { "local" => { - let signer_conf = SignerConf::from_env(&format!("TRANSACTIONSIGNERS_{}", network))?; + let signer_conf = SignerConf::from_env(&format!("{}_SUBMITTER", prefix))?; Some(Self::Local(signer_conf)) } "gelato" => { - let gelato_conf = GelatoConf::from_env(network)?; + let gelato_conf = GelatoConf::from_env(&format!("{}_SUBMITTER", prefix))?; Some(Self::Gelato(gelato_conf)) } - _ => panic!("Unknown tx submission type: {}", submission_type), + _ => panic!("Unknown tx submission type: {}", submitter_type), } } } diff --git a/configuration/src/chains/mod.rs b/configuration/src/chains/mod.rs index 8972c4f2..e76043a0 100644 --- a/configuration/src/chains/mod.rs +++ b/configuration/src/chains/mod.rs @@ -34,30 +34,31 @@ impl FromEnv for ChainConf { Some( serde_json::from_value(json) - .unwrap_or_else(|_| panic!("malformed json for {} rpc", prefix)), + .unwrap_or_else(|_| panic!("malformed json for {} rpc", network)), ) } } /// Transaction submssion configuration for some chain. #[derive(Clone, Debug, serde::Deserialize, PartialEq)] -#[serde( - tag = "rpcStyle", - content = "transactionSubmission", - rename_all = "camelCase" -)] -pub enum TransactionSubmitter { +#[serde(tag = "rpcStyle", rename_all = "camelCase")] +pub enum TransactionSubmitterConf { /// Ethereum configuration - Ethereum(ethereum::TransactionSubmitter), + Ethereum(ethereum::TransactionSubmitterConf), } -impl FromEnv for TransactionSubmitter { +impl FromEnv for TransactionSubmitterConf { fn from_env(network: &str) -> Option { - let rpc_style = std::env::var(&format!("{}_RPCSTYLE", network)).ok()?; + let rpc_style = + std::env::var(&format!("TRANSACTIONSUBMITTERS_{}_RPCSTYLE", network)).ok()?; + match rpc_style.as_ref() { - "ethereum" => Some(Self::Ethereum(ethereum::TransactionSubmitter::from_env( - network, - )?)), + "ethereum" => Some(Self::Ethereum( + ethereum::TransactionSubmitterConf::from_env(&format!( + "TRANSACTIONSUBMITTERS_{}", + network + ))?, + )), _ => panic!("Unknown transaction submission rpc style: {}", rpc_style), } } diff --git a/configuration/src/secrets.rs b/configuration/src/secrets.rs index 6b34f82e..36b4582d 100644 --- a/configuration/src/secrets.rs +++ b/configuration/src/secrets.rs @@ -3,7 +3,7 @@ //! This struct built from environment variables. It is used alongside a //! NomadConfig to build an agents `Settings` block (see settings/mod.rs). -use crate::{agent::SignerConf, chains::ethereum, ChainConf, TransactionSubmitter, FromEnv}; +use crate::{agent::SignerConf, chains::ethereum, ChainConf, FromEnv, TransactionSubmitterConf}; use eyre::Result; use serde::Deserialize; use std::collections::{HashMap, HashSet}; @@ -16,7 +16,7 @@ pub struct AgentSecrets { /// RPC endpoints pub rpcs: HashMap, /// Transaction submission variants - pub transaction_submitters: HashMap, + pub transaction_submitters: HashMap, /// Attestation signers pub attestation_signer: Option, } @@ -129,9 +129,8 @@ impl FromEnv for AgentSecrets { for network in networks.iter() { let network_upper = network.to_uppercase(); - let chain_conf = ChainConf::from_env(&format!("RPCS_{}", network_upper))?; - let transaction_submitter = - TransactionSubmitter::from_env(network)?; + let chain_conf = ChainConf::from_env(&network_upper)?; + let transaction_submitter = TransactionSubmitterConf::from_env(&network_upper)?; secrets.rpcs.insert(network.to_owned(), chain_conf); secrets diff --git a/fixtures/env.test b/fixtures/env.test index fdb6c2c6..56bb9bab 100644 --- a/fixtures/env.test +++ b/fixtures/env.test @@ -17,7 +17,14 @@ TRANSACTIONSIGNERS_ETHEREUM_KEY=0x1111111111111111111111111111111111111111111111 TRANSACTIONSIGNERS_MOONBEAM_KEY=0x1111111111111111111111111111111111111111111111111111111111111112 + +TRANSACTIONSUBMITTERS_MOONBEAM_RPCSTYLE=ethereum +TRANSACTIONSUBMITTERS_MOONBEAM_SUBMITTERTYPE=gelato +TRANSACTIONSUBMITTERS_MOONBEAM_SUBMITTER_SIGNER_KEYTYPE=hexKey +TRANSACTIONSUBMITTERS_MOONBEAM_SUBMITTER_SIGNER_KEY=0x1111111111111111111111111111111111111111111111111111111111111111 +TRANSACTIONSUBMITTERS_MOONBEAM_SUBMITTER_FEETOKEN=0xabcd + TRANSACTIONSIGNERS_EVMOS_KEY=0x1111111111111111111111111111111111111111111111111111111111111112 ATTESTATION_SIGNER_ID=dummy_id -ATTESTATION_SIGNER_REGION=dummy_region \ No newline at end of file +ATTESTATION_SIGNER_REGION=dummy_region diff --git a/fixtures/test_secrets.json b/fixtures/test_secrets.json index 42ead945..84e85771 100644 --- a/fixtures/test_secrets.json +++ b/fixtures/test_secrets.json @@ -10,22 +10,22 @@ "connection": "wss://rpc.api.moonbeam.network" } }, - "transactionSubmission": { + "transactionSubmitters": { "ethereum": { "rpcStyle": "ethereum", - "transactionSubmission": { - "submissionType": "local", + "submitterType": "local", + "submitter": { "key": "0x1111111111111111111111111111111111111111111111111111111111111111", - "type": "hexKey" + "keyType": "hexKey" } }, "moonbeam": { "rpcStyle": "ethereum", - "transactionSubmission": { - "submissionType": "gelato", + "submitterType": "gelato", + "submitter": { "signer": { "key": "0x1111111111111111111111111111111111111111111111111111111111111111", - "type": "hexKey" + "keyType": "hexKey" }, "feeToken": "0xabc" } diff --git a/nomad-base/src/settings/chains.rs b/nomad-base/src/settings/chains.rs index e35c5c3c..148c8404 100644 --- a/nomad-base/src/settings/chains.rs +++ b/nomad-base/src/settings/chains.rs @@ -4,7 +4,7 @@ use nomad_ethereum::{make_conn_manager, make_home, make_replica}; use nomad_types::NomadIdentifier; use nomad_xyz_configuration::{ contracts::CoreContracts, AgentSecrets, ChainConf, ConnectionManagerGasLimits, HomeGasLimits, - NomadConfig, ReplicaGasLimits, + NomadConfig, ReplicaGasLimits, TransactionSubmitterConf, }; use serde::Deserialize; @@ -132,7 +132,7 @@ impl ChainSetup { /// Try to convert the chain setting into a Home contract pub async fn try_into_home( &self, - signer: Option, + submitter: TransactionSubmitterConf, timelag: Option, gas: Option, ) -> Result { diff --git a/nomad-base/src/settings/mod.rs b/nomad-base/src/settings/mod.rs index ed981c0d..66afb823 100644 --- a/nomad-base/src/settings/mod.rs +++ b/nomad-base/src/settings/mod.rs @@ -17,7 +17,7 @@ use crate::{ use color_eyre::{eyre::bail, Result}; use nomad_core::{db::DB, Common, ContractLocator, Signers}; use nomad_ethereum::{make_home_indexer, make_replica_indexer}; -use nomad_xyz_configuration::{agent::SignerConf, AgentSecrets, TransactionSubmitter}; +use nomad_xyz_configuration::{agent::SignerConf, AgentSecrets, TransactionSubmitterConf}; use nomad_xyz_configuration::{contracts::CoreContracts, ChainConf, NomadConfig, NomadGasConfig}; use serde::Deserialize; use std::collections::HashSet; @@ -161,7 +161,7 @@ pub struct Settings { /// The tracing configuration pub logging: LogConfig, /// Transaction signers - pub submitters: HashMap, + pub submitters: HashMap, /// Optional attestation signer pub attestation_signer: Option, } From 0900892312744623a36dddc55facc1940c53415d Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Sat, 30 Apr 2022 23:37:55 -0700 Subject: [PATCH 14/41] prog(transaction-submitter): macro starts to take Option for contract --- chains/nomad-ethereum/src/macros.rs | 2 +- chains/nomad-ethereum/src/submitter.rs | 2 ++ configuration/src/chains/ethereum/mod.rs | 7 +++++ nomad-base/src/settings/chains.rs | 37 ++++++++++++++---------- nomad-base/src/settings/mod.rs | 13 +++------ 5 files changed, 35 insertions(+), 26 deletions(-) diff --git a/chains/nomad-ethereum/src/macros.rs b/chains/nomad-ethereum/src/macros.rs index 68afd2fb..0bc90331 100644 --- a/chains/nomad-ethereum/src/macros.rs +++ b/chains/nomad-ethereum/src/macros.rs @@ -210,7 +210,7 @@ macro_rules! boxed_contract { }}; ($name:ident, $abi:ident, $trait:ident, $($n:ident:$t:ty),*) => { #[doc = "Cast a contract locator to a live contract handle"] - pub async fn $name(conn: nomad_xyz_configuration::chains::ethereum::Connection, locator: &ContractLocator, signer: Option, timelag: Option, $($n:$t),*) -> color_eyre::Result> { + pub async fn $name(conn: nomad_xyz_configuration::ethereum::Connection, locator: &ContractLocator, submitter_conf: Option, timelag: Option, $($n:$t),*) -> color_eyre::Result> { let b: Box = match conn { nomad_xyz_configuration::chains::ethereum::Connection::Http (url) => { boxed_contract!(@http url, signer, $abi, timelag, locator, $($n),*) diff --git a/chains/nomad-ethereum/src/submitter.rs b/chains/nomad-ethereum/src/submitter.rs index afc1d0f8..fa1db77f 100644 --- a/chains/nomad-ethereum/src/submitter.rs +++ b/chains/nomad-ethereum/src/submitter.rs @@ -3,6 +3,8 @@ use color_eyre::Result; use ethers::prelude::*; use ethers::types::transaction::eip2718::TypedTransaction; use gelato_relay::RelayResponse; +use nomad_core::Signers; +use nomad_xyz_configuration::ethereum::TransactionSubmitterConf; use std::sync::Arc; use tracing::info; diff --git a/configuration/src/chains/ethereum/mod.rs b/configuration/src/chains/ethereum/mod.rs index 70996a60..912838cf 100644 --- a/configuration/src/chains/ethereum/mod.rs +++ b/configuration/src/chains/ethereum/mod.rs @@ -39,6 +39,13 @@ pub enum TransactionSubmitterConf { Gelato(GelatoConf), } +impl From for TransactionSubmitterConf { + fn from(conf: super::TransactionSubmitterConf) -> Self { + let super::TransactionSubmitterConf::Ethereum(conf) = conf; + conf + } +} + impl FromEnv for TransactionSubmitterConf { fn from_env(prefix: &str) -> Option { let submitter_type = std::env::var(&format!("{}_SUBMITTERTYPE", prefix)).ok()?; diff --git a/nomad-base/src/settings/chains.rs b/nomad-base/src/settings/chains.rs index 148c8404..00927900 100644 --- a/nomad-base/src/settings/chains.rs +++ b/nomad-base/src/settings/chains.rs @@ -4,7 +4,7 @@ use nomad_ethereum::{make_conn_manager, make_home, make_replica}; use nomad_types::NomadIdentifier; use nomad_xyz_configuration::{ contracts::CoreContracts, AgentSecrets, ChainConf, ConnectionManagerGasLimits, HomeGasLimits, - NomadConfig, ReplicaGasLimits, TransactionSubmitterConf, + NomadConfig, ReplicaGasLimits, ethereum, }; use serde::Deserialize; @@ -132,26 +132,31 @@ impl ChainSetup { /// Try to convert the chain setting into a Home contract pub async fn try_into_home( &self, - submitter: TransactionSubmitterConf, + submitter_conf: TransactionSubmitterConf, timelag: Option, gas: Option, ) -> Result { match &self.chain { - ChainConf::Ethereum(conf) => Ok(HomeVariants::Ethereum( - make_home( - conf.clone(), - &ContractLocator { - name: self.name.clone(), - domain: self.domain, - address: self.address, - }, - signer, - timelag, - gas, + ChainConf::Ethereum(conf) => { + let submitter_conf: ethereum::TransactionSubmitterConf = submitter_conf.into(); + // let submitter = + + Ok(HomeVariants::Ethereum( + make_home( + conf.clone(), + &ContractLocator { + name: self.name.clone(), + domain: self.domain, + address: self.address, + }, + signer, + timelag, + gas, + ) + .await?, ) - .await?, - ) - .into()), + .into()) + } } } diff --git a/nomad-base/src/settings/mod.rs b/nomad-base/src/settings/mod.rs index 66afb823..29503e27 100644 --- a/nomad-base/src/settings/mod.rs +++ b/nomad-base/src/settings/mod.rs @@ -186,13 +186,8 @@ impl Settings { impl Settings { /// Try to get a signer instance by name - pub async fn get_signer(&self, name: &str) -> Option> { - let conf = self.submitters.get(name); - if let Some(conf) = conf { - Some(Signers::try_from_signer_conf(conf).await) - } else { - None - } + pub async fn get_submitter_conf(&self, name: &str) -> Option<&TransactionSubmitterConf> { + self.submitters.get(name) } /// Set agent-specific index data types @@ -228,7 +223,7 @@ impl Settings { pub async fn try_home(&self) -> Result { let opt_home_timelag = self.home_timelag(); let name = &self.home.name; - let signer = self.get_signer(name).await.transpose()?; + let submitter_conf = self.get_submitter_conf(name); let gas = self.gas.get(name).map(|c| c.core.home); self.home.try_into_home(signer, opt_home_timelag, gas).await } @@ -280,7 +275,7 @@ impl Settings { /// Try to get a Replicas object pub async fn try_replica(&self, replica_name: &str) -> Result { let replica_setup = self.replicas.get(replica_name).expect("!replica"); - let signer = self.get_signer(replica_name).await.transpose()?; + let submitter_conf = self.get_submitter_conf(name); let gas = self.gas.get(replica_name).map(|c| c.core.replica); replica_setup.try_into_replica(signer, gas).await } From c6a60b229ddd0636e5d556b043b2cc7f5acfd6db Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Sun, 1 May 2022 14:09:34 -0700 Subject: [PATCH 15/41] prog(chain-submitter): can build chain submitter for contract in macro --- chains/nomad-ethereum/src/macros.rs | 103 ++++++++++++++----------- chains/nomad-ethereum/src/submitter.rs | 22 ++++++ nomad-base/src/settings/chains.rs | 6 +- 3 files changed, 84 insertions(+), 47 deletions(-) diff --git a/chains/nomad-ethereum/src/macros.rs b/chains/nomad-ethereum/src/macros.rs index 0bc90331..570be286 100644 --- a/chains/nomad-ethereum/src/macros.rs +++ b/chains/nomad-ethereum/src/macros.rs @@ -106,21 +106,34 @@ macro_rules! boxed_indexer { }; } -/// Create ethers::SignerMiddleware from http connection #[macro_export] -macro_rules! http_signer_middleware { - ($url:expr, $signer:ident) => {{ - let http: crate::retrying::RetryingProvider = $url.parse()?; - let provider = Arc::new(ethers::providers::Provider::new(http)); +macro_rules! http_provider { + ($url:expr) => {{ + let provider: crate::retrying::RetryingProvider = $url.parse()?; + Arc::new(ethers::providers::Provider::new(provider)) + }}; +} + +#[macro_export] +macro_rules! ws_provider { + ($url:expr) => {{ + let ws = ethers::providers::Ws::connect($url).await?; + Arc::new(ethers::providers::Provider::new(ws)) + }}; +} +/// Create ethers::SignerMiddleware from http connection +#[macro_export] +macro_rules! wrap_http { + ($provider:expr, $signer:ident) => {{ // First set the chain ID locally - let provider_chain_id = provider.get_chainid().await?; + let provider_chain_id = $provider.get_chainid().await?; let signer = ethers::signers::Signer::with_chain_id($signer, provider_chain_id.as_u64()); // Manage the nonce locally let address = ethers::prelude::Signer::address(&signer); let provider = - ethers::middleware::nonce_manager::NonceManagerMiddleware::new(provider, address); + ethers::middleware::nonce_manager::NonceManagerMiddleware::new($provider, address); // Kludge. Increase the gas by multiplication of every estimated gas by // 2, except the gas for chain id 1 (Ethereum Mainnet) @@ -136,19 +149,16 @@ macro_rules! http_signer_middleware { /// Create ethers::SignerMiddleware from websockets connection #[macro_export] -macro_rules! ws_signer_middleware { - ($url:expr, $signer:ident) => {{ - let ws = ethers::providers::Ws::connect($url).await?; - let provider = Arc::new(ethers::providers::Provider::new(ws)); - +macro_rules! wrap_ws { + ($provider:expr, $signer:ident) => {{ // First set the chain ID locally - let provider_chain_id = provider.get_chainid().await?; + let provider_chain_id = $provider.get_chainid().await?; let signer = ethers::signers::Signer::with_chain_id($signer, provider_chain_id.as_u64()); // Manage the nonce locally let address = ethers::prelude::Signer::address(&signer); let provider = - ethers::middleware::nonce_manager::NonceManagerMiddleware::new(provider, address); + ethers::middleware::nonce_manager::NonceManagerMiddleware::new($provider, address); // Kludge. Increase the gas by multiplication of every estimated gas by // 2, except the gas for chain id 1 (Ethereum Mainnet) @@ -162,8 +172,18 @@ macro_rules! ws_signer_middleware { }}; } +// #[macro_export] +// macro_rules! chain_submitter_local { +// ($url:expr, $signer_conf:ident) => {{ +// let provider = http_provider!($url); +// let signer = Signers::try_from_signer_conf(&$signer_conf).await?; +// let signing_provider: Arc<_> = wrap_http!(provider.clone(), signer); +// ChainSubmitter::new(Submitter::new_local(signing_provider)) +// }}; +// } + macro_rules! boxed_contract { - (@timelag $provider:expr, $abi:ident, $timelag:ident, $($tail:tt)*) => {{ + (@timelag $provider:expr, $submitter:expr, $abi:ident, $timelag:ident, $($tail:tt)*) => {{ let write_provider: Arc<_> = $provider.clone(); if let Some(lag) = $timelag { let read_provider: Arc<_> = ethers::middleware::TimeLag::new($provider, lag).into(); @@ -172,51 +192,46 @@ macro_rules! boxed_contract { Box::new(crate::$abi::new(write_provider, $provider, $($tail)*)) } }}; - (@signer $provider:expr, $signer:ident, $($tail:tt)*) => {{ - if let Some(signer) = $signer { + (@submitter $provider:expr, $submitter_conf:ident, $($tail:tt)*) => {{ + if let Some(conf) = $submitter_conf { // If there's a provided signer, we want to manage every aspect // locally + let submitter = match conf { + nomad_xyz_configuration::ethereum::TransactionSubmitterConf::Local(signer_conf) => { + let signer = Signers::try_from_signer_conf(&signer_conf).await?; + let signing_provider = wrap_http!($provider.clone(), signer); + let submitter = signing_provider.into(); + ChainSubmitter::new(submitter) + } + nomad_xyz_configuration::ethereum::TransactionSubmitterConf::Gelato(gelato_conf) => { + let signer = Signers::try_from_signer_conf(&gelato_conf.signer).await?; + let signing_provider = wrap_http!($provider.clone(), signer); + let submitter = signing_provider.into(); + ChainSubmitter::new(submitter) + } + }; - // First set the chain ID locally - let provider_chain_id = $provider.get_chainid().await?; - let signer = ethers::signers::Signer::with_chain_id(signer, provider_chain_id.as_u64()); - - // Manage the nonce locally - let address = ethers::prelude::Signer::address(&signer); - let provider = - ethers::middleware::nonce_manager::NonceManagerMiddleware::new($provider, address); - - // Kludge. Increase the gas by multiplication of every estimated gas by 2 - // except the gas for chain id 1 (Ethereum Mainnet) - let provider = crate::gas::GasAdjusterMiddleware::with_default_policy(provider, provider_chain_id.as_u64()); - - // Manage signing locally - let signing_provider = Arc::new(ethers::middleware::SignerMiddleware::new(provider, signer)); - - boxed_contract!(@timelag signing_provider, $($tail)*) + boxed_contract!(@timelag $provider, submitter, $($tail)*) } else { - boxed_contract!(@timelag $provider, $($tail)*) + panic!("Not supporting contracts with tx submitter"); } }}; (@ws $url:expr, $($tail:tt)*) => {{ - let ws = ethers::providers::Ws::connect($url).await?; - let provider = Arc::new(ethers::providers::Provider::new(ws)); - boxed_contract!(@signer provider, $($tail)*) + let provider = ws_provider!($url); + boxed_contract!(@submitter provider, $($tail)*) }}; (@http $url:expr, $($tail:tt)*) => {{ - let provider: crate::retrying::RetryingProvider = $url.parse()?; - let provider = Arc::new(ethers::providers::Provider::new(provider)); - boxed_contract!(@signer provider, $($tail)*) + let provider = http_provider!($url); + boxed_contract!(@submitter provider, $($tail)*) }}; ($name:ident, $abi:ident, $trait:ident, $($n:ident:$t:ty),*) => { #[doc = "Cast a contract locator to a live contract handle"] pub async fn $name(conn: nomad_xyz_configuration::ethereum::Connection, locator: &ContractLocator, submitter_conf: Option, timelag: Option, $($n:$t),*) -> color_eyre::Result> { let b: Box = match conn { nomad_xyz_configuration::chains::ethereum::Connection::Http (url) => { - boxed_contract!(@http url, signer, $abi, timelag, locator, $($n),*) - } + boxed_contract!(@http url, submitter_conf, $abi, timelag, locator, $($n),*) nomad_xyz_configuration::chains::ethereum::Connection::Ws (url) => { - boxed_contract!(@ws url, signer, $abi, timelag, locator, $($n),*) + boxed_contract!(@ws url, submitter_conf, $abi, timelag, locator, $($n),*) } }; Ok(b) diff --git a/chains/nomad-ethereum/src/submitter.rs b/chains/nomad-ethereum/src/submitter.rs index fa1db77f..2fa7bf8f 100644 --- a/chains/nomad-ethereum/src/submitter.rs +++ b/chains/nomad-ethereum/src/submitter.rs @@ -18,6 +18,15 @@ pub enum Submitter { Gelato(SingleChainGelatoClient), } +impl Submitter +where + M: Middleware + 'static, +{ + fn new_local(client: Arc) -> Self { + Self::Local(client) + } +} + impl From> for Submitter { fn from(client: Arc) -> Self { Self::Local(client) @@ -38,6 +47,19 @@ pub struct ChainSubmitter { } impl ChainSubmitter { + pub fn new(submitter: Submitter) -> Self { + Self { submitter } + } + + // pub async fn local_from_url_and_conf(url: String, conf: TransactionSubmitterConf) -> Result { + // Ok(match conf { + // TransactionSubmitterConf::Local(signer_conf) => { + // chain_submitter_local!(url, signer_conf) + // } + // _ => panic!("Tried to create local ethereum TransactionSubmitter with non-local conf"), + // }) + // } + /// Submit transaction to chain pub async fn submit( &self, diff --git a/nomad-base/src/settings/chains.rs b/nomad-base/src/settings/chains.rs index 00927900..938e2371 100644 --- a/nomad-base/src/settings/chains.rs +++ b/nomad-base/src/settings/chains.rs @@ -3,8 +3,8 @@ use nomad_core::{ContractLocator, Signers}; use nomad_ethereum::{make_conn_manager, make_home, make_replica}; use nomad_types::NomadIdentifier; use nomad_xyz_configuration::{ - contracts::CoreContracts, AgentSecrets, ChainConf, ConnectionManagerGasLimits, HomeGasLimits, - NomadConfig, ReplicaGasLimits, ethereum, + contracts::CoreContracts, ethereum, AgentSecrets, ChainConf, ConnectionManagerGasLimits, + HomeGasLimits, NomadConfig, ReplicaGasLimits, }; use serde::Deserialize; @@ -139,7 +139,7 @@ impl ChainSetup { match &self.chain { ChainConf::Ethereum(conf) => { let submitter_conf: ethereum::TransactionSubmitterConf = submitter_conf.into(); - // let submitter = + // let submitter = Ok(HomeVariants::Ethereum( make_home( From d1987ce6a5373c3edfaf7959499268f78f808d95 Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Sun, 1 May 2022 16:10:38 -0700 Subject: [PATCH 16/41] prog(chain-submitter): compiles with contract macro creating but not using chainsubmitter --- agents/watcher/src/watcher.rs | 11 +++++- chains/nomad-ethereum/src/macros.rs | 25 ++++++------ chains/nomad-ethereum/src/submitter.rs | 26 +++---------- nomad-base/src/settings/chains.rs | 53 ++++++++++++++------------ nomad-base/src/settings/mod.rs | 14 ++++--- 5 files changed, 63 insertions(+), 66 deletions(-) diff --git a/agents/watcher/src/watcher.rs b/agents/watcher/src/watcher.rs index 0ae33b88..90dba069 100644 --- a/agents/watcher/src/watcher.rs +++ b/agents/watcher/src/watcher.rs @@ -524,14 +524,21 @@ impl NomadAgent for Watcher { .values() { let name = &chain_setup.name; - let signer = settings.base.get_signer(name).await.transpose()?; + let submitter_conf = settings.base.get_submitter_conf(name); + + if submitter_conf.is_none() { + panic!("Cannot configure watcher connection manager without transaction submission config!"); + } + let gas = settings .as_ref() .gas .get(name) .map(|c| c.core.connection_manager); - let manager = chain_setup.try_into_connection_manager(signer, gas).await; + let manager = chain_setup + .try_into_connection_manager(submitter_conf, gas) + .await; connection_managers.push(manager); } diff --git a/chains/nomad-ethereum/src/macros.rs b/chains/nomad-ethereum/src/macros.rs index 570be286..13a5d9f7 100644 --- a/chains/nomad-ethereum/src/macros.rs +++ b/chains/nomad-ethereum/src/macros.rs @@ -106,6 +106,7 @@ macro_rules! boxed_indexer { }; } +/// Create base http retrying provider #[macro_export] macro_rules! http_provider { ($url:expr) => {{ @@ -114,6 +115,7 @@ macro_rules! http_provider { }}; } +/// Create base ws provider #[macro_export] macro_rules! ws_provider { ($url:expr) => {{ @@ -172,15 +174,15 @@ macro_rules! wrap_ws { }}; } -// #[macro_export] -// macro_rules! chain_submitter_local { -// ($url:expr, $signer_conf:ident) => {{ -// let provider = http_provider!($url); -// let signer = Signers::try_from_signer_conf(&$signer_conf).await?; -// let signing_provider: Arc<_> = wrap_http!(provider.clone(), signer); -// ChainSubmitter::new(Submitter::new_local(signing_provider)) -// }}; -// } +/// Create ChainSubmitter::Local from base provider and signer configuration +#[macro_export] +macro_rules! chain_submitter_local { + ($provider:expr, $signer_conf:ident) => {{ + let signer = Signers::try_from_signer_conf(&$signer_conf).await?; + let signing_provider: Arc<_> = wrap_http!($provider.clone(), signer); + ChainSubmitter::new(signing_provider.into()) + }}; +} macro_rules! boxed_contract { (@timelag $provider:expr, $submitter:expr, $abi:ident, $timelag:ident, $($tail:tt)*) => {{ @@ -198,10 +200,7 @@ macro_rules! boxed_contract { // locally let submitter = match conf { nomad_xyz_configuration::ethereum::TransactionSubmitterConf::Local(signer_conf) => { - let signer = Signers::try_from_signer_conf(&signer_conf).await?; - let signing_provider = wrap_http!($provider.clone(), signer); - let submitter = signing_provider.into(); - ChainSubmitter::new(submitter) + chain_submitter_local!($provider, signer_conf) } nomad_xyz_configuration::ethereum::TransactionSubmitterConf::Gelato(gelato_conf) => { let signer = Signers::try_from_signer_conf(&gelato_conf.signer).await?; diff --git a/chains/nomad-ethereum/src/submitter.rs b/chains/nomad-ethereum/src/submitter.rs index 2fa7bf8f..5d057d31 100644 --- a/chains/nomad-ethereum/src/submitter.rs +++ b/chains/nomad-ethereum/src/submitter.rs @@ -3,8 +3,6 @@ use color_eyre::Result; use ethers::prelude::*; use ethers::types::transaction::eip2718::TypedTransaction; use gelato_relay::RelayResponse; -use nomad_core::Signers; -use nomad_xyz_configuration::ethereum::TransactionSubmitterConf; use std::sync::Arc; use tracing::info; @@ -18,15 +16,6 @@ pub enum Submitter { Gelato(SingleChainGelatoClient), } -impl Submitter -where - M: Middleware + 'static, -{ - fn new_local(client: Arc) -> Self { - Self::Local(client) - } -} - impl From> for Submitter { fn from(client: Arc) -> Self { Self::Local(client) @@ -46,20 +35,15 @@ pub struct ChainSubmitter { pub submitter: Submitter, } -impl ChainSubmitter { +impl ChainSubmitter +where + M: Middleware + 'static, +{ + /// Create new ChainSubmitter from submitter pub fn new(submitter: Submitter) -> Self { Self { submitter } } - // pub async fn local_from_url_and_conf(url: String, conf: TransactionSubmitterConf) -> Result { - // Ok(match conf { - // TransactionSubmitterConf::Local(signer_conf) => { - // chain_submitter_local!(url, signer_conf) - // } - // _ => panic!("Tried to create local ethereum TransactionSubmitter with non-local conf"), - // }) - // } - /// Submit transaction to chain pub async fn submit( &self, diff --git a/nomad-base/src/settings/chains.rs b/nomad-base/src/settings/chains.rs index 938e2371..1e09d1bd 100644 --- a/nomad-base/src/settings/chains.rs +++ b/nomad-base/src/settings/chains.rs @@ -1,10 +1,10 @@ use color_eyre::Result; -use nomad_core::{ContractLocator, Signers}; +use nomad_core::ContractLocator; use nomad_ethereum::{make_conn_manager, make_home, make_replica}; use nomad_types::NomadIdentifier; use nomad_xyz_configuration::{ - contracts::CoreContracts, ethereum, AgentSecrets, ChainConf, ConnectionManagerGasLimits, - HomeGasLimits, NomadConfig, ReplicaGasLimits, + contracts::CoreContracts, AgentSecrets, ChainConf, ConnectionManagerGasLimits, HomeGasLimits, + NomadConfig, ReplicaGasLimits, TransactionSubmitterConf, }; use serde::Deserialize; @@ -132,14 +132,13 @@ impl ChainSetup { /// Try to convert the chain setting into a Home contract pub async fn try_into_home( &self, - submitter_conf: TransactionSubmitterConf, + submitter_conf: Option, timelag: Option, gas: Option, ) -> Result { match &self.chain { ChainConf::Ethereum(conf) => { - let submitter_conf: ethereum::TransactionSubmitterConf = submitter_conf.into(); - // let submitter = + let submitter_conf = submitter_conf.map(std::convert::Into::into); Ok(HomeVariants::Ethereum( make_home( @@ -149,7 +148,7 @@ impl ChainSetup { domain: self.domain, address: self.address, }, - signer, + submitter_conf, timelag, gas, ) @@ -163,34 +162,40 @@ impl ChainSetup { /// Try to convert the chain setting into a replica contract pub async fn try_into_replica( &self, - signer: Option, + submitter_conf: Option, gas: Option, ) -> Result { match &self.chain { - ChainConf::Ethereum(conf) => Ok(ReplicaVariants::Ethereum( - make_replica( - conf.clone(), - &ContractLocator { - name: self.name.clone(), - domain: self.domain, - address: self.address, - }, - signer, - None, // never need timelag for replica - gas, + ChainConf::Ethereum(conf) => { + let submitter_conf = submitter_conf.map(std::convert::Into::into); + + Ok(ReplicaVariants::Ethereum( + make_replica( + conf.clone(), + &ContractLocator { + name: self.name.clone(), + domain: self.domain, + address: self.address, + }, + submitter_conf, + None, // never need timelag for replica + gas, + ) + .await?, ) - .await?, - ) - .into()), + .into()) + } } } /// Try to convert chain setting into XAppConnectionManager contract pub async fn try_into_connection_manager( &self, - signer: Option, + submitter_conf: Option, gas: Option, ) -> Result { + let submitter_conf = submitter_conf.map(std::convert::Into::into); + match &self.chain { ChainConf::Ethereum(conf) => Ok(ConnectionManagers::Ethereum( make_conn_manager( @@ -200,7 +205,7 @@ impl ChainSetup { domain: self.domain, address: self.address, }, - signer, + submitter_conf, None, // Never need timelag for xapp connection manager gas, ) diff --git a/nomad-base/src/settings/mod.rs b/nomad-base/src/settings/mod.rs index 29503e27..67a6f027 100644 --- a/nomad-base/src/settings/mod.rs +++ b/nomad-base/src/settings/mod.rs @@ -15,7 +15,7 @@ use crate::{ ContractSync, ContractSyncMetrics, HomeIndexerVariants, HomeIndexers, Homes, NomadDB, Replicas, }; use color_eyre::{eyre::bail, Result}; -use nomad_core::{db::DB, Common, ContractLocator, Signers}; +use nomad_core::{db::DB, Common, ContractLocator}; use nomad_ethereum::{make_home_indexer, make_replica_indexer}; use nomad_xyz_configuration::{agent::SignerConf, AgentSecrets, TransactionSubmitterConf}; use nomad_xyz_configuration::{contracts::CoreContracts, ChainConf, NomadConfig, NomadGasConfig}; @@ -186,8 +186,8 @@ impl Settings { impl Settings { /// Try to get a signer instance by name - pub async fn get_submitter_conf(&self, name: &str) -> Option<&TransactionSubmitterConf> { - self.submitters.get(name) + pub fn get_submitter_conf(&self, name: &str) -> Option { + self.submitters.get(name).cloned() } /// Set agent-specific index data types @@ -225,7 +225,9 @@ impl Settings { let name = &self.home.name; let submitter_conf = self.get_submitter_conf(name); let gas = self.gas.get(name).map(|c| c.core.home); - self.home.try_into_home(signer, opt_home_timelag, gas).await + self.home + .try_into_home(submitter_conf, opt_home_timelag, gas) + .await } /// Try to get a home ContractSync @@ -275,9 +277,9 @@ impl Settings { /// Try to get a Replicas object pub async fn try_replica(&self, replica_name: &str) -> Result { let replica_setup = self.replicas.get(replica_name).expect("!replica"); - let submitter_conf = self.get_submitter_conf(name); + let submitter_conf = self.get_submitter_conf(replica_name); let gas = self.gas.get(replica_name).map(|c| c.core.replica); - replica_setup.try_into_replica(signer, gas).await + replica_setup.try_into_replica(submitter_conf, gas).await } /// Try to get a replica ContractSync From 5a77690d30ee1b70cf790f2a5acbe3d197865d09 Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Sun, 1 May 2022 16:24:06 -0700 Subject: [PATCH 17/41] prog(chain-submitter): adds gelato branch to contract creation macro (tx submitter still not in use) --- chains/nomad-ethereum/src/macros.rs | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/chains/nomad-ethereum/src/macros.rs b/chains/nomad-ethereum/src/macros.rs index 13a5d9f7..fae1bf45 100644 --- a/chains/nomad-ethereum/src/macros.rs +++ b/chains/nomad-ethereum/src/macros.rs @@ -174,16 +174,27 @@ macro_rules! wrap_ws { }}; } -/// Create ChainSubmitter::Local from base provider and signer configuration +/// Create ChainSubmitter::Local #[macro_export] macro_rules! chain_submitter_local { - ($provider:expr, $signer_conf:ident) => {{ + ($base_provider:expr, $signer_conf:ident) => {{ let signer = Signers::try_from_signer_conf(&$signer_conf).await?; - let signing_provider: Arc<_> = wrap_http!($provider.clone(), signer); + let signing_provider: Arc<_> = wrap_http!($base_provider.clone(), signer); ChainSubmitter::new(signing_provider.into()) }}; } +/// Create ChainSubmitter::Gelato +#[macro_export] +macro_rules! chain_submitter_gelato { + ($chain_id:expr, $gelato_conf:ident) => {{ + let sponsor = Signers::try_from_signer_conf(&$gelato_conf.signer).await?; + let client = + SingleChainGelatoClient::with_default_url(sponsor, $chain_id, $gelato_conf.fee_token); + ChainSubmitter::new(client.into()) + }}; +} + macro_rules! boxed_contract { (@timelag $provider:expr, $submitter:expr, $abi:ident, $timelag:ident, $($tail:tt)*) => {{ let write_provider: Arc<_> = $provider.clone(); @@ -203,10 +214,8 @@ macro_rules! boxed_contract { chain_submitter_local!($provider, signer_conf) } nomad_xyz_configuration::ethereum::TransactionSubmitterConf::Gelato(gelato_conf) => { - let signer = Signers::try_from_signer_conf(&gelato_conf.signer).await?; - let signing_provider = wrap_http!($provider.clone(), signer); - let submitter = signing_provider.into(); - ChainSubmitter::new(submitter) + let chain_id = $provider.get_chainid().await?.as_usize(); + chain_submitter_gelato!(chain_id, gelato_conf) } }; From 18acf82993f2934d3d55df5eca53dcb04e37e7ea Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Sun, 1 May 2022 17:51:17 -0700 Subject: [PATCH 18/41] feat(contract-interfaces): replace report_tx! with chain-submitter calls --- chains/nomad-ethereum/src/gelato.rs | 9 +++- chains/nomad-ethereum/src/home.rs | 59 +++++++++++----------- chains/nomad-ethereum/src/macros.rs | 28 +++++------ chains/nomad-ethereum/src/replica.rs | 63 +++++++++++++----------- chains/nomad-ethereum/src/submitter.rs | 49 +++++++++++++----- chains/nomad-ethereum/src/xapp.rs | 54 +++++++++++--------- gelato-relay/src/types.rs | 24 ++++----- nomad-core/src/traits/mod.rs | 3 ++ tools/nomad-cli/src/subcommands/prove.rs | 4 +- 9 files changed, 169 insertions(+), 124 deletions(-) diff --git a/chains/nomad-ethereum/src/gelato.rs b/chains/nomad-ethereum/src/gelato.rs index 3562506b..bfa0937e 100644 --- a/chains/nomad-ethereum/src/gelato.rs +++ b/chains/nomad-ethereum/src/gelato.rs @@ -1,5 +1,5 @@ use ethers::providers::Middleware; -use gelato_relay::{GelatoClient, RelayResponse}; +use gelato_relay::{GelatoClient, RelayResponse, TaskState}; use nomad_core::Signers; use std::marker::PhantomData; @@ -18,6 +18,13 @@ use std::marker::PhantomData; } */ +pub(crate) const ACCEPTABLE_STATES: [TaskState; 4] = [ + TaskState::CheckPending, + TaskState::ExecPending, + TaskState::ExecSuccess, + TaskState::WaitingForConfirmation, +]; + /// Gelato client for submitting txs to single chain #[derive(Debug, Clone)] pub struct SingleChainGelatoClient { diff --git a/chains/nomad-ethereum/src/home.rs b/chains/nomad-ethereum/src/home.rs index 9bbe58cd..1ee5ca29 100644 --- a/chains/nomad-ethereum/src/home.rs +++ b/chains/nomad-ethereum/src/home.rs @@ -17,7 +17,7 @@ use nomad_xyz_configuration::HomeGasLimits; use std::{convert::TryFrom, error::Error as StdError, sync::Arc}; use tracing::instrument; -use crate::{bindings::home::Home as EthereumHomeInternal, report_tx}; +use crate::{bindings::home::Home as EthereumHomeInternal, ChainSubmitter}; impl std::fmt::Display for EthereumHomeInternal where @@ -166,8 +166,8 @@ where W: ethers::providers::Middleware + 'static, R: ethers::providers::Middleware + 'static, { - write_contract: Arc>, - read_contract: Arc>, + submitter: ChainSubmitter, + contract: Arc>, domain: u32, name: String, gas: Option, @@ -181,7 +181,7 @@ where /// Create a reference to a Home at a specific Ethereum address on some /// chain pub fn new( - write_provider: Arc, + submitter: ChainSubmitter, read_provider: Arc, ContractLocator { name, @@ -191,11 +191,8 @@ where gas: Option, ) -> Self { Self { - write_contract: Arc::new(EthereumHomeInternal::new( - address.as_ethereum_address().expect("!eth address"), - write_provider, - )), - read_contract: Arc::new(EthereumHomeInternal::new( + submitter, + contract: Arc::new(EthereumHomeInternal::new( address.as_ethereum_address().expect("!eth address"), read_provider, )), @@ -218,7 +215,7 @@ where #[tracing::instrument(err, skip(self))] async fn status(&self, txid: H256) -> Result, ChainCommunicationError> { - self.read_contract + self.contract .client() .get_transaction_receipt(txid) .await @@ -229,12 +226,12 @@ where #[tracing::instrument(err, skip(self))] async fn updater(&self) -> Result { - Ok(self.read_contract.updater().call().await?.into()) + Ok(self.contract.updater().call().await?.into()) } #[tracing::instrument(err, skip(self))] async fn state(&self) -> Result { - let state = self.read_contract.state().call().await?; + let state = self.contract.state().call().await?; match state { 0 => Ok(State::Uninitialized), 1 => Ok(State::Active), @@ -245,12 +242,12 @@ where #[tracing::instrument(err, skip(self))] async fn committed_root(&self) -> Result { - Ok(self.read_contract.committed_root().call().await?.into()) + Ok(self.contract.committed_root().call().await?.into()) } #[tracing::instrument(err, skip(self, update), fields(update = %update))] async fn update(&self, update: &SignedUpdate) -> Result { - let mut tx = self.write_contract.update( + let mut tx = self.contract.update( update.update.previous_root.to_fixed_bytes(), update.update.new_root.to_fixed_bytes(), update.signature.to_vec().into(), @@ -264,7 +261,9 @@ where ); } - report_tx!(tx, &self.provider).try_into() + self.submitter + .submit(self.domain, self.contract.address(), tx.tx) + .await } #[tracing::instrument(err, skip(self, double), fields(double = %double))] @@ -272,7 +271,7 @@ where &self, double: &DoubleUpdate, ) -> Result { - let mut tx = self.write_contract.double_update( + let mut tx = self.contract.double_update( double.0.update.previous_root.to_fixed_bytes(), [ double.0.update.new_root.to_fixed_bytes(), @@ -286,7 +285,9 @@ where tx.tx.set_gas(U256::from(limits.double_update)); } - report_tx!(tx, &self.provider).try_into() + self.submitter + .submit(self.domain, self.contract.address(), tx.tx) + .await } } @@ -302,30 +303,28 @@ where #[tracing::instrument(err, skip(self))] async fn nonces(&self, destination: u32) -> Result { - Ok(self.read_contract.nonces(destination).call().await?) + Ok(self.contract.nonces(destination).call().await?) } #[tracing::instrument(err, skip(self))] async fn dispatch(&self, message: &Message) -> Result { - let tx = self.write_contract.dispatch( + let tx = self.contract.dispatch( message.destination, message.recipient.to_fixed_bytes(), message.body.clone().into(), ); - report_tx!(tx, &self.provider).try_into() + self.submitter + .submit(self.domain, self.contract.address(), tx.tx) + .await } async fn queue_length(&self) -> Result { - Ok(self.read_contract.queue_length().call().await?) + Ok(self.contract.queue_length().call().await?) } async fn queue_contains(&self, root: H256) -> Result { - Ok(self - .read_contract - .queue_contains(root.into()) - .call() - .await?) + Ok(self.contract.queue_contains(root.into()).call().await?) } #[tracing::instrument(err, skip(self), fields(hex_signature = %format!("0x{}", hex::encode(update.signature.to_vec()))))] @@ -333,7 +332,7 @@ where &self, update: &SignedUpdate, ) -> Result { - let mut tx = self.write_contract.improper_update( + let mut tx = self.contract.improper_update( update.update.previous_root.to_fixed_bytes(), update.update.new_root.to_fixed_bytes(), update.signature.to_vec().into(), @@ -347,12 +346,14 @@ where ); } - report_tx!(tx, &self.provider).try_into() + self.submitter + .submit(self.domain, self.contract.address(), tx.tx) + .await } #[tracing::instrument(err, skip(self))] async fn produce_update(&self) -> Result, ChainCommunicationError> { - let (a, b) = self.read_contract.suggest_update().call().await?; + let (a, b) = self.contract.suggest_update().call().await?; let previous_root: H256 = a.into(); let new_root: H256 = b.into(); diff --git a/chains/nomad-ethereum/src/macros.rs b/chains/nomad-ethereum/src/macros.rs index fae1bf45..ece63f98 100644 --- a/chains/nomad-ethereum/src/macros.rs +++ b/chains/nomad-ethereum/src/macros.rs @@ -1,5 +1,5 @@ /// Dispatches a transaction, logs the tx id, and returns the result -#[macro_export] +#[allow(unused_macros)] macro_rules! report_tx { ($tx:expr, $($tail:tt)*) => {{ // Simple switch between 2 implementations: @@ -56,6 +56,7 @@ macro_rules! report_tx { }}; } +#[allow(unused_macros)] macro_rules! log_tx_details { ($tx:expr) => { // "0x..." @@ -196,30 +197,29 @@ macro_rules! chain_submitter_gelato { } macro_rules! boxed_contract { - (@timelag $provider:expr, $submitter:expr, $abi:ident, $timelag:ident, $($tail:tt)*) => {{ - let write_provider: Arc<_> = $provider.clone(); - if let Some(lag) = $timelag { - let read_provider: Arc<_> = ethers::middleware::TimeLag::new($provider, lag).into(); - Box::new(crate::$abi::new(write_provider, read_provider, $($tail)*)) - } else { - Box::new(crate::$abi::new(write_provider, $provider, $($tail)*)) - } - }}; - (@submitter $provider:expr, $submitter_conf:ident, $($tail:tt)*) => {{ + (@timelag $base_provider:expr, $submitter:expr, $abi:ident, $timelag:ident, $($tail:tt)*) => {{ + if let Some(lag) = $timelag { + let read_provider: Arc<_> = ethers::middleware::TimeLag::new($base_provider, lag).into(); + Box::new(crate::$abi::new($submitter, read_provider, $($tail)*)) + } else { + Box::new(crate::$abi::new($submitter, $base_provider, $($tail)*)) + } + }}; + (@submitter $base_provider:expr, $submitter_conf:ident, $($tail:tt)*) => {{ if let Some(conf) = $submitter_conf { // If there's a provided signer, we want to manage every aspect // locally let submitter = match conf { nomad_xyz_configuration::ethereum::TransactionSubmitterConf::Local(signer_conf) => { - chain_submitter_local!($provider, signer_conf) + chain_submitter_local!($base_provider, signer_conf) } nomad_xyz_configuration::ethereum::TransactionSubmitterConf::Gelato(gelato_conf) => { - let chain_id = $provider.get_chainid().await?.as_usize(); + let chain_id = $base_provider.get_chainid().await?.as_usize(); chain_submitter_gelato!(chain_id, gelato_conf) } }; - boxed_contract!(@timelag $provider, submitter, $($tail)*) + boxed_contract!(@timelag $base_provider, submitter, $($tail)*) } else { panic!("Not supporting contracts with tx submitter"); } diff --git a/chains/nomad-ethereum/src/replica.rs b/chains/nomad-ethereum/src/replica.rs index 368511f3..9233d3e4 100644 --- a/chains/nomad-ethereum/src/replica.rs +++ b/chains/nomad-ethereum/src/replica.rs @@ -14,7 +14,7 @@ use nomad_xyz_configuration::ReplicaGasLimits; use std::{convert::TryFrom, error::Error as StdError, sync::Arc}; use tracing::instrument; -use crate::{bindings::replica::Replica as EthereumReplicaInternal, report_tx}; +use crate::{bindings::replica::Replica as EthereumReplicaInternal, ChainSubmitter}; #[derive(Debug)] /// Struct that retrieves indexes event data for Ethereum replica @@ -126,8 +126,8 @@ where W: ethers::providers::Middleware + 'static, R: ethers::providers::Middleware + 'static, { - write_contract: Arc>, - read_contract: Arc>, + submitter: ChainSubmitter, + contract: Arc>, domain: u32, name: String, gas: Option, @@ -141,7 +141,7 @@ where /// Create a reference to a Replica at a specific Ethereum address on some /// chain pub fn new( - write_provider: Arc, + submitter: ChainSubmitter, read_provider: Arc, ContractLocator { name, @@ -151,11 +151,8 @@ where gas: Option, ) -> Self { Self { - write_contract: Arc::new(EthereumReplicaInternal::new( - address.as_ethereum_address().expect("!eth address"), - write_provider, - )), - read_contract: Arc::new(EthereumReplicaInternal::new( + submitter, + contract: Arc::new(EthereumReplicaInternal::new( address.as_ethereum_address().expect("!eth address"), read_provider, )), @@ -178,7 +175,7 @@ where #[tracing::instrument(err)] async fn status(&self, txid: H256) -> Result, ChainCommunicationError> { - self.read_contract + self.contract .client() .get_transaction_receipt(txid) .await @@ -189,12 +186,12 @@ where #[tracing::instrument(err)] async fn updater(&self) -> Result { - Ok(self.read_contract.updater().call().await?.into()) + Ok(self.contract.updater().call().await?.into()) } #[tracing::instrument(err)] async fn state(&self) -> Result { - let state = self.read_contract.state().call().await?; + let state = self.contract.state().call().await?; match state { 0 => Ok(State::Uninitialized), 1 => Ok(State::Active), @@ -205,12 +202,12 @@ where #[tracing::instrument(err)] async fn committed_root(&self) -> Result { - Ok(self.read_contract.committed_root().call().await?.into()) + Ok(self.contract.committed_root().call().await?.into()) } #[tracing::instrument(err)] async fn update(&self, update: &SignedUpdate) -> Result { - let mut tx = self.write_contract.update( + let mut tx = self.contract.update( update.update.previous_root.to_fixed_bytes(), update.update.new_root.to_fixed_bytes(), update.signature.to_vec().into(), @@ -220,7 +217,9 @@ where tx.tx.set_gas(U256::from(limits.update)); } - report_tx!(tx, &self.provider).try_into() + self.submitter + .submit(self.domain, self.contract.address(), tx.tx) + .await } #[tracing::instrument(err)] @@ -228,7 +227,7 @@ where &self, double: &DoubleUpdate, ) -> Result { - let mut tx = self.write_contract.double_update( + let mut tx = self.contract.double_update( double.0.update.previous_root.to_fixed_bytes(), [ double.0.update.new_root.to_fixed_bytes(), @@ -242,7 +241,9 @@ where tx.tx.set_gas(U256::from(limits.double_update)); } - report_tx!(tx, &self.provider).try_into() + self.submitter + .submit(self.domain, self.contract.address(), tx.tx) + .await } } @@ -257,7 +258,7 @@ where } async fn remote_domain(&self) -> Result { - Ok(self.read_contract.remote_domain().call().await?) + Ok(self.contract.remote_domain().call().await?) } #[tracing::instrument(err)] @@ -269,25 +270,29 @@ where .for_each(|(i, elem)| *elem = proof.path[i].to_fixed_bytes()); let mut tx = self - .write_contract + .contract .prove(proof.leaf.into(), sol_proof, proof.index.into()); if let Some(limits) = &self.gas { tx.tx.set_gas(U256::from(limits.prove)); } - report_tx!(tx, &self.provider).try_into() + self.submitter + .submit(self.domain, self.contract.address(), tx.tx) + .await } #[tracing::instrument(err)] async fn process(&self, message: &NomadMessage) -> Result { - let mut tx = self.write_contract.process(message.to_vec().into()); + let mut tx = self.contract.process(message.to_vec().into()); if let Some(limits) = &self.gas { tx.tx.set_gas(U256::from(limits.process)); } - report_tx!(tx, &self.provider).try_into() + self.submitter + .submit(self.domain, self.contract.address(), tx.tx) + .await } #[tracing::instrument(err)] @@ -303,7 +308,7 @@ where .for_each(|(i, elem)| *elem = proof.path[i].to_fixed_bytes()); let mut tx = self - .write_contract + .contract .prove_and_process(message.to_vec().into(), sol_proof, proof.index.into()) .gas(1_900_000); @@ -311,12 +316,14 @@ where tx.tx.set_gas(U256::from(limits.prove_and_process)); } - report_tx!(tx, &self.provider).try_into() + self.submitter + .submit(self.domain, self.contract.address(), tx.tx) + .await } #[tracing::instrument(err)] async fn message_status(&self, leaf: H256) -> Result { - let status = self.read_contract.messages(leaf.into()).call().await?; + let status = self.contract.messages(leaf.into()).call().await?; match status { 0 => Ok(MessageStatus::None), 1 => Ok(MessageStatus::Proven), @@ -326,10 +333,6 @@ where } async fn acceptable_root(&self, root: H256) -> Result { - Ok(self - .read_contract - .acceptable_root(root.into()) - .call() - .await?) + Ok(self.contract.acceptable_root(root.into()).call().await?) } } diff --git a/chains/nomad-ethereum/src/submitter.rs b/chains/nomad-ethereum/src/submitter.rs index 5d057d31..e3d56f4b 100644 --- a/chains/nomad-ethereum/src/submitter.rs +++ b/chains/nomad-ethereum/src/submitter.rs @@ -1,10 +1,12 @@ -use crate::SingleChainGelatoClient; +use crate::{SingleChainGelatoClient, ACCEPTABLE_STATES}; use color_eyre::Result; use ethers::prelude::*; use ethers::types::transaction::eip2718::TypedTransaction; use gelato_relay::RelayResponse; -use std::sync::Arc; -use tracing::info; +use nomad_core::{ChainCommunicationError, TxOutcome}; +use std::{str::FromStr, sync::Arc}; +use tokio::time::{sleep, Duration}; +use tracing::{debug, info}; /// Component responsible for submitting transactions to the chain. Can /// sign/submit locally or use a transaction relay service. @@ -50,23 +52,29 @@ where domain: u32, contract_address: Address, tx: impl Into, - ) -> Result<()> { + ) -> Result { let tx: TypedTransaction = tx.into(); match &self.submitter { Submitter::Local(client) => { - let dispatched = client.send_transaction(tx, None).await?; + let dispatched = client + .send_transaction(tx, None) + .await + .map_err(|e| ChainCommunicationError::TxSubmissionError(e.into()))?; let tx_hash: ethers::core::types::H256 = *dispatched; info!("dispatched transaction with tx_hash {:?}", tx_hash); let result = dispatched .await? - .ok_or_else(|| nomad_core::ChainCommunicationError::DroppedError(tx_hash))?; + .ok_or_else(|| ChainCommunicationError::DroppedError(tx_hash))?; info!( "confirmed transaction with tx_hash {:?}", result.transaction_hash ); + + let outcome = result.try_into()?; + Ok(outcome) } Submitter::Gelato(client) => { let tx_data = tx.data().expect("!tx data"); @@ -79,17 +87,27 @@ where "Dispatching tx to Gelato relay." ); - let RelayResponse { task_id } = - client.send_relay_transaction(&address, &data).await?; - info!(task_id = ?task_id, "Submitted tx to Gelato relay."); + let RelayResponse { task_id } = client + .send_relay_transaction(&address, &data) + .await + .map_err(|e| ChainCommunicationError::TxSubmissionError(e.into()))?; + info!(task_id = ?task_id, "Submitted tx to Gelato relay. Polling task for completion..."); loop { let status = client .client() .get_task_status(&task_id) - .await? + .await + .map_err(|e| ChainCommunicationError::TxSubmissionError(e.into()))? .expect("!task status"); + if !ACCEPTABLE_STATES.contains(&status.task_state) { + return Err(ChainCommunicationError::TxSubmissionError( + format!("Gelato task failed: {:?}", status).into(), + ) + .into()); + } + if let Some(execution) = &status.execution { info!( chain = ?status.chain, @@ -98,12 +116,17 @@ where "Gelato relay executed tx." ); - break; + let tx_hash = &execution.transaction_hash; + let txid = H256::from_str(tx_hash) + .unwrap_or_else(|_| panic!("Malformed tx hash from Gelato")); + + return Ok(TxOutcome { txid }); } + + debug!(task_id = ?task_id, "Polling Gelato task."); + sleep(Duration::from_millis(500)).await; } } } - - Ok(()) } } diff --git a/chains/nomad-ethereum/src/xapp.rs b/chains/nomad-ethereum/src/xapp.rs index 1d96cdb6..a9af4061 100644 --- a/chains/nomad-ethereum/src/xapp.rs +++ b/chains/nomad-ethereum/src/xapp.rs @@ -8,9 +8,10 @@ use nomad_types::NomadIdentifier; use nomad_xyz_configuration::ConnectionManagerGasLimits; use std::sync::Arc; -use crate::bindings::xappconnectionmanager::XAppConnectionManager as EthereumConnectionManagerInternal; - -use crate::report_tx; +use crate::{ + bindings::xappconnectionmanager::XAppConnectionManager as EthereumConnectionManagerInternal, + ChainSubmitter, +}; /// A reference to a XAppConnectionManager contract on some Ethereum chain #[derive(Debug)] @@ -19,8 +20,8 @@ where W: ethers::providers::Middleware + 'static, R: ethers::providers::Middleware + 'static, { - write_contract: Arc>, - read_contract: Arc>, + submitter: ChainSubmitter, + contract: Arc>, domain: u32, name: String, gas: Option, @@ -35,7 +36,7 @@ where /// address on some chain #[allow(dead_code)] pub fn new( - write_provider: Arc, + submitter: ChainSubmitter, read_provider: Arc, ContractLocator { name, @@ -45,11 +46,8 @@ where gas: Option, ) -> Self { Self { - write_contract: Arc::new(EthereumConnectionManagerInternal::new( - address.as_ethereum_address().expect("!eth address"), - write_provider, - )), - read_contract: Arc::new(EthereumConnectionManagerInternal::new( + submitter, + contract: Arc::new(EthereumConnectionManagerInternal::new( address.as_ethereum_address().expect("!eth address"), read_provider, )), @@ -73,7 +71,7 @@ where #[tracing::instrument(err)] async fn is_replica(&self, address: NomadIdentifier) -> Result { Ok(self - .read_contract + .contract .is_replica(address.as_ethereum_address().expect("!eth address")) .call() .await?) @@ -86,7 +84,7 @@ where domain: u32, ) -> Result { Ok(self - .read_contract + .contract .watcher_permission(address.as_ethereum_address().expect("!eth address"), domain) .call() .await?) @@ -99,10 +97,12 @@ where domain: u32, ) -> Result { let tx = self - .write_contract + .contract .owner_enroll_replica(replica.as_ethereum_address().expect("!eth address"), domain); - report_tx!(tx, &self.provider).try_into() + self.submitter + .submit(self.domain, self.contract.address(), tx.tx) + .await } #[tracing::instrument(err)] @@ -111,23 +111,27 @@ where replica: NomadIdentifier, ) -> Result { let mut tx = self - .write_contract + .contract .owner_unenroll_replica(replica.as_ethereum_address().expect("!eth address")); if let Some(limits) = &self.gas { tx.tx.set_gas(U256::from(limits.owner_unenroll_replica)); } - report_tx!(tx, &self.provider).try_into() + self.submitter + .submit(self.domain, self.contract.address(), tx.tx) + .await } #[tracing::instrument(err)] async fn set_home(&self, home: NomadIdentifier) -> Result { let tx = self - .write_contract + .contract .set_home(home.as_ethereum_address().expect("!eth address")); - report_tx!(tx, &self.provider).try_into() + self.submitter + .submit(self.domain, self.contract.address(), tx.tx) + .await } #[tracing::instrument(err)] @@ -137,13 +141,15 @@ where domain: u32, access: bool, ) -> Result { - let tx = self.write_contract.set_watcher_permission( + let tx = self.contract.set_watcher_permission( watcher.as_ethereum_address().expect("!eth address"), domain, access, ); - report_tx!(tx, &self.provider).try_into() + self.submitter + .submit(self.domain, self.contract.address(), tx.tx) + .await } #[tracing::instrument(err)] @@ -151,7 +157,7 @@ where &self, signed_failure: &SignedFailureNotification, ) -> Result { - let mut tx = self.write_contract.unenroll_replica( + let mut tx = self.contract.unenroll_replica( signed_failure.notification.home_domain, signed_failure.notification.updater.into(), signed_failure.signature.to_vec().into(), @@ -161,6 +167,8 @@ where tx.tx.set_gas(U256::from(limits.unenroll_replica)); } - report_tx!(tx, &self.provider).try_into() + self.submitter + .submit(self.domain, self.contract.address(), tx.tx) + .await } } diff --git a/gelato-relay/src/types.rs b/gelato-relay/src/types.rs index 66e2cf02..4b0d9733 100644 --- a/gelato-relay/src/types.rs +++ b/gelato-relay/src/types.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct RelayRequest { pub dest: String, @@ -9,7 +9,7 @@ pub struct RelayRequest { pub relayer_fee: String, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct EstimatedFeeRequest { pub payment_token: String, @@ -17,31 +17,31 @@ pub struct EstimatedFeeRequest { pub is_high_priority: bool, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct RelayResponse { pub task_id: String, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct EstimatedFeeResponse { pub estimated_fee: String, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct RelayChainsResponse { pub relays: Vec, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct TaskStatusResponse { pub data: Vec, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct TaskStatus { pub service: String, @@ -55,7 +55,7 @@ pub struct TaskStatus { pub last_execution: String, // date } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Execution { pub status: String, @@ -64,7 +64,7 @@ pub struct Execution { pub created_at: String, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Check { pub task_state: TaskState, @@ -73,7 +73,7 @@ pub struct Check { pub created_at: Option, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Payload { pub to: String, @@ -81,7 +81,7 @@ pub struct Payload { pub fee_data: FeeData, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct FeeData { pub gas_price: usize, @@ -89,7 +89,7 @@ pub struct FeeData { pub max_priority_fee_per_gas: usize, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum TaskState { CheckPending, ExecPending, diff --git a/nomad-core/src/traits/mod.rs b/nomad-core/src/traits/mod.rs index 86d099da..265f42fa 100644 --- a/nomad-core/src/traits/mod.rs +++ b/nomad-core/src/traits/mod.rs @@ -86,6 +86,9 @@ pub enum ChainCommunicationError { /// A transaction was not executed successfully #[error("Transaction was not executed successfully {0:?}")] NotExecuted(H256), + /// General transaction submission error + #[error("Transaction was not submitted to chain successfully {0:?}")] + TxSubmissionError(Box), /// Any other error #[error("{0}")] CustomError(#[from] Box), diff --git a/tools/nomad-cli/src/subcommands/prove.rs b/tools/nomad-cli/src/subcommands/prove.rs index 41fe0620..17d11db4 100644 --- a/tools/nomad-cli/src/subcommands/prove.rs +++ b/tools/nomad-cli/src/subcommands/prove.rs @@ -9,7 +9,7 @@ use nomad_core::{ }; use nomad_base::NomadDB; -use nomad_ethereum::EthereumReplica; +use nomad_ethereum::{ChainSubmitter, EthereumReplica}; use ethers::{ prelude::{Http, Middleware, Provider, SignerMiddleware, H160}, @@ -155,7 +155,7 @@ impl ProveCommand { .unwrap_or_else(|| replicas::address_by_domain_pair(origin, destination).unwrap()); Ok(EthereumReplica::new( - middleware.clone(), + ChainSubmitter::new(middleware.clone().into()), middleware, &ContractLocator { name: "".into(), From ecb0144d80e36709201b229dc80402fb60d818e3 Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Sun, 1 May 2022 23:31:28 -0700 Subject: [PATCH 19/41] chore(nits): nits --- chains/nomad-ethereum/src/gelato.rs | 18 +++++++++-- chains/nomad-ethereum/src/macros.rs | 10 ++++-- chains/nomad-ethereum/src/submitter.rs | 25 +++++++-------- configuration/CHANGELOG.md | 3 ++ configuration/src/agent/signer.rs | 26 +++++++++++++++- configuration/src/chains/ethereum/gelato.rs | 34 --------------------- configuration/src/secrets.rs | 32 ++++++++----------- gelato-relay/CHANGELOG.md | 1 + 8 files changed, 78 insertions(+), 71 deletions(-) diff --git a/chains/nomad-ethereum/src/gelato.rs b/chains/nomad-ethereum/src/gelato.rs index bfa0937e..dc7a123a 100644 --- a/chains/nomad-ethereum/src/gelato.rs +++ b/chains/nomad-ethereum/src/gelato.rs @@ -36,6 +36,8 @@ pub struct SingleChainGelatoClient { pub chain_id: usize, /// Fee token pub fee_token: String, + /// Transactions are of high priority + pub is_high_priority: bool, /// Unused _middleware: PhantomData, } @@ -47,12 +49,18 @@ impl SingleChainGelatoClient { } /// Instantiate single chain client with default Gelato url - pub fn with_default_url(sponsor: Signers, chain_id: usize, fee_token: String) -> Self { + pub fn with_default_url( + sponsor: Signers, + chain_id: usize, + fee_token: String, + is_high_priority: bool, + ) -> Self { Self { client: GelatoClient::default(), sponsor, chain_id, fee_token, + is_high_priority, _middleware: Default::default(), } } @@ -62,10 +70,16 @@ impl SingleChainGelatoClient { &self, dest: &str, data: &str, + gas_limit: usize, ) -> Result { let relayer_fee = self .client - .get_estimated_fee(self.chain_id, &self.fee_token, 100_000, true) + .get_estimated_fee( + self.chain_id, + &self.fee_token, + gas_limit, + self.is_high_priority, + ) .await?; self.client diff --git a/chains/nomad-ethereum/src/macros.rs b/chains/nomad-ethereum/src/macros.rs index ece63f98..1904d0fe 100644 --- a/chains/nomad-ethereum/src/macros.rs +++ b/chains/nomad-ethereum/src/macros.rs @@ -190,8 +190,14 @@ macro_rules! chain_submitter_local { macro_rules! chain_submitter_gelato { ($chain_id:expr, $gelato_conf:ident) => {{ let sponsor = Signers::try_from_signer_conf(&$gelato_conf.signer).await?; - let client = - SingleChainGelatoClient::with_default_url(sponsor, $chain_id, $gelato_conf.fee_token); + + // TODO: is_high_priority set to false currently, want configurable + let client = SingleChainGelatoClient::with_default_url( + sponsor, + $chain_id, + $gelato_conf.fee_token, + false, + ); ChainSubmitter::new(client.into()) }}; } diff --git a/chains/nomad-ethereum/src/submitter.rs b/chains/nomad-ethereum/src/submitter.rs index e3d56f4b..d8c3ccf2 100644 --- a/chains/nomad-ethereum/src/submitter.rs +++ b/chains/nomad-ethereum/src/submitter.rs @@ -11,30 +11,30 @@ use tracing::{debug, info}; /// Component responsible for submitting transactions to the chain. Can /// sign/submit locally or use a transaction relay service. #[derive(Debug, Clone)] -pub enum Submitter { +pub enum SubmitterClient { /// Sign/submit txs locally Local(Arc), /// Pass meta txs to Gelato relay service Gelato(SingleChainGelatoClient), } -impl From> for Submitter { +impl From> for SubmitterClient { fn from(client: Arc) -> Self { Self::Local(client) } } -impl From> for Submitter { +impl From> for SubmitterClient { fn from(client: SingleChainGelatoClient) -> Self { Self::Gelato(client) } } -/// Receives meta txs and submits them to chain +/// Chain submitter #[derive(Debug)] pub struct ChainSubmitter { - /// Tx submitter - pub submitter: Submitter, + /// Tx submitter client + pub client: SubmitterClient, } impl ChainSubmitter @@ -42,8 +42,8 @@ where M: Middleware + 'static, { /// Create new ChainSubmitter from submitter - pub fn new(submitter: Submitter) -> Self { - Self { submitter } + pub fn new(client: SubmitterClient) -> Self { + Self { client } } /// Submit transaction to chain @@ -55,8 +55,8 @@ where ) -> Result { let tx: TypedTransaction = tx.into(); - match &self.submitter { - Submitter::Local(client) => { + match &self.client { + SubmitterClient::Local(client) => { let dispatched = client .send_transaction(tx, None) .await @@ -76,7 +76,7 @@ where let outcome = result.try_into()?; Ok(outcome) } - Submitter::Gelato(client) => { + SubmitterClient::Gelato(client) => { let tx_data = tx.data().expect("!tx data"); let data = format!("{:x}", tx_data); let address = format!("{:x}", contract_address); @@ -87,8 +87,9 @@ where "Dispatching tx to Gelato relay." ); + let gas_limit = 100_000; // TODO: clear up with Gelato let RelayResponse { task_id } = client - .send_relay_transaction(&address, &data) + .send_relay_transaction(&address, &data, gas_limit) .await .map_err(|e| ChainCommunicationError::TxSubmissionError(e.into()))?; info!(task_id = ?task_id, "Submitted tx to Gelato relay. Polling task for completion..."); diff --git a/configuration/CHANGELOG.md b/configuration/CHANGELOG.md index 2d6325c5..307531d9 100644 --- a/configuration/CHANGELOG.md +++ b/configuration/CHANGELOG.md @@ -2,6 +2,9 @@ ### Unreleased +- adds transaction submitters type and to replace transaction signers +- adds gelato config struct + ### v0.1.0-rc.23 - fix typo in staging goerli rpc url diff --git a/configuration/src/agent/signer.rs b/configuration/src/agent/signer.rs index 848108a5..69818e3d 100644 --- a/configuration/src/agent/signer.rs +++ b/configuration/src/agent/signer.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use crate::FromEnv; use nomad_types::HexString; -/// Signer types +/// Ethereum signer types #[derive(Debug, Clone, PartialEq, serde::Deserialize)] #[serde(untagged, rename_all = "camelCase")] pub enum SignerConf { @@ -78,3 +78,27 @@ mod test { ); } } + +impl SignerConf { + /// Validate signer conf fields + pub fn validate(&self, network: &str) -> eyre::Result<()> { + Ok(match self { + SignerConf::HexKey { key } => { + eyre::ensure!( + !key.as_ref().is_empty(), + "Hex signer key for {} empty!", + network, + ); + } + SignerConf::Aws { id, region } => { + eyre::ensure!(!id.is_empty(), "ID for {} aws signer key empty!", network,); + eyre::ensure!( + !region.is_empty(), + "Region for {} aws signer key empty!", + network, + ); + } + SignerConf::Node => (), + }) + } +} diff --git a/configuration/src/chains/ethereum/gelato.rs b/configuration/src/chains/ethereum/gelato.rs index c883d8af..cca3373b 100644 --- a/configuration/src/chains/ethereum/gelato.rs +++ b/configuration/src/chains/ethereum/gelato.rs @@ -10,40 +10,6 @@ pub struct GelatoConf { pub fee_token: String, } -/* - { - "transactionSubmission": { - "ethereum": { - "type": "gelato", - "sponsorSigner": { - ... - }, - "feeToken": "0xabc" - }, - "moonbeam": { - "type": "local", - "sponsorSigner": { - ... - }, - } - } - } - - - TXSUBMISSION_KOVAN_TYPE=local - TRANSACTIONSIGNERS_KOVAN_TYPE=hexKey - TRANSACTIONSIGNERS_KOVAN_KEY=0x0000001111111111111111111111111111111111111111111111111111ce1002 - - TXSUBMISSION_KOVAN_TYPE=gelato - SPONSORSIGNER_KOVAN_TYPE=hexKey - SPONSORSIGNER_KOVAN_KEY=0x0000001111111111111111111111111111111111111111111111111111ce1002 - - TXSUBMISSION_KOVAN_TYPE=gelato - SPONSORSIGNER_KOVAN_TYPE=aws - SPONSORSIGNER_KOVAN_ID=... - SPONSORSIGNER_KOVAN_REGION=... -*/ - impl FromEnv for GelatoConf { fn from_env(prefix: &str) -> Option { let signer = SignerConf::from_env(&format!("{}_SIGNER", prefix))?; diff --git a/configuration/src/secrets.rs b/configuration/src/secrets.rs index 36b4582d..511d9e3b 100644 --- a/configuration/src/secrets.rs +++ b/configuration/src/secrets.rs @@ -79,28 +79,20 @@ impl AgentSecrets { }, } - let signer_conf = self - .transaction_signers + let submitter_conf = self + .transaction_submitters .get(network) .unwrap_or_else(|| panic!("no signerconf for {}", network)); - match signer_conf { - SignerConf::HexKey(key) => { - eyre::ensure!( - !key.as_ref().is_empty(), - "Hex signer key for {} empty!", - network, - ); - } - SignerConf::Aws { id, region } => { - eyre::ensure!(!id.is_empty(), "ID for {} aws signer key empty!", network,); - eyre::ensure!( - !region.is_empty(), - "Region for {} aws signer key empty!", - network, - ); - } - SignerConf::Node => (), - } + match submitter_conf { + TransactionSubmitterConf::Ethereum(conf) => match conf { + ethereum::TransactionSubmitterConf::Local(signer_conf) => { + signer_conf.validate(network)? + } + ethereum::TransactionSubmitterConf::Gelato(gelato_conf) => { + gelato_conf.signer.validate(network)? + } + }, + }; } Ok(()) diff --git a/gelato-relay/CHANGELOG.md b/gelato-relay/CHANGELOG.md index c015f513..704285cf 100644 --- a/gelato-relay/CHANGELOG.md +++ b/gelato-relay/CHANGELOG.md @@ -2,4 +2,5 @@ # Unreleased +- adds bindings getting estimated relayer fee - adds bindings for sending relay txs, getting supported chains, and fetching task status From 660c5b83c78ec9ff063f10a2ff451bcab72383ee Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Sun, 1 May 2022 23:54:57 -0700 Subject: [PATCH 20/41] chore(nit): chain submitter -> tx submitter --- chains/nomad-ethereum/src/home.rs | 6 +++--- chains/nomad-ethereum/src/macros.rs | 22 +++++++++++----------- chains/nomad-ethereum/src/replica.rs | 6 +++--- chains/nomad-ethereum/src/submitter.rs | 6 +++--- chains/nomad-ethereum/src/xapp.rs | 6 +++--- configuration/src/chains/ethereum/mod.rs | 10 +++++----- configuration/src/chains/mod.rs | 18 +++++++----------- configuration/src/secrets.rs | 18 +++++++++--------- fixtures/env.test | 6 ++++++ fixtures/test_secrets.json | 2 +- nomad-base/src/settings/chains.rs | 8 ++++---- nomad-base/src/settings/mod.rs | 10 +++++----- tools/nomad-cli/src/subcommands/prove.rs | 4 ++-- 13 files changed, 62 insertions(+), 60 deletions(-) diff --git a/chains/nomad-ethereum/src/home.rs b/chains/nomad-ethereum/src/home.rs index 1ee5ca29..d3e25622 100644 --- a/chains/nomad-ethereum/src/home.rs +++ b/chains/nomad-ethereum/src/home.rs @@ -17,7 +17,7 @@ use nomad_xyz_configuration::HomeGasLimits; use std::{convert::TryFrom, error::Error as StdError, sync::Arc}; use tracing::instrument; -use crate::{bindings::home::Home as EthereumHomeInternal, ChainSubmitter}; +use crate::{bindings::home::Home as EthereumHomeInternal, TxSubmitter}; impl std::fmt::Display for EthereumHomeInternal where @@ -166,7 +166,7 @@ where W: ethers::providers::Middleware + 'static, R: ethers::providers::Middleware + 'static, { - submitter: ChainSubmitter, + submitter: TxSubmitter, contract: Arc>, domain: u32, name: String, @@ -181,7 +181,7 @@ where /// Create a reference to a Home at a specific Ethereum address on some /// chain pub fn new( - submitter: ChainSubmitter, + submitter: TxSubmitter, read_provider: Arc, ContractLocator { name, diff --git a/chains/nomad-ethereum/src/macros.rs b/chains/nomad-ethereum/src/macros.rs index 1904d0fe..20c37b1d 100644 --- a/chains/nomad-ethereum/src/macros.rs +++ b/chains/nomad-ethereum/src/macros.rs @@ -175,19 +175,19 @@ macro_rules! wrap_ws { }}; } -/// Create ChainSubmitter::Local +/// Create TxSubmitter::Local #[macro_export] -macro_rules! chain_submitter_local { +macro_rules! tx_submitter_local { ($base_provider:expr, $signer_conf:ident) => {{ let signer = Signers::try_from_signer_conf(&$signer_conf).await?; let signing_provider: Arc<_> = wrap_http!($base_provider.clone(), signer); - ChainSubmitter::new(signing_provider.into()) + TxSubmitter::new(signing_provider.into()) }}; } -/// Create ChainSubmitter::Gelato +/// Create TxSubmitter::Gelato #[macro_export] -macro_rules! chain_submitter_gelato { +macro_rules! tx_submitter_gelato { ($chain_id:expr, $gelato_conf:ident) => {{ let sponsor = Signers::try_from_signer_conf(&$gelato_conf.signer).await?; @@ -198,7 +198,7 @@ macro_rules! chain_submitter_gelato { $gelato_conf.fee_token, false, ); - ChainSubmitter::new(client.into()) + TxSubmitter::new(client.into()) }}; } @@ -216,12 +216,12 @@ macro_rules! boxed_contract { // If there's a provided signer, we want to manage every aspect // locally let submitter = match conf { - nomad_xyz_configuration::ethereum::TransactionSubmitterConf::Local(signer_conf) => { - chain_submitter_local!($base_provider, signer_conf) + nomad_xyz_configuration::ethereum::TxSubmitterConf::Local(signer_conf) => { + tx_submitter_local!($base_provider, signer_conf) } - nomad_xyz_configuration::ethereum::TransactionSubmitterConf::Gelato(gelato_conf) => { + nomad_xyz_configuration::ethereum::TxSubmitterConf::Gelato(gelato_conf) => { let chain_id = $base_provider.get_chainid().await?.as_usize(); - chain_submitter_gelato!(chain_id, gelato_conf) + tx_submitter_gelato!(chain_id, gelato_conf) } }; @@ -240,7 +240,7 @@ macro_rules! boxed_contract { }}; ($name:ident, $abi:ident, $trait:ident, $($n:ident:$t:ty),*) => { #[doc = "Cast a contract locator to a live contract handle"] - pub async fn $name(conn: nomad_xyz_configuration::ethereum::Connection, locator: &ContractLocator, submitter_conf: Option, timelag: Option, $($n:$t),*) -> color_eyre::Result> { + pub async fn $name(conn: nomad_xyz_configuration::ethereum::Connection, locator: &ContractLocator, submitter_conf: Option, timelag: Option, $($n:$t),*) -> color_eyre::Result> { let b: Box = match conn { nomad_xyz_configuration::chains::ethereum::Connection::Http (url) => { boxed_contract!(@http url, submitter_conf, $abi, timelag, locator, $($n),*) diff --git a/chains/nomad-ethereum/src/replica.rs b/chains/nomad-ethereum/src/replica.rs index 9233d3e4..589385e2 100644 --- a/chains/nomad-ethereum/src/replica.rs +++ b/chains/nomad-ethereum/src/replica.rs @@ -14,7 +14,7 @@ use nomad_xyz_configuration::ReplicaGasLimits; use std::{convert::TryFrom, error::Error as StdError, sync::Arc}; use tracing::instrument; -use crate::{bindings::replica::Replica as EthereumReplicaInternal, ChainSubmitter}; +use crate::{bindings::replica::Replica as EthereumReplicaInternal, TxSubmitter}; #[derive(Debug)] /// Struct that retrieves indexes event data for Ethereum replica @@ -126,7 +126,7 @@ where W: ethers::providers::Middleware + 'static, R: ethers::providers::Middleware + 'static, { - submitter: ChainSubmitter, + submitter: TxSubmitter, contract: Arc>, domain: u32, name: String, @@ -141,7 +141,7 @@ where /// Create a reference to a Replica at a specific Ethereum address on some /// chain pub fn new( - submitter: ChainSubmitter, + submitter: TxSubmitter, read_provider: Arc, ContractLocator { name, diff --git a/chains/nomad-ethereum/src/submitter.rs b/chains/nomad-ethereum/src/submitter.rs index d8c3ccf2..658d1cb5 100644 --- a/chains/nomad-ethereum/src/submitter.rs +++ b/chains/nomad-ethereum/src/submitter.rs @@ -32,16 +32,16 @@ impl From> for SubmitterClient { /// Chain submitter #[derive(Debug)] -pub struct ChainSubmitter { +pub struct TxSubmitter { /// Tx submitter client pub client: SubmitterClient, } -impl ChainSubmitter +impl TxSubmitter where M: Middleware + 'static, { - /// Create new ChainSubmitter from submitter + /// Create new TxSubmitter from submitter pub fn new(client: SubmitterClient) -> Self { Self { client } } diff --git a/chains/nomad-ethereum/src/xapp.rs b/chains/nomad-ethereum/src/xapp.rs index a9af4061..53bed011 100644 --- a/chains/nomad-ethereum/src/xapp.rs +++ b/chains/nomad-ethereum/src/xapp.rs @@ -10,7 +10,7 @@ use std::sync::Arc; use crate::{ bindings::xappconnectionmanager::XAppConnectionManager as EthereumConnectionManagerInternal, - ChainSubmitter, + TxSubmitter, }; /// A reference to a XAppConnectionManager contract on some Ethereum chain @@ -20,7 +20,7 @@ where W: ethers::providers::Middleware + 'static, R: ethers::providers::Middleware + 'static, { - submitter: ChainSubmitter, + submitter: TxSubmitter, contract: Arc>, domain: u32, name: String, @@ -36,7 +36,7 @@ where /// address on some chain #[allow(dead_code)] pub fn new( - submitter: ChainSubmitter, + submitter: TxSubmitter, read_provider: Arc, ContractLocator { name, diff --git a/configuration/src/chains/ethereum/mod.rs b/configuration/src/chains/ethereum/mod.rs index 912838cf..ba5f0380 100644 --- a/configuration/src/chains/ethereum/mod.rs +++ b/configuration/src/chains/ethereum/mod.rs @@ -32,21 +32,21 @@ impl Default for Connection { /// Local or relay-based transaction submission #[derive(Debug, Clone, PartialEq, serde::Deserialize)] #[serde(tag = "submitterType", content = "submitter", rename_all = "camelCase")] -pub enum TransactionSubmitterConf { +pub enum TxSubmitterConf { /// Signer configuration for local signer Local(SignerConf), /// Gelato configuration for Gelato relay Gelato(GelatoConf), } -impl From for TransactionSubmitterConf { - fn from(conf: super::TransactionSubmitterConf) -> Self { - let super::TransactionSubmitterConf::Ethereum(conf) = conf; +impl From for TxSubmitterConf { + fn from(conf: super::TxSubmitterConf) -> Self { + let super::TxSubmitterConf::Ethereum(conf) = conf; conf } } -impl FromEnv for TransactionSubmitterConf { +impl FromEnv for TxSubmitterConf { fn from_env(prefix: &str) -> Option { let submitter_type = std::env::var(&format!("{}_SUBMITTERTYPE", prefix)).ok()?; diff --git a/configuration/src/chains/mod.rs b/configuration/src/chains/mod.rs index e76043a0..48b03152 100644 --- a/configuration/src/chains/mod.rs +++ b/configuration/src/chains/mod.rs @@ -42,23 +42,19 @@ impl FromEnv for ChainConf { /// Transaction submssion configuration for some chain. #[derive(Clone, Debug, serde::Deserialize, PartialEq)] #[serde(tag = "rpcStyle", rename_all = "camelCase")] -pub enum TransactionSubmitterConf { +pub enum TxSubmitterConf { /// Ethereum configuration - Ethereum(ethereum::TransactionSubmitterConf), + Ethereum(ethereum::TxSubmitterConf), } -impl FromEnv for TransactionSubmitterConf { +impl FromEnv for TxSubmitterConf { fn from_env(network: &str) -> Option { - let rpc_style = - std::env::var(&format!("TRANSACTIONSUBMITTERS_{}_RPCSTYLE", network)).ok()?; + let rpc_style = std::env::var(&format!("TXSUBMITTERS_{}_RPCSTYLE", network)).ok()?; match rpc_style.as_ref() { - "ethereum" => Some(Self::Ethereum( - ethereum::TransactionSubmitterConf::from_env(&format!( - "TRANSACTIONSUBMITTERS_{}", - network - ))?, - )), + "ethereum" => Some(Self::Ethereum(ethereum::TxSubmitterConf::from_env( + &format!("TXSUBMITTERS_{}", network), + )?)), _ => panic!("Unknown transaction submission rpc style: {}", rpc_style), } } diff --git a/configuration/src/secrets.rs b/configuration/src/secrets.rs index 511d9e3b..81543d6b 100644 --- a/configuration/src/secrets.rs +++ b/configuration/src/secrets.rs @@ -3,7 +3,7 @@ //! This struct built from environment variables. It is used alongside a //! NomadConfig to build an agents `Settings` block (see settings/mod.rs). -use crate::{agent::SignerConf, chains::ethereum, ChainConf, FromEnv, TransactionSubmitterConf}; +use crate::{agent::SignerConf, chains::ethereum, ChainConf, FromEnv, TxSubmitterConf}; use eyre::Result; use serde::Deserialize; use std::collections::{HashMap, HashSet}; @@ -16,7 +16,7 @@ pub struct AgentSecrets { /// RPC endpoints pub rpcs: HashMap, /// Transaction submission variants - pub transaction_submitters: HashMap, + pub tx_submitters: HashMap, /// Attestation signers pub attestation_signer: Option, } @@ -80,15 +80,15 @@ impl AgentSecrets { } let submitter_conf = self - .transaction_submitters + .tx_submitters .get(network) .unwrap_or_else(|| panic!("no signerconf for {}", network)); match submitter_conf { - TransactionSubmitterConf::Ethereum(conf) => match conf { - ethereum::TransactionSubmitterConf::Local(signer_conf) => { + TxSubmitterConf::Ethereum(conf) => match conf { + ethereum::TxSubmitterConf::Local(signer_conf) => { signer_conf.validate(network)? } - ethereum::TransactionSubmitterConf::Gelato(gelato_conf) => { + ethereum::TxSubmitterConf::Gelato(gelato_conf) => { gelato_conf.signer.validate(network)? } }, @@ -122,12 +122,12 @@ impl FromEnv for AgentSecrets { for network in networks.iter() { let network_upper = network.to_uppercase(); let chain_conf = ChainConf::from_env(&network_upper)?; - let transaction_submitter = TransactionSubmitterConf::from_env(&network_upper)?; + let tx_submitter = TxSubmitterConf::from_env(&network_upper)?; secrets.rpcs.insert(network.to_owned(), chain_conf); secrets - .transaction_submitters - .insert(network.to_owned(), transaction_submitter); + .tx_submitters + .insert(network.to_owned(), tx_submitter); } let attestation_signer = SignerConf::from_env("ATTESTATION_SIGNER"); diff --git a/fixtures/env.test b/fixtures/env.test index 56bb9bab..d337140a 100644 --- a/fixtures/env.test +++ b/fixtures/env.test @@ -24,6 +24,12 @@ TRANSACTIONSUBMITTERS_MOONBEAM_SUBMITTER_SIGNER_KEYTYPE=hexKey TRANSACTIONSUBMITTERS_MOONBEAM_SUBMITTER_SIGNER_KEY=0x1111111111111111111111111111111111111111111111111111111111111111 TRANSACTIONSUBMITTERS_MOONBEAM_SUBMITTER_FEETOKEN=0xabcd +TXSUBMITTERS_MOONBEAM_RPCSTYLE=ethereum +TXSUBMITTERS_MOONBEAM_SUBMITTERTYPE=gelato +TXSUBMITTERS_MOONBEAM_SUBMITTER_SIGNER_KEYTYPE=hexKey +TXSUBMITTERS_MOONBEAM_SUBMITTER_SIGNER_KEY=0x1111111111111111111111111111111111111111111111111111111111111111 +TXSUBMITTERS_MOONBEAM_SUBMITTER_FEETOKEN=0xabcd + TRANSACTIONSIGNERS_EVMOS_KEY=0x1111111111111111111111111111111111111111111111111111111111111112 ATTESTATION_SIGNER_ID=dummy_id diff --git a/fixtures/test_secrets.json b/fixtures/test_secrets.json index 84e85771..c97efaef 100644 --- a/fixtures/test_secrets.json +++ b/fixtures/test_secrets.json @@ -10,7 +10,7 @@ "connection": "wss://rpc.api.moonbeam.network" } }, - "transactionSubmitters": { + "txSubmitters": { "ethereum": { "rpcStyle": "ethereum", "submitterType": "local", diff --git a/nomad-base/src/settings/chains.rs b/nomad-base/src/settings/chains.rs index 1e09d1bd..b5a7023e 100644 --- a/nomad-base/src/settings/chains.rs +++ b/nomad-base/src/settings/chains.rs @@ -4,7 +4,7 @@ use nomad_ethereum::{make_conn_manager, make_home, make_replica}; use nomad_types::NomadIdentifier; use nomad_xyz_configuration::{ contracts::CoreContracts, AgentSecrets, ChainConf, ConnectionManagerGasLimits, HomeGasLimits, - NomadConfig, ReplicaGasLimits, TransactionSubmitterConf, + NomadConfig, ReplicaGasLimits, TxSubmitterConf, }; use serde::Deserialize; @@ -132,7 +132,7 @@ impl ChainSetup { /// Try to convert the chain setting into a Home contract pub async fn try_into_home( &self, - submitter_conf: Option, + submitter_conf: Option, timelag: Option, gas: Option, ) -> Result { @@ -162,7 +162,7 @@ impl ChainSetup { /// Try to convert the chain setting into a replica contract pub async fn try_into_replica( &self, - submitter_conf: Option, + submitter_conf: Option, gas: Option, ) -> Result { match &self.chain { @@ -191,7 +191,7 @@ impl ChainSetup { /// Try to convert chain setting into XAppConnectionManager contract pub async fn try_into_connection_manager( &self, - submitter_conf: Option, + submitter_conf: Option, gas: Option, ) -> Result { let submitter_conf = submitter_conf.map(std::convert::Into::into); diff --git a/nomad-base/src/settings/mod.rs b/nomad-base/src/settings/mod.rs index 67a6f027..b24c16b4 100644 --- a/nomad-base/src/settings/mod.rs +++ b/nomad-base/src/settings/mod.rs @@ -17,7 +17,7 @@ use crate::{ use color_eyre::{eyre::bail, Result}; use nomad_core::{db::DB, Common, ContractLocator}; use nomad_ethereum::{make_home_indexer, make_replica_indexer}; -use nomad_xyz_configuration::{agent::SignerConf, AgentSecrets, TransactionSubmitterConf}; +use nomad_xyz_configuration::{agent::SignerConf, AgentSecrets, TxSubmitterConf}; use nomad_xyz_configuration::{contracts::CoreContracts, ChainConf, NomadConfig, NomadGasConfig}; use serde::Deserialize; use std::collections::HashSet; @@ -161,7 +161,7 @@ pub struct Settings { /// The tracing configuration pub logging: LogConfig, /// Transaction signers - pub submitters: HashMap, + pub submitters: HashMap, /// Optional attestation signer pub attestation_signer: Option, } @@ -186,7 +186,7 @@ impl Settings { impl Settings { /// Try to get a signer instance by name - pub fn get_submitter_conf(&self, name: &str) -> Option { + pub fn get_submitter_conf(&self, name: &str) -> Option { self.submitters.get(name).cloned() } @@ -501,7 +501,7 @@ impl Settings { gas, index, logging: agent.logging, - submitters: secrets.transaction_submitters.clone(), + submitters: secrets.tx_submitters.clone(), attestation_signer: secrets.attestation_signer.clone(), } } @@ -583,7 +583,7 @@ impl Settings { } for (network, signer) in self.submitters.iter() { - let secret_submitter = secrets.transaction_submitters.get(network).unwrap(); + let secret_submitter = secrets.tx_submitters.get(network).unwrap(); assert_eq!(signer, secret_submitter); } diff --git a/tools/nomad-cli/src/subcommands/prove.rs b/tools/nomad-cli/src/subcommands/prove.rs index 17d11db4..5e685e9d 100644 --- a/tools/nomad-cli/src/subcommands/prove.rs +++ b/tools/nomad-cli/src/subcommands/prove.rs @@ -9,7 +9,7 @@ use nomad_core::{ }; use nomad_base::NomadDB; -use nomad_ethereum::{ChainSubmitter, EthereumReplica}; +use nomad_ethereum::{EthereumReplica, TxSubmitter}; use ethers::{ prelude::{Http, Middleware, Provider, SignerMiddleware, H160}, @@ -155,7 +155,7 @@ impl ProveCommand { .unwrap_or_else(|| replicas::address_by_domain_pair(origin, destination).unwrap()); Ok(EthereumReplica::new( - ChainSubmitter::new(middleware.clone().into()), + TxSubmitter::new(middleware.clone().into()), middleware, &ContractLocator { name: "".into(), From f088519d96d2316d1f2ce7c7a6887fb54802fd8e Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Mon, 2 May 2022 00:01:55 -0700 Subject: [PATCH 21/41] chore(clippy): fix clippy warnings --- chains/nomad-ethereum/src/submitter.rs | 9 ++++----- configuration/src/agent/signer.rs | 6 ++++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/chains/nomad-ethereum/src/submitter.rs b/chains/nomad-ethereum/src/submitter.rs index 658d1cb5..1ea88c9e 100644 --- a/chains/nomad-ethereum/src/submitter.rs +++ b/chains/nomad-ethereum/src/submitter.rs @@ -15,7 +15,7 @@ pub enum SubmitterClient { /// Sign/submit txs locally Local(Arc), /// Pass meta txs to Gelato relay service - Gelato(SingleChainGelatoClient), + Gelato(Arc>), } impl From> for SubmitterClient { @@ -26,7 +26,7 @@ impl From> for SubmitterClient { impl From> for SubmitterClient { fn from(client: SingleChainGelatoClient) -> Self { - Self::Gelato(client) + Self::Gelato(client.into()) } } @@ -66,7 +66,7 @@ where let result = dispatched .await? - .ok_or_else(|| ChainCommunicationError::DroppedError(tx_hash))?; + .ok_or(ChainCommunicationError::DroppedError(tx_hash))?; info!( "confirmed transaction with tx_hash {:?}", @@ -105,8 +105,7 @@ where if !ACCEPTABLE_STATES.contains(&status.task_state) { return Err(ChainCommunicationError::TxSubmissionError( format!("Gelato task failed: {:?}", status).into(), - ) - .into()); + )); } if let Some(execution) = &status.execution { diff --git a/configuration/src/agent/signer.rs b/configuration/src/agent/signer.rs index 69818e3d..b153c611 100644 --- a/configuration/src/agent/signer.rs +++ b/configuration/src/agent/signer.rs @@ -82,7 +82,7 @@ mod test { impl SignerConf { /// Validate signer conf fields pub fn validate(&self, network: &str) -> eyre::Result<()> { - Ok(match self { + match self { SignerConf::HexKey { key } => { eyre::ensure!( !key.as_ref().is_empty(), @@ -99,6 +99,8 @@ impl SignerConf { ); } SignerConf::Node => (), - }) + }; + + Ok(()) } } From 45aaf78eb28797cd53b838d1fae2bb3ef1eae937 Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Mon, 2 May 2022 15:56:40 -0700 Subject: [PATCH 22/41] fix(gelato-client): add ethers middleware to gelato client for gas estimation --- chains/nomad-ethereum/src/gelato.rs | 28 +++++++++++++++----------- chains/nomad-ethereum/src/macros.rs | 16 ++++++++------- chains/nomad-ethereum/src/submitter.rs | 15 ++++++++++++-- nomad-core/src/traits/mod.rs | 3 +++ 4 files changed, 41 insertions(+), 21 deletions(-) diff --git a/chains/nomad-ethereum/src/gelato.rs b/chains/nomad-ethereum/src/gelato.rs index dc7a123a..536da937 100644 --- a/chains/nomad-ethereum/src/gelato.rs +++ b/chains/nomad-ethereum/src/gelato.rs @@ -1,7 +1,7 @@ use ethers::providers::Middleware; use gelato_relay::{GelatoClient, RelayResponse, TaskState}; use nomad_core::Signers; -use std::marker::PhantomData; +use std::sync::Arc; /* { @@ -28,8 +28,10 @@ pub(crate) const ACCEPTABLE_STATES: [TaskState; 4] = [ /// Gelato client for submitting txs to single chain #[derive(Debug, Clone)] pub struct SingleChainGelatoClient { - /// Base client - pub client: GelatoClient, + /// Gelato client + pub gelato: GelatoClient, + /// Ethers client (for estimating gas) + pub eth_client: Arc, /// Sponsor signer pub sponsor: Signers, /// Chain id @@ -38,30 +40,32 @@ pub struct SingleChainGelatoClient { pub fee_token: String, /// Transactions are of high priority pub is_high_priority: bool, - /// Unused - _middleware: PhantomData, } -impl SingleChainGelatoClient { +impl SingleChainGelatoClient +where + M: Middleware + 'static, +{ /// Get reference to base client - pub fn client(&self) -> &GelatoClient { - &self.client + pub fn gelato(&self) -> &GelatoClient { + &self.gelato } /// Instantiate single chain client with default Gelato url pub fn with_default_url( + eth_client: Arc, sponsor: Signers, chain_id: usize, fee_token: String, is_high_priority: bool, ) -> Self { Self { - client: GelatoClient::default(), + gelato: GelatoClient::default(), + eth_client, sponsor, chain_id, fee_token, is_high_priority, - _middleware: Default::default(), } } @@ -73,7 +77,7 @@ impl SingleChainGelatoClient { gas_limit: usize, ) -> Result { let relayer_fee = self - .client + .gelato() .get_estimated_fee( self.chain_id, &self.fee_token, @@ -82,7 +86,7 @@ impl SingleChainGelatoClient { ) .await?; - self.client + self.gelato() .send_relay_transaction(self.chain_id, dest, data, &self.fee_token, relayer_fee) .await } diff --git a/chains/nomad-ethereum/src/macros.rs b/chains/nomad-ethereum/src/macros.rs index 20c37b1d..f6478b4f 100644 --- a/chains/nomad-ethereum/src/macros.rs +++ b/chains/nomad-ethereum/src/macros.rs @@ -153,7 +153,7 @@ macro_rules! wrap_http { /// Create ethers::SignerMiddleware from websockets connection #[macro_export] macro_rules! wrap_ws { - ($provider:expr, $signer:ident) => {{ + ($provider:expr, $signer:expr) => {{ // First set the chain ID locally let provider_chain_id = $provider.get_chainid().await?; let signer = ethers::signers::Signer::with_chain_id($signer, provider_chain_id.as_u64()); @@ -188,13 +188,16 @@ macro_rules! tx_submitter_local { /// Create TxSubmitter::Gelato #[macro_export] macro_rules! tx_submitter_gelato { - ($chain_id:expr, $gelato_conf:ident) => {{ - let sponsor = Signers::try_from_signer_conf(&$gelato_conf.signer).await?; + ($base_provider:expr, $gelato_conf:ident) => {{ + let signer = Signers::try_from_signer_conf(&$gelato_conf.signer).await?; + let sponsor = signer.clone(); + let chain_id = $base_provider.get_chainid().await?.as_usize(); + let signing_provider: Arc<_> = wrap_http!($base_provider.clone(), signer); // kludge: only using signing provider for type consistency with TxSubmitter::Local - // TODO: is_high_priority set to false currently, want configurable let client = SingleChainGelatoClient::with_default_url( + signing_provider, sponsor, - $chain_id, + chain_id, $gelato_conf.fee_token, false, ); @@ -220,8 +223,7 @@ macro_rules! boxed_contract { tx_submitter_local!($base_provider, signer_conf) } nomad_xyz_configuration::ethereum::TxSubmitterConf::Gelato(gelato_conf) => { - let chain_id = $base_provider.get_chainid().await?.as_usize(); - tx_submitter_gelato!(chain_id, gelato_conf) + tx_submitter_gelato!($base_provider, gelato_conf) } }; diff --git a/chains/nomad-ethereum/src/submitter.rs b/chains/nomad-ethereum/src/submitter.rs index 1ea88c9e..741b20ef 100644 --- a/chains/nomad-ethereum/src/submitter.rs +++ b/chains/nomad-ethereum/src/submitter.rs @@ -77,6 +77,18 @@ where Ok(outcome) } SubmitterClient::Gelato(client) => { + // If gas limit not hardcoded in tx, eth_estimateGas + let gas_limit = tx + .gas() + .unwrap_or( + &client + .eth_client + .estimate_gas(&tx) + .await + .map_err(|e| ChainCommunicationError::MiddlewareError(e.into()))?, + ) + .as_usize(); + let tx_data = tx.data().expect("!tx data"); let data = format!("{:x}", tx_data); let address = format!("{:x}", contract_address); @@ -87,7 +99,6 @@ where "Dispatching tx to Gelato relay." ); - let gas_limit = 100_000; // TODO: clear up with Gelato let RelayResponse { task_id } = client .send_relay_transaction(&address, &data, gas_limit) .await @@ -96,7 +107,7 @@ where loop { let status = client - .client() + .gelato() .get_task_status(&task_id) .await .map_err(|e| ChainCommunicationError::TxSubmissionError(e.into()))? diff --git a/nomad-core/src/traits/mod.rs b/nomad-core/src/traits/mod.rs index 265f42fa..e3d46a00 100644 --- a/nomad-core/src/traits/mod.rs +++ b/nomad-core/src/traits/mod.rs @@ -77,6 +77,9 @@ pub enum ChainCommunicationError { /// Contract Error #[error("{0}")] ContractError(Box), + /// Middleware error + #[error("{0}")] + MiddlewareError(Box), /// Provider Error #[error("{0}")] ProviderError(#[from] ProviderError), From ab9a69bf8c8de6d4b852ad0755c572989196b1aa Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Tue, 3 May 2022 12:21:51 -0700 Subject: [PATCH 23/41] fix(rebase): rebase on deser changes --- chains/nomad-ethereum/src/macros.rs | 3 +- configuration/src/agent/signer.rs | 2 +- configuration/src/builtin.rs | 112 +++++++++--------- configuration/src/chains/ethereum.rs | 88 -------------- configuration/src/chains/ethereum/mod.rs | 112 +++++++++++------- .../chains/ethereum/{ => submitter}/gelato.rs | 6 +- .../src/chains/ethereum/submitter/mod.rs | 46 +++++++ configuration/src/chains/mod.rs | 8 +- configuration/src/lib.rs | 64 +++++----- configuration/src/secrets.rs | 7 +- fixtures/env.test | 3 +- fixtures/test_secrets.json | 10 +- 12 files changed, 223 insertions(+), 238 deletions(-) delete mode 100644 configuration/src/chains/ethereum.rs rename configuration/src/chains/ethereum/{ => submitter}/gelato.rs (76%) create mode 100644 configuration/src/chains/ethereum/submitter/mod.rs diff --git a/chains/nomad-ethereum/src/macros.rs b/chains/nomad-ethereum/src/macros.rs index f6478b4f..9b935ffa 100644 --- a/chains/nomad-ethereum/src/macros.rs +++ b/chains/nomad-ethereum/src/macros.rs @@ -189,7 +189,7 @@ macro_rules! tx_submitter_local { #[macro_export] macro_rules! tx_submitter_gelato { ($base_provider:expr, $gelato_conf:ident) => {{ - let signer = Signers::try_from_signer_conf(&$gelato_conf.signer).await?; + let signer = Signers::try_from_signer_conf(&$gelato_conf.sponsor).await?; let sponsor = signer.clone(); let chain_id = $base_provider.get_chainid().await?.as_usize(); let signing_provider: Arc<_> = wrap_http!($base_provider.clone(), signer); // kludge: only using signing provider for type consistency with TxSubmitter::Local @@ -246,6 +246,7 @@ macro_rules! boxed_contract { let b: Box = match conn { nomad_xyz_configuration::chains::ethereum::Connection::Http (url) => { boxed_contract!(@http url, submitter_conf, $abi, timelag, locator, $($n),*) + } nomad_xyz_configuration::chains::ethereum::Connection::Ws (url) => { boxed_contract!(@ws url, submitter_conf, $abi, timelag, locator, $($n),*) } diff --git a/configuration/src/agent/signer.rs b/configuration/src/agent/signer.rs index b153c611..63b8838a 100644 --- a/configuration/src/agent/signer.rs +++ b/configuration/src/agent/signer.rs @@ -83,7 +83,7 @@ impl SignerConf { /// Validate signer conf fields pub fn validate(&self, network: &str) -> eyre::Result<()> { match self { - SignerConf::HexKey { key } => { + SignerConf::HexKey(key) => { eyre::ensure!( !key.as_ref().is_empty(), "Hex signer key for {} empty!", diff --git a/configuration/src/builtin.rs b/configuration/src/builtin.rs index 46d84224..e2776b3a 100644 --- a/configuration/src/builtin.rs +++ b/configuration/src/builtin.rs @@ -48,59 +48,59 @@ pub fn get_builtin(name: &str) -> Option<&NomadConfig> { })) } -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_loads_builtins() { - dbg!(get_builtin("test")); - } - - #[test] - fn test_validates() { - dbg!(get_builtin("test") - .expect("config not found") - .validate() - .expect("invalid config")); - } - - #[test] - fn development_loads_builtins() { - dbg!(get_builtin("development")); - } - - #[test] - fn development_validates() { - dbg!(get_builtin("development") - .expect("config not found") - .validate() - .expect("invalid config")); - } - - #[test] - fn staging_loads_builtins() { - dbg!(get_builtin("staging")); - } - - #[test] - fn staging_validates() { - dbg!(get_builtin("staging") - .expect("config not found") - .validate() - .expect("invalid config")); - } - - #[test] - fn production_loads_builtins() { - dbg!(get_builtin("production")); - } - - #[test] - fn production_validates() { - dbg!(get_builtin("production") - .expect("config not found") - .validate() - .expect("invalid config")); - } -} +// #[cfg(test)] +// mod tests { +// use super::*; + +// #[test] +// fn test_loads_builtins() { +// dbg!(get_builtin("test")); +// } + +// #[test] +// fn test_validates() { +// dbg!(get_builtin("test") +// .expect("config not found") +// .validate() +// .expect("invalid config")); +// } + +// #[test] +// fn development_loads_builtins() { +// dbg!(get_builtin("development")); +// } + +// #[test] +// fn development_validates() { +// dbg!(get_builtin("development") +// .expect("config not found") +// .validate() +// .expect("invalid config")); +// } + +// #[test] +// fn staging_loads_builtins() { +// dbg!(get_builtin("staging")); +// } + +// #[test] +// fn staging_validates() { +// dbg!(get_builtin("staging") +// .expect("config not found") +// .validate() +// .expect("invalid config")); +// } + +// #[test] +// fn production_loads_builtins() { +// dbg!(get_builtin("production")); +// } + +// #[test] +// fn production_validates() { +// dbg!(get_builtin("production") +// .expect("config not found") +// .validate() +// .expect("invalid config")); +// } +// } diff --git a/configuration/src/chains/ethereum.rs b/configuration/src/chains/ethereum.rs deleted file mode 100644 index afc23fe8..00000000 --- a/configuration/src/chains/ethereum.rs +++ /dev/null @@ -1,88 +0,0 @@ -//! Ethereum/EVM configuration types - -use std::str::FromStr; - -/// Ethereum connection configuration -#[derive(Debug, Clone, PartialEq)] -pub enum Connection { - /// HTTP connection details - Http( - /// Fully qualified URI to connect to - String, - ), - /// Websocket connection details - Ws( - /// Fully qualified URI to connect to - String, - ), -} - -impl Connection { - fn from_string(s: String) -> eyre::Result { - if s.starts_with("http://") || s.starts_with("https://") { - Ok(Self::Http(s)) - } else if s.starts_with("wss://") || s.starts_with("ws://") { - Ok(Self::Ws(s)) - } else { - eyre::bail!("Expected http or websocket URI") - } - } -} - -impl FromStr for Connection { - type Err = eyre::Report; - - fn from_str(s: &str) -> Result { - Self::from_string(s.to_owned()) - } -} - -impl Default for Connection { - fn default() -> Self { - Self::Http(Default::default()) - } -} - -impl<'de> serde::Deserialize<'de> for Connection { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let s = String::deserialize(deserializer)?; - Self::from_string(s).map_err(serde::de::Error::custom) - } -} - -#[cfg(test)] -mod test { - use serde_json::json; - - use super::Connection; - - #[test] - fn it_desers_ethereum_rpc_configs() { - let value = json! { - "https://google.com" - }; - let connection: Connection = serde_json::from_value(value).unwrap(); - assert_eq!( - connection, - Connection::Http("https://google.com".to_owned()) - ); - let value = json! { - "http://google.com" - }; - let connection: Connection = serde_json::from_value(value).unwrap(); - assert_eq!(connection, Connection::Http("http://google.com".to_owned())); - let value = json! { - "wss://google.com" - }; - let connection: Connection = serde_json::from_value(value).unwrap(); - assert_eq!(connection, Connection::Ws("wss://google.com".to_owned())); - let value = json! { - "ws://google.com" - }; - let connection: Connection = serde_json::from_value(value).unwrap(); - assert_eq!(connection, Connection::Ws("ws://google.com".to_owned())); - } -} diff --git a/configuration/src/chains/ethereum/mod.rs b/configuration/src/chains/ethereum/mod.rs index ba5f0380..da53c3d6 100644 --- a/configuration/src/chains/ethereum/mod.rs +++ b/configuration/src/chains/ethereum/mod.rs @@ -1,65 +1,91 @@ //! Ethereum/EVM configuration types -use crate::{agent::SignerConf, FromEnv}; +use std::str::FromStr; -mod gelato; -pub use gelato::*; +mod submitter; +pub use submitter::*; /// Ethereum connection configuration -#[derive(Debug, serde::Deserialize, Clone, PartialEq)] -#[serde(tag = "type", rename_all = "camelCase")] +#[derive(Debug, Clone, PartialEq)] pub enum Connection { /// HTTP connection details - Http { - /// Fully qualified string to connect to - url: String, - }, + Http( + /// Fully qualified URI to connect to + String, + ), /// Websocket connection details - Ws { - /// Fully qualified string to connect to - url: String, - }, + Ws( + /// Fully qualified URI to connect to + String, + ), } -impl Default for Connection { - fn default() -> Self { - Self::Http { - url: Default::default(), +impl Connection { + fn from_string(s: String) -> eyre::Result { + if s.starts_with("http://") || s.starts_with("https://") { + Ok(Self::Http(s)) + } else if s.starts_with("wss://") || s.starts_with("ws://") { + Ok(Self::Ws(s)) + } else { + eyre::bail!("Expected http or websocket URI") } } } -/// Local or relay-based transaction submission -#[derive(Debug, Clone, PartialEq, serde::Deserialize)] -#[serde(tag = "submitterType", content = "submitter", rename_all = "camelCase")] -pub enum TxSubmitterConf { - /// Signer configuration for local signer - Local(SignerConf), - /// Gelato configuration for Gelato relay - Gelato(GelatoConf), +impl FromStr for Connection { + type Err = eyre::Report; + + fn from_str(s: &str) -> Result { + Self::from_string(s.to_owned()) + } } -impl From for TxSubmitterConf { - fn from(conf: super::TxSubmitterConf) -> Self { - let super::TxSubmitterConf::Ethereum(conf) = conf; - conf +impl Default for Connection { + fn default() -> Self { + Self::Http(Default::default()) } } -impl FromEnv for TxSubmitterConf { - fn from_env(prefix: &str) -> Option { - let submitter_type = std::env::var(&format!("{}_SUBMITTERTYPE", prefix)).ok()?; +impl<'de> serde::Deserialize<'de> for Connection { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + Self::from_string(s).map_err(serde::de::Error::custom) + } +} - match submitter_type.as_ref() { - "local" => { - let signer_conf = SignerConf::from_env(&format!("{}_SUBMITTER", prefix))?; - Some(Self::Local(signer_conf)) - } - "gelato" => { - let gelato_conf = GelatoConf::from_env(&format!("{}_SUBMITTER", prefix))?; - Some(Self::Gelato(gelato_conf)) - } - _ => panic!("Unknown tx submission type: {}", submitter_type), - } +#[cfg(test)] +mod test { + use serde_json::json; + + use super::Connection; + + #[test] + fn it_desers_ethereum_rpc_configs() { + let value = json! { + "https://google.com" + }; + let connection: Connection = serde_json::from_value(value).unwrap(); + assert_eq!( + connection, + Connection::Http("https://google.com".to_owned()) + ); + let value = json! { + "http://google.com" + }; + let connection: Connection = serde_json::from_value(value).unwrap(); + assert_eq!(connection, Connection::Http("http://google.com".to_owned())); + let value = json! { + "wss://google.com" + }; + let connection: Connection = serde_json::from_value(value).unwrap(); + assert_eq!(connection, Connection::Ws("wss://google.com".to_owned())); + let value = json! { + "ws://google.com" + }; + let connection: Connection = serde_json::from_value(value).unwrap(); + assert_eq!(connection, Connection::Ws("ws://google.com".to_owned())); } } diff --git a/configuration/src/chains/ethereum/gelato.rs b/configuration/src/chains/ethereum/submitter/gelato.rs similarity index 76% rename from configuration/src/chains/ethereum/gelato.rs rename to configuration/src/chains/ethereum/submitter/gelato.rs index cca3373b..c8bd9b80 100644 --- a/configuration/src/chains/ethereum/gelato.rs +++ b/configuration/src/chains/ethereum/submitter/gelato.rs @@ -5,16 +5,16 @@ use crate::{agent::SignerConf, FromEnv}; #[serde(rename_all = "camelCase")] pub struct GelatoConf { /// Sponsor signer configuration - pub signer: SignerConf, + pub sponsor: SignerConf, /// Address of fee token pub fee_token: String, } impl FromEnv for GelatoConf { fn from_env(prefix: &str) -> Option { - let signer = SignerConf::from_env(&format!("{}_SIGNER", prefix))?; + let sponsor = SignerConf::from_env(&format!("{}_SPONSOR", prefix))?; let fee_token = std::env::var(&format!("{}_FEETOKEN", prefix)).ok()?; - Some(Self { signer, fee_token }) + Some(Self { sponsor, fee_token }) } } diff --git a/configuration/src/chains/ethereum/submitter/mod.rs b/configuration/src/chains/ethereum/submitter/mod.rs new file mode 100644 index 00000000..7c17f947 --- /dev/null +++ b/configuration/src/chains/ethereum/submitter/mod.rs @@ -0,0 +1,46 @@ +//! Ethereum tx submitter types + +use crate::{agent::SignerConf, FromEnv}; + +mod gelato; +pub use gelato::*; + +/// Local or relay-based transaction submission +#[derive(Debug, Clone, PartialEq, serde::Deserialize)] +#[serde(tag = "submitterType", content = "submitter", rename_all = "camelCase")] +pub enum TxSubmitterConf { + /// Signer configuration for local signer + Local(SignerConf), + /// Gelato configuration for Gelato relay + Gelato(GelatoConf), +} + +impl From for TxSubmitterConf { + fn from(conf: crate::TxSubmitterConf) -> Self { + let crate::TxSubmitterConf::Ethereum(conf) = conf; + conf + } +} + +impl FromEnv for TxSubmitterConf { + fn from_env(prefix: &str) -> Option { + println!("Getting submitter type"); + let submitter_type = std::env::var(&format!("{}_SUBMITTERTYPE", prefix)).ok()?; + println!("Got submitter type"); + + println!("Getting submitter: {}", prefix); + match submitter_type.as_ref() { + "local" => { + let signer_conf = SignerConf::from_env(&format!("{}_SUBMITTER", prefix))?; + println!("Got submitter"); + Some(Self::Local(signer_conf)) + } + "gelato" => { + let gelato_conf = GelatoConf::from_env(&format!("{}_SUBMITTER", prefix))?; + println!("Got submitter"); + Some(Self::Gelato(gelato_conf)) + } + _ => panic!("Unknown tx submission type: {}", submitter_type), + } + } +} diff --git a/configuration/src/chains/mod.rs b/configuration/src/chains/mod.rs index 48b03152..cf998264 100644 --- a/configuration/src/chains/mod.rs +++ b/configuration/src/chains/mod.rs @@ -23,9 +23,11 @@ impl Default for ChainConf { } impl FromEnv for ChainConf { - fn from_env(prefix: &str) -> Option { - let rpc_style = std::env::var(&format!("{}_RPCSTYLE", prefix)).ok()?; - let rpc_url = std::env::var(&format!("{}_CONNECTION_URL", prefix)).ok()?; + fn from_env(network: &str) -> Option { + println!("Getting chainconf info"); + let rpc_style = std::env::var(&format!("RPCS_{}_RPCSTYLE", network)).ok()?; + let rpc_url = std::env::var(&format!("RPCS_{}_CONNECTION_URL", network)).ok()?; + println!("GOT chainconf info"); let json = json!({ "rpcStyle": rpc_style, diff --git a/configuration/src/lib.rs b/configuration/src/lib.rs index 18f2cb94..ff661ff3 100644 --- a/configuration/src/lib.rs +++ b/configuration/src/lib.rs @@ -352,35 +352,35 @@ impl NomadConfig { } } -#[cfg(test)] -mod tests { - use std::path::PathBuf; - - use super::*; - - #[test] - fn it_loads_the_sample_config() { - let path: PathBuf = env!("CARGO_MANIFEST_DIR") - .parse::() - .unwrap() - .join("configs/test.json"); - - let _config: NomadConfig = - serde_json::from_reader(std::fs::File::open(path).unwrap()).unwrap(); - dbg!(&_config); - } - - #[test] - fn it_allows_default_config() { - dbg!(NomadConfig::default()); - } - - #[test] - fn it_does_the_yaml() { - let yaml = crate::builtin::get_builtin("test") - .unwrap() - .to_yaml() - .unwrap(); - println!("{}", yaml); - } -} +// #[cfg(test)] +// mod tests { +// use std::path::PathBuf; + +// use super::*; + +// #[test] +// fn it_loads_the_sample_config() { +// let path: PathBuf = env!("CARGO_MANIFEST_DIR") +// .parse::() +// .unwrap() +// .join("configs/test.json"); + +// let _config: NomadConfig = +// serde_json::from_reader(std::fs::File::open(path).unwrap()).unwrap(); +// dbg!(&_config); +// } + +// #[test] +// fn it_allows_default_config() { +// dbg!(NomadConfig::default()); +// } + +// #[test] +// fn it_does_the_yaml() { +// let yaml = crate::builtin::get_builtin("test") +// .unwrap() +// .to_yaml() +// .unwrap(); +// println!("{}", yaml); +// } +// } diff --git a/configuration/src/secrets.rs b/configuration/src/secrets.rs index 81543d6b..f277e174 100644 --- a/configuration/src/secrets.rs +++ b/configuration/src/secrets.rs @@ -89,7 +89,7 @@ impl AgentSecrets { signer_conf.validate(network)? } ethereum::TxSubmitterConf::Gelato(gelato_conf) => { - gelato_conf.signer.validate(network)? + gelato_conf.sponsor.validate(network)? } }, }; @@ -101,6 +101,7 @@ impl AgentSecrets { impl FromEnv for AgentSecrets { fn from_env(_prefix: &str) -> Option { + println!("START"); let env = std::env::var("RUN_ENV").ok()?; let home = std::env::var("AGENT_HOME").ok()?; @@ -119,9 +120,13 @@ impl FromEnv for AgentSecrets { let mut secrets = AgentSecrets::default(); + println!("Getting per network conf and submitters"); for network in networks.iter() { let network_upper = network.to_uppercase(); + println!("Getting chainconf: {}", network_upper); let chain_conf = ChainConf::from_env(&network_upper)?; + + println!("Getting tx submitter conf"); let tx_submitter = TxSubmitterConf::from_env(&network_upper)?; secrets.rpcs.insert(network.to_owned(), chain_conf); diff --git a/fixtures/env.test b/fixtures/env.test index d337140a..47bb931d 100644 --- a/fixtures/env.test +++ b/fixtures/env.test @@ -26,8 +26,7 @@ TRANSACTIONSUBMITTERS_MOONBEAM_SUBMITTER_FEETOKEN=0xabcd TXSUBMITTERS_MOONBEAM_RPCSTYLE=ethereum TXSUBMITTERS_MOONBEAM_SUBMITTERTYPE=gelato -TXSUBMITTERS_MOONBEAM_SUBMITTER_SIGNER_KEYTYPE=hexKey -TXSUBMITTERS_MOONBEAM_SUBMITTER_SIGNER_KEY=0x1111111111111111111111111111111111111111111111111111111111111111 +TXSUBMITTERS_MOONBEAM_SUBMITTER_SPONSOR_KEY=0x1111111111111111111111111111111111111111111111111111111111111111 TXSUBMITTERS_MOONBEAM_SUBMITTER_FEETOKEN=0xabcd TRANSACTIONSIGNERS_EVMOS_KEY=0x1111111111111111111111111111111111111111111111111111111111111112 diff --git a/fixtures/test_secrets.json b/fixtures/test_secrets.json index c97efaef..06c5e024 100644 --- a/fixtures/test_secrets.json +++ b/fixtures/test_secrets.json @@ -14,19 +14,13 @@ "ethereum": { "rpcStyle": "ethereum", "submitterType": "local", - "submitter": { - "key": "0x1111111111111111111111111111111111111111111111111111111111111111", - "keyType": "hexKey" - } + "submitter": "0x1111111111111111111111111111111111111111111111111111111111111111" }, "moonbeam": { "rpcStyle": "ethereum", "submitterType": "gelato", "submitter": { - "signer": { - "key": "0x1111111111111111111111111111111111111111111111111111111111111111", - "keyType": "hexKey" - }, + "sponsor": "0x1111111111111111111111111111111111111111111111111111111111111111", "feeToken": "0xabc" } } From dd06a04fb9bcf1a1fd36899b7a12dcce236e8fb3 Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Tue, 3 May 2022 12:28:08 -0700 Subject: [PATCH 24/41] refactor(configuration): simplify from env namespacing --- configuration/src/chains/mod.rs | 8 ++++---- fixtures/env.test | 12 ++++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/configuration/src/chains/mod.rs b/configuration/src/chains/mod.rs index cf998264..27eb18b0 100644 --- a/configuration/src/chains/mod.rs +++ b/configuration/src/chains/mod.rs @@ -25,8 +25,8 @@ impl Default for ChainConf { impl FromEnv for ChainConf { fn from_env(network: &str) -> Option { println!("Getting chainconf info"); - let rpc_style = std::env::var(&format!("RPCS_{}_RPCSTYLE", network)).ok()?; - let rpc_url = std::env::var(&format!("RPCS_{}_CONNECTION_URL", network)).ok()?; + let rpc_style = std::env::var(&format!("{}_RPCSTYLE", network)).ok()?; + let rpc_url = std::env::var(&format!("{}_CONNECTION_URL", network)).ok()?; println!("GOT chainconf info"); let json = json!({ @@ -51,11 +51,11 @@ pub enum TxSubmitterConf { impl FromEnv for TxSubmitterConf { fn from_env(network: &str) -> Option { - let rpc_style = std::env::var(&format!("TXSUBMITTERS_{}_RPCSTYLE", network)).ok()?; + let rpc_style = std::env::var(&format!("{}_RPCSTYLE", network)).ok()?; match rpc_style.as_ref() { "ethereum" => Some(Self::Ethereum(ethereum::TxSubmitterConf::from_env( - &format!("TXSUBMITTERS_{}", network), + network, )?)), _ => panic!("Unknown transaction submission rpc style: {}", rpc_style), } diff --git a/fixtures/env.test b/fixtures/env.test index 47bb931d..cb86e3e1 100644 --- a/fixtures/env.test +++ b/fixtures/env.test @@ -4,11 +4,11 @@ RUN_ENV=test AGENT_HOME_NAME=ethereum AGENT_REPLICAS_ALL=true -RPCS_ETHEREUM_RPCSTYLE=ethereum -RPCS_ETHEREUM_CONNECTION_URL=https://main-light.eth.linkpool.io/ +ETHEREUM_RPCSTYLE=ethereum +MOONBEAM_RPCSTYLE=ethereum -RPCS_MOONBEAM_RPCSTYLE=ethereum -RPCS_MOONBEAM_CONNECTION_URL=https://rpc.api.moonbeam.network +ETHEREUM_CONNECTION_URL=https://main-light.eth.linkpool.io/ +MOONBEAM_CONNECTION_URL=https://rpc.api.moonbeam.network RPCS_EVMOS_RPCSTYLE=ethereum RPCS_EVMOS_CONNECTION_URL=https://eth.bd.evmos.org:8545 @@ -29,6 +29,10 @@ TXSUBMITTERS_MOONBEAM_SUBMITTERTYPE=gelato TXSUBMITTERS_MOONBEAM_SUBMITTER_SPONSOR_KEY=0x1111111111111111111111111111111111111111111111111111111111111111 TXSUBMITTERS_MOONBEAM_SUBMITTER_FEETOKEN=0xabcd +MOONBEAM_SUBMITTERTYPE=gelato +MOONBEAM_SUBMITTER_SPONSOR_KEY=0x1111111111111111111111111111111111111111111111111111111111111111 +MOONBEAM_SUBMITTER_FEETOKEN=0xabcd + TRANSACTIONSIGNERS_EVMOS_KEY=0x1111111111111111111111111111111111111111111111111111111111111112 ATTESTATION_SIGNER_ID=dummy_id From 77eced7fe07beb1a7b6a12e90eda88bf0e6a3658 Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Tue, 3 May 2022 23:06:33 -0700 Subject: [PATCH 25/41] chore(nits): remove printlns and fix comments --- chains/nomad-ethereum/src/gelato.rs | 9 +- chains/nomad-ethereum/src/macros.rs | 4 +- configuration/src/builtin.rs | 112 +++++++++--------- .../src/chains/ethereum/submitter/mod.rs | 5 - configuration/src/lib.rs | 64 +++++----- configuration/src/secrets.rs | 6 +- 6 files changed, 95 insertions(+), 105 deletions(-) diff --git a/chains/nomad-ethereum/src/gelato.rs b/chains/nomad-ethereum/src/gelato.rs index 536da937..9c2b4997 100644 --- a/chains/nomad-ethereum/src/gelato.rs +++ b/chains/nomad-ethereum/src/gelato.rs @@ -6,14 +6,15 @@ use std::sync::Arc; /* { chainId: number; - target: string; ** contract address? + target: string; data: BytesLike; feeToken: string; - paymentType: number; ** some kind of enum for gas tank vs. legacy? - maxFee: string; ** just call get_estimated_fee? + paymentType: number; + maxFee: string; sponsor: string; sponsorChainId: number; - nonce: number; ** does this enforce ordering too? + nonce: number; + enforceSponsorNonce: boolean; sponsorSignature: BytesLike; } */ diff --git a/chains/nomad-ethereum/src/macros.rs b/chains/nomad-ethereum/src/macros.rs index 9b935ffa..9b6f1dfb 100644 --- a/chains/nomad-ethereum/src/macros.rs +++ b/chains/nomad-ethereum/src/macros.rs @@ -216,8 +216,6 @@ macro_rules! boxed_contract { }}; (@submitter $base_provider:expr, $submitter_conf:ident, $($tail:tt)*) => {{ if let Some(conf) = $submitter_conf { - // If there's a provided signer, we want to manage every aspect - // locally let submitter = match conf { nomad_xyz_configuration::ethereum::TxSubmitterConf::Local(signer_conf) => { tx_submitter_local!($base_provider, signer_conf) @@ -229,7 +227,7 @@ macro_rules! boxed_contract { boxed_contract!(@timelag $base_provider, submitter, $($tail)*) } else { - panic!("Not supporting contracts with tx submitter"); + panic!("Not supporting contracts with tx submitter"); // TODO: allow readonly contracts? } }}; (@ws $url:expr, $($tail:tt)*) => {{ diff --git a/configuration/src/builtin.rs b/configuration/src/builtin.rs index e2776b3a..46d84224 100644 --- a/configuration/src/builtin.rs +++ b/configuration/src/builtin.rs @@ -48,59 +48,59 @@ pub fn get_builtin(name: &str) -> Option<&NomadConfig> { })) } -// #[cfg(test)] -// mod tests { -// use super::*; - -// #[test] -// fn test_loads_builtins() { -// dbg!(get_builtin("test")); -// } - -// #[test] -// fn test_validates() { -// dbg!(get_builtin("test") -// .expect("config not found") -// .validate() -// .expect("invalid config")); -// } - -// #[test] -// fn development_loads_builtins() { -// dbg!(get_builtin("development")); -// } - -// #[test] -// fn development_validates() { -// dbg!(get_builtin("development") -// .expect("config not found") -// .validate() -// .expect("invalid config")); -// } - -// #[test] -// fn staging_loads_builtins() { -// dbg!(get_builtin("staging")); -// } - -// #[test] -// fn staging_validates() { -// dbg!(get_builtin("staging") -// .expect("config not found") -// .validate() -// .expect("invalid config")); -// } - -// #[test] -// fn production_loads_builtins() { -// dbg!(get_builtin("production")); -// } - -// #[test] -// fn production_validates() { -// dbg!(get_builtin("production") -// .expect("config not found") -// .validate() -// .expect("invalid config")); -// } -// } +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_loads_builtins() { + dbg!(get_builtin("test")); + } + + #[test] + fn test_validates() { + dbg!(get_builtin("test") + .expect("config not found") + .validate() + .expect("invalid config")); + } + + #[test] + fn development_loads_builtins() { + dbg!(get_builtin("development")); + } + + #[test] + fn development_validates() { + dbg!(get_builtin("development") + .expect("config not found") + .validate() + .expect("invalid config")); + } + + #[test] + fn staging_loads_builtins() { + dbg!(get_builtin("staging")); + } + + #[test] + fn staging_validates() { + dbg!(get_builtin("staging") + .expect("config not found") + .validate() + .expect("invalid config")); + } + + #[test] + fn production_loads_builtins() { + dbg!(get_builtin("production")); + } + + #[test] + fn production_validates() { + dbg!(get_builtin("production") + .expect("config not found") + .validate() + .expect("invalid config")); + } +} diff --git a/configuration/src/chains/ethereum/submitter/mod.rs b/configuration/src/chains/ethereum/submitter/mod.rs index 7c17f947..99b9f117 100644 --- a/configuration/src/chains/ethereum/submitter/mod.rs +++ b/configuration/src/chains/ethereum/submitter/mod.rs @@ -24,20 +24,15 @@ impl From for TxSubmitterConf { impl FromEnv for TxSubmitterConf { fn from_env(prefix: &str) -> Option { - println!("Getting submitter type"); let submitter_type = std::env::var(&format!("{}_SUBMITTERTYPE", prefix)).ok()?; - println!("Got submitter type"); - println!("Getting submitter: {}", prefix); match submitter_type.as_ref() { "local" => { let signer_conf = SignerConf::from_env(&format!("{}_SUBMITTER", prefix))?; - println!("Got submitter"); Some(Self::Local(signer_conf)) } "gelato" => { let gelato_conf = GelatoConf::from_env(&format!("{}_SUBMITTER", prefix))?; - println!("Got submitter"); Some(Self::Gelato(gelato_conf)) } _ => panic!("Unknown tx submission type: {}", submitter_type), diff --git a/configuration/src/lib.rs b/configuration/src/lib.rs index ff661ff3..18f2cb94 100644 --- a/configuration/src/lib.rs +++ b/configuration/src/lib.rs @@ -352,35 +352,35 @@ impl NomadConfig { } } -// #[cfg(test)] -// mod tests { -// use std::path::PathBuf; - -// use super::*; - -// #[test] -// fn it_loads_the_sample_config() { -// let path: PathBuf = env!("CARGO_MANIFEST_DIR") -// .parse::() -// .unwrap() -// .join("configs/test.json"); - -// let _config: NomadConfig = -// serde_json::from_reader(std::fs::File::open(path).unwrap()).unwrap(); -// dbg!(&_config); -// } - -// #[test] -// fn it_allows_default_config() { -// dbg!(NomadConfig::default()); -// } - -// #[test] -// fn it_does_the_yaml() { -// let yaml = crate::builtin::get_builtin("test") -// .unwrap() -// .to_yaml() -// .unwrap(); -// println!("{}", yaml); -// } -// } +#[cfg(test)] +mod tests { + use std::path::PathBuf; + + use super::*; + + #[test] + fn it_loads_the_sample_config() { + let path: PathBuf = env!("CARGO_MANIFEST_DIR") + .parse::() + .unwrap() + .join("configs/test.json"); + + let _config: NomadConfig = + serde_json::from_reader(std::fs::File::open(path).unwrap()).unwrap(); + dbg!(&_config); + } + + #[test] + fn it_allows_default_config() { + dbg!(NomadConfig::default()); + } + + #[test] + fn it_does_the_yaml() { + let yaml = crate::builtin::get_builtin("test") + .unwrap() + .to_yaml() + .unwrap(); + println!("{}", yaml); + } +} diff --git a/configuration/src/secrets.rs b/configuration/src/secrets.rs index f277e174..35969799 100644 --- a/configuration/src/secrets.rs +++ b/configuration/src/secrets.rs @@ -82,7 +82,7 @@ impl AgentSecrets { let submitter_conf = self .tx_submitters .get(network) - .unwrap_or_else(|| panic!("no signerconf for {}", network)); + .unwrap_or_else(|| panic!("no submitter conf for {}", network)); match submitter_conf { TxSubmitterConf::Ethereum(conf) => match conf { ethereum::TxSubmitterConf::Local(signer_conf) => { @@ -101,7 +101,6 @@ impl AgentSecrets { impl FromEnv for AgentSecrets { fn from_env(_prefix: &str) -> Option { - println!("START"); let env = std::env::var("RUN_ENV").ok()?; let home = std::env::var("AGENT_HOME").ok()?; @@ -120,13 +119,10 @@ impl FromEnv for AgentSecrets { let mut secrets = AgentSecrets::default(); - println!("Getting per network conf and submitters"); for network in networks.iter() { let network_upper = network.to_uppercase(); - println!("Getting chainconf: {}", network_upper); let chain_conf = ChainConf::from_env(&network_upper)?; - println!("Getting tx submitter conf"); let tx_submitter = TxSubmitterConf::from_env(&network_upper)?; secrets.rpcs.insert(network.to_owned(), chain_conf); From b5ba03ca9e709104a906acff73228e0340c3a385 Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Tue, 3 May 2022 23:23:36 -0700 Subject: [PATCH 26/41] prog(gelato): add utils stub for sponsor signing --- Cargo.lock | 2 ++ chains/nomad-ethereum/Cargo.toml | 2 ++ .../src/{gelato.rs => gelato/mod.rs} | 3 ++ chains/nomad-ethereum/src/gelato/sponsor.rs | 31 +++++++++++++++++++ 4 files changed, 38 insertions(+) rename chains/nomad-ethereum/src/{gelato.rs => gelato/mod.rs} (98%) create mode 100644 chains/nomad-ethereum/src/gelato/sponsor.rs diff --git a/Cargo.lock b/Cargo.lock index 2aed8200..51e51daf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2528,6 +2528,7 @@ dependencies = [ "futures-util", "gelato-relay", "hex", + "lazy_static", "nomad-core", "nomad-types", "nomad-xyz-configuration", @@ -2537,6 +2538,7 @@ dependencies = [ "rocksdb", "serde 1.0.136", "serde_json", + "sha3 0.9.1", "thiserror", "tokio", "tracing", diff --git a/chains/nomad-ethereum/Cargo.toml b/chains/nomad-ethereum/Cargo.toml index 8016fa52..da77c54b 100644 --- a/chains/nomad-ethereum/Cargo.toml +++ b/chains/nomad-ethereum/Cargo.toml @@ -29,6 +29,8 @@ tracing-futures = "0.2.5" url = "2.2.2" thiserror = "1.0.30" reqwest = { version = "0.11.10", features = ["json"]} +lazy_static = "1.4.0" +sha3 = "0.9.1" nomad-xyz-configuration = { path = "../../configuration" } nomad-types = { path = "../../nomad-types" } diff --git a/chains/nomad-ethereum/src/gelato.rs b/chains/nomad-ethereum/src/gelato/mod.rs similarity index 98% rename from chains/nomad-ethereum/src/gelato.rs rename to chains/nomad-ethereum/src/gelato/mod.rs index 9c2b4997..a00a3d12 100644 --- a/chains/nomad-ethereum/src/gelato.rs +++ b/chains/nomad-ethereum/src/gelato/mod.rs @@ -3,6 +3,9 @@ use gelato_relay::{GelatoClient, RelayResponse, TaskState}; use nomad_core::Signers; use std::sync::Arc; +mod sponsor; +pub use sponsor::*; + /* { chainId: number; diff --git a/chains/nomad-ethereum/src/gelato/sponsor.rs b/chains/nomad-ethereum/src/gelato/sponsor.rs new file mode 100644 index 00000000..002ad995 --- /dev/null +++ b/chains/nomad-ethereum/src/gelato/sponsor.rs @@ -0,0 +1,31 @@ +use ethers::prelude::*; +use lazy_static::lazy_static; +use sha3::{Digest, Keccak256}; + +pub(crate) const EIP712_DOMAIN_TYPE: &str = + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"; + +lazy_static! { + pub(crate) static ref FORWARD_REQUEST_TYPEHASH: H256 = H256::from_slice( + Keccak256::new() + .chain("ForwardRequest(uint256 chainId,address target,bytes data,address feeToken,uint256 paymentType,uint256 maxFee,address sponsor,uint256 sponsorChainId,uint256 nonce,bool enforceSponsorNonce)".as_bytes()) + .finalize() + .as_slice(), + ); +} + +pub(crate) fn get_domain_separator(address: Address, chain_id: u64) -> H256 { + H256::from_slice( + Keccak256::new() + .chain(EIP712_DOMAIN_TYPE.as_bytes()) + .chain("GelatoRelayForwarder".as_bytes()) + .chain("V1".as_bytes()) + .chain(chain_id.to_be_bytes()) + .chain(address) + .finalize() + .as_slice(), + ) +} + +// TODO: implement +// pub(crate) fn abi_encode_forward_request(...) -> H256 {} From 9136f00f56f67caa1c6ad4660de2b51282bb7aab Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Wed, 4 May 2022 11:36:15 -0700 Subject: [PATCH 27/41] feat(gelato): tentative sponsor signing logic --- chains/nomad-ethereum/src/gelato/sponsor.rs | 70 ++++++++++++++++----- gelato-relay/src/types.rs | 34 ++++++++++ 2 files changed, 88 insertions(+), 16 deletions(-) diff --git a/chains/nomad-ethereum/src/gelato/sponsor.rs b/chains/nomad-ethereum/src/gelato/sponsor.rs index 002ad995..39a08510 100644 --- a/chains/nomad-ethereum/src/gelato/sponsor.rs +++ b/chains/nomad-ethereum/src/gelato/sponsor.rs @@ -1,31 +1,69 @@ +use color_eyre::Result; +use ethers::abi::Token; use ethers::prelude::*; +use ethers::utils::{hash_message, keccak256}; +use gelato_relay::ForwardRequest; use lazy_static::lazy_static; +use nomad_core::SignerExt; +use nomad_core::Signers; use sha3::{Digest, Keccak256}; +use std::str::FromStr; pub(crate) const EIP712_DOMAIN_TYPE: &str = "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"; lazy_static! { - pub(crate) static ref FORWARD_REQUEST_TYPEHASH: H256 = H256::from_slice( - Keccak256::new() - .chain("ForwardRequest(uint256 chainId,address target,bytes data,address feeToken,uint256 paymentType,uint256 maxFee,address sponsor,uint256 sponsorChainId,uint256 nonce,bool enforceSponsorNonce)".as_bytes()) - .finalize() - .as_slice(), + pub(crate) static ref FORWARD_REQUEST_TYPEHASH: [u8; 32] = keccak256( + &keccak256("ForwardRequest(uint256 chainId,address target,bytes data,address feeToken,uint256 paymentType,uint256 maxFee,address sponsor,uint256 sponsorChainId,uint256 nonce,bool enforceSponsorNonce)") ); } -pub(crate) fn get_domain_separator(address: Address, chain_id: u64) -> H256 { - H256::from_slice( +pub(crate) fn get_domain_separator(address: Address, chain_id: U256) -> H256 { + let domain_separator = abi::encode(&[ + Token::FixedBytes(keccak256(EIP712_DOMAIN_TYPE).to_vec()), + Token::FixedBytes(keccak256("GelatoRelayForwarder").to_vec()), + Token::FixedBytes(keccak256("V1").to_vec()), + Token::FixedBytes(format!("{:x}", chain_id).into_bytes()), + Token::Address(address), + ]); + + H256::from_slice(&keccak256(domain_separator)) +} + +pub(crate) fn forward_request_hash(request: ForwardRequest) -> H256 { + let encoded_request = abi::encode(&[ + Token::FixedBytes((*FORWARD_REQUEST_TYPEHASH).to_vec()), + Token::Uint(U256::from(request.chain_id)), + Token::Address(Address::from_str(&request.target).expect("!target")), + Token::FixedBytes(keccak256(&request.data).to_vec()), + Token::Address(Address::from_str(&request.fee_token).expect("!feetoken")), + Token::Uint(U256::from(request.payment_type)), + Token::Uint(U256::from_str(&request.max_fee).expect("!maxfee")), + Token::Address(Address::from_str(&request.sponsor).expect("!sponsor")), + Token::Uint(U256::from(request.sponsor_chain_id)), + Token::Uint(U256::from(request.nonce)), + Token::Bool(request.enforce_sponsor_nonce), + ]); + + H256::from_slice(&encoded_request) +} + +pub(crate) async fn sponsor_sign( + sponsor: &S, + domain_separator: H256, + request_hash: H256, +) -> Result, S::Error> { + let digest = H256::from_slice( Keccak256::new() - .chain(EIP712_DOMAIN_TYPE.as_bytes()) - .chain("GelatoRelayForwarder".as_bytes()) - .chain("V1".as_bytes()) - .chain(chain_id.to_be_bytes()) - .chain(address) + .chain("\x19\x01") + .chain(domain_separator) + .chain(request_hash) .finalize() .as_slice(), - ) -} + ); -// TODO: implement -// pub(crate) fn abi_encode_forward_request(...) -> H256 {} + sponsor + .sign_message_without_eip_155(digest) + .await + .map(|s| s.to_vec()) +} diff --git a/gelato-relay/src/types.rs b/gelato-relay/src/types.rs index 4b0d9733..2df1f5d4 100644 --- a/gelato-relay/src/types.rs +++ b/gelato-relay/src/types.rs @@ -1,5 +1,39 @@ use serde::{Deserialize, Serialize}; +/* +{ + typeId: string (pass "ForwardRequest"), + chainId: number; + target: string; + data: BytesLike; + feeToken: string; + paymentType: number (pass 1); + maxFee: string (will be available from our fee oracle endpoint); + sponsor: string; + sponsorChainId: number (same as chainId); + nonce: number (can just pass 0 if next field is false); + enforceSponsorNonce: boolean (pass false in case you don't want to track nonces); + sponsorSignature: BytesLike (EIP-712 signature of struct ForwardRequest{chainId, target, ..., nonce, enforceSponsorNonce}) + } +*/ + +/// Request for forwarding tx to gas-tank based relay service +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct ForwardRequest { + pub chain_id: usize, + pub target: String, + pub data: String, + pub fee_token: String, + pub payment_type: usize, // 1 = gas tank + pub max_fee: String, + pub sponsor: String, + pub sponsor_chain_id: usize, // same as chain_id + pub nonce: usize, // can default 0 if next field false + pub enforce_sponsor_nonce: bool, // default false given replay safe + pub sponsor_signature: Vec, +} + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct RelayRequest { From f065d7d2baf0fe4745bbce0fbebabd76762fad35 Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Wed, 4 May 2022 13:49:13 -0700 Subject: [PATCH 28/41] prog(gelato): more work on gelato forward request --- chains/nomad-ethereum/src/gelato/mod.rs | 69 ++++++++++++--------- chains/nomad-ethereum/src/gelato/sponsor.rs | 38 ++++++++---- chains/nomad-ethereum/src/macros.rs | 1 + chains/nomad-ethereum/src/submitter.rs | 2 +- gelato-relay/src/types.rs | 2 +- 5 files changed, 66 insertions(+), 46 deletions(-) diff --git a/chains/nomad-ethereum/src/gelato/mod.rs b/chains/nomad-ethereum/src/gelato/mod.rs index a00a3d12..162d5e10 100644 --- a/chains/nomad-ethereum/src/gelato/mod.rs +++ b/chains/nomad-ethereum/src/gelato/mod.rs @@ -1,27 +1,14 @@ use ethers::providers::Middleware; -use gelato_relay::{GelatoClient, RelayResponse, TaskState}; -use nomad_core::Signers; +use ethers::types::Address; +use ethers_signers::Signer; +use gelato_relay::{ForwardRequest, GelatoClient, RelayResponse, TaskState}; +use nomad_core::{ChainCommunicationError, Signers}; use std::sync::Arc; +/// Sponsor data encoding/signing mod sponsor; pub use sponsor::*; -/* -{ - chainId: number; - target: string; - data: BytesLike; - feeToken: string; - paymentType: number; - maxFee: string; - sponsor: string; - sponsorChainId: number; - nonce: number; - enforceSponsorNonce: boolean; - sponsorSignature: BytesLike; -} - */ - pub(crate) const ACCEPTABLE_STATES: [TaskState; 4] = [ TaskState::CheckPending, TaskState::ExecPending, @@ -38,6 +25,8 @@ pub struct SingleChainGelatoClient { pub eth_client: Arc, /// Sponsor signer pub sponsor: Signers, + /// Gelato relay forwarder address + pub forwarder: Address, /// Chain id pub chain_id: usize, /// Fee token @@ -59,6 +48,7 @@ where pub fn with_default_url( eth_client: Arc, sponsor: Signers, + forwarder: Address, chain_id: usize, fee_token: String, is_high_priority: bool, @@ -67,6 +57,7 @@ where gelato: GelatoClient::default(), eth_client, sponsor, + forwarder, chain_id, fee_token, is_high_priority, @@ -74,24 +65,40 @@ where } /// Send relay transaction - pub async fn send_relay_transaction( + pub async fn send_forward_request( &self, - dest: &str, + target: &str, data: &str, gas_limit: usize, - ) -> Result { - let relayer_fee = self + ) -> Result { + let max_fee = self .gelato() - .get_estimated_fee( - self.chain_id, - &self.fee_token, - gas_limit, - self.is_high_priority, - ) - .await?; + .get_estimated_fee(self.chain_id, &self.fee_token, gas_limit, false) + .await + .map_err(|e| ChainCommunicationError::CustomError(e.into()))?; - self.gelato() - .send_relay_transaction(self.chain_id, dest, data, &self.fee_token, relayer_fee) + let mut request = ForwardRequest { + chain_id: self.chain_id, + target: target.to_owned(), + data: data.to_owned(), + fee_token: self.fee_token.to_owned(), + payment_type: 1, // gas tank + max_fee: max_fee.to_string(), + sponsor: format!("{:x}", self.sponsor.address()), + sponsor_chain_id: self.chain_id, + nonce: 0, // default, not needed + enforce_sponsor_nonce: false, // replay safety builtin to contracts + sponsor_signature: None, // not yet signed + }; + + let sponsor_signature = sponsor_sign_request(&self.sponsor, self.forwarder, &request) .await + .map_err(|e| ChainCommunicationError::CustomError(e.into()))?; + + request.sponsor_signature = Some(sponsor_signature); + + Ok(RelayResponse { + task_id: "id".to_owned(), + }) // TODO: replace with call to endpoint } } diff --git a/chains/nomad-ethereum/src/gelato/sponsor.rs b/chains/nomad-ethereum/src/gelato/sponsor.rs index 39a08510..c63c34c2 100644 --- a/chains/nomad-ethereum/src/gelato/sponsor.rs +++ b/chains/nomad-ethereum/src/gelato/sponsor.rs @@ -1,24 +1,25 @@ use color_eyre::Result; use ethers::abi::Token; use ethers::prelude::*; -use ethers::utils::{hash_message, keccak256}; +use ethers::utils::keccak256; use gelato_relay::ForwardRequest; use lazy_static::lazy_static; use nomad_core::SignerExt; -use nomad_core::Signers; use sha3::{Digest, Keccak256}; use std::str::FromStr; -pub(crate) const EIP712_DOMAIN_TYPE: &str = +const EIP712_DOMAIN_TYPE: &str = "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"; lazy_static! { - pub(crate) static ref FORWARD_REQUEST_TYPEHASH: [u8; 32] = keccak256( + /// Typehash of ForwardRequest signature + pub static ref FORWARD_REQUEST_TYPEHASH: [u8; 32] = keccak256( &keccak256("ForwardRequest(uint256 chainId,address target,bytes data,address feeToken,uint256 paymentType,uint256 maxFee,address sponsor,uint256 sponsorChainId,uint256 nonce,bool enforceSponsorNonce)") ); } -pub(crate) fn get_domain_separator(address: Address, chain_id: U256) -> H256 { +/// Get domain separator for GelatoRelayerForwarder address and chain id +pub fn get_domain_separator(address: Address, chain_id: U256) -> H256 { let domain_separator = abi::encode(&[ Token::FixedBytes(keccak256(EIP712_DOMAIN_TYPE).to_vec()), Token::FixedBytes(keccak256("GelatoRelayForwarder").to_vec()), @@ -30,29 +31,40 @@ pub(crate) fn get_domain_separator(address: Address, chain_id: U256) -> H256 { H256::from_slice(&keccak256(domain_separator)) } -pub(crate) fn forward_request_hash(request: ForwardRequest) -> H256 { +/// Get hash of abi encoded ForwardRequest +pub fn get_forward_request_hash(request: &ForwardRequest) -> H256 { let encoded_request = abi::encode(&[ Token::FixedBytes((*FORWARD_REQUEST_TYPEHASH).to_vec()), - Token::Uint(U256::from(request.chain_id)), + Token::Uint(request.chain_id.into()), Token::Address(Address::from_str(&request.target).expect("!target")), Token::FixedBytes(keccak256(&request.data).to_vec()), Token::Address(Address::from_str(&request.fee_token).expect("!feetoken")), - Token::Uint(U256::from(request.payment_type)), + Token::Uint(request.payment_type.into()), Token::Uint(U256::from_str(&request.max_fee).expect("!maxfee")), Token::Address(Address::from_str(&request.sponsor).expect("!sponsor")), - Token::Uint(U256::from(request.sponsor_chain_id)), - Token::Uint(U256::from(request.nonce)), + Token::Uint(request.sponsor_chain_id.into()), + Token::Uint(request.nonce.into()), Token::Bool(request.enforce_sponsor_nonce), ]); H256::from_slice(&encoded_request) } -pub(crate) async fn sponsor_sign( +/// Sign request that will be given to GelatoRelayForwarder on given chain +pub async fn sponsor_sign_request( sponsor: &S, - domain_separator: H256, - request_hash: H256, + forwarder: Address, + request: &ForwardRequest, ) -> Result, S::Error> { + assert!( + request.sponsor_signature.is_none(), + "Tried to sign already signed forward request: {:?}", + request + ); + + let domain_separator = get_domain_separator(forwarder, request.chain_id.into()); + let request_hash = get_forward_request_hash(request); + let digest = H256::from_slice( Keccak256::new() .chain("\x19\x01") diff --git a/chains/nomad-ethereum/src/macros.rs b/chains/nomad-ethereum/src/macros.rs index 9b6f1dfb..1519b7d6 100644 --- a/chains/nomad-ethereum/src/macros.rs +++ b/chains/nomad-ethereum/src/macros.rs @@ -197,6 +197,7 @@ macro_rules! tx_submitter_gelato { let client = SingleChainGelatoClient::with_default_url( signing_provider, sponsor, + H160::zero(), // TODO: take in gelato address in config chain_id, $gelato_conf.fee_token, false, diff --git a/chains/nomad-ethereum/src/submitter.rs b/chains/nomad-ethereum/src/submitter.rs index 741b20ef..b7224c4f 100644 --- a/chains/nomad-ethereum/src/submitter.rs +++ b/chains/nomad-ethereum/src/submitter.rs @@ -100,7 +100,7 @@ where ); let RelayResponse { task_id } = client - .send_relay_transaction(&address, &data, gas_limit) + .send_forward_request(&address, &data, gas_limit) .await .map_err(|e| ChainCommunicationError::TxSubmissionError(e.into()))?; info!(task_id = ?task_id, "Submitted tx to Gelato relay. Polling task for completion..."); diff --git a/gelato-relay/src/types.rs b/gelato-relay/src/types.rs index 2df1f5d4..7b6bc3b2 100644 --- a/gelato-relay/src/types.rs +++ b/gelato-relay/src/types.rs @@ -31,7 +31,7 @@ pub struct ForwardRequest { pub sponsor_chain_id: usize, // same as chain_id pub nonce: usize, // can default 0 if next field false pub enforce_sponsor_nonce: bool, // default false given replay safe - pub sponsor_signature: Vec, + pub sponsor_signature: Option>, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] From b0c33b0790bf0c78dde8ad6dcc352fa2dfa1dd05 Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Fri, 6 May 2022 23:40:09 -0700 Subject: [PATCH 29/41] fix(gelato): fix domain separator calculation --- chains/nomad-ethereum/src/gelato/sponsor.rs | 27 +++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/chains/nomad-ethereum/src/gelato/sponsor.rs b/chains/nomad-ethereum/src/gelato/sponsor.rs index c63c34c2..18b97745 100644 --- a/chains/nomad-ethereum/src/gelato/sponsor.rs +++ b/chains/nomad-ethereum/src/gelato/sponsor.rs @@ -1,5 +1,5 @@ use color_eyre::Result; -use ethers::abi::Token; +use ethers::abi::{ethereum_types::BigEndianHash, Token}; use ethers::prelude::*; use ethers::utils::keccak256; use gelato_relay::ForwardRequest; @@ -24,7 +24,7 @@ pub fn get_domain_separator(address: Address, chain_id: U256) -> H256 { Token::FixedBytes(keccak256(EIP712_DOMAIN_TYPE).to_vec()), Token::FixedBytes(keccak256("GelatoRelayForwarder").to_vec()), Token::FixedBytes(keccak256("V1").to_vec()), - Token::FixedBytes(format!("{:x}", chain_id).into_bytes()), + Token::FixedBytes(H256::from_uint(&chain_id).as_bytes().to_vec()), Token::Address(address), ]); @@ -79,3 +79,26 @@ pub async fn sponsor_sign_request( .await .map(|s| s.to_vec()) } + +#[cfg(test)] +mod test { + use super::*; + use ethers::prelude::U256; + + const KOVAN_GELATO_RELAY_FORWARDER: &str = "0xC176f63f3827afE6789FD737f4679B299F97d108"; + const KOVAN_CHAIN_ID: u64 = 42; + + #[test] + fn it_computes_domain_separator() { + let domain_separator = get_domain_separator( + H160::from_str(KOVAN_GELATO_RELAY_FORWARDER).unwrap(), + U256::from(KOVAN_CHAIN_ID), + ); + dbg!("{:?}", domain_separator); + + assert_eq!( + format!("{:#x}", domain_separator), + "0x80d0833d2a99df6a94d491cee0d9b3b5586c41d9b01edaf54538f65d01474c94" + ); + } +} From 8b0ddf7e531ce2c57067319df9e8a4d1149ad17e Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Sat, 7 May 2022 00:02:25 -0700 Subject: [PATCH 30/41] feat(gelato): send forward request lib function --- chains/nomad-ethereum/src/gelato/mod.rs | 21 +++++----- chains/nomad-ethereum/src/gelato/sponsor.rs | 44 +++++++++++++++++---- gelato-relay/src/lib.rs | 17 ++++++++ gelato-relay/src/types.rs | 2 +- 4 files changed, 65 insertions(+), 19 deletions(-) diff --git a/chains/nomad-ethereum/src/gelato/mod.rs b/chains/nomad-ethereum/src/gelato/mod.rs index 162d5e10..55cc7d60 100644 --- a/chains/nomad-ethereum/src/gelato/mod.rs +++ b/chains/nomad-ethereum/src/gelato/mod.rs @@ -1,7 +1,7 @@ use ethers::providers::Middleware; use ethers::types::Address; use ethers_signers::Signer; -use gelato_relay::{ForwardRequest, GelatoClient, RelayResponse, TaskState}; +use gelato_relay::{GelatoClient, RelayResponse, TaskState}; use nomad_core::{ChainCommunicationError, Signers}; use std::sync::Arc; @@ -77,7 +77,7 @@ where .await .map_err(|e| ChainCommunicationError::CustomError(e.into()))?; - let mut request = ForwardRequest { + let unfilled_request = UnfilledFowardRequest { chain_id: self.chain_id, target: target.to_owned(), data: data.to_owned(), @@ -88,17 +88,18 @@ where sponsor_chain_id: self.chain_id, nonce: 0, // default, not needed enforce_sponsor_nonce: false, // replay safety builtin to contracts - sponsor_signature: None, // not yet signed }; - let sponsor_signature = sponsor_sign_request(&self.sponsor, self.forwarder, &request) - .await - .map_err(|e| ChainCommunicationError::CustomError(e.into()))?; + let sponsor_signature = + sponsor_sign_request(&self.sponsor, self.forwarder, &unfilled_request) + .await + .map_err(|e| ChainCommunicationError::CustomError(e.into()))?; - request.sponsor_signature = Some(sponsor_signature); + let filled_request = unfilled_request.to_filled(sponsor_signature); - Ok(RelayResponse { - task_id: "id".to_owned(), - }) // TODO: replace with call to endpoint + self.gelato() + .send_forward_request(&filled_request) + .await + .map_err(|e| ChainCommunicationError::TxSubmissionError(e.into())) } } diff --git a/chains/nomad-ethereum/src/gelato/sponsor.rs b/chains/nomad-ethereum/src/gelato/sponsor.rs index 18b97745..070f986c 100644 --- a/chains/nomad-ethereum/src/gelato/sponsor.rs +++ b/chains/nomad-ethereum/src/gelato/sponsor.rs @@ -8,6 +8,40 @@ use nomad_core::SignerExt; use sha3::{Digest, Keccak256}; use std::str::FromStr; +#[allow(missing_docs)] +#[derive(Debug, Clone)] +pub struct UnfilledFowardRequest { + pub chain_id: usize, + pub target: String, + pub data: String, + pub fee_token: String, + pub payment_type: usize, // 1 = gas tank + pub max_fee: String, + pub sponsor: String, + pub sponsor_chain_id: usize, // same as chain_id + pub nonce: usize, // can default 0 if next field false + pub enforce_sponsor_nonce: bool, // default false given replay safe +} + +impl UnfilledFowardRequest { + /// Fill ForwardRequest with sponsor signature and return full request struct + pub fn to_filled(self, sponsor_signature: Vec) -> ForwardRequest { + ForwardRequest { + chain_id: self.chain_id, + target: self.target, + data: self.data, + fee_token: self.fee_token, + payment_type: self.payment_type, + max_fee: self.max_fee, + sponsor: self.sponsor, + sponsor_chain_id: self.sponsor_chain_id, + nonce: self.nonce, + enforce_sponsor_nonce: self.enforce_sponsor_nonce, + sponsor_signature, + } + } +} + const EIP712_DOMAIN_TYPE: &str = "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"; @@ -32,7 +66,7 @@ pub fn get_domain_separator(address: Address, chain_id: U256) -> H256 { } /// Get hash of abi encoded ForwardRequest -pub fn get_forward_request_hash(request: &ForwardRequest) -> H256 { +pub fn get_forward_request_hash(request: &UnfilledFowardRequest) -> H256 { let encoded_request = abi::encode(&[ Token::FixedBytes((*FORWARD_REQUEST_TYPEHASH).to_vec()), Token::Uint(request.chain_id.into()), @@ -54,14 +88,8 @@ pub fn get_forward_request_hash(request: &ForwardRequest) -> H256 { pub async fn sponsor_sign_request( sponsor: &S, forwarder: Address, - request: &ForwardRequest, + request: &UnfilledFowardRequest, ) -> Result, S::Error> { - assert!( - request.sponsor_signature.is_none(), - "Tried to sign already signed forward request: {:?}", - request - ); - let domain_separator = get_domain_separator(forwarder, request.chain_id.into()); let request_hash = get_forward_request_hash(request); diff --git a/gelato-relay/src/lib.rs b/gelato-relay/src/lib.rs index d01b8baa..d30bb284 100644 --- a/gelato-relay/src/lib.rs +++ b/gelato-relay/src/lib.rs @@ -47,6 +47,23 @@ impl GelatoClient { res.json().await } + pub async fn send_forward_request( + &self, + params: &ForwardRequest, + ) -> Result { + let url = format!( + "https://gateway.api.gelato.digital/metabox-relays/{}", + params.chain_id + ); + let res = reqwest::Client::new() + .post(url) + .json(¶ms) + .send() + .await?; + + res.json().await + } + pub async fn is_chain_supported(&self, chain_id: usize) -> Result { let supported_chains = self.get_gelato_relay_chains().await?; Ok(supported_chains.contains(&chain_id.to_string())) diff --git a/gelato-relay/src/types.rs b/gelato-relay/src/types.rs index 7b6bc3b2..2df1f5d4 100644 --- a/gelato-relay/src/types.rs +++ b/gelato-relay/src/types.rs @@ -31,7 +31,7 @@ pub struct ForwardRequest { pub sponsor_chain_id: usize, // same as chain_id pub nonce: usize, // can default 0 if next field false pub enforce_sponsor_nonce: bool, // default false given replay safe - pub sponsor_signature: Option>, + pub sponsor_signature: Vec, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] From fdde9598d48f15b85f8028b7eba7158cc115d0b5 Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Sat, 7 May 2022 19:44:41 -0700 Subject: [PATCH 31/41] prog(gelato): smooth out formatting after testing forward request endpoint --- Cargo.lock | 1 + chains/nomad-ethereum/src/gelato/mod.rs | 33 +++++++++++++-------- chains/nomad-ethereum/src/gelato/sponsor.rs | 10 +++++-- chains/nomad-ethereum/src/submitter.rs | 10 +++---- configuration/configs/development.json | 8 ++--- configuration/src/chains/mod.rs | 2 -- gelato-relay/Cargo.toml | 1 + gelato-relay/src/lib.rs | 1 + gelato-relay/src/types.rs | 3 +- 9 files changed, 41 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 51e51daf..b244389d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1555,6 +1555,7 @@ dependencies = [ "serde_json", "thiserror", "tokio", + "tracing", ] [[package]] diff --git a/chains/nomad-ethereum/src/gelato/mod.rs b/chains/nomad-ethereum/src/gelato/mod.rs index 55cc7d60..61660d53 100644 --- a/chains/nomad-ethereum/src/gelato/mod.rs +++ b/chains/nomad-ethereum/src/gelato/mod.rs @@ -1,5 +1,5 @@ -use ethers::providers::Middleware; use ethers::types::Address; +use ethers::{prelude::Bytes, providers::Middleware}; use ethers_signers::Signer; use gelato_relay::{GelatoClient, RelayResponse, TaskState}; use nomad_core::{ChainCommunicationError, Signers}; @@ -9,6 +9,8 @@ use std::sync::Arc; mod sponsor; pub use sponsor::*; +pub(crate) const FORWARD_REQUEST_TYPE_ID: &str = "ForwardRequest"; + pub(crate) const ACCEPTABLE_STATES: [TaskState; 4] = [ TaskState::CheckPending, TaskState::ExecPending, @@ -67,24 +69,31 @@ where /// Send relay transaction pub async fn send_forward_request( &self, - target: &str, - data: &str, + target: Address, + data: &Bytes, gas_limit: usize, ) -> Result { - let max_fee = self - .gelato() - .get_estimated_fee(self.chain_id, &self.fee_token, gas_limit, false) - .await - .map_err(|e| ChainCommunicationError::CustomError(e.into()))?; + // let estimated_fee = self + // .gelato() + // .get_estimated_fee(self.chain_id, &self.fee_token, gas_limit, false) + // .await + // .map_err(|e| ChainCommunicationError::CustomError(e.into()))?; + + let max_fee = "10000"; + + let target = format!("{:#x}", target); + let data = format!("{:#x}", data); + let sponsor = format!("{:#x}", self.sponsor.address()); let unfilled_request = UnfilledFowardRequest { + type_id: FORWARD_REQUEST_TYPE_ID.to_owned(), chain_id: self.chain_id, - target: target.to_owned(), - data: data.to_owned(), + target, + data, fee_token: self.fee_token.to_owned(), payment_type: 1, // gas tank max_fee: max_fee.to_string(), - sponsor: format!("{:x}", self.sponsor.address()), + sponsor, sponsor_chain_id: self.chain_id, nonce: 0, // default, not needed enforce_sponsor_nonce: false, // replay safety builtin to contracts @@ -95,7 +104,7 @@ where .await .map_err(|e| ChainCommunicationError::CustomError(e.into()))?; - let filled_request = unfilled_request.to_filled(sponsor_signature); + let filled_request = unfilled_request.into_filled(sponsor_signature); self.gelato() .send_forward_request(&filled_request) diff --git a/chains/nomad-ethereum/src/gelato/sponsor.rs b/chains/nomad-ethereum/src/gelato/sponsor.rs index 070f986c..e1a7994c 100644 --- a/chains/nomad-ethereum/src/gelato/sponsor.rs +++ b/chains/nomad-ethereum/src/gelato/sponsor.rs @@ -11,6 +11,7 @@ use std::str::FromStr; #[allow(missing_docs)] #[derive(Debug, Clone)] pub struct UnfilledFowardRequest { + pub type_id: String, pub chain_id: usize, pub target: String, pub data: String, @@ -25,8 +26,11 @@ pub struct UnfilledFowardRequest { impl UnfilledFowardRequest { /// Fill ForwardRequest with sponsor signature and return full request struct - pub fn to_filled(self, sponsor_signature: Vec) -> ForwardRequest { + pub fn into_filled(self, sponsor_signature: Vec) -> ForwardRequest { + let hex_sig = format!("0x{}", hex::encode(sponsor_signature)); + ForwardRequest { + type_id: self.type_id, chain_id: self.chain_id, target: self.target, data: self.data, @@ -37,7 +41,7 @@ impl UnfilledFowardRequest { sponsor_chain_id: self.sponsor_chain_id, nonce: self.nonce, enforce_sponsor_nonce: self.enforce_sponsor_nonce, - sponsor_signature, + sponsor_signature: hex_sig, } } } @@ -81,7 +85,7 @@ pub fn get_forward_request_hash(request: &UnfilledFowardRequest) -> H256 { Token::Bool(request.enforce_sponsor_nonce), ]); - H256::from_slice(&encoded_request) + H256::from_slice(&keccak256(encoded_request)) } /// Sign request that will be given to GelatoRelayForwarder on given chain diff --git a/chains/nomad-ethereum/src/submitter.rs b/chains/nomad-ethereum/src/submitter.rs index b7224c4f..c41161ba 100644 --- a/chains/nomad-ethereum/src/submitter.rs +++ b/chains/nomad-ethereum/src/submitter.rs @@ -89,20 +89,18 @@ where ) .as_usize(); - let tx_data = tx.data().expect("!tx data"); - let data = format!("{:x}", tx_data); - let address = format!("{:x}", contract_address); - info!( domain = domain, - contract_address = ?address, + contract_address = ?contract_address, "Dispatching tx to Gelato relay." ); + let data = tx.data().expect("!tx data"); let RelayResponse { task_id } = client - .send_forward_request(&address, &data, gas_limit) + .send_forward_request(contract_address, data, gas_limit) .await .map_err(|e| ChainCommunicationError::TxSubmissionError(e.into()))?; + info!(task_id = ?task_id, "Submitted tx to Gelato relay. Polling task for completion..."); loop { diff --git a/configuration/configs/development.json b/configuration/configs/development.json index 65ab789b..64b9b9d3 100644 --- a/configuration/configs/development.json +++ b/configuration/configs/development.json @@ -474,7 +474,7 @@ "agent": { "rinkeby": { "rpcStyle": "ethereum", - "db": "/usr/share/nomad", + "db": "db", "metrics": 9090, "logging": { "fmt": "json", @@ -513,7 +513,7 @@ }, "goerli": { "rpcStyle": "ethereum", - "db": "/usr/share/nomad", + "db": "db", "metrics": 9090, "logging": { "fmt": "json", @@ -557,7 +557,7 @@ }, "evmostestnet": { "rpcStyle": "ethereum", - "db": "/usr/share/nomad", + "db": "db", "metrics": 9090, "logging": { "fmt": "json", @@ -596,7 +596,7 @@ }, "kovan": { "rpcStyle": "ethereum", - "db": "/usr/share/nomad", + "db": "db", "metrics": 9090, "logging": { "fmt": "json", diff --git a/configuration/src/chains/mod.rs b/configuration/src/chains/mod.rs index 27eb18b0..e22d5ded 100644 --- a/configuration/src/chains/mod.rs +++ b/configuration/src/chains/mod.rs @@ -24,10 +24,8 @@ impl Default for ChainConf { impl FromEnv for ChainConf { fn from_env(network: &str) -> Option { - println!("Getting chainconf info"); let rpc_style = std::env::var(&format!("{}_RPCSTYLE", network)).ok()?; let rpc_url = std::env::var(&format!("{}_CONNECTION_URL", network)).ok()?; - println!("GOT chainconf info"); let json = json!({ "rpcStyle": rpc_style, diff --git a/gelato-relay/Cargo.toml b/gelato-relay/Cargo.toml index 9ef8c8f2..9659fa7c 100644 --- a/gelato-relay/Cargo.toml +++ b/gelato-relay/Cargo.toml @@ -15,6 +15,7 @@ async-trait = { version = "0.1.42", default-features = false } futures-util = "0.3.12" color-eyre = "0.5.0" reqwest = { version = "0.11.10", features = ["json"]} +tracing = "0.1.22" [[bin]] name = "test" diff --git a/gelato-relay/src/lib.rs b/gelato-relay/src/lib.rs index d30bb284..99998aad 100644 --- a/gelato-relay/src/lib.rs +++ b/gelato-relay/src/lib.rs @@ -55,6 +55,7 @@ impl GelatoClient { "https://gateway.api.gelato.digital/metabox-relays/{}", params.chain_id ); + let res = reqwest::Client::new() .post(url) .json(¶ms) diff --git a/gelato-relay/src/types.rs b/gelato-relay/src/types.rs index 2df1f5d4..a9e713fd 100644 --- a/gelato-relay/src/types.rs +++ b/gelato-relay/src/types.rs @@ -21,6 +21,7 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct ForwardRequest { + pub type_id: String, pub chain_id: usize, pub target: String, pub data: String, @@ -31,7 +32,7 @@ pub struct ForwardRequest { pub sponsor_chain_id: usize, // same as chain_id pub nonce: usize, // can default 0 if next field false pub enforce_sponsor_nonce: bool, // default false given replay safe - pub sponsor_signature: Vec, + pub sponsor_signature: String, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] From 81a7899643fe20f69e8eda22e5e5da2b37231354 Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Sun, 8 May 2022 13:00:14 -0700 Subject: [PATCH 32/41] prog(gelato): successfully computes digest but signing not eip 712 --- chains/nomad-ethereum/src/gelato/mod.rs | 11 ++- chains/nomad-ethereum/src/gelato/sponsor.rs | 100 +++++++++++--------- chains/nomad-ethereum/src/gelato/types.rs | 39 ++++++++ 3 files changed, 100 insertions(+), 50 deletions(-) create mode 100644 chains/nomad-ethereum/src/gelato/types.rs diff --git a/chains/nomad-ethereum/src/gelato/mod.rs b/chains/nomad-ethereum/src/gelato/mod.rs index 61660d53..6ea98a89 100644 --- a/chains/nomad-ethereum/src/gelato/mod.rs +++ b/chains/nomad-ethereum/src/gelato/mod.rs @@ -5,6 +5,10 @@ use gelato_relay::{GelatoClient, RelayResponse, TaskState}; use nomad_core::{ChainCommunicationError, Signers}; use std::sync::Arc; +/// EIP-712 request structure +mod types; +pub use types::*; + /// Sponsor data encoding/signing mod sponsor; pub use sponsor::*; @@ -79,20 +83,19 @@ where // .await // .map_err(|e| ChainCommunicationError::CustomError(e.into()))?; - let max_fee = "10000"; + let max_fee = 10000; let target = format!("{:#x}", target); - let data = format!("{:#x}", data); let sponsor = format!("{:#x}", self.sponsor.address()); let unfilled_request = UnfilledFowardRequest { type_id: FORWARD_REQUEST_TYPE_ID.to_owned(), chain_id: self.chain_id, target, - data, + data: data.to_string(), fee_token: self.fee_token.to_owned(), payment_type: 1, // gas tank - max_fee: max_fee.to_string(), + max_fee, sponsor, sponsor_chain_id: self.chain_id, nonce: 0, // default, not needed diff --git a/chains/nomad-ethereum/src/gelato/sponsor.rs b/chains/nomad-ethereum/src/gelato/sponsor.rs index e1a7994c..39ab580d 100644 --- a/chains/nomad-ethereum/src/gelato/sponsor.rs +++ b/chains/nomad-ethereum/src/gelato/sponsor.rs @@ -1,59 +1,19 @@ +use crate::UnfilledFowardRequest; use color_eyre::Result; use ethers::abi::{ethereum_types::BigEndianHash, Token}; use ethers::prelude::*; use ethers::utils::keccak256; -use gelato_relay::ForwardRequest; use lazy_static::lazy_static; use nomad_core::SignerExt; use sha3::{Digest, Keccak256}; use std::str::FromStr; -#[allow(missing_docs)] -#[derive(Debug, Clone)] -pub struct UnfilledFowardRequest { - pub type_id: String, - pub chain_id: usize, - pub target: String, - pub data: String, - pub fee_token: String, - pub payment_type: usize, // 1 = gas tank - pub max_fee: String, - pub sponsor: String, - pub sponsor_chain_id: usize, // same as chain_id - pub nonce: usize, // can default 0 if next field false - pub enforce_sponsor_nonce: bool, // default false given replay safe -} - -impl UnfilledFowardRequest { - /// Fill ForwardRequest with sponsor signature and return full request struct - pub fn into_filled(self, sponsor_signature: Vec) -> ForwardRequest { - let hex_sig = format!("0x{}", hex::encode(sponsor_signature)); - - ForwardRequest { - type_id: self.type_id, - chain_id: self.chain_id, - target: self.target, - data: self.data, - fee_token: self.fee_token, - payment_type: self.payment_type, - max_fee: self.max_fee, - sponsor: self.sponsor, - sponsor_chain_id: self.sponsor_chain_id, - nonce: self.nonce, - enforce_sponsor_nonce: self.enforce_sponsor_nonce, - sponsor_signature: hex_sig, - } - } -} - const EIP712_DOMAIN_TYPE: &str = "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"; lazy_static! { /// Typehash of ForwardRequest signature - pub static ref FORWARD_REQUEST_TYPEHASH: [u8; 32] = keccak256( - &keccak256("ForwardRequest(uint256 chainId,address target,bytes data,address feeToken,uint256 paymentType,uint256 maxFee,address sponsor,uint256 sponsorChainId,uint256 nonce,bool enforceSponsorNonce)") - ); + pub static ref FORWARD_REQUEST_TYPEHASH: [u8; 32] = keccak256("ForwardRequest(uint256 chainId,address target,bytes data,address feeToken,uint256 paymentType,uint256 maxFee,address sponsor,uint256 sponsorChainId,uint256 nonce,bool enforceSponsorNonce)"); } /// Get domain separator for GelatoRelayerForwarder address and chain id @@ -72,19 +32,24 @@ pub fn get_domain_separator(address: Address, chain_id: U256) -> H256 { /// Get hash of abi encoded ForwardRequest pub fn get_forward_request_hash(request: &UnfilledFowardRequest) -> H256 { let encoded_request = abi::encode(&[ - Token::FixedBytes((*FORWARD_REQUEST_TYPEHASH).to_vec()), + Token::FixedBytes(FORWARD_REQUEST_TYPEHASH.to_vec()), Token::Uint(request.chain_id.into()), Token::Address(Address::from_str(&request.target).expect("!target")), - Token::FixedBytes(keccak256(&request.data).to_vec()), + Token::FixedBytes(keccak256(hex::decode(&request.data).expect("!data")).to_vec()), Token::Address(Address::from_str(&request.fee_token).expect("!feetoken")), Token::Uint(request.payment_type.into()), - Token::Uint(U256::from_str(&request.max_fee).expect("!maxfee")), + Token::Uint(request.max_fee.into()), Token::Address(Address::from_str(&request.sponsor).expect("!sponsor")), Token::Uint(request.sponsor_chain_id.into()), Token::Uint(request.nonce.into()), Token::Bool(request.enforce_sponsor_nonce), ]); + println!( + "Encoded request: {}", + format!("0x{}", hex::encode(&encoded_request)) + ); + H256::from_slice(&keccak256(encoded_request)) } @@ -96,6 +61,13 @@ pub async fn sponsor_sign_request( ) -> Result, S::Error> { let domain_separator = get_domain_separator(forwarder, request.chain_id.into()); let request_hash = get_forward_request_hash(request); + println!("Forward request hash: {:?}", request_hash); + + // let digest = H256::from_slice(&keccak256(format!("0x{}{}{}", + // "1901", + // &format!("{:#x}", domain_separator)[2..], + // &format!("{:#x}", request_hash)[2..], + // ))); let digest = H256::from_slice( Keccak256::new() @@ -106,6 +78,8 @@ pub async fn sponsor_sign_request( .as_slice(), ); + println!("Digest: {}", format!("{:#x}", digest)); + sponsor .sign_message_without_eip_155(digest) .await @@ -115,10 +89,12 @@ pub async fn sponsor_sign_request( #[cfg(test)] mod test { use super::*; - use ethers::prelude::U256; + use ethers::signers::LocalWallet; + const DUMMY_SPONSOR: &str = "fae558d7fb0ac7970a7a472559f332c2a67d2ec283c98fd2afa58403bdfd74a5"; const KOVAN_GELATO_RELAY_FORWARDER: &str = "0xC176f63f3827afE6789FD737f4679B299F97d108"; const KOVAN_CHAIN_ID: u64 = 42; + const SPONSOR_SIGNATURE: &str = "0xc09004502ade171bc918fbb6eb4911045b7defdb78435b37de693a9f4ee80d9e2b17d45237d1012e7df27aaac6a2b51072ba1fecfa4a151d1f85fbc278e85e7f1b"; #[test] fn it_computes_domain_separator() { @@ -133,4 +109,36 @@ mod test { "0x80d0833d2a99df6a94d491cee0d9b3b5586c41d9b01edaf54538f65d01474c94" ); } + + #[tokio::test] + async fn it_computes_and_signs_digest() { + let sponsor: LocalWallet = DUMMY_SPONSOR.parse().unwrap(); + println!("Sponsor address: {:#x}", sponsor.address()); + + let request = UnfilledFowardRequest { + type_id: "ForwardRequest".to_owned(), + chain_id: 42, + target: "0x61bBe925A5D646cE074369A6335e5095Ea7abB7A".to_owned(), + data: "4b327067000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + .to_owned(), + fee_token: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE".to_owned(), + payment_type: 1, + max_fee: 10000000000000000000, + sponsor: "0xcaCE8809B0F21A2dd707CA7B3E4CB04ffcCB5A3e".to_owned(), + sponsor_chain_id: 42, + nonce: 0, + enforce_sponsor_nonce: false, + }; + + let signature = sponsor_sign_request( + &sponsor, + H160::from_str(KOVAN_GELATO_RELAY_FORWARDER).unwrap(), + &request, + ) + .await + .unwrap(); + + let hex_sig = format!("Signature: 0x{}", hex::encode(signature)); + println!("{}", hex_sig); + } } diff --git a/chains/nomad-ethereum/src/gelato/types.rs b/chains/nomad-ethereum/src/gelato/types.rs new file mode 100644 index 00000000..e48917c9 --- /dev/null +++ b/chains/nomad-ethereum/src/gelato/types.rs @@ -0,0 +1,39 @@ +use gelato_relay::ForwardRequest; + +#[allow(missing_docs)] +#[derive(Debug, Clone)] +pub struct UnfilledFowardRequest { + pub type_id: String, + pub chain_id: usize, + pub target: String, + pub data: String, + pub fee_token: String, + pub payment_type: usize, // 1 = gas tank + pub max_fee: usize, + pub sponsor: String, + pub sponsor_chain_id: usize, // same as chain_id + pub nonce: usize, // can default 0 if next field false + pub enforce_sponsor_nonce: bool, // default false given replay safe +} + +impl UnfilledFowardRequest { + /// Fill ForwardRequest with sponsor signature and return full request struct + pub fn into_filled(self, sponsor_signature: Vec) -> ForwardRequest { + let hex_sig = format!("0x{}", hex::encode(sponsor_signature)); + + ForwardRequest { + type_id: self.type_id, + chain_id: self.chain_id, + target: self.target, + data: self.data, + fee_token: self.fee_token, + payment_type: self.payment_type, + max_fee: self.max_fee.to_string(), + sponsor: self.sponsor, + sponsor_chain_id: self.sponsor_chain_id, + nonce: self.nonce, + enforce_sponsor_nonce: self.enforce_sponsor_nonce, + sponsor_signature: hex_sig, + } + } +} From daf6a292cfdec3736b5af8a017da429c7279719f Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Sun, 8 May 2022 14:46:34 -0700 Subject: [PATCH 33/41] feat(gelato): successfully signs forward request eip 712 --- chains/nomad-ethereum/src/gelato/mod.rs | 21 ++- chains/nomad-ethereum/src/gelato/sponsor.rs | 144 -------------------- chains/nomad-ethereum/src/gelato/types.rs | 117 ++++++++++++++++ 3 files changed, 126 insertions(+), 156 deletions(-) delete mode 100644 chains/nomad-ethereum/src/gelato/sponsor.rs diff --git a/chains/nomad-ethereum/src/gelato/mod.rs b/chains/nomad-ethereum/src/gelato/mod.rs index 6ea98a89..398e428b 100644 --- a/chains/nomad-ethereum/src/gelato/mod.rs +++ b/chains/nomad-ethereum/src/gelato/mod.rs @@ -1,18 +1,14 @@ +use ethers::signers::Signer; use ethers::types::Address; use ethers::{prelude::Bytes, providers::Middleware}; -use ethers_signers::Signer; use gelato_relay::{GelatoClient, RelayResponse, TaskState}; use nomad_core::{ChainCommunicationError, Signers}; use std::sync::Arc; -/// EIP-712 request structure +/// EIP-712 forward request structure mod types; pub use types::*; -/// Sponsor data encoding/signing -mod sponsor; -pub use sponsor::*; - pub(crate) const FORWARD_REQUEST_TYPE_ID: &str = "ForwardRequest"; pub(crate) const ACCEPTABLE_STATES: [TaskState; 4] = [ @@ -75,7 +71,7 @@ where &self, target: Address, data: &Bytes, - gas_limit: usize, + _gas_limit: usize, ) -> Result { // let estimated_fee = self // .gelato() @@ -102,12 +98,13 @@ where enforce_sponsor_nonce: false, // replay safety builtin to contracts }; - let sponsor_signature = - sponsor_sign_request(&self.sponsor, self.forwarder, &unfilled_request) - .await - .map_err(|e| ChainCommunicationError::CustomError(e.into()))?; + let sponsor_signature = self + .sponsor + .sign_typed_data(&unfilled_request) + .await + .unwrap(); - let filled_request = unfilled_request.into_filled(sponsor_signature); + let filled_request = unfilled_request.into_filled(sponsor_signature.to_vec()); self.gelato() .send_forward_request(&filled_request) diff --git a/chains/nomad-ethereum/src/gelato/sponsor.rs b/chains/nomad-ethereum/src/gelato/sponsor.rs deleted file mode 100644 index 39ab580d..00000000 --- a/chains/nomad-ethereum/src/gelato/sponsor.rs +++ /dev/null @@ -1,144 +0,0 @@ -use crate::UnfilledFowardRequest; -use color_eyre::Result; -use ethers::abi::{ethereum_types::BigEndianHash, Token}; -use ethers::prelude::*; -use ethers::utils::keccak256; -use lazy_static::lazy_static; -use nomad_core::SignerExt; -use sha3::{Digest, Keccak256}; -use std::str::FromStr; - -const EIP712_DOMAIN_TYPE: &str = - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"; - -lazy_static! { - /// Typehash of ForwardRequest signature - pub static ref FORWARD_REQUEST_TYPEHASH: [u8; 32] = keccak256("ForwardRequest(uint256 chainId,address target,bytes data,address feeToken,uint256 paymentType,uint256 maxFee,address sponsor,uint256 sponsorChainId,uint256 nonce,bool enforceSponsorNonce)"); -} - -/// Get domain separator for GelatoRelayerForwarder address and chain id -pub fn get_domain_separator(address: Address, chain_id: U256) -> H256 { - let domain_separator = abi::encode(&[ - Token::FixedBytes(keccak256(EIP712_DOMAIN_TYPE).to_vec()), - Token::FixedBytes(keccak256("GelatoRelayForwarder").to_vec()), - Token::FixedBytes(keccak256("V1").to_vec()), - Token::FixedBytes(H256::from_uint(&chain_id).as_bytes().to_vec()), - Token::Address(address), - ]); - - H256::from_slice(&keccak256(domain_separator)) -} - -/// Get hash of abi encoded ForwardRequest -pub fn get_forward_request_hash(request: &UnfilledFowardRequest) -> H256 { - let encoded_request = abi::encode(&[ - Token::FixedBytes(FORWARD_REQUEST_TYPEHASH.to_vec()), - Token::Uint(request.chain_id.into()), - Token::Address(Address::from_str(&request.target).expect("!target")), - Token::FixedBytes(keccak256(hex::decode(&request.data).expect("!data")).to_vec()), - Token::Address(Address::from_str(&request.fee_token).expect("!feetoken")), - Token::Uint(request.payment_type.into()), - Token::Uint(request.max_fee.into()), - Token::Address(Address::from_str(&request.sponsor).expect("!sponsor")), - Token::Uint(request.sponsor_chain_id.into()), - Token::Uint(request.nonce.into()), - Token::Bool(request.enforce_sponsor_nonce), - ]); - - println!( - "Encoded request: {}", - format!("0x{}", hex::encode(&encoded_request)) - ); - - H256::from_slice(&keccak256(encoded_request)) -} - -/// Sign request that will be given to GelatoRelayForwarder on given chain -pub async fn sponsor_sign_request( - sponsor: &S, - forwarder: Address, - request: &UnfilledFowardRequest, -) -> Result, S::Error> { - let domain_separator = get_domain_separator(forwarder, request.chain_id.into()); - let request_hash = get_forward_request_hash(request); - println!("Forward request hash: {:?}", request_hash); - - // let digest = H256::from_slice(&keccak256(format!("0x{}{}{}", - // "1901", - // &format!("{:#x}", domain_separator)[2..], - // &format!("{:#x}", request_hash)[2..], - // ))); - - let digest = H256::from_slice( - Keccak256::new() - .chain("\x19\x01") - .chain(domain_separator) - .chain(request_hash) - .finalize() - .as_slice(), - ); - - println!("Digest: {}", format!("{:#x}", digest)); - - sponsor - .sign_message_without_eip_155(digest) - .await - .map(|s| s.to_vec()) -} - -#[cfg(test)] -mod test { - use super::*; - use ethers::signers::LocalWallet; - - const DUMMY_SPONSOR: &str = "fae558d7fb0ac7970a7a472559f332c2a67d2ec283c98fd2afa58403bdfd74a5"; - const KOVAN_GELATO_RELAY_FORWARDER: &str = "0xC176f63f3827afE6789FD737f4679B299F97d108"; - const KOVAN_CHAIN_ID: u64 = 42; - const SPONSOR_SIGNATURE: &str = "0xc09004502ade171bc918fbb6eb4911045b7defdb78435b37de693a9f4ee80d9e2b17d45237d1012e7df27aaac6a2b51072ba1fecfa4a151d1f85fbc278e85e7f1b"; - - #[test] - fn it_computes_domain_separator() { - let domain_separator = get_domain_separator( - H160::from_str(KOVAN_GELATO_RELAY_FORWARDER).unwrap(), - U256::from(KOVAN_CHAIN_ID), - ); - dbg!("{:?}", domain_separator); - - assert_eq!( - format!("{:#x}", domain_separator), - "0x80d0833d2a99df6a94d491cee0d9b3b5586c41d9b01edaf54538f65d01474c94" - ); - } - - #[tokio::test] - async fn it_computes_and_signs_digest() { - let sponsor: LocalWallet = DUMMY_SPONSOR.parse().unwrap(); - println!("Sponsor address: {:#x}", sponsor.address()); - - let request = UnfilledFowardRequest { - type_id: "ForwardRequest".to_owned(), - chain_id: 42, - target: "0x61bBe925A5D646cE074369A6335e5095Ea7abB7A".to_owned(), - data: "4b327067000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" - .to_owned(), - fee_token: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE".to_owned(), - payment_type: 1, - max_fee: 10000000000000000000, - sponsor: "0xcaCE8809B0F21A2dd707CA7B3E4CB04ffcCB5A3e".to_owned(), - sponsor_chain_id: 42, - nonce: 0, - enforce_sponsor_nonce: false, - }; - - let signature = sponsor_sign_request( - &sponsor, - H160::from_str(KOVAN_GELATO_RELAY_FORWARDER).unwrap(), - &request, - ) - .await - .unwrap(); - - let hex_sig = format!("Signature: 0x{}", hex::encode(signature)); - println!("{}", hex_sig); - } -} diff --git a/chains/nomad-ethereum/src/gelato/types.rs b/chains/nomad-ethereum/src/gelato/types.rs index e48917c9..59fc2c8b 100644 --- a/chains/nomad-ethereum/src/gelato/types.rs +++ b/chains/nomad-ethereum/src/gelato/types.rs @@ -1,4 +1,11 @@ +use ethers::abi::{self, Token}; +use ethers::types::{transaction::eip712::*, Address}; +use ethers::utils::hex::FromHexError; +use ethers::utils::keccak256; use gelato_relay::ForwardRequest; +use std::str::FromStr; + +const FORWARD_REQUEST_TYPE: &str = "ForwardRequest(uint256 chainId,address target,bytes data,address feeToken,uint256 paymentType,uint256 maxFee,address sponsor,uint256 sponsorChainId,uint256 nonce,bool enforceSponsorNonce)"; #[allow(missing_docs)] #[derive(Debug, Clone)] @@ -16,6 +23,51 @@ pub struct UnfilledFowardRequest { pub enforce_sponsor_nonce: bool, // default false given replay safe } +/// ForwardRequest error +#[derive(Debug, thiserror::Error, Clone, Copy)] +pub enum ForwardRequestError { + /// Hex decoding error + #[error("Hex decoding error: {0}")] + FromHexError(#[from] FromHexError), +} + +impl Eip712 for UnfilledFowardRequest { + type Error = ForwardRequestError; + + fn domain(&self) -> Result { + Ok(EIP712Domain { + name: "GelatoRelayForwarder".to_owned(), + version: "V1".to_owned(), + chain_id: self.chain_id.into(), + verifying_contract: Address::from_str("0xC176f63f3827afE6789FD737f4679B299F97d108") + .expect("!verifying contract"), // TODO: fetch from Gelato API + salt: None, + }) + } + + fn type_hash() -> Result<[u8; 32], Self::Error> { + Ok(keccak256(FORWARD_REQUEST_TYPE)) + } + + fn struct_hash(&self) -> Result<[u8; 32], Self::Error> { + let encoded_request = abi::encode(&[ + Token::FixedBytes(Self::type_hash()?.to_vec()), + Token::Uint(self.chain_id.into()), + Token::Address(Address::from_str(&self.target).expect("!target")), + Token::FixedBytes(keccak256(hex::decode(&self.data)?).to_vec()), + Token::Address(Address::from_str(&self.fee_token).expect("!fee token")), + Token::Uint(self.payment_type.into()), + Token::Uint(self.max_fee.into()), + Token::Address(Address::from_str(&self.sponsor).expect("!sponsor")), + Token::Uint(self.sponsor_chain_id.into()), + Token::Uint(self.nonce.into()), + Token::Bool(self.enforce_sponsor_nonce), + ]); + + Ok(keccak256(encoded_request)) + } +} + impl UnfilledFowardRequest { /// Fill ForwardRequest with sponsor signature and return full request struct pub fn into_filled(self, sponsor_signature: Vec) -> ForwardRequest { @@ -37,3 +89,68 @@ impl UnfilledFowardRequest { } } } + +#[cfg(test)] +mod test { + use crate::UnfilledFowardRequest; + use ethers::signers::LocalWallet; + use ethers::signers::Signer; + use ethers::types::transaction::eip712::Eip712; + use lazy_static::lazy_static; + + const DUMMY_SPONSOR_KEY: &str = + "fae558d7fb0ac7970a7a472559f332c2a67d2ec283c98fd2afa58403bdfd74a5"; + const SPONSOR_SIGNATURE: &str = "0xc09004502ade171bc918fbb6eb4911045b7defdb78435b37de693a9f4ee80d9e2b17d45237d1012e7df27aaac6a2b51072ba1fecfa4a151d1f85fbc278e85e7f1b"; + + lazy_static! { + pub static ref REQUEST: UnfilledFowardRequest = UnfilledFowardRequest { + type_id: "ForwardRequest".to_owned(), + chain_id: 42, + target: "0x61bBe925A5D646cE074369A6335e5095Ea7abB7A".to_owned(), + data: "4b327067000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + .to_owned(), + fee_token: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE".to_owned(), + payment_type: 1, + max_fee: 10000000000000000000, + sponsor: "0xcaCE8809B0F21A2dd707CA7B3E4CB04ffcCB5A3e".to_owned(), + sponsor_chain_id: 42, + nonce: 0, + enforce_sponsor_nonce: false, + }; + } + + #[test] + fn it_computes_domain_separator() { + let domain_separator = REQUEST.domain_separator().unwrap(); + + assert_eq!( + format!("0x{}", hex::encode(domain_separator)), + "0x80d0833d2a99df6a94d491cee0d9b3b5586c41d9b01edaf54538f65d01474c94" + ); + } + + #[tokio::test] + async fn it_computes_and_signs_digest() { + let sponsor: LocalWallet = DUMMY_SPONSOR_KEY.parse().unwrap(); + + let request = UnfilledFowardRequest { + type_id: "ForwardRequest".to_owned(), + chain_id: 42, + target: "0x61bBe925A5D646cE074369A6335e5095Ea7abB7A".to_owned(), + data: "4b327067000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + .to_owned(), + fee_token: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE".to_owned(), + payment_type: 1, + max_fee: 10000000000000000000, + sponsor: "0xcaCE8809B0F21A2dd707CA7B3E4CB04ffcCB5A3e".to_owned(), + sponsor_chain_id: 42, + nonce: 0, + enforce_sponsor_nonce: false, + }; + + let signature = sponsor.sign_typed_data(&request).await.unwrap().to_vec(); + + let hex_sig = format!("0x{}", hex::encode(signature)); + assert_eq!(SPONSOR_SIGNATURE, hex_sig); + } +} From 600de9ab3d46de65ddb600ca32521588cc089c46 Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Sun, 8 May 2022 16:01:37 -0700 Subject: [PATCH 34/41] prog(gelato): properly handles hex formatting for request.data, minor fixes to TaskStatus response struct --- chains/nomad-ethereum/src/gelato/mod.rs | 3 ++- chains/nomad-ethereum/src/gelato/types.rs | 3 ++- gelato-relay/bin/test.rs | 2 +- gelato-relay/src/lib.rs | 5 ++++- gelato-relay/src/types.rs | 3 ++- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/chains/nomad-ethereum/src/gelato/mod.rs b/chains/nomad-ethereum/src/gelato/mod.rs index 398e428b..d96198fb 100644 --- a/chains/nomad-ethereum/src/gelato/mod.rs +++ b/chains/nomad-ethereum/src/gelato/mod.rs @@ -83,12 +83,13 @@ where let target = format!("{:#x}", target); let sponsor = format!("{:#x}", self.sponsor.address()); + let data = data.to_string().strip_prefix("0x").unwrap().to_owned(); let unfilled_request = UnfilledFowardRequest { type_id: FORWARD_REQUEST_TYPE_ID.to_owned(), chain_id: self.chain_id, target, - data: data.to_string(), + data, fee_token: self.fee_token.to_owned(), payment_type: 1, // gas tank max_fee, diff --git a/chains/nomad-ethereum/src/gelato/types.rs b/chains/nomad-ethereum/src/gelato/types.rs index 59fc2c8b..7dd023c1 100644 --- a/chains/nomad-ethereum/src/gelato/types.rs +++ b/chains/nomad-ethereum/src/gelato/types.rs @@ -72,12 +72,13 @@ impl UnfilledFowardRequest { /// Fill ForwardRequest with sponsor signature and return full request struct pub fn into_filled(self, sponsor_signature: Vec) -> ForwardRequest { let hex_sig = format!("0x{}", hex::encode(sponsor_signature)); + let hex_data = format!("0x{}", self.data); ForwardRequest { type_id: self.type_id, chain_id: self.chain_id, target: self.target, - data: self.data, + data: hex_data, fee_token: self.fee_token, payment_type: self.payment_type, max_fee: self.max_fee.to_string(), diff --git a/gelato-relay/bin/test.rs b/gelato-relay/bin/test.rs index 6f60d84e..a6e2f9fb 100644 --- a/gelato-relay/bin/test.rs +++ b/gelato-relay/bin/test.rs @@ -8,7 +8,7 @@ async fn main() -> Result<(), reqwest::Error> { println!("Relay chains: {:?}", chains); let task_status = gelato - .get_task_status("0xeefc20b15402c30ead95d572034532c7097488726a6582d3d6674971e9d97879") + .get_task_status("0x1a976f2bed20b154cb02a8c039705e34d4f5971a0f7b82ae1cdfd80bc1636d8f") .await?; println!("Task status: {:?}", task_status); diff --git a/gelato-relay/src/lib.rs b/gelato-relay/src/lib.rs index 99998aad..84a15566 100644 --- a/gelato-relay/src/lib.rs +++ b/gelato-relay/src/lib.rs @@ -108,7 +108,10 @@ impl GelatoClient { &self, task_id: &str, ) -> Result, reqwest::Error> { - let url = format!("{}/tasks/{}", &self.url, task_id); + let url = format!( + "https://gateway.api.gelato.digital/tasks/GelatoMetaBox/{}", + task_id + ); let res = reqwest::get(url).await?; let task_status: TaskStatusResponse = res.json().await?; Ok(task_status.data.first().cloned()) diff --git a/gelato-relay/src/types.rs b/gelato-relay/src/types.rs index a9e713fd..b6f46f2c 100644 --- a/gelato-relay/src/types.rs +++ b/gelato-relay/src/types.rs @@ -85,7 +85,7 @@ pub struct TaskStatus { pub task_state: TaskState, #[serde(rename = "created_at")] pub created_at: String, // date - pub last_check: Option, + pub last_check: Option, pub execution: Option, pub last_execution: String, // date } @@ -96,6 +96,7 @@ pub struct Execution { pub status: String, pub transaction_hash: String, pub block_number: usize, + #[serde(rename = "created_at")] pub created_at: String, } From 2281e0aac8cff607987fbffa14a9dc189360dee2 Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Mon, 9 May 2022 07:37:33 -0700 Subject: [PATCH 35/41] fix(gelato): deserialize check or date --- gelato-relay/src/types.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/gelato-relay/src/types.rs b/gelato-relay/src/types.rs index b6f46f2c..4a98b058 100644 --- a/gelato-relay/src/types.rs +++ b/gelato-relay/src/types.rs @@ -85,7 +85,7 @@ pub struct TaskStatus { pub task_state: TaskState, #[serde(rename = "created_at")] pub created_at: String, // date - pub last_check: Option, + pub last_check: Option, pub execution: Option, pub last_execution: String, // date } @@ -100,6 +100,13 @@ pub struct Execution { pub created_at: String, } +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[serde(untagged, rename_all = "camelCase")] +pub enum CheckOrDate { + Check(Check), + Date(String), +} + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Check { From 2ebb1e498f4f5802baf8bb9d07a9901f45abab76 Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Mon, 9 May 2022 12:33:19 -0700 Subject: [PATCH 36/41] refactor(tx-submitter): move gelato complexity to gelato module --- chains/nomad-ethereum/src/gelato/mod.rs | 119 +++++++++++++++++++++--- chains/nomad-ethereum/src/submitter.rs | 68 +------------- gelato-relay/src/lib.rs | 5 +- 3 files changed, 113 insertions(+), 79 deletions(-) diff --git a/chains/nomad-ethereum/src/gelato/mod.rs b/chains/nomad-ethereum/src/gelato/mod.rs index d96198fb..afb88cc0 100644 --- a/chains/nomad-ethereum/src/gelato/mod.rs +++ b/chains/nomad-ethereum/src/gelato/mod.rs @@ -1,9 +1,13 @@ use ethers::signers::Signer; -use ethers::types::Address; +use ethers::types::transaction::eip2718::TypedTransaction; +use ethers::types::{Address, H256}; use ethers::{prelude::Bytes, providers::Middleware}; use gelato_relay::{GelatoClient, RelayResponse, TaskState}; -use nomad_core::{ChainCommunicationError, Signers}; -use std::sync::Arc; +use nomad_core::{ChainCommunicationError, Signers, TxOutcome}; +use std::{str::FromStr, sync::Arc}; +use tokio::task::JoinHandle; +use tokio::time::{sleep, Duration}; +use tracing::{debug, info}; /// EIP-712 forward request structure mod types; @@ -22,7 +26,7 @@ pub(crate) const ACCEPTABLE_STATES: [TaskState; 4] = [ #[derive(Debug, Clone)] pub struct SingleChainGelatoClient { /// Gelato client - pub gelato: GelatoClient, + pub gelato: Arc, /// Ethers client (for estimating gas) pub eth_client: Arc, /// Sponsor signer @@ -42,8 +46,8 @@ where M: Middleware + 'static, { /// Get reference to base client - pub fn gelato(&self) -> &GelatoClient { - &self.gelato + pub fn gelato(&self) -> Arc { + self.gelato.clone() } /// Instantiate single chain client with default Gelato url @@ -56,7 +60,7 @@ where is_high_priority: bool, ) -> Self { Self { - gelato: GelatoClient::default(), + gelato: GelatoClient::default().into(), eth_client, sponsor, forwarder, @@ -66,20 +70,105 @@ where } } + /// Submit a transaction to Gelato and poll until completion or failure. + pub async fn submit_blocking( + &self, + domain: u32, + contract_address: Address, + tx: &TypedTransaction, + ) -> Result { + let RelayResponse { task_id } = self.dispatch_tx(domain, contract_address, tx).await?; + + info!(task_id = ?&task_id, "Submitted tx to Gelato relay. Polling task for completion..."); + Self::poll_task_id(task_id, self.gelato()) + .await + .map_err(|e| ChainCommunicationError::TxSubmissionError(e.into()))? + } + + /// Dispatch tx to Gelato and return task id. + pub async fn dispatch_tx( + &self, + domain: u32, + contract_address: Address, + tx: &TypedTransaction, + ) -> Result { + // If gas limit not hardcoded in tx, eth_estimateGas + let gas_limit = tx + .gas() + .unwrap_or( + &self + .eth_client + .estimate_gas(tx) + .await + .map_err(|e| ChainCommunicationError::MiddlewareError(e.into()))?, + ) + .as_usize(); + let data = tx.data().expect("!tx data"); + + info!( + domain = domain, + contract_address = ?contract_address, + "Dispatching tx to Gelato relay." + ); + + self.send_forward_request(contract_address, data, gas_limit) + .await + .map_err(|e| ChainCommunicationError::TxSubmissionError(e.into())) + } + + /// Poll task id and return tx hash of transaction if successful, error if + /// otherwise. + pub fn poll_task_id( + task_id: String, + gelato: Arc, + ) -> JoinHandle> { + tokio::spawn(async move { + loop { + let status = gelato + .get_task_status(&task_id) + .await + .map_err(|e| ChainCommunicationError::TxSubmissionError(e.into()))? + .expect("!task status"); + + if !ACCEPTABLE_STATES.contains(&status.task_state) { + return Err(ChainCommunicationError::TxSubmissionError( + format!("Gelato task failed: {:?}", status).into(), + )); + } + + if let Some(execution) = &status.execution { + info!( + chain = ?status.chain, + task_id = ?status.task_id, + execution = ?execution, + "Gelato relay executed tx." + ); + + let tx_hash = &execution.transaction_hash; + let txid = H256::from_str(tx_hash) + .unwrap_or_else(|_| panic!("Malformed tx hash from Gelato")); + + return Ok(TxOutcome { txid }); + } + + debug!(task_id = ?task_id, "Polling Gelato task."); + sleep(Duration::from_millis(500)).await; + } + }) + } + /// Send relay transaction pub async fn send_forward_request( &self, target: Address, data: &Bytes, - _gas_limit: usize, + gas_limit: usize, ) -> Result { - // let estimated_fee = self - // .gelato() - // .get_estimated_fee(self.chain_id, &self.fee_token, gas_limit, false) - // .await - // .map_err(|e| ChainCommunicationError::CustomError(e.into()))?; - - let max_fee = 10000; + let max_fee = self + .gelato() + .get_estimated_fee(self.chain_id, &self.fee_token, gas_limit + 100_000, false) + .await + .map_err(|e| ChainCommunicationError::CustomError(e.into()))?; // add 100k gas padding for Gelato contract ops let target = format!("{:#x}", target); let sponsor = format!("{:#x}", self.sponsor.address()); diff --git a/chains/nomad-ethereum/src/submitter.rs b/chains/nomad-ethereum/src/submitter.rs index c41161ba..0f57514a 100644 --- a/chains/nomad-ethereum/src/submitter.rs +++ b/chains/nomad-ethereum/src/submitter.rs @@ -1,12 +1,10 @@ -use crate::{SingleChainGelatoClient, ACCEPTABLE_STATES}; +use crate::SingleChainGelatoClient; use color_eyre::Result; use ethers::prelude::*; use ethers::types::transaction::eip2718::TypedTransaction; -use gelato_relay::RelayResponse; use nomad_core::{ChainCommunicationError, TxOutcome}; -use std::{str::FromStr, sync::Arc}; -use tokio::time::{sleep, Duration}; -use tracing::{debug, info}; +use std::sync::Arc; +use tracing::info; /// Component responsible for submitting transactions to the chain. Can /// sign/submit locally or use a transaction relay service. @@ -61,6 +59,7 @@ where .send_transaction(tx, None) .await .map_err(|e| ChainCommunicationError::TxSubmissionError(e.into()))?; + let tx_hash: ethers::core::types::H256 = *dispatched; info!("dispatched transaction with tx_hash {:?}", tx_hash); @@ -77,64 +76,7 @@ where Ok(outcome) } SubmitterClient::Gelato(client) => { - // If gas limit not hardcoded in tx, eth_estimateGas - let gas_limit = tx - .gas() - .unwrap_or( - &client - .eth_client - .estimate_gas(&tx) - .await - .map_err(|e| ChainCommunicationError::MiddlewareError(e.into()))?, - ) - .as_usize(); - - info!( - domain = domain, - contract_address = ?contract_address, - "Dispatching tx to Gelato relay." - ); - - let data = tx.data().expect("!tx data"); - let RelayResponse { task_id } = client - .send_forward_request(contract_address, data, gas_limit) - .await - .map_err(|e| ChainCommunicationError::TxSubmissionError(e.into()))?; - - info!(task_id = ?task_id, "Submitted tx to Gelato relay. Polling task for completion..."); - - loop { - let status = client - .gelato() - .get_task_status(&task_id) - .await - .map_err(|e| ChainCommunicationError::TxSubmissionError(e.into()))? - .expect("!task status"); - - if !ACCEPTABLE_STATES.contains(&status.task_state) { - return Err(ChainCommunicationError::TxSubmissionError( - format!("Gelato task failed: {:?}", status).into(), - )); - } - - if let Some(execution) = &status.execution { - info!( - chain = ?status.chain, - task_id = ?status.task_id, - execution = ?execution, - "Gelato relay executed tx." - ); - - let tx_hash = &execution.transaction_hash; - let txid = H256::from_str(tx_hash) - .unwrap_or_else(|_| panic!("Malformed tx hash from Gelato")); - - return Ok(TxOutcome { txid }); - } - - debug!(task_id = ?task_id, "Polling Gelato task."); - sleep(Duration::from_millis(500)).await; - } + client.submit_blocking(domain, contract_address, &tx).await } } } diff --git a/gelato-relay/src/lib.rs b/gelato-relay/src/lib.rs index 84a15566..9db123ea 100644 --- a/gelato-relay/src/lib.rs +++ b/gelato-relay/src/lib.rs @@ -92,7 +92,10 @@ impl GelatoClient { ("isHighPriority", is_high_priority), ]); - let base_url = format!("{}/oracles/{}/estimate", &self.url, chain_id); + let base_url = format!( + "https://gateway.api.gelato.digital/oracles/{}/estimate", + chain_id + ); let url = reqwest::Url::parse_with_params(&base_url, params).expect("!url"); let res = reqwest::get(url).await?; From f481235f1a389176d991121e4ed4337ffb8f2907 Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Wed, 11 May 2022 09:46:33 -0700 Subject: [PATCH 37/41] fix(gelato): polling for gelato tx takes self --- chains/nomad-ethereum/src/gelato/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/chains/nomad-ethereum/src/gelato/mod.rs b/chains/nomad-ethereum/src/gelato/mod.rs index afb88cc0..3a9054f4 100644 --- a/chains/nomad-ethereum/src/gelato/mod.rs +++ b/chains/nomad-ethereum/src/gelato/mod.rs @@ -80,7 +80,7 @@ where let RelayResponse { task_id } = self.dispatch_tx(domain, contract_address, tx).await?; info!(task_id = ?&task_id, "Submitted tx to Gelato relay. Polling task for completion..."); - Self::poll_task_id(task_id, self.gelato()) + self.poll_task_id(task_id) .await .map_err(|e| ChainCommunicationError::TxSubmissionError(e.into()))? } @@ -119,9 +119,11 @@ where /// Poll task id and return tx hash of transaction if successful, error if /// otherwise. pub fn poll_task_id( + &self, task_id: String, - gelato: Arc, ) -> JoinHandle> { + let gelato = self.gelato(); + tokio::spawn(async move { loop { let status = gelato From 816fe51d27e9d7655343e5c30617236a91007ce2 Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Fri, 13 May 2022 15:11:17 -0700 Subject: [PATCH 38/41] fix(rebase): fix env files and secrets implementation after rebase on configuration changes --- configuration/src/secrets.rs | 62 +++++++++------------------------- fixtures/env.external | 15 ++++---- fixtures/env.partial | 15 ++++---- fixtures/env.test | 28 +++++---------- fixtures/external_secrets.json | 29 +++++++++------- fixtures/test_secrets.json | 9 +++++ 6 files changed, 69 insertions(+), 89 deletions(-) diff --git a/configuration/src/secrets.rs b/configuration/src/secrets.rs index 35969799..2fa547ae 100644 --- a/configuration/src/secrets.rs +++ b/configuration/src/secrets.rs @@ -36,14 +36,14 @@ impl AgentSecrets { for network in networks.iter() { let network_upper = network.to_uppercase(); - let chain_conf = ChainConf::from_env(&format!("RPCS_{}", network_upper))?; - let transaction_signer = - SignerConf::from_env(&format!("TRANSACTIONSIGNERS_{}", network_upper))?; + + let chain_conf = ChainConf::from_env(&network_upper)?; + let tx_submitter = TxSubmitterConf::from_env(&network_upper)?; secrets.rpcs.insert(network.to_owned(), chain_conf); secrets - .transaction_signers - .insert(network.to_owned(), transaction_signer); + .tx_submitters + .insert(network.to_owned(), tx_submitter); } let attestation_signer = SignerConf::from_env("ATTESTATION_SIGNER"); @@ -99,45 +99,6 @@ impl AgentSecrets { } } -impl FromEnv for AgentSecrets { - fn from_env(_prefix: &str) -> Option { - let env = std::env::var("RUN_ENV").ok()?; - let home = std::env::var("AGENT_HOME").ok()?; - - let config = crate::get_builtin(&env) - .expect("couldn't retrieve config!") - .to_owned(); - - let mut networks = config - .protocol() - .networks - .get(&home) - .expect("!networks") - .connections - .to_owned(); - networks.insert(home); - - let mut secrets = AgentSecrets::default(); - - for network in networks.iter() { - let network_upper = network.to_uppercase(); - let chain_conf = ChainConf::from_env(&network_upper)?; - - let tx_submitter = TxSubmitterConf::from_env(&network_upper)?; - - secrets.rpcs.insert(network.to_owned(), chain_conf); - secrets - .tx_submitters - .insert(network.to_owned(), tx_submitter); - } - - let attestation_signer = SignerConf::from_env("ATTESTATION_SIGNER"); - secrets.attestation_signer = attestation_signer; - - Some(secrets) - } -} - #[cfg(test)] mod test { use super::*; @@ -148,11 +109,20 @@ mod test { fn it_builds_from_env() { let networks = &crate::get_builtin("test").unwrap().networks; dotenv::from_filename(SECRETS_ENV_PATH).unwrap(); - AgentSecrets::from_env(networks).expect("Failed to load secrets from env"); + let secrets = AgentSecrets::from_env(networks).expect("Failed to load secrets from env"); + secrets + .validate("", networks) + .expect("Failed to validate secrets"); } #[test] fn it_builds_from_file() { - AgentSecrets::from_file(SECRETS_JSON_PATH).expect("Failed to load secrets from file"); + let networks = &crate::get_builtin("test").unwrap().networks; + dotenv::from_filename(SECRETS_ENV_PATH).unwrap(); + let secrets = + AgentSecrets::from_file(SECRETS_JSON_PATH).expect("Failed to load secrets from file"); + secrets + .validate("", networks) + .expect("Failed to validate secrets"); } } diff --git a/fixtures/env.external b/fixtures/env.external index 4d5ac8bf..eda18dda 100644 --- a/fixtures/env.external +++ b/fixtures/env.external @@ -3,15 +3,18 @@ AGENT_HOME_NAME=ethereum AGENT_REPLICA_0_NAME=polygon -RPCS_ETHEREUM_RPCSTYLE=ethereum -RPCS_ETHEREUM_CONNECTION_URL=https://main-light.eth.linkpool.io/ +ETHEREUM_RPCSTYLE=ethereum +POLYGON_RPCSTYLE=ethereum -RPCS_POLYGON_RPCSTYLE=ethereum -RPCS_POLYGON_CONNECTION_URL=https://polygon-rpc.com +ETHEREUM_CONNECTION_URL=https://main-light.eth.linkpool.io/ +POLYGON_CONNECTION_URL=https://polygon-rpc.com -TRANSACTIONSIGNERS_ETHEREUM_KEY=0x1111111111111111111111111111111111111111111111111111111111111111 +ETHEREUM_SUBMITTERTYPE=local +ETHEREUM_SUBMITTER_KEY=0x1111111111111111111111111111111111111111111111111111111111111111 -TRANSACTIONSIGNERS_POLYGON_KEY=0x1111111111111111111111111111111111111111111111111111111111111112 +POLYGON_SUBMITTERTYPE=gelato +POLYGON_SUBMITTER_SPONSOR_KEY=0x1111111111111111111111111111111111111111111111111111111111111111 +POLYGON_SUBMITTER_FEETOKEN=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE ATTESTATION_SIGNER_ID=dummy_id ATTESTATION_SIGNER_REGION=dummy_region \ No newline at end of file diff --git a/fixtures/env.partial b/fixtures/env.partial index 8aaa437f..e7b647b3 100644 --- a/fixtures/env.partial +++ b/fixtures/env.partial @@ -4,15 +4,18 @@ RUN_ENV=test AGENT_HOME_NAME=ethereum AGENT_REPLICA_0_NAME=moonbeam -RPCS_ETHEREUM_RPCSTYLE=ethereum -RPCS_ETHEREUM_CONNECTION_URL=https://main-light.eth.linkpool.io/ +ETHEREUM_RPCSTYLE=ethereum +MOONBEAM_RPCSTYLE=ethereum -RPCS_MOONBEAM_RPCSTYLE=ethereum -RPCS_MOONBEAM_CONNECTION_URL=https://rpc.api.moonbeam.network +ETHEREUM_CONNECTION_URL=https://main-light.eth.linkpool.io/ +MOONBEAM_CONNECTION_URL=https://rpc.api.moonbeam.network -TRANSACTIONSIGNERS_ETHEREUM_KEY=0x1111111111111111111111111111111111111111111111111111111111111111 +ETHEREUM_SUBMITTERTYPE=local +ETHEREUM_SUBMITTER_KEY=0x1111111111111111111111111111111111111111111111111111111111111111 -TRANSACTIONSIGNERS_MOONBEAM_KEY=0x1111111111111111111111111111111111111111111111111111111111111112 +MOONBEAM_SUBMITTERTYPE=gelato +MOONBEAM_SUBMITTER_SPONSOR_KEY=0x1111111111111111111111111111111111111111111111111111111111111111 +MOONBEAM_SUBMITTER_FEETOKEN=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE ATTESTATION_SIGNER_ID=dummy_id ATTESTATION_SIGNER_REGION=dummy_region \ No newline at end of file diff --git a/fixtures/env.test b/fixtures/env.test index cb86e3e1..b2b7864f 100644 --- a/fixtures/env.test +++ b/fixtures/env.test @@ -1,39 +1,29 @@ # Matches configuration/configs/test.json +RUST_BACKTRACE=1 + RUN_ENV=test AGENT_HOME_NAME=ethereum AGENT_REPLICAS_ALL=true ETHEREUM_RPCSTYLE=ethereum MOONBEAM_RPCSTYLE=ethereum +EVMOS_RPCSTYLE=ethereum ETHEREUM_CONNECTION_URL=https://main-light.eth.linkpool.io/ MOONBEAM_CONNECTION_URL=https://rpc.api.moonbeam.network +EVMOS_CONNECTION_URL=https://eth.bd.evmos.org:8545 -RPCS_EVMOS_RPCSTYLE=ethereum -RPCS_EVMOS_CONNECTION_URL=https://eth.bd.evmos.org:8545 - -TRANSACTIONSIGNERS_ETHEREUM_KEY=0x1111111111111111111111111111111111111111111111111111111111111111 - -TRANSACTIONSIGNERS_MOONBEAM_KEY=0x1111111111111111111111111111111111111111111111111111111111111112 - - -TRANSACTIONSUBMITTERS_MOONBEAM_RPCSTYLE=ethereum -TRANSACTIONSUBMITTERS_MOONBEAM_SUBMITTERTYPE=gelato -TRANSACTIONSUBMITTERS_MOONBEAM_SUBMITTER_SIGNER_KEYTYPE=hexKey -TRANSACTIONSUBMITTERS_MOONBEAM_SUBMITTER_SIGNER_KEY=0x1111111111111111111111111111111111111111111111111111111111111111 -TRANSACTIONSUBMITTERS_MOONBEAM_SUBMITTER_FEETOKEN=0xabcd -TXSUBMITTERS_MOONBEAM_RPCSTYLE=ethereum -TXSUBMITTERS_MOONBEAM_SUBMITTERTYPE=gelato -TXSUBMITTERS_MOONBEAM_SUBMITTER_SPONSOR_KEY=0x1111111111111111111111111111111111111111111111111111111111111111 -TXSUBMITTERS_MOONBEAM_SUBMITTER_FEETOKEN=0xabcd +ETHEREUM_SUBMITTERTYPE=local +ETHEREUM_SUBMITTER_KEY=0x1111111111111111111111111111111111111111111111111111111111111111 MOONBEAM_SUBMITTERTYPE=gelato MOONBEAM_SUBMITTER_SPONSOR_KEY=0x1111111111111111111111111111111111111111111111111111111111111111 -MOONBEAM_SUBMITTER_FEETOKEN=0xabcd +MOONBEAM_SUBMITTER_FEETOKEN=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE -TRANSACTIONSIGNERS_EVMOS_KEY=0x1111111111111111111111111111111111111111111111111111111111111112 +EVMOS_SUBMITTERTYPE=local +EVMOS_SUBMITTER_KEY=0x1111111111111111111111111111111111111111111111111111111111111111 ATTESTATION_SIGNER_ID=dummy_id ATTESTATION_SIGNER_REGION=dummy_region diff --git a/fixtures/external_secrets.json b/fixtures/external_secrets.json index 79be6fdd..9c3a07c9 100644 --- a/fixtures/external_secrets.json +++ b/fixtures/external_secrets.json @@ -1,24 +1,29 @@ { - "__COMMENT__": "matches fixtures/external_config.json", + "__COMMENT__": "matches configuration/configs/test.json", "rpcs": { "ethereum": { "rpcStyle": "ethereum", - "connection": { - "type": "http", - "url": "https://main-light.eth.linkpool.io/" - } + "connection": "https://main-light.eth.linkpool.io/" }, "polygon": { "rpcStyle": "ethereum", - "connection": { - "type": "http", - "url": "https://polygon-rpc.com" - } + "connection": "wss://polygon-rpc.com" } }, - "transactionSigners": { - "ethereum": "0x1111111111111111111111111111111111111111111111111111111111111111", - "polygon": "0x1111111111111111111111111111111111111111111111111111111111111112" + "txSubmitters": { + "ethereum": { + "rpcStyle": "ethereum", + "submitterType": "local", + "submitter": "0x1111111111111111111111111111111111111111111111111111111111111111" + }, + "polygon": { + "rpcStyle": "ethereum", + "submitterType": "gelato", + "submitter": { + "sponsor": "0x1111111111111111111111111111111111111111111111111111111111111111", + "feeToken": "0xabc" + } + } }, "attestationSigner": { "id": "hello", diff --git a/fixtures/test_secrets.json b/fixtures/test_secrets.json index 06c5e024..f674cd22 100644 --- a/fixtures/test_secrets.json +++ b/fixtures/test_secrets.json @@ -8,6 +8,10 @@ "moonbeam": { "rpcStyle": "ethereum", "connection": "wss://rpc.api.moonbeam.network" + }, + "evmos": { + "rpcStyle": "ethereum", + "connection": "https://eth.bd.evmos.org:8545" } }, "txSubmitters": { @@ -23,6 +27,11 @@ "sponsor": "0x1111111111111111111111111111111111111111111111111111111111111111", "feeToken": "0xabc" } + }, + "evmos": { + "rpcStyle": "ethereum", + "submitterType": "local", + "submitter": "0x1111111111111111111111111111111111111111111111111111111111111111" } }, "attestationSigner": { From e2431ab6f84c403040def9ec356fd6f676ed3973 Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Fri, 13 May 2022 15:53:33 -0700 Subject: [PATCH 39/41] feat(gelato): add utils map for chain id to forwarder proxy --- chains/nomad-ethereum/src/gelato/mod.rs | 2 ++ chains/nomad-ethereum/src/gelato/types.rs | 6 ++++-- chains/nomad-ethereum/src/gelato/utils.rs | 25 +++++++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 chains/nomad-ethereum/src/gelato/utils.rs diff --git a/chains/nomad-ethereum/src/gelato/mod.rs b/chains/nomad-ethereum/src/gelato/mod.rs index 3a9054f4..5b0e888b 100644 --- a/chains/nomad-ethereum/src/gelato/mod.rs +++ b/chains/nomad-ethereum/src/gelato/mod.rs @@ -13,6 +13,8 @@ use tracing::{debug, info}; mod types; pub use types::*; +pub mod utils; + pub(crate) const FORWARD_REQUEST_TYPE_ID: &str = "ForwardRequest"; pub(crate) const ACCEPTABLE_STATES: [TaskState; 4] = [ diff --git a/chains/nomad-ethereum/src/gelato/types.rs b/chains/nomad-ethereum/src/gelato/types.rs index 7dd023c1..b550d6ac 100644 --- a/chains/nomad-ethereum/src/gelato/types.rs +++ b/chains/nomad-ethereum/src/gelato/types.rs @@ -1,3 +1,4 @@ +use super::utils::CHAIN_ID_TO_FORWARDER; use ethers::abi::{self, Token}; use ethers::types::{transaction::eip712::*, Address}; use ethers::utils::hex::FromHexError; @@ -39,8 +40,9 @@ impl Eip712 for UnfilledFowardRequest { name: "GelatoRelayForwarder".to_owned(), version: "V1".to_owned(), chain_id: self.chain_id.into(), - verifying_contract: Address::from_str("0xC176f63f3827afE6789FD737f4679B299F97d108") - .expect("!verifying contract"), // TODO: fetch from Gelato API + verifying_contract: *CHAIN_ID_TO_FORWARDER + .get(&self.chain_id) + .expect("!forwarder"), salt: None, }) } diff --git a/chains/nomad-ethereum/src/gelato/utils.rs b/chains/nomad-ethereum/src/gelato/utils.rs new file mode 100644 index 00000000..c3d1bcee --- /dev/null +++ b/chains/nomad-ethereum/src/gelato/utils.rs @@ -0,0 +1,25 @@ +use std::collections::HashMap; + +use ethers::types::Address; +use lazy_static::lazy_static; +use std::str::FromStr; + +lazy_static! { + pub static ref CHAIN_ID_TO_FORWARDER: HashMap = HashMap::from( + // Kovan + [( + 42, + Address::from_str("0xC176f63f3827afE6789FD737f4679B299F97d108").expect("!forwarder proxy"), + ), + // Goerli + ( + 5, + Address::from_str("0xDde7416baE4CcfB1f131038482D424AdD61cF378").expect("!forwarder proxy"), + ), + // Rinkeby + ( + 4, + Address::from_str("0x0343Af039E2E1c25A9691eEb654Ce0de1910C3e2").expect("!forwarder proxy"), + ), + ]); +} From 6d36e3a1316276350b0984263c9b0268a839eca8 Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Thu, 19 May 2022 11:27:43 -0700 Subject: [PATCH 40/41] fix(gelato): update forward request interface after gelato audit --- chains/nomad-ethereum/src/gelato/mod.rs | 5 ++- chains/nomad-ethereum/src/gelato/types.rs | 43 ++++++++++------------- chains/nomad-ethereum/src/gelato/utils.rs | 2 +- 3 files changed, 21 insertions(+), 29 deletions(-) diff --git a/chains/nomad-ethereum/src/gelato/mod.rs b/chains/nomad-ethereum/src/gelato/mod.rs index 5b0e888b..eebca405 100644 --- a/chains/nomad-ethereum/src/gelato/mod.rs +++ b/chains/nomad-ethereum/src/gelato/mod.rs @@ -15,8 +15,6 @@ pub use types::*; pub mod utils; -pub(crate) const FORWARD_REQUEST_TYPE_ID: &str = "ForwardRequest"; - pub(crate) const ACCEPTABLE_STATES: [TaskState; 4] = [ TaskState::CheckPending, TaskState::ExecPending, @@ -179,17 +177,18 @@ where let data = data.to_string().strip_prefix("0x").unwrap().to_owned(); let unfilled_request = UnfilledFowardRequest { - type_id: FORWARD_REQUEST_TYPE_ID.to_owned(), chain_id: self.chain_id, target, data, fee_token: self.fee_token.to_owned(), payment_type: 1, // gas tank max_fee, + gas: gas_limit, sponsor, sponsor_chain_id: self.chain_id, nonce: 0, // default, not needed enforce_sponsor_nonce: false, // replay safety builtin to contracts + enforce_sponsor_nonce_ordering: false, }; let sponsor_signature = self diff --git a/chains/nomad-ethereum/src/gelato/types.rs b/chains/nomad-ethereum/src/gelato/types.rs index b550d6ac..67c51fba 100644 --- a/chains/nomad-ethereum/src/gelato/types.rs +++ b/chains/nomad-ethereum/src/gelato/types.rs @@ -6,22 +6,23 @@ use ethers::utils::keccak256; use gelato_relay::ForwardRequest; use std::str::FromStr; -const FORWARD_REQUEST_TYPE: &str = "ForwardRequest(uint256 chainId,address target,bytes data,address feeToken,uint256 paymentType,uint256 maxFee,address sponsor,uint256 sponsorChainId,uint256 nonce,bool enforceSponsorNonce)"; +const FORWARD_REQUEST_TYPE: &str = "ForwardRequest(uint256 chainId,address target,bytes data,address feeToken,uint256 paymentType,uint256 maxFee,uint256 gas,address sponsor,uint256 sponsorChainId,uint256 nonce,bool enforceSponsorNonce,bool enforceSponsorNonceOrdering)"; #[allow(missing_docs)] #[derive(Debug, Clone)] pub struct UnfilledFowardRequest { - pub type_id: String, pub chain_id: usize, pub target: String, pub data: String, pub fee_token: String, pub payment_type: usize, // 1 = gas tank pub max_fee: usize, + pub gas: usize, pub sponsor: String, pub sponsor_chain_id: usize, // same as chain_id pub nonce: usize, // can default 0 if next field false pub enforce_sponsor_nonce: bool, // default false given replay safe + pub enforce_sponsor_nonce_ordering: bool, } /// ForwardRequest error @@ -60,10 +61,12 @@ impl Eip712 for UnfilledFowardRequest { Token::Address(Address::from_str(&self.fee_token).expect("!fee token")), Token::Uint(self.payment_type.into()), Token::Uint(self.max_fee.into()), + Token::Uint(self.gas.into()), Token::Address(Address::from_str(&self.sponsor).expect("!sponsor")), Token::Uint(self.sponsor_chain_id.into()), Token::Uint(self.nonce.into()), Token::Bool(self.enforce_sponsor_nonce), + Token::Bool(self.enforce_sponsor_nonce_ordering), ]); Ok(keccak256(encoded_request)) @@ -77,7 +80,7 @@ impl UnfilledFowardRequest { let hex_data = format!("0x{}", self.data); ForwardRequest { - type_id: self.type_id, + type_id: "ForwardRequest".to_owned(), chain_id: self.chain_id, target: self.target, data: hex_data, @@ -101,24 +104,28 @@ mod test { use ethers::types::transaction::eip712::Eip712; use lazy_static::lazy_static; + const DOMAIN_SEPARATOR: &str = + "0x1b927f522830945610cf8f0521ef8b3f69352936e1b0920968dcad9cf1e30762"; const DUMMY_SPONSOR_KEY: &str = - "fae558d7fb0ac7970a7a472559f332c2a67d2ec283c98fd2afa58403bdfd74a5"; - const SPONSOR_SIGNATURE: &str = "0xc09004502ade171bc918fbb6eb4911045b7defdb78435b37de693a9f4ee80d9e2b17d45237d1012e7df27aaac6a2b51072ba1fecfa4a151d1f85fbc278e85e7f1b"; + "9cb3a530d61728e337290409d967db069f5219279f89e5ddb5ae4af76a8da5f4"; + const DUMMY_SPONSOR_ADDRESS: &str = "0x4e4f0d95bc1a4275b748a63221796080b1aa5c10"; + const SPONSOR_SIGNATURE: &str = "0x23c272c0cba2b897de0fd8fe87d419f0f273c82ef10917520b733da889688b1c6fec89412c6f121fccbc30ce89b20a3de2f405018f1ac1249b9ff705fdb62a521b"; lazy_static! { pub static ref REQUEST: UnfilledFowardRequest = UnfilledFowardRequest { - type_id: "ForwardRequest".to_owned(), chain_id: 42, target: "0x61bBe925A5D646cE074369A6335e5095Ea7abB7A".to_owned(), - data: "4b327067000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + data: "4b327067000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeaeeeeeeeeeeeeeeeee" .to_owned(), fee_token: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE".to_owned(), payment_type: 1, max_fee: 10000000000000000000, - sponsor: "0xcaCE8809B0F21A2dd707CA7B3E4CB04ffcCB5A3e".to_owned(), + gas: 200000, + sponsor: DUMMY_SPONSOR_ADDRESS.to_owned(), sponsor_chain_id: 42, nonce: 0, enforce_sponsor_nonce: false, + enforce_sponsor_nonce_ordering: false, }; } @@ -128,30 +135,16 @@ mod test { assert_eq!( format!("0x{}", hex::encode(domain_separator)), - "0x80d0833d2a99df6a94d491cee0d9b3b5586c41d9b01edaf54538f65d01474c94" + DOMAIN_SEPARATOR, ); } #[tokio::test] async fn it_computes_and_signs_digest() { let sponsor: LocalWallet = DUMMY_SPONSOR_KEY.parse().unwrap(); + assert_eq!(DUMMY_SPONSOR_ADDRESS, format!("{:#x}", sponsor.address())); - let request = UnfilledFowardRequest { - type_id: "ForwardRequest".to_owned(), - chain_id: 42, - target: "0x61bBe925A5D646cE074369A6335e5095Ea7abB7A".to_owned(), - data: "4b327067000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" - .to_owned(), - fee_token: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE".to_owned(), - payment_type: 1, - max_fee: 10000000000000000000, - sponsor: "0xcaCE8809B0F21A2dd707CA7B3E4CB04ffcCB5A3e".to_owned(), - sponsor_chain_id: 42, - nonce: 0, - enforce_sponsor_nonce: false, - }; - - let signature = sponsor.sign_typed_data(&request).await.unwrap().to_vec(); + let signature = sponsor.sign_typed_data(&*REQUEST).await.unwrap().to_vec(); let hex_sig = format!("0x{}", hex::encode(signature)); assert_eq!(SPONSOR_SIGNATURE, hex_sig); diff --git a/chains/nomad-ethereum/src/gelato/utils.rs b/chains/nomad-ethereum/src/gelato/utils.rs index c3d1bcee..71b32f64 100644 --- a/chains/nomad-ethereum/src/gelato/utils.rs +++ b/chains/nomad-ethereum/src/gelato/utils.rs @@ -9,7 +9,7 @@ lazy_static! { // Kovan [( 42, - Address::from_str("0xC176f63f3827afE6789FD737f4679B299F97d108").expect("!forwarder proxy"), + Address::from_str("0x4F36f93F58d36DcbC1E60b9bdBE213482285C482").expect("!forwarder proxy"), ), // Goerli ( From 021938ded0c65caa960bdd4717792f7e9a829f7f Mon Sep 17 00:00:00 2001 From: Luke Tchang Date: Thu, 19 May 2022 11:53:58 -0700 Subject: [PATCH 41/41] fix(gelato-bindings): add new API fields to web reqs --- chains/nomad-ethereum/src/gelato/mod.rs | 4 +++- chains/nomad-ethereum/src/gelato/types.rs | 2 ++ gelato-relay/bin/test.rs | 2 +- gelato-relay/src/types.rs | 2 ++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/chains/nomad-ethereum/src/gelato/mod.rs b/chains/nomad-ethereum/src/gelato/mod.rs index eebca405..3e24b916 100644 --- a/chains/nomad-ethereum/src/gelato/mod.rs +++ b/chains/nomad-ethereum/src/gelato/mod.rs @@ -79,7 +79,9 @@ where ) -> Result { let RelayResponse { task_id } = self.dispatch_tx(domain, contract_address, tx).await?; - info!(task_id = ?&task_id, "Submitted tx to Gelato relay. Polling task for completion..."); + info!(task_id = ?&task_id, "Submitted tx to Gelato relay."); + + info!(task_id = ?&task_id, "Polling Gelato task..."); self.poll_task_id(task_id) .await .map_err(|e| ChainCommunicationError::TxSubmissionError(e.into()))? diff --git a/chains/nomad-ethereum/src/gelato/types.rs b/chains/nomad-ethereum/src/gelato/types.rs index 67c51fba..5c2c5546 100644 --- a/chains/nomad-ethereum/src/gelato/types.rs +++ b/chains/nomad-ethereum/src/gelato/types.rs @@ -87,10 +87,12 @@ impl UnfilledFowardRequest { fee_token: self.fee_token, payment_type: self.payment_type, max_fee: self.max_fee.to_string(), + gas: self.gas.to_string(), sponsor: self.sponsor, sponsor_chain_id: self.sponsor_chain_id, nonce: self.nonce, enforce_sponsor_nonce: self.enforce_sponsor_nonce, + enforce_sponsor_nonce_ordering: self.enforce_sponsor_nonce_ordering, sponsor_signature: hex_sig, } } diff --git a/gelato-relay/bin/test.rs b/gelato-relay/bin/test.rs index a6e2f9fb..2e3d936e 100644 --- a/gelato-relay/bin/test.rs +++ b/gelato-relay/bin/test.rs @@ -8,7 +8,7 @@ async fn main() -> Result<(), reqwest::Error> { println!("Relay chains: {:?}", chains); let task_status = gelato - .get_task_status("0x1a976f2bed20b154cb02a8c039705e34d4f5971a0f7b82ae1cdfd80bc1636d8f") + .get_task_status("0xce52ae7a6a3032848d76b161ac4c131fa995dcc67e3be5392dfb8466275d6679") .await?; println!("Task status: {:?}", task_status); diff --git a/gelato-relay/src/types.rs b/gelato-relay/src/types.rs index 4a98b058..2bde7552 100644 --- a/gelato-relay/src/types.rs +++ b/gelato-relay/src/types.rs @@ -28,10 +28,12 @@ pub struct ForwardRequest { pub fee_token: String, pub payment_type: usize, // 1 = gas tank pub max_fee: String, + pub gas: String, pub sponsor: String, pub sponsor_chain_id: usize, // same as chain_id pub nonce: usize, // can default 0 if next field false pub enforce_sponsor_nonce: bool, // default false given replay safe + pub enforce_sponsor_nonce_ordering: bool, pub sponsor_signature: String, }