Skip to content

Commit

Permalink
feat: Add estimate_gas and get_gas_price calls (#34)
Browse files Browse the repository at this point in the history
* feat: Add estimate_gas and get_gas_price calls

* Make 'to' a required argument
  • Loading branch information
zvolin committed Aug 18, 2023
1 parent 0e66e6c commit e6f6ca9
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 34 deletions.
15 changes: 15 additions & 0 deletions src/ethereum-canister/candid.did
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ type setup_request = record {
execution_rpc_url: text;
};

type estimate_gas_request = record {
from: opt address;
to: address;
gas_limit: opt u256;
gas_price: opt u256;
value: opt u256;
// Hash of the method signature and encoded parameters. See the Ethereum contract ABI specification.
data: opt blob;
};

type erc20_balance_of_request = record {
contract: address;
account: address;
Expand All @@ -24,8 +34,13 @@ type erc721_owner_of_request = record {

service : {
"setup": (setup_request) -> ();

"get_block_number": () -> (nat) query;

"get_gas_price": () -> (u256) query;

"estimate_gas": (estimate_gas_request) -> (u256);

"erc20_balance_of": (erc20_balance_of_request) -> (u256);

"erc721_owner_of": (erc721_owner_of_request) -> (address);
Expand Down
25 changes: 24 additions & 1 deletion src/ethereum-canister/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ use candid::Nat;
use ic_cdk::{init, post_upgrade, pre_upgrade, query, update};
use ic_cdk_timers::set_timer;
use interface::{
Address, Erc20BalanceOfRequest, Erc721OwnerOfRequest, Network, SetupRequest, U256,
Address, Erc20BalanceOfRequest, Erc721OwnerOfRequest, EstimateGasRequest, Network,
SetupRequest, U256,
};
use log::{debug, error};

use crate::stable_memory::{
init_stable_cell_default, load_static_string, save_static_string, StableCell,
LAST_CHECKPOINT_ID, LAST_CONSENSUS_RPC_URL_ID, LAST_EXECUTION_RPC_URL_ID, LAST_NETWORK_ID,
};
use crate::utils::IntoCallOpts;

mod erc20;
mod erc721;
Expand Down Expand Up @@ -71,6 +73,27 @@ async fn get_block_number() -> Nat {
head_block_num.into()
}

#[query]
async fn get_gas_price() -> U256 {
let helios = helios::client();

let gas_price = helios.get_gas_price().await.expect("get_gas_price failed");

gas_price.into()
}

#[update]
async fn estimate_gas(request: EstimateGasRequest) -> U256 {
let helios = helios::client();

let gas_cost_estimation = helios
.estimate_gas(&request.into_call_opts())
.await
.expect("estimate_gas failed");

gas_cost_estimation.into()
}

#[update]
async fn erc20_balance_of(request: Erc20BalanceOfRequest) -> U256 {
erc20::balance_of(request.contract.into(), request.account.into())
Expand Down
19 changes: 19 additions & 0 deletions src/ethereum-canister/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use candid::Nat;
use ethers_core::types::U256;
use helios_execution::types::CallOpts;
use interface::EstimateGasRequest;
use num_bigint::BigUint;

pub(crate) trait ToNat {
Expand All @@ -13,3 +15,20 @@ impl ToNat for U256 {
Nat(BigUint::from_bytes_le(&bytes))
}
}

pub(crate) trait IntoCallOpts {
fn into_call_opts(self) -> CallOpts;
}

impl IntoCallOpts for EstimateGasRequest {
fn into_call_opts(self) -> CallOpts {
CallOpts {
from: self.from.map(Into::into),
to: Some(self.to.into()),
gas: self.gas_limit.map(Into::into),
gas_price: self.gas_price.map(Into::into),
value: self.value.map(Into::into),
data: self.data,
}
}
}
35 changes: 35 additions & 0 deletions src/ethereum-canister/tests/canister.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use candid::Nat;
use contracts_abi::erc20::BalanceOfCall;
use ethers_core::abi::AbiEncode;
use interface::EstimateGasRequest;

mod test_canister;

Expand All @@ -12,6 +15,38 @@ fn get_block_number() {
assert!(block_num.0 > 17880732u128);
}

#[test]
fn get_gas_price() {
let canister = setup_ethereum_canister();

let gas: (Nat,) = call!(canister, "get_gas_price").unwrap();
assert_ne!(gas.0, 0u128);
}

#[test]
fn estimate_gas() {
let canister = setup_ethereum_canister();

let erc20_balance_of = BalanceOfCall {
account: "0xF977814e90dA44bFA03b6295A0616a897441aceC"
.parse()
.unwrap(),
};
let request = EstimateGasRequest {
from: None,
to: "0xdAC17F958D2ee523a2206206994597C13D831ec7" // usdt
.parse()
.unwrap(),
gas_limit: None,
gas_price: None,
value: None,
data: Some(erc20_balance_of.encode()),
};

let gas: (Nat,) = call!(canister, "estimate_gas", request).unwrap();
assert_ne!(gas.0, 0u128);
}

mod erc20 {
use interface::{Erc20BalanceOfRequest, U256};

Expand Down
43 changes: 10 additions & 33 deletions src/interface/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
use std::fmt::{self, Display};
use std::str::FromStr;

use candid::CandidType;
use serde::Deserialize;
use thiserror::Error;

mod address;
mod network;
mod u256;

pub use address::Address;
pub use network::{BadNetwork, Network};
pub use u256::{U256ConvertError, U256};

#[derive(Debug, Clone, PartialEq, Eq, CandidType, Deserialize)]
Expand All @@ -30,33 +28,12 @@ pub struct Erc721OwnerOfRequest {
pub token_id: U256,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, CandidType, Deserialize)]
pub enum Network {
Mainnet,
Goerli,
}

impl Display for Network {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Network::Mainnet => f.write_str("Mainnet"),
Network::Goerli => f.write_str("Goerli"),
}
}
}

#[derive(Debug, Error)]
#[error("Bad network")]
pub struct BadNetwork;

impl FromStr for Network {
type Err = BadNetwork;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Mainnet" | "mainnet" => Ok(Network::Mainnet),
"Goerli" | "goerli" => Ok(Network::Goerli),
_ => Err(BadNetwork),
}
}
#[derive(Debug, Clone, PartialEq, Eq, CandidType, Deserialize)]
pub struct EstimateGasRequest {
pub from: Option<Address>,
pub to: Address,
pub gas_limit: Option<U256>,
pub gas_price: Option<U256>,
pub value: Option<U256>,
pub data: Option<Vec<u8>>,
}
37 changes: 37 additions & 0 deletions src/interface/src/network.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use std::fmt::{self, Display};
use std::str::FromStr;

use candid::CandidType;
use serde::Deserialize;
use thiserror::Error;

#[derive(Debug, Clone, Copy, PartialEq, Eq, CandidType, Deserialize)]
pub enum Network {
Mainnet,
Goerli,
}

impl Display for Network {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Network::Mainnet => f.write_str("Mainnet"),
Network::Goerli => f.write_str("Goerli"),
}
}
}

#[derive(Debug, Error)]
#[error("Bad network")]
pub struct BadNetwork;

impl FromStr for Network {
type Err = BadNetwork;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Mainnet" | "mainnet" => Ok(Network::Mainnet),
"Goerli" | "goerli" => Ok(Network::Goerli),
_ => Err(BadNetwork),
}
}
}

0 comments on commit e6f6ca9

Please sign in to comment.