Skip to content

Commit

Permalink
invoke V3
Browse files Browse the repository at this point in the history
  • Loading branch information
jbcaron committed Nov 6, 2024
1 parent bcba751 commit 4aaec6e
Show file tree
Hide file tree
Showing 9 changed files with 142 additions and 29 deletions.
3 changes: 2 additions & 1 deletion src/bin/hive_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ async fn main() -> eyre::Result<()> {
std::env::set_var("STARKNET_NETWORK", STARKNET_RPC_URL);

// Prepare the relayer
let relayer_balance = starknet_provider.balance_at(args.relayer_address, BlockId::Tag(BlockTag::Latest)).await?;
let relayer_balance =
starknet_provider.balance_at_native(args.relayer_address, BlockId::Tag(BlockTag::Latest)).await?;
let relayer_balance = into_via_try_wrapper!(relayer_balance)?;

let relayer = Relayer::new(
Expand Down
54 changes: 54 additions & 0 deletions src/models/felt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,28 @@ impl TryFrom<Felt252Wrapper> for Address {
}
}

impl TryFrom<Felt252Wrapper> for u64 {
type Error = EthereumDataFormatError;

fn try_from(felt: Felt252Wrapper) -> Result<Self, Self::Error> {
match felt.to_be_digits() {
[0, 0, 0, d] => Ok(d),
_ => Err(EthereumDataFormatError::Primitive),
}
}
}

impl TryFrom<Felt252Wrapper> for u128 {
type Error = EthereumDataFormatError;

fn try_from(felt: Felt252Wrapper) -> Result<Self, Self::Error> {
match felt.to_be_digits() {
[0, 0, d1, d2] => Ok(Self::from(d1) << 64 | Self::from(d2)),
_ => Err(EthereumDataFormatError::Primitive),
}
}
}

impl From<B256> for Felt252Wrapper {
fn from(value: B256) -> Self {
Self(Felt::from_bytes_be(value.as_ref()))
Expand Down Expand Up @@ -191,4 +213,36 @@ mod tests {
// When
assert_eq!(Felt252Wrapper::from(hash).0, Felt::ZERO,);
}

#[test]
fn test_u64_try_from_felt_should_pass() {
let value = Felt::from(u64::MAX);

let value = u64::try_from(Felt252Wrapper::from(value)).unwrap();
assert_eq!(u64::MAX, value);
}

#[test]
fn test_u64_try_from_felt_should_fail() {
let value = Felt::from(u64::MAX) + Felt::ONE;

let value = u64::try_from(Felt252Wrapper::from(value));
assert!(value.is_err());
}

#[test]
fn test_u128_try_from_felt_should_pass() {
let value = Felt::from(u128::MAX);

let value = u128::try_from(Felt252Wrapper::from(value)).unwrap();
assert_eq!(u128::MAX, value);
}

#[test]
fn test_u128_try_from_felt_should_fail() {
let value = Felt::from(u128::MAX) + Felt::ONE;

let value = u128::try_from(Felt252Wrapper::from(value));
assert!(value.is_err());
}
}
2 changes: 1 addition & 1 deletion src/pool/mempool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ impl<SP: starknet::providers::Provider + Send + Sync + Clone + 'static> AccountM
// Get the balance of the address for the Pending block.
self.eth_client
.starknet_provider()
.balance_at(account_address, starknet::core::types::BlockId::Tag(BlockTag::Pending))
.balance_at_native(account_address, starknet::core::types::BlockId::Tag(BlockTag::Pending))
.await
.map_err(Into::into)
}
Expand Down
9 changes: 6 additions & 3 deletions src/providers/eth_provider/starknet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ pub mod relayer;

use cainome::rs::abigen_legacy;
use starknet::core::types::Felt;
use std::sync::LazyLock;

abigen_legacy!(ERC20, "./.kakarot/artifacts/ERC20.json");

/// Starknet native token address
pub static STARKNET_NATIVE_TOKEN: LazyLock<Felt> =
LazyLock::new(|| Felt::from_hex("0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7").unwrap());
pub static STARKNET_NATIVE_TOKEN: Felt =
Felt::from_hex_unchecked("0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d");

/// Starknet parent token address
pub static STARKNET_PARENT_TOKEN: Felt =
Felt::from_hex_unchecked("0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7");
36 changes: 29 additions & 7 deletions src/providers/eth_provider/starknet/relayer.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
use crate::{
constants::STARKNET_CHAIN_ID,
models::transaction::transaction_data_to_starknet_calldata,
models::{felt::Felt252Wrapper, transaction::transaction_data_to_starknet_calldata},
providers::eth_provider::{
database::{ethereum::EthereumTransactionStore, types::transaction::EthStarknetHashes, Database},
error::{SignatureError, TransactionError},
error::{KakarotError, SignatureError, TransactionError},
provider::EthApiResult,
starknet::kakarot_core::{starknet_address, EXECUTE_FROM_OUTSIDE},
},
};
use reth_primitives::TransactionSigned;
use starknet::{
accounts::{Account, ConnectedAccount, ExecutionEncoding, ExecutionV1, SingleOwnerAccount},
accounts::{Account, ConnectedAccount, ExecutionEncoding, ExecutionV3, SingleOwnerAccount},
core::types::{BlockTag, Felt, NonZeroFelt},
providers::Provider,
signers::{LocalWallet, SigningKey},
Expand All @@ -37,7 +37,7 @@ static RELAYER_SIGNER: LazyLock<LocalWallet> = LazyLock::new(|| {
pub struct Relayer<SP: Provider + Send + Sync> {
/// The account used to sign and broadcast the transaction
account: SingleOwnerAccount<SP, LocalWallet>,
/// The balance of the relayer
/// The balance of the relayer in STRK
balance: Felt,
/// The database used to store the relayer's transaction hashes map (Ethereum -> Starknet)
database: Option<Arc<Database>>,
Expand Down Expand Up @@ -76,7 +76,7 @@ where

// Construct the call
let call = starknet::core::types::Call { to: eoa_address, selector: *EXECUTE_FROM_OUTSIDE, calldata };
let mut execution = ExecutionV1::new(vec![call], &self.account);
let mut execution = ExecutionV3::new(vec![call], &self.account);

// Fetch the relayer nonce from the Starknet provider
let relayer_nonce = self
Expand All @@ -88,9 +88,31 @@ where

execution = execution.nonce(relayer_nonce);

// We set the max fee to the balance of the account / 5. This means that the account could
// Fetch the current gas price from the Starknet provider
// TODO: fetch the gas price in background and cache it
let fri_gas_price = self
.account
.provider()
.get_block_with_tx_hashes(starknet::core::types::BlockId::Tag(BlockTag::Pending))
.await
.map_err(KakarotError::from)?
.l1_gas_price()
.price_in_fri;

// We set the gas to the balance of the account / 5. This means that the account could
// send up to 5 transactions before hitting a feeder gateway error.
execution = execution.max_fee(self.balance.floor_div(&NonZeroFelt::from_felt_unchecked(5.into())));
let max_fee = self.balance.floor_div(&NonZeroFelt::from_felt_unchecked(5.into()));

let max_gas_price = fri_gas_price.double();
let max_gas = max_fee.floor_div(&NonZeroFelt::from_felt_unchecked(if max_gas_price == Felt::ZERO {
Felt::from(1)
} else {
max_gas_price
}));

execution = execution
.gas(Felt252Wrapper::from(max_gas).try_into()?)
.gas_price(Felt252Wrapper::from(max_gas_price).try_into()?);

let prepared = execution.prepared().map_err(|_| SignatureError::SigningFailure)?;
let res = prepared.send().await.map_err(|err| TransactionError::Broadcast(err.into()))?;
Expand Down
5 changes: 4 additions & 1 deletion src/providers/eth_provider/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,10 @@ where
// Convert the optional Ethereum block ID to a Starknet block ID.
let starknet_block_id = self.to_starknet_block_id(block_id).await?;
// Get the balance of the address at the given block ID.
self.starknet_provider().balance_at(starknet_address(address), starknet_block_id).await.map_err(Into::into)
self.starknet_provider()
.balance_at_native(starknet_address(address), starknet_block_id)
.await
.map_err(Into::into)
}

async fn storage_at(
Expand Down
52 changes: 41 additions & 11 deletions src/providers/sn_provider/starknet_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{
into_via_wrapper,
providers::eth_provider::{
error::ExecutionError,
starknet::{ERC20Reader, STARKNET_NATIVE_TOKEN},
starknet::{ERC20Reader, STARKNET_NATIVE_TOKEN, STARKNET_PARENT_TOKEN},
utils::{class_hash_not_declared, contract_not_found},
},
};
Expand Down Expand Up @@ -38,25 +38,55 @@ where
Self { provider }
}

/// Retrieves the balance of a Starknet address for a specified block.
/// Retrieves the balance of the parent token for a Starknet address.
///
/// This method interacts with the Starknet native token contract to query the balance of the given
/// address at a specific block.
/// # Arguments
/// * `token_address` - The address of the token contract
/// * `address` - The address to check the balance for
/// * `block_id` - The block ID at which to check the balance
///
/// If the contract is not deployed or the class hash is not declared, a balance of 0 is returned
/// instead of an error.
pub async fn balance_at(&self, address: Felt, block_id: BlockId) -> Result<U256, ExecutionError> {
// Create a new `ERC20Reader` instance for the Starknet native token
let eth_contract = ERC20Reader::new(*STARKNET_NATIVE_TOKEN, &self.provider);
/// # Returns
/// * `Result<U256, ExecutionError>` - The balance as a U256 value or an execution error
pub async fn balance_at_parent(&self, address: Felt, block_id: BlockId) -> Result<U256, ExecutionError> {
self.balance_at(STARKNET_PARENT_TOKEN, address, block_id).await
}

/// Retrieves the balance of the native Starknet token for an address.
///
/// # Arguments
/// * `token_address` - The address of the token contract
/// * `address` - The address to check the balance for
/// * `block_id` - The block ID at which to check the balance
///
/// # Returns
/// * `Result<U256, ExecutionError>` - The balance as a U256 value or an execution error
pub async fn balance_at_native(&self, address: Felt, block_id: BlockId) -> Result<U256, ExecutionError> {
self.balance_at(STARKNET_NATIVE_TOKEN, address, block_id).await
}

/// Internal method to retrieve the balance of any ERC20 token for a Starknet address.
///
/// # Arguments
/// * `token_address` - The address of the ERC20 token contract
/// * `address` - The address to check the balance for
/// * `block_id` - The block ID at which to check the balance
///
/// # Returns
/// * `Result<U256, ExecutionError>` - The balance as a U256 value or an execution error
///
/// # Notes
/// - Returns a balance of 0 if the contract is not deployed or the class hash is not declared
/// - The balance is returned as a U256, combining low and high 128-bit components
async fn balance_at(&self, token_address: Felt, address: Felt, block_id: BlockId) -> Result<U256, ExecutionError> {
// Create a new `ERC20Reader` instance for the Starknet parent native token
let eth_contract = ERC20Reader::new(token_address, &self.provider);

// Call the `balanceOf` method on the contract for the given address and block ID, awaiting the result
let span = tracing::span!(tracing::Level::INFO, "sn::balance");
let res = eth_contract.balanceOf(&address).block_id(block_id).call().instrument(span).await;

// Check if the contract was not found or the class hash not declared,
// returning a default balance of 0 if true.
// The native token contract should be deployed on Kakarot, so this should not happen
// We want to avoid errors in this case and return a default balance of 0
if contract_not_found(&res) || class_hash_not_declared(&res) {
return Ok(Default::default());
}
Expand Down
4 changes: 2 additions & 2 deletions src/test_utils/eoa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ impl<P: Provider + Send + Sync + Clone> KakarotEOA<P> {
let relayer_balance = self
.eth_client
.starknet_provider()
.balance_at(self.relayer.address(), BlockId::Tag(BlockTag::Latest))
.balance_at_native(self.relayer.address(), BlockId::Tag(BlockTag::Latest))
.await?;
let relayer_balance = into_via_try_wrapper!(relayer_balance)?;

Expand Down Expand Up @@ -226,7 +226,7 @@ impl<P: Provider + Send + Sync + Clone> KakarotEOA<P> {
let relayer_balance = self
.eth_client
.starknet_provider()
.balance_at(self.relayer.address(), BlockId::Tag(BlockTag::Latest))
.balance_at_native(self.relayer.address(), BlockId::Tag(BlockTag::Latest))
.await?;
let relayer_balance = into_via_try_wrapper!(relayer_balance)?;

Expand Down
6 changes: 3 additions & 3 deletions tests/tests/eth_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -746,7 +746,7 @@ async fn test_send_raw_transaction(#[future] katana_empty: Katana, _setup: ()) {
// Prepare the relayer
let relayer_balance = eth_client
.starknet_provider()
.balance_at(katana.eoa.relayer.address(), BlockId::Tag(BlockTag::Latest))
.balance_at_native(katana.eoa.relayer.address(), BlockId::Tag(BlockTag::Latest))
.await
.expect("Failed to get relayer balance");
let relayer_balance = into_via_try_wrapper!(relayer_balance).expect("Failed to convert balance");
Expand Down Expand Up @@ -1018,7 +1018,7 @@ async fn test_send_raw_transaction_pre_eip_155(#[future] katana_empty: Katana, _
let relayer_balance = katana
.eth_client
.starknet_provider()
.balance_at(katana.eoa.relayer.address(), BlockId::Tag(BlockTag::Latest))
.balance_at_native(katana.eoa.relayer.address(), BlockId::Tag(BlockTag::Latest))
.await
.expect("Failed to get relayer balance");
let relayer_balance = into_via_try_wrapper!(relayer_balance).expect("Failed to convert balance");
Expand Down Expand Up @@ -1391,7 +1391,7 @@ async fn test_transaction_by_hash(#[future] katana_empty: Katana, _setup: ()) {
let relayer_balance = katana_empty
.eth_client
.starknet_provider()
.balance_at(katana_empty.eoa.relayer.address(), BlockId::Tag(BlockTag::Latest))
.balance_at_native(katana_empty.eoa.relayer.address(), BlockId::Tag(BlockTag::Latest))
.await
.expect("Failed to get relayer balance");
let relayer_balance = into_via_try_wrapper!(relayer_balance).expect("Failed to convert balance");
Expand Down

0 comments on commit 4aaec6e

Please sign in to comment.