From 9db77083eece8fc61c7f8daca8f1332dd7ffe397 Mon Sep 17 00:00:00 2001 From: j1mbo64 <83956685+j1mbo64@users.noreply.github.com> Date: Fri, 17 May 2024 11:17:39 +0200 Subject: [PATCH] feat: support strk as fee token (#1597) Co-authored-by: j1mbo64 --- CHANGELOG.md | 1 + configs/genesis-assets/genesis.json | 90 +++++- configs/index.json | 2 +- crates/pallets/starknet/src/tests/block.rs | 4 +- .../pallets/starknet/src/tests/constants.rs | 3 +- crates/pallets/starknet/src/tests/events.rs | 4 +- .../starknet/src/tests/fees_disabled.rs | 8 +- .../pallets/starknet/src/tests/invoke_tx.rs | 295 +++++++++++++++++- .../starknet/src/tests/mock/genesis.json | 109 +++++++ crates/pallets/starknet/src/tests/mod.rs | 44 ++- crates/pallets/starknet/src/tests/utils.rs | 41 +++ docs/genesis.md | 28 ++ 12 files changed, 613 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33c6b29262..ac75a49305 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Next release +- feat: support strk as fee token - dev: pallet test for estimate_fee that skip validation - feat: add versioned constants to pallet constants - bug: fix contract serialisation diff --git a/configs/genesis-assets/genesis.json b/configs/genesis-assets/genesis.json index 89a066f43c..e21addbaaa 100644 --- a/configs/genesis-assets/genesis.json +++ b/configs/genesis-assets/genesis.json @@ -118,6 +118,10 @@ "0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", "0x0372ee6669dc86563007245ed7343d5180b96221ce28f44408cff2898038dbd4" ], + [ + "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d", + "0x0372ee6669dc86563007245ed7343d5180b96221ce28f44408cff2898038dbd4" + ], [ "0x041a78e741e5af2fec34b695679bc6891742439f7afb8484ecd7766661ad02bf", "0x07b3e05f48f0c69e4a65ce5e076a66271a527aff2c34ce1083ec6e1526997a69" @@ -244,6 +248,90 @@ ], "0xffffffffffffffffffffffffffffffff" ], + [ + [ + "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d", + "0x341c1bdfd89f69748aa00b5742b03adbffd79b8e80cab5c50d91cd8c2a79be1" + ], + "0x20202020537461726b6e657420546f6b656e" + ], + [ + [ + "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d", + "0x0b6ce5410fca59d078ee9b2a4371a9d684c530d697c64fbef0ae6d5e8f0ac72" + ], + "0x5354524b" + ], + [ + [ + "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d", + "0x1f0d4aa99431d246bac9b8e48c33e888245b15e9678f64f9bdfc8823dc8f979" + ], + "0x12" + ], + [ + [ + "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d", + "0x7b62949c85c6af8a50c11c22927f9302f7a2e40bc93b4c988415915b0f97f09" + ], + "0xffffffffffffffffffffffffffffffff" + ], + [ + [ + "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d", + "0x7b62949c85c6af8a50c11c22927f9302f7a2e40bc93b4c988415915b0f97f0a" + ], + "0xffffffffffffffffffffffffffffffff" + ], + [ + [ + "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d", + "0x45abe05a3e7fb0c2ae1fa912be22a7dbc4832915e00562e2783dee710b9e4bc" + ], + "0xffffffffffffffffffffffffffffffff" + ], + [ + [ + "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d", + "0x40795dc9acd6d37d07ba939bbafe8ff2d0a9b78de88db2c74b4eb57d757c0eb" + ], + "0xffffffffffffffffffffffffffffffff" + ], + [ + [ + "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d", + "0x45abe05a3e7fb0c2ae1fa912be22a7dbc4832915e00562e2783dee710b9e4bd" + ], + "0xffffffffffffffffffffffffffffffff" + ], + [ + [ + "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d", + "0x262e096a838c0d8f34f641ff917d47d7dcb345c69efe61d9ab6b675e7340fc6" + ], + "0xffffffffffffffffffffffffffffffff" + ], + [ + [ + "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d", + "0x262e096a838c0d8f34f641ff917d47d7dcb345c69efe61d9ab6b675e7340fc7" + ], + "0xffffffffffffffffffffffffffffffff" + ], + [ + [ + "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d", + "0x1d8bbc4f93f5ab9858f6c0c0de2769599fb97511503d5bf2872ef6846f2146f" + ], + "0xffffffffffffffffffffffffffffffff" + ], + [ + [ + "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d", + "0x1d8bbc4f93f5ab9858f6c0c0de2769599fb97511503d5bf2872ef6846f21470" + ], + "0xffffffffffffffffffffffffffffffff" + ], [ [ "0x040e59c2c182a58fb0a74349bfa4769cbbcba32547591dd3fb1def8623997d00", @@ -281,6 +369,6 @@ ] ], "eth_fee_token_address": "0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", - "strk_fee_token_address": "0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc8", + "strk_fee_token_address": "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d", "chain_id": "MADARA" } diff --git a/configs/index.json b/configs/index.json index 40368dc8d4..f5fd10a6cd 100644 --- a/configs/index.json +++ b/configs/index.json @@ -15,7 +15,7 @@ }, { "name": "genesis.json", - "sha3_256": "ec869153b70b838f5c58d602948145552bf9e8fcc1e22f28bd9250d3cf321993" + "sha3_256": "c35bcc00b70f9d6bfee3416fae0a0ec1e7a1fffe01c3d7fbb660db93a7bed87a" }, { "name": "NoValidateAccount.casm.json", diff --git a/crates/pallets/starknet/src/tests/block.rs b/crates/pallets/starknet/src/tests/block.rs index 5cfe3fefcb..c1517a9ecb 100644 --- a/crates/pallets/starknet/src/tests/block.rs +++ b/crates/pallets/starknet/src/tests/block.rs @@ -12,7 +12,7 @@ use starknet_api::hash::StarkFelt; use super::mock::default_mock::*; use super::mock::*; -use crate::tests::constants::FEE_TOKEN_ADDRESS; +use crate::tests::constants::ETH_FEE_TOKEN_ADDRESS; use crate::tests::get_invoke_dummy; use crate::{SeqAddrUpdate, SequencerAddress}; @@ -108,7 +108,7 @@ fn get_block_context_works() { assert_eq!(default_addr, block_context.block_info().sequencer_address); // correct fee_token_address assert_eq!( - ContractAddress::try_from(StarkFelt::try_from(FEE_TOKEN_ADDRESS).unwrap()).unwrap(), + ContractAddress::try_from(StarkFelt::try_from(ETH_FEE_TOKEN_ADDRESS).unwrap()).unwrap(), block_context.chain_info().fee_token_address(&FeeType::Eth) ); // correct gas_price, diff --git a/crates/pallets/starknet/src/tests/constants.rs b/crates/pallets/starknet/src/tests/constants.rs index 64ac807ab8..06e721fa33 100644 --- a/crates/pallets/starknet/src/tests/constants.rs +++ b/crates/pallets/starknet/src/tests/constants.rs @@ -10,7 +10,8 @@ pub const BLOCKIFIER_ACCOUNT_ADDRESS: &str = "0x02356b628d108863baf8644c945d97ba pub const BRAAVOS_ACCOUNT_CLASS_HASH_CAIRO_0: &str = "0x0244ca3d9fe8b47dd565a6f4270d979ba31a7d6ff2c3bf8776198161505e8b52"; pub const BRAAVOS_PROXY_CLASS_HASH_CAIRO_0: &str = "0x06a89ae7bd72c96202c040341c1ee422474b562e1d73c6848f08cae429c33262"; -pub const FEE_TOKEN_ADDRESS: &str = "0x00000000000000000000000000000000000000000000000000000000000000AA"; +pub const ETH_FEE_TOKEN_ADDRESS: &str = "0x00000000000000000000000000000000000000000000000000000000000000AA"; +pub const STRK_FEE_TOKEN_ADDRESS: &str = "0x00000000000000000000000000000000000000000000000000000000000000BB"; pub const K: &str = "0x0000000000000000000000000000000000000000000000000000000000000001"; pub const OPENZEPPELIN_ACCOUNT_CLASS_HASH_CAIRO_0: &str = "0x006280083f8c2a2db9f737320d5e3029b380e0e820fe24b8d312a6a34fdba0cd"; diff --git a/crates/pallets/starknet/src/tests/events.rs b/crates/pallets/starknet/src/tests/events.rs index 35da6d760a..8368deb326 100644 --- a/crates/pallets/starknet/src/tests/events.rs +++ b/crates/pallets/starknet/src/tests/events.rs @@ -8,7 +8,7 @@ use starknet_api::hash::StarkFelt; use starknet_api::transaction::{Calldata, Fee, InvokeTransactionV1, TransactionSignature}; use starknet_core::utils::get_selector_from_name; -use super::constants::{FEE_TOKEN_ADDRESS, MULTIPLE_EVENT_EMITTING_CONTRACT_ADDRESS}; +use super::constants::{ETH_FEE_TOKEN_ADDRESS, MULTIPLE_EVENT_EMITTING_CONTRACT_ADDRESS}; use super::mock::default_mock::*; use super::mock::*; @@ -25,7 +25,7 @@ fn internal_and_external_events_are_emitted_in_the_right_order() { ContractAddress(PatriciaKey(StarkFelt::try_from(MULTIPLE_EVENT_EMITTING_CONTRACT_ADDRESS).unwrap())); let inner_contract_address = ContractAddress(PatriciaKey(StarkFelt::try_from(INNER_EVENT_EMITTING_CONTRACT_ADDRESS).unwrap())); - let fee_token_address = ContractAddress(PatriciaKey(StarkFelt::try_from(FEE_TOKEN_ADDRESS).unwrap())); + let fee_token_address = ContractAddress(PatriciaKey(StarkFelt::try_from(ETH_FEE_TOKEN_ADDRESS).unwrap())); let sender_account = get_account_address(None, AccountType::V0(AccountTypeV0Inner::NoValidate)); let emit_selector: StarkFelt = Felt252Wrapper::from(get_selector_from_name("emit_sandwich").unwrap()).into(); diff --git a/crates/pallets/starknet/src/tests/fees_disabled.rs b/crates/pallets/starknet/src/tests/fees_disabled.rs index ac80cacfa6..f163f35083 100644 --- a/crates/pallets/starknet/src/tests/fees_disabled.rs +++ b/crates/pallets/starknet/src/tests/fees_disabled.rs @@ -4,7 +4,7 @@ use starknet_api::core::{ContractAddress, EntryPointSelector, Nonce, PatriciaKey use starknet_api::hash::StarkFelt; use starknet_api::transaction::Calldata; -use super::constants::FEE_TOKEN_ADDRESS; +use super::constants::ETH_FEE_TOKEN_ADDRESS; use super::mock::*; use super::utils::{ build_get_balance_contract_call, build_transfer_invoke_transaction, BuildTransferInvokeTransaction, @@ -59,7 +59,7 @@ fn build_invoke_transaction( chain_id, BuildTransferInvokeTransaction { sender_address: address, - token_address: ContractAddress(PatriciaKey(StarkFelt::try_from(FEE_TOKEN_ADDRESS).unwrap())), + token_address: ContractAddress(PatriciaKey(StarkFelt::try_from(ETH_FEE_TOKEN_ADDRESS).unwrap())), recipient: address, amount_low: StarkFelt::ZERO, amount_high: StarkFelt::ZERO, @@ -71,7 +71,7 @@ fn build_invoke_transaction( fn get_balance_default_mock(account_address: ContractAddress) -> (Felt252Wrapper, Felt252Wrapper) { let (selector, calldata) = build_get_balance_call(account_address); let result = default_mock::Starknet::call_contract( - ContractAddress(PatriciaKey(StarkFelt::try_from(FEE_TOKEN_ADDRESS).unwrap())), + ContractAddress(PatriciaKey(StarkFelt::try_from(ETH_FEE_TOKEN_ADDRESS).unwrap())), selector, calldata, ) @@ -82,7 +82,7 @@ fn get_balance_default_mock(account_address: ContractAddress) -> (Felt252Wrapper fn get_balance_fees_disabled_mock(account_address: ContractAddress) -> (Felt252Wrapper, Felt252Wrapper) { let (selector, calldata) = build_get_balance_call(account_address); let result = fees_disabled_mock::Starknet::call_contract( - ContractAddress(PatriciaKey(StarkFelt::try_from(FEE_TOKEN_ADDRESS).unwrap())), + ContractAddress(PatriciaKey(StarkFelt::try_from(ETH_FEE_TOKEN_ADDRESS).unwrap())), selector, calldata, ) diff --git a/crates/pallets/starknet/src/tests/invoke_tx.rs b/crates/pallets/starknet/src/tests/invoke_tx.rs index 7928ef8d8c..e6a8fb9ab5 100644 --- a/crates/pallets/starknet/src/tests/invoke_tx.rs +++ b/crates/pallets/starknet/src/tests/invoke_tx.rs @@ -22,16 +22,19 @@ use starknet_core::utils::{get_selector_from_name, get_udc_deployed_address, Udc use starknet_crypto::FieldElement; use super::constants::{ - BLOCKIFIER_ACCOUNT_ADDRESS, MULTIPLE_EVENT_EMITTING_CONTRACT_ADDRESS, TEST_CONTRACT_ADDRESS, TRANSFER_SELECTOR_NAME, + BLOCKIFIER_ACCOUNT_ADDRESS, ETH_FEE_TOKEN_ADDRESS, MULTIPLE_EVENT_EMITTING_CONTRACT_ADDRESS, + STRK_FEE_TOKEN_ADDRESS, TEST_CONTRACT_ADDRESS, TRANSFER_SELECTOR_NAME, }; use super::mock::default_mock::*; use super::mock::*; -use super::utils::{get_contract_class, sign_message_hash}; +use super::utils::{ + get_balance_contract_call, get_contract_class, set_account_erc20_balance_to_zero, sign_message_hash, +}; use crate::tests::constants::{UDC_ADDRESS, UDC_SELECTOR}; use crate::tests::{ get_invoke_argent_dummy, get_invoke_braavos_dummy, get_invoke_dummy, get_invoke_emit_event_dummy, - get_invoke_nonce_dummy, get_invoke_openzeppelin_dummy, get_storage_read_write_dummy, set_infinite_tokens, - set_nonce, + get_invoke_nonce_dummy, get_invoke_openzeppelin_dummy, get_invoke_v3_dummy, get_storage_read_write_dummy, + set_infinite_tokens, set_nonce, }; use crate::{Call, Error, StorageView}; @@ -617,3 +620,287 @@ fn storage_changes_should_revert_on_transaction_revert() { assert_eq!(balance_value, vec![Felt252Wrapper::ZERO]) }) } + +#[test] +fn given_hardcoded_contract_run_invoke_v1_without_strk_with_eth_fee_token_it_works() { + new_test_ext::().execute_with(|| { + basic_test_setup(2); + let none_origin = RuntimeOrigin::none(); + + // Account that gonna make the transaction + let sender_address = get_account_address(None, AccountType::V0(AccountTypeV0Inner::NoValidate)); + + // Ethereum fee token contract address + let eth_fee_contract_address = + ContractAddress(PatriciaKey(StarkFelt::try_from(ETH_FEE_TOKEN_ADDRESS).unwrap())); + + // Starknet fee token contract address + let strk_fee_contract_address = + ContractAddress(PatriciaKey(StarkFelt::try_from(STRK_FEE_TOKEN_ADDRESS).unwrap())); + + let mut transaction = get_invoke_dummy(Starknet::chain_id(), NONCE_ZERO); + + if let starknet_api::transaction::InvokeTransaction::V1(tx) = &mut transaction.tx { + tx.sender_address = sender_address; + }; + + set_account_erc20_balance_to_zero(sender_address, strk_fee_contract_address); + + let eth_initial_balance_vec = get_balance_contract_call(sender_address, eth_fee_contract_address); + + // Ensure that eth fee token balance is not empty + assert_eq!( + eth_initial_balance_vec, + vec![ + Felt252Wrapper::from_hex_be("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap(), + Felt252Wrapper::from_hex_be("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap() + ] + ); + + assert_ok!(Starknet::invoke(none_origin, transaction)); + let eth_final_balance_vec = get_balance_contract_call(sender_address, eth_fee_contract_address); + + // Ensure ETH is consumed and STRK balance still the same + assert!(eth_final_balance_vec[1] == eth_initial_balance_vec[1]); + assert!(eth_final_balance_vec[0] < eth_initial_balance_vec[0]); + }); +} + +#[test] +fn given_hardcoded_contract_run_invoke_v1_without_eth_with_strk_fee_token_it_fails() { + new_test_ext::().execute_with(|| { + basic_test_setup(2); + let none_origin = RuntimeOrigin::none(); + + // Account that gonna make the transaction + let sender_address = get_account_address(None, AccountType::V0(AccountTypeV0Inner::NoValidate)); + + // Ethereum fee token contract address + let eth_fee_contract_address = + ContractAddress(PatriciaKey(StarkFelt::try_from(ETH_FEE_TOKEN_ADDRESS).unwrap())); + + // Starknet fee token contract address + let strk_fee_contract_address = + ContractAddress(PatriciaKey(StarkFelt::try_from(STRK_FEE_TOKEN_ADDRESS).unwrap())); + + let mut transaction = get_invoke_dummy(Starknet::chain_id(), NONCE_ZERO); + + if let starknet_api::transaction::InvokeTransaction::V1(tx) = &mut transaction.tx { + tx.sender_address = sender_address; + }; + + set_account_erc20_balance_to_zero(sender_address, eth_fee_contract_address); + + // Ensure that strk fee token balance is not empty + assert_eq!( + get_balance_contract_call(sender_address, strk_fee_contract_address), + vec![ + Felt252Wrapper::from_hex_be("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap(), + Felt252Wrapper::from_hex_be("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap() + ] + ); + + assert_err!(Starknet::invoke(none_origin, transaction), Error::::TransactionExecutionFailed); + }); +} + +#[test] +fn given_hardcoded_contract_run_invoke_v3_without_fees_token_it_fails() { + new_test_ext::().execute_with(|| { + basic_test_setup(2); + let none_origin = RuntimeOrigin::none(); + + // Account that gonna make the transaction + let sender_address = get_account_address(None, AccountType::V0(AccountTypeV0Inner::NoValidate)); + + // Ethereum fee token contract address + let eth_fee_contract_address = + ContractAddress(PatriciaKey(StarkFelt::try_from(ETH_FEE_TOKEN_ADDRESS).unwrap())); + + // Starknet fee token contract address + let strk_fee_contract_address = + ContractAddress(PatriciaKey(StarkFelt::try_from(STRK_FEE_TOKEN_ADDRESS).unwrap())); + + let mut transaction = get_invoke_v3_dummy(Starknet::chain_id(), NONCE_ZERO); + + if let starknet_api::transaction::InvokeTransaction::V3(tx) = &mut transaction.tx { + tx.sender_address = sender_address; + }; + + set_account_erc20_balance_to_zero(sender_address, eth_fee_contract_address); + set_account_erc20_balance_to_zero(sender_address, strk_fee_contract_address); + + assert_err!(Starknet::invoke(none_origin, transaction), Error::::TransactionExecutionFailed); + }); +} + +#[test] +fn given_hardcoded_contract_run_invoke_v3_without_eth_with_strk_fee_token_it_works() { + new_test_ext::().execute_with(|| { + basic_test_setup(2); + + let none_origin = RuntimeOrigin::none(); + + // Account that gonna make the transaction + let sender_address = get_account_address(None, AccountType::V0(AccountTypeV0Inner::NoValidate)); + + // Ethereum fee token contract address + let eth_fee_contract_address = + ContractAddress(PatriciaKey(StarkFelt::try_from(ETH_FEE_TOKEN_ADDRESS).unwrap())); + + // Starknet fee token contract address + let strk_fee_contract_address = + ContractAddress(PatriciaKey(StarkFelt::try_from(STRK_FEE_TOKEN_ADDRESS).unwrap())); + + let mut transaction = get_invoke_v3_dummy(Starknet::chain_id(), NONCE_ZERO); + + if let starknet_api::transaction::InvokeTransaction::V3(tx) = &mut transaction.tx { + tx.sender_address = sender_address; + }; + + set_account_erc20_balance_to_zero(sender_address, eth_fee_contract_address); + + let initial_balance_vec = get_balance_contract_call(sender_address, strk_fee_contract_address); + + // Ensure that strk fee token balance is not empty + assert_eq!( + initial_balance_vec, + vec![ + Felt252Wrapper::from_hex_be("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap(), + Felt252Wrapper::from_hex_be("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap() + ] + ); + + assert_ok!(Starknet::invoke(none_origin, transaction)); + let final_balance_vec = get_balance_contract_call(sender_address, strk_fee_contract_address); + assert!(final_balance_vec[1] == initial_balance_vec[1]); + assert!(final_balance_vec[0] < initial_balance_vec[0]); + }); +} + +#[test] +fn given_hardcoded_contract_run_invoke_v3_without_strk_with_eth_fee_token_it_fails() { + new_test_ext::().execute_with(|| { + basic_test_setup(2); + + let none_origin = RuntimeOrigin::none(); + + // Account that gonna make the transaction + let sender_address = get_account_address(None, AccountType::V0(AccountTypeV0Inner::NoValidate)); + + // Ethereum fee token contract address + let eth_fee_contract_address = + ContractAddress(PatriciaKey(StarkFelt::try_from(ETH_FEE_TOKEN_ADDRESS).unwrap())); + + // Starknet fee token contract address + let strk_fee_contract_address = + ContractAddress(PatriciaKey(StarkFelt::try_from(STRK_FEE_TOKEN_ADDRESS).unwrap())); + + let mut transaction = get_invoke_v3_dummy(Starknet::chain_id(), NONCE_ZERO); + + if let starknet_api::transaction::InvokeTransaction::V3(tx) = &mut transaction.tx { + tx.sender_address = sender_address; + }; + + set_account_erc20_balance_to_zero(sender_address, strk_fee_contract_address); + + // Ensure that eth fee token balance is not empty + assert_eq!( + get_balance_contract_call(sender_address, eth_fee_contract_address), + vec![ + Felt252Wrapper::from_hex_be("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap(), + Felt252Wrapper::from_hex_be("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap() + ] + ); + + assert_err!(Starknet::invoke(none_origin, transaction), Error::::TransactionExecutionFailed); + }); +} + +#[test] +fn given_hardcoded_contract_run_invoke_v3_with_eth_with_strk_fees_token_it_works() { + new_test_ext::().execute_with(|| { + basic_test_setup(2); + + let none_origin = RuntimeOrigin::none(); + + // Account that gonna make the transaction + let sender_address = get_account_address(None, AccountType::V0(AccountTypeV0Inner::NoValidate)); + + // Ethereum fee token contract address + let eth_fee_contract_address = + ContractAddress(PatriciaKey(StarkFelt::try_from(ETH_FEE_TOKEN_ADDRESS).unwrap())); + + // Starknet fee token contract address + let strk_fee_contract_address = + ContractAddress(PatriciaKey(StarkFelt::try_from(STRK_FEE_TOKEN_ADDRESS).unwrap())); + + let mut transaction = get_invoke_v3_dummy(Starknet::chain_id(), NONCE_ZERO); + + if let starknet_api::transaction::InvokeTransaction::V3(tx) = &mut transaction.tx { + tx.sender_address = sender_address; + }; + + let strk_initial_balance_vec = get_balance_contract_call(sender_address, strk_fee_contract_address); + let eth_initial_balance_vec = get_balance_contract_call(sender_address, eth_fee_contract_address); + + // Ensure that strk fee token balance is not empty + assert_eq!( + strk_initial_balance_vec, + vec![ + Felt252Wrapper::from_hex_be("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap(), + Felt252Wrapper::from_hex_be("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap() + ] + ); + + assert_eq!( + eth_initial_balance_vec, + vec![ + Felt252Wrapper::from_hex_be("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap(), + Felt252Wrapper::from_hex_be("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap() + ] + ); + + assert_ok!(Starknet::invoke(none_origin, transaction)); + let strk_final_balance_vec = get_balance_contract_call(sender_address, strk_fee_contract_address); + let eth_final_balance_vec = get_balance_contract_call(sender_address, eth_fee_contract_address); + + // Ensure STRK is consumed and ETH balance still the same + assert!(strk_final_balance_vec[1] == strk_initial_balance_vec[1]); + assert!(strk_final_balance_vec[0] < strk_initial_balance_vec[0]); + assert!(eth_final_balance_vec[1] == eth_initial_balance_vec[1]); + assert!(eth_final_balance_vec[0] == eth_initial_balance_vec[0]); + }); +} + +#[test] +fn given_hardcoded_contract_set_erc20_balance_to_zero() { + new_test_ext::().execute_with(|| { + basic_test_setup(2); + + // Account that gonna make the transaction + let account_address = get_account_address(None, AccountType::V0(AccountTypeV0Inner::NoValidate)); + + // ERC20 contract address (ETH in this case) + let erc20_contract_address = ContractAddress(PatriciaKey(StarkFelt::try_from(ETH_FEE_TOKEN_ADDRESS).unwrap())); + + let erc20_initial_balance_vec = get_balance_contract_call(account_address, erc20_contract_address); + + assert_eq!( + erc20_initial_balance_vec, + vec![ + Felt252Wrapper::from_hex_be("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap(), + Felt252Wrapper::from_hex_be("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap() + ] + ); + + set_account_erc20_balance_to_zero(account_address, erc20_contract_address); + let erc20_final_balance_vec = get_balance_contract_call(account_address, erc20_contract_address); + + // Ensure ERC20 balance of account is zero + assert_eq!( + erc20_final_balance_vec, + vec![Felt252Wrapper::from_hex_be("0x0").unwrap(), Felt252Wrapper::from_hex_be("0x0").unwrap()] + ); + }); +} diff --git a/crates/pallets/starknet/src/tests/mock/genesis.json b/crates/pallets/starknet/src/tests/mock/genesis.json index 685f7fd2cc..b22455a808 100644 --- a/crates/pallets/starknet/src/tests/mock/genesis.json +++ b/crates/pallets/starknet/src/tests/mock/genesis.json @@ -150,6 +150,10 @@ "0x00000000000000000000000000000000000000000000000000000000000000AA", "0x06232eeb9ecb5de85fc927599f144913bfee6ac413f2482668c9f03ce4d07922" ], + [ + "0x00000000000000000000000000000000000000000000000000000000000000BB", + "0x06232eeb9ecb5de85fc927599f144913bfee6ac413f2482668c9f03ce4d07922" + ], [ "0x041a78e741e5af2fec34b695679bc6891742439f7afb8484ecd7766661ad02cf", "0x91000" @@ -278,6 +282,111 @@ ], "0xffffffffffffffffffffffffffffffff" ], + [ + [ + "0x00000000000000000000000000000000000000000000000000000000000000BB", + "0x0341c1bdfd89f69748aa00b5742b03adbffd79b8e80cab5c50d91cd8c2a79be1" + ], + "0x20202020537461726b6e657420546f6b656e" + ], + [ + [ + "0x00000000000000000000000000000000000000000000000000000000000000BB", + "0x00b6ce5410fca59d078ee9b2a4371a9d684c530d697c64fbef0ae6d5e8f0ac72" + ], + "0x5354524b" + ], + [ + [ + "0x00000000000000000000000000000000000000000000000000000000000000BB", + "0x01f0d4aa99431d246bac9b8e48c33e888245b15e9678f64f9bdfc8823dc8f979" + ], + "0x12" + ], + [ + [ + "0x00000000000000000000000000000000000000000000000000000000000000BB", + "0x3701645da930cd7f63318f7f118a9134e72d64ab73c72ece81cae2bd5fb403f" + ], + "0xffffffffffffffffffffffffffffffff" + ], + [ + [ + "0x00000000000000000000000000000000000000000000000000000000000000BB", + "0x3701645da930cd7f63318f7f118a9134e72d64ab73c72ece81cae2bd5fb4040" + ], + "0xffffffffffffffffffffffffffffffff" + ], + [ + [ + "0x00000000000000000000000000000000000000000000000000000000000000BB", + "0x25aa869465e1c3ac7ed6e933ef1af43f4d9126339b8f453f692d631c4a40d24" + ], + "0xffffffffffffffffffffffffffffffff" + ], + [ + [ + "0x00000000000000000000000000000000000000000000000000000000000000BB", + "0x25aa869465e1c3ac7ed6e933ef1af43f4d9126339b8f453f692d631c4a40d25" + ], + "0xffffffffffffffffffffffffffffffff" + ], + [ + [ + "0x00000000000000000000000000000000000000000000000000000000000000BB", + "0x6afaa15cba5e9ea552a55fec494d2d859b4b73506794bf5afbb3d73c1fb00aa" + ], + "0xffffffffffffffffffffffffffffffff" + ], + [ + [ + "0x00000000000000000000000000000000000000000000000000000000000000BB", + "0x6afaa15cba5e9ea552a55fec494d2d859b4b73506794bf5afbb3d73c1fb00ab" + ], + "0xffffffffffffffffffffffffffffffff" + ], + [ + [ + "0x00000000000000000000000000000000000000000000000000000000000000BB", + "0x2231fbd06f0277a2cbcd41f94a6d6cf930a586168e7faa4d62281f554934236" + ], + "0xffffffffffffffffffffffffffffffff" + ], + [ + [ + "0x00000000000000000000000000000000000000000000000000000000000000BB", + "0x2231fbd06f0277a2cbcd41f94a6d6cf930a586168e7faa4d62281f554934237" + ], + "0xffffffffffffffffffffffffffffffff" + ], + [ + [ + "0x00000000000000000000000000000000000000000000000000000000000000BB", + "0x60b6ac06a42730e54bfd5d389ca51256c926bc9317adb44f7c1029711f8bf8e" + ], + "0xffffffffffffffffffffffffffffffff" + ], + [ + [ + "0x00000000000000000000000000000000000000000000000000000000000000BB", + "0x60b6ac06a42730e54bfd5d389ca51256c926bc9317adb44f7c1029711f8bf8f" + ], + "0xffffffffffffffffffffffffffffffff" + ], + [ + [ + "0x00000000000000000000000000000000000000000000000000000000000000BB", + "0x78f9a7bb317327b7ad49232784f8e6acfa88269879253bbf780c5bc7a18149a" + ], + "0xffffffffffffffffffffffffffffffff" + ], + [ + [ + "0x00000000000000000000000000000000000000000000000000000000000000BB", + "0x78f9a7bb317327b7ad49232784f8e6acfa88269879253bbf780c5bc7a18149b" + ], + "0xffffffffffffffffffffffffffffffff" + ], [ [ "0x06e2616a2dceff4355997369246c25a78e95093df7a49e5ca6a06ce1544ffd50", diff --git a/crates/pallets/starknet/src/tests/mod.rs b/crates/pallets/starknet/src/tests/mod.rs index c973e911ef..0e64d799b6 100644 --- a/crates/pallets/starknet/src/tests/mod.rs +++ b/crates/pallets/starknet/src/tests/mod.rs @@ -9,6 +9,7 @@ use mp_transactions::compute_hash::ComputeTransactionHash; use starknet_api::core::{ calculate_contract_address, ClassHash, ContractAddress, EntryPointSelector, Nonce, PatriciaKey, }; +use starknet_api::data_availability::DataAvailabilityMode; use starknet_api::hash::StarkFelt; use starknet_api::transaction::{ Calldata, ContractAddressSalt, DeclareTransactionV0V1, DeployAccountTransactionV1, Fee, TransactionSignature, @@ -17,7 +18,7 @@ use starknet_api::transaction::{ use self::mock::default_mock::Starknet; use self::mock::{get_account_address, AccountType}; -use self::utils::get_contract_class; +use self::utils::{create_resource_bounds, get_contract_class}; use crate::blockifier_state_adapter::BlockifierStateAdapter; use crate::tests::mock::account_helper; use crate::tests::utils::sign_message_hash; @@ -78,6 +79,41 @@ pub fn get_invoke_dummy( blockifier::transaction::transactions::InvokeTransaction { tx, tx_hash, only_query: false } } +pub fn get_invoke_v3_dummy( + chain_id: Felt252Wrapper, + nonce: Nonce, +) -> blockifier::transaction::transactions::InvokeTransaction { + let signature = TransactionSignature(vec![ + StarkFelt::try_from("0x00f513fe663ffefb9ad30058bb2d2f7477022b149a0c02fb63072468d3406168").unwrap(), + StarkFelt::try_from("0x02e29e92544d31c03e89ecb2005941c88c28b4803a3647a7834afda12c77f096").unwrap(), + ]); + let sender_address = + ContractAddress(PatriciaKey(StarkFelt::try_from(constants::BLOCKIFIER_ACCOUNT_ADDRESS).unwrap())); + let calldata = Calldata(Arc::new(vec![ + StarkFelt::try_from("0x024d1e355f6b9d27a5a420c8f4b50cea9154a8e34ad30fc39d7c98d3c177d0d7").unwrap(), /* contract_address */ + StarkFelt::try_from("0x00e7def693d16806ca2a2f398d8de5951344663ba77f340ed7a958da731872fc").unwrap(), /* selector for the `with_arg` external */ + StarkFelt::try_from("0x1").unwrap(), // calldata_len + StarkFelt::try_from("0x19").unwrap(), // calldata[0] + ])); + + let tx = starknet_api::transaction::InvokeTransaction::V3(starknet_api::transaction::InvokeTransactionV3 { + resource_bounds: create_resource_bounds(), + tip: starknet_api::transaction::Tip::default(), + calldata, + sender_address, + nonce, + signature, + nonce_data_availability_mode: DataAvailabilityMode::L1, + fee_data_availability_mode: DataAvailabilityMode::L1, + paymaster_data: starknet_api::transaction::PaymasterData(vec![]), + account_deployment_data: starknet_api::transaction::AccountDeploymentData(vec![]), + }); + + let tx_hash = tx.compute_hash(chain_id, false); + + blockifier::transaction::transactions::InvokeTransaction { tx, tx_hash, only_query: false } +} + // ref: https://github.com/argentlabs/argent-contracts-starknet/blob/develop/contracts/account/ArgentAccount.cairo fn get_invoke_argent_dummy(chain_id: Felt252Wrapper) -> blockifier::transaction::transactions::InvokeTransaction { let sender_address = ContractAddress(PatriciaKey( @@ -356,6 +392,12 @@ pub fn set_infinite_tokens(contract_address: &ContractAddress) { state_adapter .set_storage_at(fee_token_addresses.eth_fee_token_address, balance_key_high, StarkFelt::from(u64::MAX as u128)) .unwrap(); + state_adapter + .set_storage_at(fee_token_addresses.strk_fee_token_address, balance_key_low, StarkFelt::from(u64::MAX as u128)) + .unwrap(); + state_adapter + .set_storage_at(fee_token_addresses.strk_fee_token_address, balance_key_high, StarkFelt::from(u64::MAX as u128)) + .unwrap(); } /// Sets nonce for the given address. diff --git a/crates/pallets/starknet/src/tests/utils.rs b/crates/pallets/starknet/src/tests/utils.rs index a99d721d01..e2c83b1f63 100644 --- a/crates/pallets/starknet/src/tests/utils.rs +++ b/crates/pallets/starknet/src/tests/utils.rs @@ -1,3 +1,4 @@ +use std::collections::BTreeMap; use std::path::PathBuf; use std::str::FromStr; use std::sync::Arc; @@ -14,7 +15,10 @@ use starknet_api::transaction::{Calldata, Fee, TransactionHash, TransactionSigna use starknet_crypto::{sign, FieldElement}; use super::constants::{ACCOUNT_PRIVATE_KEY, K}; +use super::mock::default_mock::*; use crate::genesis_loader::read_contract_class_from_json; +use crate::tests::mock::get_storage_key; +use crate::StorageView; pub fn get_contract_class(resource_path: &str, version: u8) -> ContractClass { let cargo_dir = String::from(env!("CARGO_MANIFEST_DIR")); @@ -112,3 +116,40 @@ pub fn build_get_balance_contract_call(account_address: StarkFelt) -> (EntryPoin (balance_of_selector, calldata) } + +pub fn create_resource_bounds() -> starknet_api::transaction::ResourceBoundsMapping { + let mut map = BTreeMap::new(); + map.insert( + starknet_api::transaction::Resource::L1Gas, + starknet_api::transaction::ResourceBounds { max_amount: 10000, max_price_per_unit: 12000 }, + ); + map.insert( + starknet_api::transaction::Resource::L2Gas, + starknet_api::transaction::ResourceBounds { max_amount: 10000, max_price_per_unit: 12000 }, + ); + starknet_api::transaction::ResourceBoundsMapping(map) +} + +pub fn get_balance_contract_call( + sender_address: ContractAddress, + contract_address: ContractAddress, +) -> Vec { + let call_args = build_get_balance_contract_call(sender_address.0.0); + Starknet::call_contract(contract_address, call_args.0, call_args.1).unwrap() +} + +pub fn set_account_erc20_balance_to_zero(account_address: ContractAddress, erc20_contract_address: ContractAddress) { + // ContractAddress to FieldElement + let field_contract_address = FieldElement::from_bytes_be(&account_address.key().0).unwrap(); + + // Get balance variable key + let balance_low_storage_key = + get_storage_key(&erc20_contract_address, "ERC20_balances", &[field_contract_address], 0); + + let balance_high_storage_key = + get_storage_key(&erc20_contract_address, "ERC20_balances", &[field_contract_address], 1); + + // Set balance storage value to zero + StorageView::::insert(balance_low_storage_key, StarkFelt::try_from("0").unwrap()); + StorageView::::insert(balance_high_storage_key, StarkFelt::try_from("0").unwrap()); +} diff --git a/docs/genesis.md b/docs/genesis.md index dc492a8038..6445aa7081 100644 --- a/docs/genesis.md +++ b/docs/genesis.md @@ -80,6 +80,7 @@ where `pk` is the following vector of `u8`: | 0x040e59c2c182a58fb0a74349bfa4769cbbcba32547591dd3fb1def8623997d00 | 0x0372ee6669dc86563007245ed7343d5180b96221ce28f44408cff2898038dbd4 | | 0x040e59c2c182a58fb0a74349bfa4769cbbcba32547591dd3fb1def8623997d02 | 0x077cc28ed3c661419fda16bf120fb81f1f8f28617f5543b05a86d63b0926bbf4 | | 0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7 | 0x0372ee6669dc86563007245ed7343d5180b96221ce28f44408cff2898038dbd4 | +| 0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d | 0x0372ee6669dc86563007245ed7343d5180b96221ce28f44408cff2898038dbd4 | | 0x041a78e741e5af2fec34b695679bc6891742439f7afb8484ecd7766661ad02bf | 0x07b3e05f48f0c69e4a65ce5e076a66271a527aff2c34ce1083ec6e1526997a69 | @@ -117,6 +118,17 @@ deployed as a ERC721 contract (given the class hash of 0x80000). | 0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7 | 0x262e096a838c0d8f34f641ff917d47d7dcb345c69efe61d9ab6b675e7340fc7 (ERC20_balances(0x3).high) | 0xffffffffffffffffffffffffffffffff (U128::MAX) | | 0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7 | 0x45abe05a3e7fb0c2ae1fa912be22a7dbc4832915e00562e2783dee710b9e4bc (ERC20_balances(0x4).low) | 0xffffffffffffffffffffffffffffffff (U128::MAX) | | 0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7 | 0x45abe05a3e7fb0c2ae1fa912be22a7dbc4832915e00562e2783dee710b9e4bd (ERC20_balances(0x4).high) | 0xffffffffffffffffffffffffffffffff (U128::MAX) | +| 0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d | 0x341c1bdfd89f69748aa00b5742b03adbffd79b8e80cab5c50d91cd8c2a79be1 (ERC20_name) | 0x20202020537461726b6e657420546f6b656e | +| 0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d | 0x0b6ce5410fca59d078ee9b2a4371a9d684c530d697c64fbef0ae6d5e8f0ac72 (ERC20_symbol) | 0x5354524b | +| 0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d | 0x1f0d4aa99431d246bac9b8e48c33e888245b15e9678f64f9bdfc8823dc8f979 (ERC20_decimals) | 0x12 | +| 0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d | 0x7b62949c85c6af8a50c11c22927f9302f7a2e40bc93b4c988415915b0f97f09 (ERC20_balances(0x1).low) | 0xffffffffffffffffffffffffffffffff (U128::MAX) | +| 0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d | 0x7b62949c85c6af8a50c11c22927f9302f7a2e40bc93b4c988415915b0f97f0a (ERC20_balances(0x1).high) | 0xffffffffffffffffffffffffffffffff (U128::MAX) | +| 0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d | 0x1d8bbc4f93f5ab9858f6c0c0de2769599fb97511503d5bf2872ef6846f2146f (ERC20_balances(0x2).low) | 0xffffffffffffffffffffffffffffffff (U128::MAX) | +| 0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d | 0x1d8bbc4f93f5ab9858f6c0c0de2769599fb97511503d5bf2872ef6846f21470 (ERC20_balances(0x2).high) | 0xffffffffffffffffffffffffffffffff (U128::MAX) | +| 0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d | 0x262e096a838c0d8f34f641ff917d47d7dcb345c69efe61d9ab6b675e7340fc6 (ERC20_balances(0x3).low) | 0xffffffffffffffffffffffffffffffff (U128::MAX) | +| 0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d | 0x262e096a838c0d8f34f641ff917d47d7dcb345c69efe61d9ab6b675e7340fc7 (ERC20_balances(0x3).high) | 0xffffffffffffffffffffffffffffffff (U128::MAX) | +| 0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d | 0x45abe05a3e7fb0c2ae1fa912be22a7dbc4832915e00562e2783dee710b9e4bc (ERC20_balances(0x4).low) | 0xffffffffffffffffffffffffffffffff (U128::MAX) | +| 0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d | 0x45abe05a3e7fb0c2ae1fa912be22a7dbc4832915e00562e2783dee710b9e4bd (ERC20_balances(0x4).high) | 0xffffffffffffffffffffffffffffffff (U128::MAX) | | 0x040e59c2c182a58fb0a74349bfa4769cbbcba32547591dd3fb1def8623997d00 | 0x7b62949c85c6af8a50c11c22927f9302f7a2e40bc93b4c988415915b0f97f09 (ERC20_balances(0x1).low) | 0xffffffffffffffffffffffffffffffff (U128::MAX) | | 0x040e59c2c182a58fb0a74349bfa4769cbbcba32547591dd3fb1def8623997d00 | 0x7b62949c85c6af8a50c11c22927f9302f7a2e40bc93b4c988415915b0f97f0a (ERC20_balances(0x1).high) | 0xffffffffffffffffffffffffffffffff (U128::MAX) | | 0x2 | 0x1ccc09c8a19948e048de7add6929589945e25f22059c7345aaf7837188d8d05 (\_signer) | 0x3603a2692a2ae60abb343e832ee53b55d6b25f02a3ef1565ec691edc7a209b2 (Signer public key) | @@ -166,6 +178,7 @@ deployed as a ERC721 contract (given the class hash of 0x80000). | 0x0642a8b9e2c6cc3a9ddb84575123f262a21415f78db453b0625d889e1e06ac32 | 0x02f99bf9799ada84cd5ac0d0fe36b9d8f65efcb377cd2e8cf8309ad2daf15e4b | | 0x0764d66462958b670b4dbd46e00eb3d60100f329dc0365d9b059e0549a4c6f58 | 0x071aaf68d30c3e52e1c4b7d1209b0e09525939c31bb0275919dffd4cd53f57c4 | | 0x00000000000000000000000000000000000000000000000000000000000000AA | 0x06232eeb9ecb5de85fc927599f144913bfee6ac413f2482668c9f03ce4d07922 | +| 0x00000000000000000000000000000000000000000000000000000000000000BB | 0x06232eeb9ecb5de85fc927599f144913bfee6ac413f2482668c9f03ce4d07922 | | 0x041a78e741e5af2fec34b695679bc6891742439f7afb8484ecd7766661ad02cf | 0x91000 | | 0x051a78e741e5af2fec34b695679bc6891742439f7afb8484ecd7766661ad02cf | 0x92000 | @@ -192,6 +205,21 @@ deployed as a ERC721 contract (given the class hash of 0x80000). | 0x00000000000000000000000000000000000000000000000000000000000000AA | 0x60b6ac06a42730e54bfd5d389ca51256c926bc9317adb44f7c1029711f8bf8f (ERC20_balances(0x02e63de215f650e9d7e2313c6e9ed26b4f920606fb08576b1663c21a7c4a28c5).high) | 0xffffffffffffffffffffffffffffffff | | 0x00000000000000000000000000000000000000000000000000000000000000AA | 0x78f9a7bb317327b7ad49232784f8e6acfa88269879253bbf780c5bc7a18149a (ERC20_balances(0x05ef3fba22df259bf84890945352df711bcc9a4e3b6858cb93e9c90d053cf122).high) | 0xffffffffffffffffffffffffffffffff | | 0x00000000000000000000000000000000000000000000000000000000000000AA | 0x78f9a7bb317327b7ad49232784f8e6acfa88269879253bbf780c5bc7a18149b (ERC20_balances(0x05ef3fba22df259bf84890945352df711bcc9a4e3b6858cb93e9c90d053cf122).high) | 0xffffffffffffffffffffffffffffffff | +| 0x00000000000000000000000000000000000000000000000000000000000000BB | 0x341c1bdfd89f69748aa00b5742b03adbffd79b8e80cab5c50d91cd8c2a79be1 (ERC20_name) | 0x20202020537461726b6e657420546f6b656e | +| 0x00000000000000000000000000000000000000000000000000000000000000BB | 0x0b6ce5410fca59d078ee9b2a4371a9d684c530d697c64fbef0ae6d5e8f0ac72 (ERC20_symbol) | 0x5354524b | +| 0x00000000000000000000000000000000000000000000000000000000000000BB | 0x1f0d4aa99431d246bac9b8e48c33e888245b15e9678f64f9bdfc8823dc8f979 (ERC20_decimals) | 0x12 | +| 0x00000000000000000000000000000000000000000000000000000000000000BB | 0x3701645da930cd7f63318f7f118a9134e72d64ab73c72ece81cae2bd5fb403f (ERC20_balances(0x01a3339ec92ac1061e3e0f8e704106286c642eaf302e94a582e5f95ef5e6b4d0).low) | 0xffffffffffffffffffffffffffffffff | +| 0x00000000000000000000000000000000000000000000000000000000000000BB | 0x3701645da930cd7f63318f7f118a9134e72d64ab73c72ece81cae2bd5fb4040 (ERC20_balances(0x01a3339ec92ac1061e3e0f8e704106286c642eaf302e94a582e5f95ef5e6b4d0).high) | 0xffffffffffffffffffffffffffffffff | +| 0x00000000000000000000000000000000000000000000000000000000000000BB | 0x25aa869465e1c3ac7ed6e933ef1af43f4d9126339b8f453f692d631c4a40d24 (ERC20_balances(0x0642a8b9e2c6cc3a9ddb84575123f262a21415f78db453b0625d889e1e06ac32).low) | 0xffffffffffffffffffffffffffffffff | +| 0x00000000000000000000000000000000000000000000000000000000000000BB | 0x25aa869465e1c3ac7ed6e933ef1af43f4d9126339b8f453f692d631c4a40d25 (ERC20_balances(0x0642a8b9e2c6cc3a9ddb84575123f262a21415f78db453b0625d889e1e06ac32).high) | 0xffffffffffffffffffffffffffffffff | +| 0x00000000000000000000000000000000000000000000000000000000000000BB | 0x6afaa15cba5e9ea552a55fec494d2d859b4b73506794bf5afbb3d73c1fb00aa (ERC20_balances(0x02356b628d108863baf8644c945d97bad70190af5957031f4852d00d0f690a77).low) | 0xffffffffffffffffffffffffffffffff | +| 0x00000000000000000000000000000000000000000000000000000000000000BB | 0x6afaa15cba5e9ea552a55fec494d2d859b4b73506794bf5afbb3d73c1fb00ab (ERC20_balances(0x02356b628d108863baf8644c945d97bad70190af5957031f4852d00d0f690a77).high) | 0xffffffffffffffffffffffffffffffff | +| 0x00000000000000000000000000000000000000000000000000000000000000BB | 0x2231fbd06f0277a2cbcd41f94a6d6cf930a586168e7faa4d62281f554934236 (ERC20_balances(0x06e2616a2dceff4355997369246c25a78e95093df7a49e5ca6a06ce1544ffd50).low) | 0xffffffffffffffffffffffffffffffff | +| 0x00000000000000000000000000000000000000000000000000000000000000BB | 0x2231fbd06f0277a2cbcd41f94a6d6cf930a586168e7faa4d62281f554934237 (ERC20_balances(0x06e2616a2dceff4355997369246c25a78e95093df7a49e5ca6a06ce1544ffd50).high) | 0xffffffffffffffffffffffffffffffff | +| 0x00000000000000000000000000000000000000000000000000000000000000BB | 0x60b6ac06a42730e54bfd5d389ca51256c926bc9317adb44f7c1029711f8bf8e (ERC20_balances(0x02e63de215f650e9d7e2313c6e9ed26b4f920606fb08576b1663c21a7c4a28c5).high) | 0xffffffffffffffffffffffffffffffff | +| 0x00000000000000000000000000000000000000000000000000000000000000BB | 0x60b6ac06a42730e54bfd5d389ca51256c926bc9317adb44f7c1029711f8bf8f (ERC20_balances(0x02e63de215f650e9d7e2313c6e9ed26b4f920606fb08576b1663c21a7c4a28c5).high) | 0xffffffffffffffffffffffffffffffff | +| 0x00000000000000000000000000000000000000000000000000000000000000BB | 0x78f9a7bb317327b7ad49232784f8e6acfa88269879253bbf780c5bc7a18149a (ERC20_balances(0x05ef3fba22df259bf84890945352df711bcc9a4e3b6858cb93e9c90d053cf122).high) | 0xffffffffffffffffffffffffffffffff | +| 0x00000000000000000000000000000000000000000000000000000000000000BB | 0x78f9a7bb317327b7ad49232784f8e6acfa88269879253bbf780c5bc7a18149b (ERC20_balances(0x05ef3fba22df259bf84890945352df711bcc9a4e3b6858cb93e9c90d053cf122).high) | 0xffffffffffffffffffffffffffffffff | | 0x06e2616a2dceff4355997369246c25a78e95093df7a49e5ca6a06ce1544ffd50 | 0x1379ac0624b939ceb9dede92211d7db5ee174fe28be72245b0a1a2abd81c98f (Account_public_key) | 0x3603a2692a2ae60abb343e832ee53b55d6b25f02a3ef1565ec691edc7a209b2 (Signer public key) | | 0x02e63de215f650e9d7e2313c6e9ed26b4f920606fb08576b1663c21a7c4a28c5 | 0x1ccc09c8a19948e048de7add6929589945e25f22059c7345aaf7837188d8d05 (\_signer) | 0x3603a2692a2ae60abb343e832ee53b55d6b25f02a3ef1565ec691edc7a209b2 (Signer public key) | | 0x05ef3fba22df259bf84890945352df711bcc9a4e3b6858cb93e9c90d053cf122 | 0x1f23302c120008f28b62f70efc67ccd75cfe0b9631d77df231d78b0538dcd8f (Account_signers(0x0)) | 0x3603a2692a2ae60abb343e832ee53b55d6b25f02a3ef1565ec691edc7a209b2 (Signer public key) |