From 925bb269cc3131d8510fe9402e671a449f165eac Mon Sep 17 00:00:00 2001 From: Kshitij Chaurasiya Date: Sun, 23 Jun 2024 22:00:39 +0530 Subject: [PATCH 1/2] Introduce ActionBuilder, add Delegate actions and add test config reader for running examples. --- near-accounts/examples/access_keys.rs | 10 +- near-accounts/examples/account_balance.rs | 15 +- near-accounts/examples/async_tx.rs | 18 +- near-accounts/examples/create_account.rs | 28 +-- near-accounts/examples/create_subaccount.rs | 13 +- near-accounts/examples/delete_account.rs | 21 +- near-accounts/examples/delete_key.rs | 17 +- near-accounts/examples/deploy_contract.rs | 32 +-- near-accounts/examples/example_config.rs | 62 ++++++ near-accounts/examples/function_call.rs | 11 +- near-accounts/examples/get_access_key.rs | 2 - .../resources/config/test_config.json | 13 ++ .../contract-wasm/status_message.wasm | Bin near-accounts/examples/send_money.rs | 14 +- near-accounts/examples/view_function.rs | 16 +- near-accounts/src/accounts.rs | 197 +++++------------- near-accounts/src/lib.rs | 1 + near-accounts/src/transaction_sender.rs | 94 +++++++++ near-transactions/Cargo.toml | 9 +- near-transactions/examples/function_call.rs | 74 +++++++ .../examples/meta_transaction.rs | 108 ++++++++++ near-transactions/src/action_builder.rs | 116 +++++++++++ near-transactions/src/delegate_action.rs | 57 +++++ near-transactions/src/lib.rs | 4 + near-transactions/src/transaction_builder.rs | 83 +------- 25 files changed, 660 insertions(+), 355 deletions(-) create mode 100644 near-accounts/examples/example_config.rs create mode 100644 near-accounts/examples/resources/config/test_config.json rename near-accounts/examples/{ => resources}/contract-wasm/status_message.wasm (100%) create mode 100644 near-accounts/src/transaction_sender.rs create mode 100644 near-transactions/examples/function_call.rs create mode 100644 near-transactions/examples/meta_transaction.rs create mode 100644 near-transactions/src/action_builder.rs create mode 100644 near-transactions/src/delegate_action.rs diff --git a/near-accounts/examples/access_keys.rs b/near-accounts/examples/access_keys.rs index 6c9df5e..fac4790 100644 --- a/near-accounts/examples/access_keys.rs +++ b/near-accounts/examples/access_keys.rs @@ -1,3 +1,4 @@ +mod example_config; use near_accounts::Account; use near_crypto::InMemorySigner; use near_primitives::types::Balance; @@ -7,16 +8,9 @@ mod utils; use near_primitives::types::AccountId; async fn add_full_access() -> Result<(), Box> { - let signer_account_id: AccountId = utils::input("Enter the signer Account ID: ")?.parse()?; - let signer_secret_key = utils::input("Enter the signer's private key: ")?.parse()?; - let signer = InMemorySigner::from_secret_key(signer_account_id.clone(), signer_secret_key); - let new_secret_key = near_crypto::SecretKey::from_random(near_crypto::KeyType::ED25519); - let provider = Arc::new(JsonRpcProvider::new("https://rpc.testnet.near.org")); - let signer = Arc::new(signer); - - let account = Account::new(signer_account_id, signer, provider); + let account = example_config::create_account(); let result = account .add_key(new_secret_key.public_key(), None, None, None) diff --git a/near-accounts/examples/account_balance.rs b/near-accounts/examples/account_balance.rs index c858606..bb773d8 100644 --- a/near-accounts/examples/account_balance.rs +++ b/near-accounts/examples/account_balance.rs @@ -1,3 +1,4 @@ +mod example_config; use near_accounts::accounts::get_account_balance; use near_primitives::types::AccountId; use near_providers::JsonRpcProvider; @@ -7,13 +8,21 @@ use std::sync::Arc; async fn main() -> Result<(), Box> { env_logger::init(); - let account_id: AccountId = "contract.near-api-rs.testnet".parse::()?; + let config = example_config::get_test_config(); + let account_id: AccountId = config.near_account.account_id.parse().unwrap(); - let provider = Arc::new(JsonRpcProvider::new("https://rpc.testnet.near.org")); + let provider = Arc::new(JsonRpcProvider::new(&config.rpc_testnet_endpoint)); let result = get_account_balance(provider, account_id).await; - println!("response: {:#?}", result); + match result { + Ok(res) => { + println!("available balance: {:#?}", res.available); + println!("total balance: {:#?}", res.total); + println!("state staked {:#?}", res.state_staked); + } + Err(err) => println!("Error: {:#?}", err), + } Ok(()) } diff --git a/near-accounts/examples/async_tx.rs b/near-accounts/examples/async_tx.rs index 60ebc3d..3638f3a 100644 --- a/near-accounts/examples/async_tx.rs +++ b/near-accounts/examples/async_tx.rs @@ -1,6 +1,5 @@ //! This example uses the transact_advance method to send transaction and check its status -use near_accounts::Account; -use near_crypto::{InMemorySigner, SecretKey}; +mod example_config; use near_primitives::views::TxExecutionStatus; use near_primitives::{types::Gas, views::FinalExecutionOutcomeViewEnum}; use near_providers::jsonrpc_primitives::types::transactions::TransactionInfo; @@ -15,18 +14,13 @@ use tokio::time; #[tokio::main] async fn main() -> Result<(), Box> { env_logger::init(); - - let signer_account_id: AccountId = "near-api-rs.testnet".parse::()?; - let signer_secret_key = "ed25519:29nYmQCZMsQeYtztXZzm57ayQt2uBHXdn2SAjK4ccMGSQaNUFNJ7Aoteno81eKTex9cGBbk1FuDuqJRsdzx34xDY".parse::()?; let contract_id: AccountId = "contract.near-api-rs.testnet".parse::()?; - let signer = InMemorySigner::from_secret_key(signer_account_id.clone(), signer_secret_key); let gas: Gas = 100_000_000_000_000; // Example amount in yoctoNEAR let provider = Arc::new(JsonRpcProvider::new("https://rpc.testnet.near.org")); - let signer = Arc::new(signer); - let account = Account::new(signer_account_id, signer, provider.clone()); + let account = example_config::create_account(); let method_name = "set_status".to_string(); let args_json = json!({"message": "working1"}); @@ -45,13 +39,13 @@ async fn main() -> Result<(), Box> { Ok(res) => match &res.final_execution_outcome { //Final Execution outcome for finality NONE would always be empty. Some(FinalExecutionOutcomeViewEnum::FinalExecutionOutcome(outcome)) => { - println!("Final Exuecution outcome: {:?}", outcome); - println!("Final Exuecution outcome: {:?}", outcome.transaction); + println!("Final Execution outcome: {:?}", outcome); + println!("Final Execution outcome: {:?}", outcome.transaction); } Some(FinalExecutionOutcomeViewEnum::FinalExecutionOutcomeWithReceipt( outcome_receipt, )) => { - println!("Final Exuecution outcome_reciepts: {:?}", outcome_receipt) + println!("Final Execution outcome_receipts: {:?}", outcome_receipt) } None => println!("No Final execution outcome."), }, @@ -79,7 +73,7 @@ async fn main() -> Result<(), Box> { Err(err) => println!("Error: {:#?}", err), } - println!("Time taken for aysnc request: {:?}", t2 - t1); + println!("Time taken for async request: {:?}", t2 - t1); println!("Time taken for status request: {:?}", t4 - t3); Ok(()) } diff --git a/near-accounts/examples/create_account.rs b/near-accounts/examples/create_account.rs index cbb5e95..0503cc6 100644 --- a/near-accounts/examples/create_account.rs +++ b/near-accounts/examples/create_account.rs @@ -1,8 +1,5 @@ -use near_accounts::Account; -use near_crypto::InMemorySigner; +mod example_config; use near_primitives::{types::Gas, views::FinalExecutionOutcomeViewEnum}; -use near_providers::JsonRpcProvider; -use std::sync::Arc; mod utils; use near_primitives::types::{AccountId, Balance}; use serde_json::json; @@ -10,32 +7,25 @@ use serde_json::json; #[tokio::main] async fn main() -> Result<(), Box> { env_logger::init(); - - let signer_account_id: AccountId = utils::input("Enter the signer Account ID: ")?.parse()?; - let signer_secret_key = utils::input("Enter the signer's private key: ")?.parse()?; - //To-do, implement account exist check. let new_account_id: AccountId = utils::input("Enter new account name: ")?.parse()?; - - let signer = InMemorySigner::from_secret_key(signer_account_id.clone(), signer_secret_key); - // Amount to transfer to the new account let gas: Gas = 100_000_000_000_000; // Example amount in yoctoNEAR let amount: Balance = 10_000_000_000_000_000_000_000; // Example amount in yoctoNEAR - let new_secret_key = near_crypto::SecretKey::from_random(near_crypto::KeyType::ED25519); - let provider = Arc::new(JsonRpcProvider::new("https://rpc.testnet.near.org")); - let signer = Arc::new(signer); - - let account = Account::new(signer_account_id, signer, provider); + let account = example_config::create_account(); let contract_id: AccountId = "testnet".parse::()?; let method_name = "create_account".to_string(); + let new_secret_key = near_crypto::SecretKey::from_random(near_crypto::KeyType::ED25519); let args_json = json!({ "new_account_id": new_account_id, "new_public_key": new_secret_key.public_key() }); + println!("New Secret key : {}", new_secret_key); + println!("New Public key: {}", new_secret_key.public_key()); + let result = account .function_call(&contract_id, method_name, args_json, gas, amount) .await? @@ -45,13 +35,13 @@ async fn main() -> Result<(), Box> { match result { Ok(res) => match &res.final_execution_outcome { Some(FinalExecutionOutcomeViewEnum::FinalExecutionOutcome(outcome)) => { - println!("Final Exuecution outcome: {:?}", outcome); - println!("Final Exuecution outcome: {:?}", outcome.transaction); + println!("Final Execution outcome Status: {:?}", outcome.status); + println!("Final Execution Transaction: {:?}", outcome.transaction); } Some(FinalExecutionOutcomeViewEnum::FinalExecutionOutcomeWithReceipt( outcome_receipt, )) => { - println!("Final Exuecution outcome: {:?}", outcome_receipt) + println!("Final Execution outcome receipt: {:?}", outcome_receipt) } None => println!("No Final execution outcome."), }, diff --git a/near-accounts/examples/create_subaccount.rs b/near-accounts/examples/create_subaccount.rs index b9ebee1..268138e 100644 --- a/near-accounts/examples/create_subaccount.rs +++ b/near-accounts/examples/create_subaccount.rs @@ -1,30 +1,21 @@ //use near_providers::Provider; -use near_accounts::Account; -use near_crypto::InMemorySigner; +mod example_config; use near_primitives::types::Balance; -use near_providers::JsonRpcProvider; -use std::sync::Arc; mod utils; use near_primitives::types::AccountId; #[tokio::main] async fn main() -> Result<(), Box> { env_logger::init(); - - let signer_account_id: AccountId = utils::input("Enter the signer Account ID: ")?.parse()?; - let signer_secret_key = utils::input("Enter the signer's private key: ")?.parse()?; let new_account_id: AccountId = utils::input("Enter the account name of new account ")?.parse()?; - let signer = InMemorySigner::from_secret_key(signer_account_id.clone(), signer_secret_key); // Amount to transfer to the new account let amount: Balance = 10_000_000_000_000_000_000_000; // Example amount in yoctoNEAR let new_key_pair = near_crypto::SecretKey::from_random(near_crypto::KeyType::ED25519); - let provider = Arc::new(JsonRpcProvider::new("https://rpc.testnet.near.org")); - let signer = Arc::new(signer); - let account = Account::new(signer_account_id, signer, provider); + let account = example_config::create_account(); // Call create_account let result = account .create_account(&new_account_id, new_key_pair.public_key(), amount) diff --git a/near-accounts/examples/delete_account.rs b/near-accounts/examples/delete_account.rs index cc8cf37..74faa46 100644 --- a/near-accounts/examples/delete_account.rs +++ b/near-accounts/examples/delete_account.rs @@ -1,26 +1,15 @@ -use near_accounts::Account; -use near_crypto::InMemorySigner; -use near_providers::JsonRpcProvider; -use std::sync::Arc; +mod example_config; mod utils; use near_primitives::types::AccountId; #[tokio::main] async fn main() -> Result<(), Box> { env_logger::init(); + let beneficiary_account_id: AccountId = + utils::input("Enter the account name where you want to transfer current account balance before deleting it")?.parse()?; + let account = example_config::create_account(); - let signer_account_id: AccountId = utils::input("Enter the signer Account ID: ")?.parse()?; - let signer_secret_key = utils::input("Enter the signer's private key: ")?.parse()?; - let user_account_id: AccountId = - utils::input("Enter the account name which need to be deleted ")?.parse()?; - let signer = InMemorySigner::from_secret_key(signer_account_id.clone(), signer_secret_key); - - let provider = Arc::new(JsonRpcProvider::new("https://rpc.testnet.near.org")); - let signer = Arc::new(signer); - - let account = Account::new(signer_account_id, signer, provider); - - let response = account.delete_account(user_account_id.clone()).await; + let response = account.delete_account(beneficiary_account_id.clone()).await; match response { Ok(res) => { diff --git a/near-accounts/examples/delete_key.rs b/near-accounts/examples/delete_key.rs index c019940..9f932f9 100644 --- a/near-accounts/examples/delete_key.rs +++ b/near-accounts/examples/delete_key.rs @@ -1,22 +1,11 @@ -use near_accounts::Account; -use near_crypto::{InMemorySigner, PublicKey}; -use near_providers::JsonRpcProvider; -use std::sync::Arc; +mod example_config; +use near_crypto::PublicKey; mod utils; -use near_primitives::types::AccountId; #[tokio::main] async fn main() -> Result<(), Box> { env_logger::init(); - - let signer_account_id: AccountId = utils::input("Enter the signer Account ID: ")?.parse()?; - let signer_secret_key = utils::input("Enter the signer's private key: ")?.parse()?; - let signer = InMemorySigner::from_secret_key(signer_account_id.clone(), signer_secret_key); - - let provider = Arc::new(JsonRpcProvider::new("https://rpc.testnet.near.org")); - let signer = Arc::new(signer); - - let account = Account::new(signer_account_id, signer, provider); + let account = example_config::create_account(); let public_key: PublicKey = "ed25519:EohEtHT8Dt8jURC3DcJ661hWCx6ExPRtDV82FpT4jfNB".parse::()?; diff --git a/near-accounts/examples/deploy_contract.rs b/near-accounts/examples/deploy_contract.rs index 4760fed..f25ca6a 100644 --- a/near-accounts/examples/deploy_contract.rs +++ b/near-accounts/examples/deploy_contract.rs @@ -1,35 +1,14 @@ use near_accounts::Account; -use near_crypto::InMemorySigner; -use near_providers::JsonRpcProvider; -use std::sync::Arc; +mod example_config; mod utils; -use near_primitives::types::AccountId; -use std::fs::File; -use std::io; -use std::io::Read; - -fn read_wasm_file() -> io::Result> { - let file_path = "examples/contract-wasm/status_message.wasm"; - let mut file = File::open(file_path)?; - let mut contents = Vec::new(); - file.read_to_end(&mut contents)?; - Ok(contents) -} #[tokio::main] async fn main() -> Result<(), Box> { env_logger::init(); - let signer_account_id: AccountId = utils::input("Enter the signer Account ID: ")?.parse()?; - let signer_secret_key = utils::input("Enter the signer's private key: ")?.parse()?; - let signer = InMemorySigner::from_secret_key(signer_account_id.clone(), signer_secret_key); - - let provider = Arc::new(JsonRpcProvider::new("https://rpc.testnet.near.org")); - let signer = Arc::new(signer); + let account: Account = example_config::create_account(); - let account = Account::new(signer_account_id, signer, provider); - - let wasm_code = read_wasm_file()?; + let wasm_code = example_config::read_wasm_file()?; let response = account.deploy_contract(&wasm_code).await; @@ -43,8 +22,3 @@ async fn main() -> Result<(), Box> { } Ok(()) } - -// New Account ID: contract.near-api-rs.testnet -// Secret Key: ed25519:2ytXTGiGkMfpdW1JujZNebTCKRFQAFqq89fbkq9akBXy8kqqfhTqUCzmDexeNrCD1sjijMATdPWKzyCj9XnteFgN -// Public Key: ed25519:4mKgZ8e9PgSJvrVtJ4omkgmPR7ssgpCPGc2N5AGWkhfQ -// Deposit: 10000000000000000000000 diff --git a/near-accounts/examples/example_config.rs b/near-accounts/examples/example_config.rs new file mode 100644 index 0000000..1a289b2 --- /dev/null +++ b/near-accounts/examples/example_config.rs @@ -0,0 +1,62 @@ +use near_accounts::Account; +use near_crypto::{InMemorySigner, SecretKey}; +use near_primitives::types::AccountId; +use near_providers::JsonRpcProvider; +use serde::Deserialize; +use std::fs; +use std::fs::File; +use std::io; +use std::io::Read; +use std::sync::Arc; + +#[derive(Debug, Deserialize)] +#[serde()] +pub struct TestConfig { + pub near_account: AccountConfig, + pub rpc_testnet_endpoint: String, + pub contract_account: AccountConfig, +} + +#[derive(Debug, Deserialize)] +#[serde()] +pub struct AccountConfig { + pub account_id: String, + pub secret_key: String, + pub public_key: String, +} + +pub fn get_test_config() -> TestConfig { + let the_file = "examples/resources/config/test_config.json"; + + let data = fs::read_to_string(the_file).expect("Unable to read file"); + let test_config: TestConfig = + serde_json::from_str(data.as_str()).expect("JSON was not well-formatted"); + return test_config; +} + +#[allow(dead_code)] +pub fn create_account() -> Account { + let config = get_test_config(); + let signer_account_id: AccountId = config.near_account.account_id.parse().unwrap(); + let signer_secret_key: SecretKey = config.near_account.secret_key.parse().unwrap(); + let signer = InMemorySigner::from_secret_key(signer_account_id.clone(), signer_secret_key); + + let provider = Arc::new(JsonRpcProvider::new(config.rpc_testnet_endpoint.as_str())); + let signer = Arc::new(signer); + + return Account::new(signer_account_id, signer, provider); +} + +#[allow(dead_code)] +pub fn read_wasm_file() -> io::Result> { + let file_path = "examples/resources/contract-wasm/status_message.wasm"; + let mut file = File::open(file_path)?; + let mut contents = Vec::new(); + file.read_to_end(&mut contents)?; + Ok(contents) +} + +#[allow(dead_code)] +fn main() { + panic!("not a binary") +} diff --git a/near-accounts/examples/function_call.rs b/near-accounts/examples/function_call.rs index b6b8f54..f0cba84 100644 --- a/near-accounts/examples/function_call.rs +++ b/near-accounts/examples/function_call.rs @@ -1,3 +1,4 @@ +mod example_config; use near_accounts::Account; use near_crypto::InMemorySigner; use near_crypto::SecretKey; @@ -12,14 +13,16 @@ use serde_json::json; async fn main() -> Result<(), Box> { env_logger::init(); - let signer_account_id: AccountId = "near-api-rs.testnet".parse::()?; - let signer_secret_key = "ed25519:29nYmQCZMsQeYtztXZzm57ayQt2uBHXdn2SAjK4ccMGSQaNUFNJ7Aoteno81eKTex9cGBbk1FuDuqJRsdzx34xDY".parse::()?; - let contract_id: AccountId = "contract.near-api-rs.testnet".parse::()?; + let config = example_config::get_test_config(); + let signer_account_id: AccountId = config.near_account.account_id.parse().unwrap(); + let signer_secret_key: SecretKey = config.near_account.secret_key.parse().unwrap(); + + let contract_id: AccountId = config.contract_account.account_id.parse().unwrap(); let signer = InMemorySigner::from_secret_key(signer_account_id.clone(), signer_secret_key); let gas: Gas = 100_000_000_000_000; // Example amount in yoctoNEAR - let provider = Arc::new(JsonRpcProvider::new("https://rpc.testnet.near.org")); + let provider = Arc::new(JsonRpcProvider::new(config.rpc_testnet_endpoint.as_str())); let signer = Arc::new(signer); let account = Account::new(signer_account_id, signer, provider); diff --git a/near-accounts/examples/get_access_key.rs b/near-accounts/examples/get_access_key.rs index 2e1a306..3f9e0bc 100644 --- a/near-accounts/examples/get_access_key.rs +++ b/near-accounts/examples/get_access_key.rs @@ -8,11 +8,9 @@ async fn main() -> Result<(), Box> { env_logger::init(); let account_id: AccountId = "near-api-rs.testnet".parse::()?; - let provider = Arc::new(JsonRpcProvider::new("https://rpc.testnet.near.org")); let result = get_access_key(provider, account_id).await; - println!("response: {:#?}", result); Ok(()) diff --git a/near-accounts/examples/resources/config/test_config.json b/near-accounts/examples/resources/config/test_config.json new file mode 100644 index 0000000..34cd341 --- /dev/null +++ b/near-accounts/examples/resources/config/test_config.json @@ -0,0 +1,13 @@ +{ + "near_account": { + "account_id": "near-api-rs-1.testnet", + "secret_key": "ed25519:3pYeqbRyzJ3rKrdXszMPNQosw7yGZguTSsQQ8Vzc4sEHSo1cpdwxWxe5SkqNDNgX6SfWtqu8YaYtfXNMWhFPZRsA", + "public_key": "ed25519:7ceeaCPaQPCw28GLFZPW76MmyzRgW6Y6V1Ymquok7P4x" + }, + "rpc_testnet_endpoint": "https://rpc.testnet.near.org", + "contract_account": { + "account_id": "contract.near-api-rs.testnet", + "secret_key": "ed25519:2ytXTGiGkMfpdW1JujZNebTCKRFQAFqq89fbkq9akBXy8kqqfhTqUCzmDexeNrCD1sjijMATdPWKzyCj9XnteFgN", + "public_key": "ed25519:4mKgZ8e9PgSJvrVtJ4omkgmPR7ssgpCPGc2N5AGWkhfQ" + } +} \ No newline at end of file diff --git a/near-accounts/examples/contract-wasm/status_message.wasm b/near-accounts/examples/resources/contract-wasm/status_message.wasm similarity index 100% rename from near-accounts/examples/contract-wasm/status_message.wasm rename to near-accounts/examples/resources/contract-wasm/status_message.wasm diff --git a/near-accounts/examples/send_money.rs b/near-accounts/examples/send_money.rs index 9a77467..d4f45fe 100644 --- a/near-accounts/examples/send_money.rs +++ b/near-accounts/examples/send_money.rs @@ -1,28 +1,18 @@ -use near_accounts::Account; -use near_crypto::InMemorySigner; +mod example_config; use near_primitives::types::Balance; -use near_providers::JsonRpcProvider; -use std::sync::Arc; mod utils; use near_primitives::types::AccountId; #[tokio::main] async fn main() -> Result<(), Box> { env_logger::init(); - - let signer_account_id: AccountId = utils::input("Enter the signer Account ID: ")?.parse()?; - let signer_secret_key = utils::input("Enter the signer's private key: ")?.parse()?; let receiver_account_id: AccountId = utils::input("Enter the account name of receiver account ")?.parse()?; - let signer = InMemorySigner::from_secret_key(signer_account_id.clone(), signer_secret_key); // Amount to transfer to the receiver account let amount: Balance = 10_000_000_000; // Example amount in yoctoNEAR - let provider = Arc::new(JsonRpcProvider::new("https://rpc.testnet.near.org")); - let signer = Arc::new(signer); - - let account = Account::new(signer_account_id, signer, provider); + let account = example_config::create_account(); // Call create_account let result = account.send_money(&receiver_account_id, amount).await; diff --git a/near-accounts/examples/view_function.rs b/near-accounts/examples/view_function.rs index bc1a18a..1e8b0ed 100644 --- a/near-accounts/examples/view_function.rs +++ b/near-accounts/examples/view_function.rs @@ -15,7 +15,13 @@ async fn single_thread() -> Result<(), Box> { let result = view_function(provider, contract_id, method_name, args_json).await; - println!("response: {:#?}", result); + match result { + Ok(res) => match std::str::from_utf8(&res.result) { + Ok(str_result) => println!("{}", str_result), + Err(err) => println!("Error converting result to string: {:#?}", err), + }, + Err(err) => println!("Error: {:#?}", err), + } Ok(()) } @@ -30,7 +36,13 @@ async fn multi_thread() -> Result<(), Box> { let handle = tokio::spawn(async move { let result = view_function(provider, contract_id, method_name, args_json).await; - println!("response: {:#?}", result); + match result { + Ok(res) => match std::str::from_utf8(&res.result) { + Ok(str_result) => println!("{}", str_result), + Err(err) => println!("Error converting result to string: {:#?}", err), + }, + Err(err) => println!("Error: {:#?}", err), + } }); // You can do more work here or wait for the handle if needed diff --git a/near-accounts/src/accounts.rs b/near-accounts/src/accounts.rs index b677084..d18c8a6 100644 --- a/near-accounts/src/accounts.rs +++ b/near-accounts/src/accounts.rs @@ -4,113 +4,23 @@ //! making it easier to perform account-related operations. use crate::access_keys::{full_access_key, function_call_access_key}; +use crate::transaction_sender::TransactionSender; use near_crypto::{PublicKey, Signer}; use near_primitives::account::AccessKey; -use near_primitives::hash::CryptoHash; use near_primitives::transaction::SignedTransaction; use near_primitives::types::{AccountId, Balance, BlockReference, Finality, Gas}; -use near_primitives::views::{FinalExecutionOutcomeView, QueryRequest, TxExecutionStatus}; +use near_primitives::views::{FinalExecutionOutcomeView, QueryRequest}; use near_providers::types::query::{QueryResponseKind, RpcQueryResponse}; -use near_providers::types::transactions::RpcTransactionResponse; use near_providers::Provider; -use near_transactions::TransactionBuilder; +use near_transactions::{ActionBuilder, TransactionBuilder}; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::ops::{Add, Mul, Sub}; use std::sync::Arc; -type ArcProviderSendSync = Arc; -type ArcSignerSendSync = Arc; - -///This struct represent a Transaction Sender used specifically if you want to send transactions manually. -/// This gives user more control over how they want to send their transactions to the NEAR network for examples, asyn, sync or advanced. -/// It is only used by function_call method from Account for now to enable this flexibility. -#[derive(Clone)] -pub struct TransactionSender { - pub signed_transaction: SignedTransaction, - provider: ArcProviderSendSync, -} - -impl TransactionSender { - /// Constructs a new `TransactionSender` instance. - /// - /// # Arguments - /// - /// * `signed_transaction` - Signed transaction to be sent to the NEAR chain. - /// * `provider` - A provider instance for interacting with the blockchain. - /// - /// # Returns - /// - /// A new `Account` instance. - pub fn new(signed_transaction: SignedTransaction, provider: ArcProviderSendSync) -> Self { - Self { - signed_transaction, - provider, - } - } - - ///Send your transaction to the NEAR blockchain synchronously using the send_tx RPC end point and default wait_until value - pub async fn transact(self) -> Result> { - self.provider - .send_tx(self.signed_transaction, TxExecutionStatus::default()) - .await - .map_err(|e| Box::new(e) as Box) - } - - ///Send your transaction to the NEAR blockchain asynchronously using the send_tx RPC end point and default wait_until None. - pub async fn transact_async( - self, - ) -> Result> { - self.provider - .send_tx(self.signed_transaction, TxExecutionStatus::None) - .await - .map_err(|e| Box::new(e) as Box) - } - - ///Send your transaction to the NEAR blockchain using the send_tx RPC end point and custom wait_until value. - /// Different wait_until values and what they mean: - /// - /// * None - /// Transaction is waiting to be included into the block - /// - /// * Included - /// Transaction is included into the block. The block may be not finalised yet - /// - /// * ExecutedOptimistic, - /// Transaction is included into the block + - /// All the transaction receipts finished their execution. - /// The corresponding blocks for tx and each receipt may be not finalised yet - /// It is also the default value unless defined otherwise. - /// - /// * IncludedFinal - /// Transaction is included into finalised block - /// - /// * Executed - /// Transaction is included into finalised block + - /// All the transaction receipts finished their execution. - /// The corresponding blocks for each receipt may be not finalised yet - /// - /// * Final - /// Transaction is included into finalised block + - /// Execution of transaction receipts is finalised - pub async fn transact_advanced( - self, - wait_until_str: &str, - ) -> Result> { - let wait_until: TxExecutionStatus = - serde_json::from_value(serde_json::json!(wait_until_str))?; - self.provider - .send_tx(self.signed_transaction, wait_until) - .await - .map_err(|e| Box::new(e) as Box) - } - - /// Returns transaction hash for a given signed transaction - pub fn get_transaction_hash(self) -> Result> { - Ok(self.signed_transaction.get_hash()) - } -} +pub type ArcProviderSendSync = Arc; +pub type ArcSignerSendSync = Arc; /// Represents a NEAR account, encapsulating account ID, signer, and provider for blockchain interaction. pub struct Account { @@ -152,19 +62,21 @@ impl Account { } } - /// Prepares a `TransactionBuilder` for constructing a transaction. + /// Create a `SignedTransaction` for constructing a signed transaction. /// /// # Arguments /// - /// * `receiver_id` - The account ID of the transaction's receiver. + /// * `receiver_id: account_id of the receiver` + /// * `actions: vector of actions which needs to be performed`. /// /// # Returns /// - /// A result containing a `TransactionBuilder` instance or an error if fetching the nonce or block hash failed. - async fn get_transaction_builder( + /// A result containing a `SignedTransaction` instance or an error if fetching the nonce or block hash failed. + async fn create_signed_transaction( &self, receiver_id: &AccountId, - ) -> Result> { + actions: &ActionBuilder, + ) -> Result> { // Fetch the current nonce for the signer account and latest block hash let nonce = self .fetch_nonce(&self.account_id, &self.signer.public_key()) @@ -176,14 +88,16 @@ impl Account { let block_hash = block.header.hash; // Use TransactionBuilder to construct the transaction - let signed_tx = TransactionBuilder::new( + let signed_transaction: SignedTransaction = TransactionBuilder::new( self.account_id.clone(), self.signer.public_key(), receiver_id.clone(), nonce + 1, block_hash, - ); - Ok(signed_tx) + ) + .set_action(&actions.build()) + .sign_transaction(&*self.signer); + Ok(signed_transaction) } /// Fetches the current nonce for an account's access key. @@ -236,17 +150,19 @@ impl Account { public_key: PublicKey, amount: Balance, ) -> Result> { - // Use TransactionBuilder to construct the transaction - let signed_tx = &self - .get_transaction_builder(new_account_id) - .await? + let mut action_builder = ActionBuilder::new(); + action_builder .create_account() .transfer(amount) - .add_key(public_key, full_access_key()) - .sign_transaction(&*self.signer); // Sign the transaction + .add_key(public_key.clone(), full_access_key()); + + let signed_txn = self + .create_signed_transaction(new_account_id, &action_builder) + .await?; + // Use TransactionBuilder to construct the transaction // Send the transaction - let transaction_result = self.provider.send_transaction(signed_tx.clone()).await?; + let transaction_result = self.provider.send_transaction(signed_txn.clone()).await?; Ok(transaction_result) } @@ -280,12 +196,13 @@ impl Account { None => full_access_key(), }; - // Use TransactionBuilder to construct the transaction let signed_tx = self - .get_transaction_builder(&self.account_id) - .await? - .add_key(public_key, access_key) - .sign_transaction(&*self.signer); // Sign the transaction + .create_signed_transaction( + &self.account_id, + ActionBuilder::new().add_key(public_key, access_key), + ) + .await?; + // Use TransactionBuilder to construct the transaction // Send the transaction let transaction_result = self.provider.send_transaction(signed_tx).await; @@ -308,12 +225,12 @@ impl Account { &self, public_key: PublicKey, ) -> Result> { - // Use TransactionBuilder to construct the transaction let signed_tx = self - .get_transaction_builder(&self.account_id) - .await? - .delete_key(public_key) - .sign_transaction(&*self.signer); // Sign the transaction + .create_signed_transaction( + &self.account_id, + ActionBuilder::new().delete_key(public_key), + ) + .await?; // Send the transaction let transaction_result = self.provider.send_transaction(signed_tx).await; @@ -336,12 +253,12 @@ impl Account { &self, byte_code: &[u8], ) -> Result> { - // Use TransactionBuilder to construct the transaction let signed_tx = self - .get_transaction_builder(&self.account_id) - .await? - .deploy_contract(byte_code) - .sign_transaction(&*self.signer); // Sign the transaction + .create_signed_transaction( + &self.account_id, + ActionBuilder::new().deploy_contract(byte_code), + ) + .await?; // Send the transaction let transaction_result = self.provider.send_transaction(signed_tx).await; @@ -364,13 +281,12 @@ impl Account { &self, beneficiary_id: AccountId, ) -> Result> { - // Use TransactionBuilder to construct the transaction let signed_tx = self - .get_transaction_builder(&self.account_id) - .await? - .delete_account(beneficiary_id) - .sign_transaction(&*self.signer); // Sign the transaction - + .create_signed_transaction( + &self.account_id, + ActionBuilder::new().delete_account(beneficiary_id), + ) + .await?; // Send the transaction let transaction_result = self.provider.send_transaction(signed_tx).await; match transaction_result { @@ -395,13 +311,9 @@ impl Account { receiver_id: &AccountId, amount: Balance, ) -> Result> { - // Use TransactionBuilder to construct the transaction let signed_tx = self - .get_transaction_builder(receiver_id) - .await? - .transfer(amount) - .sign_transaction(&*self.signer); // Sign the transaction - + .create_signed_transaction(receiver_id, ActionBuilder::new().transfer(amount)) + .await?; // Send the transaction let transaction_result = self.provider.send_transaction(signed_tx).await; match transaction_result { @@ -434,13 +346,12 @@ impl Account { // Serialize the JSON to a Vec let args = serde_json::to_vec(&args)?; - // Use TransactionBuilder to construct the transaction let signed_tx = self - .get_transaction_builder(contract_id) - .await? - .function_call(method_name, args, gas, deposit) - .sign_transaction(&*self.signer); // Sign the transaction - + .create_signed_transaction( + contract_id, + ActionBuilder::new().function_call(method_name, args, gas, deposit), + ) + .await?; // To-do. Needs error handling here. Ok(TransactionSender::new(signed_tx, self.provider.clone())) } diff --git a/near-accounts/src/lib.rs b/near-accounts/src/lib.rs index 790c1d8..6869daa 100644 --- a/near-accounts/src/lib.rs +++ b/near-accounts/src/lib.rs @@ -76,3 +76,4 @@ pub use crate::accounts::Account; mod access_keys; pub mod accounts; +mod transaction_sender; diff --git a/near-accounts/src/transaction_sender.rs b/near-accounts/src/transaction_sender.rs new file mode 100644 index 0000000..2a76006 --- /dev/null +++ b/near-accounts/src/transaction_sender.rs @@ -0,0 +1,94 @@ +use crate::accounts::ArcProviderSendSync; +use near_primitives::hash::CryptoHash; +use near_primitives::transaction::SignedTransaction; +use near_primitives::views::TxExecutionStatus; +use near_providers::types::transactions::RpcTransactionResponse; + +///This struct represent a Transaction Sender used specifically if you want to send transactions manually. +/// This gives user more control over how they want to send their transactions to the NEAR network for examples, asyn, sync or advanced. +/// It is only used by function_call method from Account for now to enable this flexibility. +#[derive(Clone)] +pub struct TransactionSender { + pub signed_transaction: SignedTransaction, + provider: ArcProviderSendSync, +} + +impl TransactionSender { + /// Constructs a new `TransactionSender` instance. + /// + /// # Arguments + /// + /// * `signed_transaction` - Signed transaction to be sent to the NEAR chain. + /// * `provider` - A provider instance for interacting with the blockchain. + /// + /// # Returns + /// + /// A new `Account` instance. + pub fn new(signed_transaction: SignedTransaction, provider: ArcProviderSendSync) -> Self { + Self { + signed_transaction, + provider, + } + } + + ///Send your transaction to the NEAR blockchain synchronously using the send_tx RPC end point and default wait_until value + pub async fn transact(self) -> Result> { + self.provider + .send_tx(self.signed_transaction, TxExecutionStatus::default()) + .await + .map_err(|e| Box::new(e) as Box) + } + + ///Send your transaction to the NEAR blockchain asynchronously using the send_tx RPC end point and default wait_until None. + pub async fn transact_async( + self, + ) -> Result> { + self.provider + .send_tx(self.signed_transaction, TxExecutionStatus::None) + .await + .map_err(|e| Box::new(e) as Box) + } + + ///Send your transaction to the NEAR blockchain using the send_tx RPC end point and custom wait_until value. + /// Different wait_until values and what they mean: + /// + /// * None + /// Transaction is waiting to be included into the block + /// + /// * Included + /// Transaction is included into the block. The block may be not finalised yet + /// + /// * ExecutedOptimistic, + /// Transaction is included into the block + + /// All the transaction receipts finished their execution. + /// The corresponding blocks for tx and each receipt may be not finalized yet + /// It is also the default value unless defined otherwise. + /// + /// * IncludedFinal + /// Transaction is included into finalized block + /// + /// * Executed + /// Transaction is included into finalized block + + /// All the transaction receipts finished their execution. + /// The corresponding blocks for each receipt may be not finalized yet + /// + /// * Final + /// Transaction is included into finalize block + + /// Execution of transaction receipts is finalized + pub async fn transact_advanced( + self, + wait_until_str: &str, + ) -> Result> { + let wait_until: TxExecutionStatus = + serde_json::from_value(serde_json::json!(wait_until_str))?; + self.provider + .send_tx(self.signed_transaction, wait_until) + .await + .map_err(|e| Box::new(e) as Box) + } + + /// Returns transaction hash for a given signed transaction + pub fn get_transaction_hash(self) -> Result> { + Ok(self.signed_transaction.get_hash()) + } +} diff --git a/near-transactions/Cargo.toml b/near-transactions/Cargo.toml index dc38697..7026e2a 100644 --- a/near-transactions/Cargo.toml +++ b/near-transactions/Cargo.toml @@ -9,4 +9,11 @@ license = "MIT OR Apache-2.0" [dependencies] near-crypto = "0.21.1" -near-primitives = "0.21.1" \ No newline at end of file +near-primitives = "0.21.1" +serde = "1.0.197" +serde_json = "1.0.85" + +[dev-dependencies] +tokio = { version = "1", features = ["full", "test-util"] } +env_logger = "0.11.3" +near-providers = { path = "../near-providers", version = "0.1.0-alpha" } \ No newline at end of file diff --git a/near-transactions/examples/function_call.rs b/near-transactions/examples/function_call.rs new file mode 100644 index 0000000..25677d6 --- /dev/null +++ b/near-transactions/examples/function_call.rs @@ -0,0 +1,74 @@ +use near_crypto::{InMemorySigner, PublicKey, SecretKey}; +use near_primitives::transaction::Transaction; +use near_primitives::types::{AccountId, BlockReference, Finality, Gas}; +use near_primitives::views::QueryRequest; +use near_providers::types::query::{QueryResponseKind, RpcQueryResponse}; +use near_providers::JsonRpcProvider; +use near_providers::Provider; +use near_transactions::ActionBuilder; +use serde_json::json; + +#[tokio::main] +async fn main() -> Result<(), Box> { + env_logger::init(); + + let provider = JsonRpcProvider::new("https://rpc.testnet.near.org"); + + //create a function call Action + let method_name = "set_status".to_string(); + let args_json = json!({"message": "meta_transactions"}); + let gas: Gas = 100_000_000_000_000; // Example amount in yoctoNEAR + let args = serde_json::to_vec(&args_json)?; + let actions = ActionBuilder::new() + .function_call(method_name, args, gas, 0) + .build(); + + let contract_id: AccountId = "contract.near-api-rs.testnet".parse::()?; + + let signer_account_id: AccountId = "near-api-rs-1.testnet".parse::()?; + let signer_secret_key = "ed25519:3pYeqbRyzJ3rKrdXszMPNQosw7yGZguTSsQQ8Vzc4sEHSo1cpdwxWxe5SkqNDNgX6SfWtqu8YaYtfXNMWhFPZRsA".parse::()?; + let signer = InMemorySigner::from_secret_key(signer_account_id.clone(), signer_secret_key); + + let nonce = fetch_nonce(&signer_account_id, &signer.public_key, &provider).await?; + + //Block hash + let block_reference = BlockReference::Finality(Finality::Final); + let block = provider.block(block_reference).await?; + let block_hash = block.header.hash; + + let tx: Transaction = Transaction { + signer_id: signer_account_id, + public_key: signer.clone().public_key, + nonce: nonce + 1, + receiver_id: contract_id, + block_hash, + actions, + }; + + let signed_tx = tx.sign(&signer); + let result = provider.send_transaction(signed_tx).await; + println!("response: {:#?}", result); + + Ok(()) +} + +pub async fn fetch_nonce( + account_id: &AccountId, + public_key: &PublicKey, + provider: &dyn Provider, +) -> Result> { + let query_request = QueryRequest::ViewAccessKey { + account_id: account_id.clone(), + public_key: public_key.clone(), + }; + + // Send the query to the NEAR blockchain + let response: RpcQueryResponse = provider.query(query_request).await?; + + // Extract the access key view from the response + if let QueryResponseKind::AccessKey(access_key_view) = response.kind { + Ok(access_key_view.nonce) + } else { + Err("Unexpected response kind".into()) + } +} diff --git a/near-transactions/examples/meta_transaction.rs b/near-transactions/examples/meta_transaction.rs new file mode 100644 index 0000000..8b0e71d --- /dev/null +++ b/near-transactions/examples/meta_transaction.rs @@ -0,0 +1,108 @@ +use near_crypto::{InMemorySigner, PublicKey, SecretKey}; +use near_primitives::types::{AccountId, BlockReference, Finality, Gas}; +use near_primitives::views::QueryRequest; +use near_providers::types::query::{QueryResponseKind, RpcQueryResponse}; +use near_providers::JsonRpcProvider; +use near_providers::Provider; +use near_transactions::ActionBuilder; +use near_transactions::{create_delegate_action, create_signed_delegate_action}; +use serde_json::json; + +#[tokio::main] +async fn main() -> Result<(), Box> { + env_logger::init(); + + let provider = JsonRpcProvider::new("https://rpc.testnet.near.org"); + + let inner_signer_account_id: AccountId = "near-api-rs-inner.testnet".parse::()?; + let inner_signer_secret_key = "ed25519:216VjYT45eFkeVEjNLWvfdqhMKuiZnu31UoWxkXzoFHCMvbYThcvc5hRkRfY9FWraWo4NAZnHEXSXqvdY3EzXQaW".parse::()?; + let inner_signer = + InMemorySigner::from_secret_key(inner_signer_account_id.clone(), inner_signer_secret_key); + + let method_name = "set_status".to_string(); + let args_json = json!({"message": "meta_transactions"}); + let gas: Gas = 100_000_000_000_000; // Example amount in yoctoNEAR + + let args = serde_json::to_vec(&args_json)?; + let actions = ActionBuilder::new() + .function_call(method_name, args, gas, 0) + .build(); + + let contract_id: AccountId = "contract.near-api-rs.testnet".parse::()?; + + let inner_nonce = fetch_nonce( + &inner_signer_account_id, + &inner_signer.public_key, + &provider, + ) + .await?; + + //Block hash + let block_reference = BlockReference::Finality(Finality::Final); + let block = provider.block(block_reference).await?; + let block_height = block.header.height; + + let delegate_action = create_delegate_action( + inner_signer_account_id.clone(), + contract_id, + actions, + inner_nonce + 1, + block_height + 100, + inner_signer.clone().public_key, + ); + + let signed_delegate_action = create_signed_delegate_action(delegate_action, &inner_signer); + + let outer_signer_account_id: AccountId = "near-api-rs-1.testnet".parse::()?; + let outer_signer_secret_key = "ed25519:3pYeqbRyzJ3rKrdXszMPNQosw7yGZguTSsQQ8Vzc4sEHSo1cpdwxWxe5SkqNDNgX6SfWtqu8YaYtfXNMWhFPZRsA".parse::()?; + + let outer_signer = + InMemorySigner::from_secret_key(outer_signer_account_id.clone(), outer_signer_secret_key); + + let outer_nonce = fetch_nonce( + &outer_signer_account_id, + &outer_signer.public_key, + &provider, + ) + .await?; + + let block_hash = block.header.hash; + + let tx = near_primitives::transaction::Transaction { + signer_id: outer_signer_account_id, + public_key: outer_signer.clone().public_key, + nonce: outer_nonce + 1, + block_hash, + receiver_id: inner_signer_account_id, + actions: vec![signed_delegate_action], + }; + + let signed_tx = tx.sign(&outer_signer); + + let result = provider.send_transaction(signed_tx).await; + + println!("response: {:#?}", result); + + Ok(()) +} + +pub async fn fetch_nonce( + account_id: &AccountId, + public_key: &PublicKey, + provider: &dyn Provider, +) -> Result> { + let query_request = QueryRequest::ViewAccessKey { + account_id: account_id.clone(), + public_key: public_key.clone(), + }; + + // Send the query to the NEAR blockchain + let response: RpcQueryResponse = provider.query(query_request).await?; + + // Extract the access key view from the response + if let QueryResponseKind::AccessKey(access_key_view) = response.kind { + Ok(access_key_view.nonce) + } else { + Err("Unexpected response kind".into()) + } +} diff --git a/near-transactions/src/action_builder.rs b/near-transactions/src/action_builder.rs new file mode 100644 index 0000000..32dc1ed --- /dev/null +++ b/near-transactions/src/action_builder.rs @@ -0,0 +1,116 @@ +//! Provides a builder pattern for constructing NEAR blockchain transactions. +//! +//! The `ActionBuilder` facilitates the addition for various actions on the NEAR blockchain, +//! such as transferring tokens, creating accounts, deploying contracts, and more. It abstracts away the complexities +//! involved in constructing transactions manually, ensuring that transactions are built correctly before submission. +//! +//! With `ActionBuilder`, users can dynamically add actions to a transaction and chain these actions together +//! in a fluent API style. After constructing a transaction, it can be signed with a `Signer` implementation, +//! producing a `SignedTransaction` that is ready for submission to the NEAR blockchain. +//! +//! This module aims to simplify transaction creation and enhance developer experience by providing a clear and concise +//! way to interact with the NEAR blockchain programmatically. +use near_crypto::PublicKey; +use near_primitives::{ + account::AccessKey, + action::StakeAction, + transaction::{ + Action, AddKeyAction, CreateAccountAction, DeleteAccountAction, DeleteKeyAction, + DeployContractAction, FunctionCallAction, TransferAction, + }, + types::{AccountId, Balance, Gas}, +}; + +use serde::{Deserialize, Serialize}; + +// Define the ActionBuilder struct +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct ActionBuilder { + actions: Vec, +} + +impl Default for ActionBuilder { + fn default() -> Self { + Self::new() + } +} + +impl ActionBuilder { + // Constructor for ActionBuilder + pub fn new() -> Self { + Self { + actions: Vec::new(), + } + } + + // Methods to add various actions to the builder + pub fn create_account(&mut self) -> &mut Self { + self.actions + .push(Action::CreateAccount(CreateAccountAction {})); + self + } + + pub fn deploy_contract(&mut self, code: &[u8]) -> &mut Self { + self.actions + .push(Action::DeployContract(DeployContractAction { + code: code.to_vec(), + })); + self + } + + pub fn function_call( + &mut self, + method_name: String, + args: Vec, + gas: Gas, + deposit: Balance, + ) -> &mut Self { + self.actions + .push(Action::FunctionCall(Box::new(FunctionCallAction { + method_name, + args, + gas, + deposit, + }))); + self + } + + pub fn transfer(&mut self, deposit: Balance) -> &mut Self { + self.actions + .push(Action::Transfer(TransferAction { deposit })); + self + } + + pub fn stake(&mut self, stake: Balance, public_key: PublicKey) -> &mut Self { + self.actions + .push(Action::Stake(Box::new(StakeAction { stake, public_key }))); + self + } + + pub fn add_key(&mut self, public_key: PublicKey, access_key: AccessKey) -> &mut Self { + self.actions.push(Action::AddKey(Box::new(AddKeyAction { + public_key, + access_key, + }))); + self + } + + pub fn delete_key(&mut self, public_key: PublicKey) -> &mut Self { + self.actions + .push(Action::DeleteKey(Box::new(DeleteKeyAction { public_key }))); + self + } + + pub fn delete_account(&mut self, beneficiary_id: AccountId) -> &mut Self { + self.actions + .push(Action::DeleteAccount(DeleteAccountAction { + beneficiary_id, + })); + self + } + + // Build method to finalize and retrieve the actions + pub fn build(&self) -> Vec { + self.clone().actions + } +} diff --git a/near-transactions/src/delegate_action.rs b/near-transactions/src/delegate_action.rs new file mode 100644 index 0000000..e1afd6c --- /dev/null +++ b/near-transactions/src/delegate_action.rs @@ -0,0 +1,57 @@ +use near_crypto::{PublicKey, Signer}; +use near_primitives::{ + action::delegate::{DelegateAction, NonDelegateAction, SignedDelegateAction}, + transaction::Action, + types::{AccountId, BlockHeight, Nonce}, +}; + +//DelegateAction looks like a transaction actually. +// pub struct DelegateAction { +// /// Signer of the delegated actions +// pub sender_id: AccountId, +// /// Receiver of the delegated actions. +// pub receiver_id: AccountId, +// /// List of actions to be executed. +// /// +// /// With the meta transactions MVP defined in NEP-366, nested +// /// DelegateActions are not allowed. A separate type is used to enforce it. +// pub actions: Vec, +// /// Nonce to ensure that the same delegate action is not sent twice by a +// /// relayer and should match for given account's `public_key`. +// /// After this action is processed it will increment. +// pub nonce: Nonce, +// /// The maximal height of the block in the blockchain below which the given DelegateAction is valid. +// pub max_block_height: BlockHeight, +// /// Public key used to sign this delegated action. +// pub public_key: PublicKey, +// } +// +// +// +pub fn create_signed_delegate_action(delegate: DelegateAction, signer: &dyn Signer) -> Action { + Action::Delegate(Box::new(SignedDelegateAction { + delegate_action: delegate.clone(), + signature: signer.sign(delegate.get_nep461_hash().as_bytes()), + })) +} + +pub fn create_delegate_action( + signer_id: AccountId, + receiver_id: AccountId, + actions: Vec, + nonce: Nonce, + max_block_height: BlockHeight, + public_key: PublicKey, +) -> DelegateAction { + DelegateAction { + sender_id: signer_id, + receiver_id, + actions: actions + .iter() + .map(|a| NonDelegateAction::try_from(a.clone()).unwrap()) + .collect(), + nonce, + max_block_height, + public_key, + } +} diff --git a/near-transactions/src/lib.rs b/near-transactions/src/lib.rs index f46cb0e..a512b37 100644 --- a/near-transactions/src/lib.rs +++ b/near-transactions/src/lib.rs @@ -26,6 +26,10 @@ //! This crate aims to simplify transaction creation and management, making it more accessible for developers to //! interact with the NEAR blockchain programmatically. +pub use crate::action_builder::ActionBuilder; +pub use crate::delegate_action::{create_delegate_action, create_signed_delegate_action}; pub use crate::transaction_builder::TransactionBuilder; +mod action_builder; +mod delegate_action; mod transaction_builder; diff --git a/near-transactions/src/transaction_builder.rs b/near-transactions/src/transaction_builder.rs index ddd4cb8..1fb30cb 100644 --- a/near-transactions/src/transaction_builder.rs +++ b/near-transactions/src/transaction_builder.rs @@ -12,14 +12,9 @@ //! way to interact with the NEAR blockchain programmatically. use near_crypto::{PublicKey, Signer}; use near_primitives::{ - account::AccessKey, hash::CryptoHash, - transaction::{ - Action, AddKeyAction, CreateAccountAction, DeleteAccountAction, DeleteKeyAction, - DeployContractAction, FunctionCallAction, SignedTransaction, StakeAction, Transaction, - TransferAction, - }, - types::{AccountId, Balance, Gas, Nonce}, + transaction::{Action, SignedTransaction, Transaction}, + types::{AccountId, Nonce}, }; // TransactionBuilder struct @@ -55,78 +50,8 @@ impl TransactionBuilder { SignedTransaction::new(signature, self.transaction.clone()) } - /// Methods to add CreateAccount action directly to the Transaction's actions vector - pub fn create_account(&mut self) -> &mut Self { - self.transaction - .actions - .push(Action::CreateAccount(CreateAccountAction {})); - self - } - - /// Method to add a DeployContract action - pub fn deploy_contract(&mut self, code: &[u8]) -> &mut Self { - self.transaction - .actions - .push(Action::DeployContract(DeployContractAction { - code: code.to_vec(), - })); - self - } - - pub fn function_call( - &mut self, - method_name: String, - args: Vec, - gas: Gas, - deposit: Balance, - ) -> &mut Self { - self.transaction - .actions - .push(Action::FunctionCall(Box::new(FunctionCallAction { - method_name, - args, - gas, - deposit, - }))); - self - } - - pub fn transfer(&mut self, deposit: Balance) -> &mut Self { - self.transaction - .actions - .push(Action::Transfer(TransferAction { deposit })); - self - } - - pub fn stake(&mut self, stake: Balance, public_key: PublicKey) -> &mut Self { - self.transaction - .actions - .push(Action::Stake(Box::new(StakeAction { stake, public_key }))); - self - } - pub fn add_key(&mut self, public_key: PublicKey, access_key: AccessKey) -> &mut Self { - self.transaction - .actions - .push(Action::AddKey(Box::new(AddKeyAction { - public_key, - access_key, - }))); - self - } - - pub fn delete_key(&mut self, public_key: PublicKey) -> &mut Self { - self.transaction - .actions - .push(Action::DeleteKey(Box::new(DeleteKeyAction { public_key }))); - self - } - - pub fn delete_account(&mut self, beneficiary_id: AccountId) -> &mut Self { - self.transaction - .actions - .push(Action::DeleteAccount(DeleteAccountAction { - beneficiary_id, - })); + pub fn set_action(&mut self, actions: &[Action]) -> &mut Self { + self.transaction.actions = actions.to_owned(); self } From 29dbcf37237e1dd151307f55898c240e1ea427c4 Mon Sep 17 00:00:00 2001 From: jaswinder6991 Date: Mon, 24 Jun 2024 13:40:21 +0530 Subject: [PATCH 2/2] removed create_account function from examples and removed TransactionBuilder from near-accounts as well. --- near-accounts/examples/access_keys.rs | 44 ++++++++--- near-accounts/examples/async_tx.rs | 40 +++++----- near-accounts/examples/create_account.rs | 25 +++++- near-accounts/examples/create_subaccount.rs | 29 ++++++- near-accounts/examples/delete_account.rs | 23 +++++- near-accounts/examples/delete_key.rs | 23 +++++- near-accounts/examples/deploy_contract.rs | 25 +++++- near-accounts/examples/function_call.rs | 19 +++-- near-accounts/examples/multi-thread.rs | 1 - near-accounts/examples/send_money.rs | 24 +++++- near-accounts/src/accounts.rs | 85 ++++++++++++--------- near-transactions/src/lib.rs | 1 + 12 files changed, 256 insertions(+), 83 deletions(-) diff --git a/near-accounts/examples/access_keys.rs b/near-accounts/examples/access_keys.rs index fac4790..b804058 100644 --- a/near-accounts/examples/access_keys.rs +++ b/near-accounts/examples/access_keys.rs @@ -1,6 +1,6 @@ mod example_config; use near_accounts::Account; -use near_crypto::InMemorySigner; +use near_crypto::{InMemorySigner, SecretKey}; use near_primitives::types::Balance; use near_providers::JsonRpcProvider; use std::sync::Arc; @@ -8,10 +8,27 @@ mod utils; use near_primitives::types::AccountId; async fn add_full_access() -> Result<(), Box> { - let new_secret_key = near_crypto::SecretKey::from_random(near_crypto::KeyType::ED25519); + // Get test account and rpc details. + let config = example_config::get_test_config(); + + //Create a signer + let signer_account_id: AccountId = config.near_account.account_id.parse().unwrap(); + let signer_secret_key: SecretKey = config.near_account.secret_key.parse().unwrap(); + let signer = Arc::new(InMemorySigner::from_secret_key( + signer_account_id.clone(), + signer_secret_key, + )); - let account = example_config::create_account(); + //Creat a Provider + let provider = Arc::new(JsonRpcProvider::new(config.rpc_testnet_endpoint.as_str())); + //Create an Account object + let account = Account::new(signer_account_id, signer, provider); + + //Generate a secret Key for new access key + let new_secret_key = near_crypto::SecretKey::from_random(near_crypto::KeyType::ED25519); + + //Call add_key function on an Account let result = account .add_key(new_secret_key.public_key(), None, None, None) .await; @@ -27,17 +44,26 @@ async fn add_full_access() -> Result<(), Box> { } async fn add_function_call_key() -> Result<(), Box> { - let signer_account_id: AccountId = utils::input("Enter the signer Account ID: ")?.parse()?; - let signer_secret_key = utils::input("Enter the signer's private key: ")?.parse()?; - let signer = InMemorySigner::from_secret_key(signer_account_id.clone(), signer_secret_key); + // Get test account and rpc details. + let config = example_config::get_test_config(); - let new_secret_key = near_crypto::SecretKey::from_random(near_crypto::KeyType::ED25519); + //Create a signer + let signer_account_id: AccountId = config.near_account.account_id.parse().unwrap(); + let signer_secret_key: SecretKey = config.near_account.secret_key.parse().unwrap(); + let signer = Arc::new(InMemorySigner::from_secret_key( + signer_account_id.clone(), + signer_secret_key, + )); - let provider = Arc::new(JsonRpcProvider::new("https://rpc.testnet.near.org")); - let signer = Arc::new(signer); + //Creat a Provider + let provider = Arc::new(JsonRpcProvider::new(config.rpc_testnet_endpoint.as_str())); + //Create an Account object let account = Account::new(signer_account_id, signer, provider); + // Create a secret key for the new function call access key + let new_secret_key = near_crypto::SecretKey::from_random(near_crypto::KeyType::ED25519); + let allowance: Balance = 1_000_000_000_000_000_000_000_000; // Example amount in yoctoNEAR let contract_id = "contract.near-api-rs.testnet".to_string(); //Create an array of methods diff --git a/near-accounts/examples/async_tx.rs b/near-accounts/examples/async_tx.rs index 3638f3a..1b28d38 100644 --- a/near-accounts/examples/async_tx.rs +++ b/near-accounts/examples/async_tx.rs @@ -7,6 +7,8 @@ use near_providers::JsonRpcProvider; use near_providers::Provider; use std::sync::Arc; mod utils; +use near_accounts::Account; +use near_crypto::{InMemorySigner, SecretKey}; use near_primitives::types::AccountId; use serde_json::json; use tokio::time; @@ -14,27 +16,40 @@ use tokio::time; #[tokio::main] async fn main() -> Result<(), Box> { env_logger::init(); - let contract_id: AccountId = "contract.near-api-rs.testnet".parse::()?; + // Get test account and rpc details. + let config = example_config::get_test_config(); - let gas: Gas = 100_000_000_000_000; // Example amount in yoctoNEAR + //Create a signer + let signer_account_id: AccountId = config.near_account.account_id.parse().unwrap(); + let signer_secret_key: SecretKey = config.near_account.secret_key.parse().unwrap(); + let signer = Arc::new(InMemorySigner::from_secret_key( + signer_account_id.clone(), + signer_secret_key, + )); - let provider = Arc::new(JsonRpcProvider::new("https://rpc.testnet.near.org")); + //Creat a Provider + let provider = Arc::new(JsonRpcProvider::new(config.rpc_testnet_endpoint.as_str())); - let account = example_config::create_account(); - let method_name = "set_status".to_string(); + //Create an Account object + let account = Account::new(signer_account_id, signer, provider.clone()); + //Create argumements for function_call + //Contract id, method_name, method args, gas and deposit. + let contract_id: AccountId = "contract.near-api-rs.testnet".parse::()?; + let method_name = "set_status".to_string(); let args_json = json!({"message": "working1"}); + let gas: Gas = 100_000_000_000_000; // Example amount in yoctoNEAR + //Create a Transaction Sender Object; let transaction_sender = account .function_call(&contract_id, method_name, args_json, gas, 0) .await?; - + //Get the transaction hash to query the chain later. let tx_hash = transaction_sender.clone().get_transaction_hash().unwrap(); - let t1 = time::Instant::now(); + //Send the transaction //Different Wait_until values: None, Included, ExecutedOptimistic, IncludedFinal, Executed, Final let result = transaction_sender.transact_advanced("NONE").await; - let t2 = time::Instant::now(); match result { Ok(res) => match &res.final_execution_outcome { //Final Execution outcome for finality NONE would always be empty. @@ -58,22 +73,13 @@ async fn main() -> Result<(), Box> { sender_account_id: account.account_id, }; let wait_until = TxExecutionStatus::ExecutedOptimistic; - - time::sleep(time::Duration::from_secs(5)).await; - - let t3 = time::Instant::now(); let tx_status = provider.tx_status(transaction_info, wait_until).await; - let t4 = time::Instant::now(); match tx_status { Ok(response) => { - //println!("response gotten after: {}s", delta); println!("response: {:#?}", response); } Err(err) => println!("Error: {:#?}", err), } - - println!("Time taken for async request: {:?}", t2 - t1); - println!("Time taken for status request: {:?}", t4 - t3); Ok(()) } diff --git a/near-accounts/examples/create_account.rs b/near-accounts/examples/create_account.rs index 0503cc6..c5c279b 100644 --- a/near-accounts/examples/create_account.rs +++ b/near-accounts/examples/create_account.rs @@ -1,19 +1,40 @@ mod example_config; +use near_accounts::Account; +use near_crypto::{InMemorySigner, SecretKey}; use near_primitives::{types::Gas, views::FinalExecutionOutcomeViewEnum}; mod utils; use near_primitives::types::{AccountId, Balance}; +use near_providers::JsonRpcProvider; use serde_json::json; +use std::sync::Arc; #[tokio::main] async fn main() -> Result<(), Box> { env_logger::init(); + + // Get test account and rpc details. + let config = example_config::get_test_config(); + + //Create a signer + let signer_account_id: AccountId = config.near_account.account_id.parse().unwrap(); + let signer_secret_key: SecretKey = config.near_account.secret_key.parse().unwrap(); + let signer = Arc::new(InMemorySigner::from_secret_key( + signer_account_id.clone(), + signer_secret_key, + )); + + //Creat a Provider + let provider = Arc::new(JsonRpcProvider::new(config.rpc_testnet_endpoint.as_str())); + + //Create an Account object + let account = Account::new(signer_account_id, signer, provider.clone()); + + //Ask the user for the new Account id. let new_account_id: AccountId = utils::input("Enter new account name: ")?.parse()?; // Amount to transfer to the new account let gas: Gas = 100_000_000_000_000; // Example amount in yoctoNEAR let amount: Balance = 10_000_000_000_000_000_000_000; // Example amount in yoctoNEAR - let account = example_config::create_account(); - let contract_id: AccountId = "testnet".parse::()?; let method_name = "create_account".to_string(); diff --git a/near-accounts/examples/create_subaccount.rs b/near-accounts/examples/create_subaccount.rs index 268138e..3fe549a 100644 --- a/near-accounts/examples/create_subaccount.rs +++ b/near-accounts/examples/create_subaccount.rs @@ -1,12 +1,34 @@ -//use near_providers::Provider; mod example_config; -use near_primitives::types::Balance; +use near_accounts::Account; +use near_crypto::{InMemorySigner, SecretKey}; mod utils; -use near_primitives::types::AccountId; +use near_primitives::types::{AccountId, Balance}; +use near_providers::JsonRpcProvider; +use std::sync::Arc; #[tokio::main] async fn main() -> Result<(), Box> { env_logger::init(); + + // Get test account and rpc details. + let config = example_config::get_test_config(); + + //Create a signer + let signer_account_id: AccountId = config.near_account.account_id.parse().unwrap(); + let signer_secret_key: SecretKey = config.near_account.secret_key.parse().unwrap(); + let signer = Arc::new(InMemorySigner::from_secret_key( + signer_account_id.clone(), + signer_secret_key, + )); + + //Creat a Provider + let provider = Arc::new(JsonRpcProvider::new(config.rpc_testnet_endpoint.as_str())); + + //Create an Account object + let account = Account::new(signer_account_id, signer, provider.clone()); + + //Ask user for the new account id, it should be of the form something.near-api-rs.testnet + //or whatever signer account you are using. let new_account_id: AccountId = utils::input("Enter the account name of new account ")?.parse()?; @@ -15,7 +37,6 @@ async fn main() -> Result<(), Box> { let new_key_pair = near_crypto::SecretKey::from_random(near_crypto::KeyType::ED25519); - let account = example_config::create_account(); // Call create_account let result = account .create_account(&new_account_id, new_key_pair.public_key(), amount) diff --git a/near-accounts/examples/delete_account.rs b/near-accounts/examples/delete_account.rs index 74faa46..d5b1d6c 100644 --- a/near-accounts/examples/delete_account.rs +++ b/near-accounts/examples/delete_account.rs @@ -1,13 +1,34 @@ mod example_config; +use near_accounts::Account; +use near_crypto::{InMemorySigner, SecretKey}; +use near_providers::JsonRpcProvider; +use std::sync::Arc; mod utils; use near_primitives::types::AccountId; #[tokio::main] async fn main() -> Result<(), Box> { env_logger::init(); + + // Get test account and rpc details. + let config = example_config::get_test_config(); + + //Create a signer + let signer_account_id: AccountId = config.near_account.account_id.parse().unwrap(); + let signer_secret_key: SecretKey = config.near_account.secret_key.parse().unwrap(); + let signer = Arc::new(InMemorySigner::from_secret_key( + signer_account_id.clone(), + signer_secret_key, + )); + + //Creat a Provider + let provider = Arc::new(JsonRpcProvider::new(config.rpc_testnet_endpoint.as_str())); + + //Create an Account object + let account = Account::new(signer_account_id, signer, provider.clone()); + let beneficiary_account_id: AccountId = utils::input("Enter the account name where you want to transfer current account balance before deleting it")?.parse()?; - let account = example_config::create_account(); let response = account.delete_account(beneficiary_account_id.clone()).await; diff --git a/near-accounts/examples/delete_key.rs b/near-accounts/examples/delete_key.rs index 9f932f9..46f03b6 100644 --- a/near-accounts/examples/delete_key.rs +++ b/near-accounts/examples/delete_key.rs @@ -1,11 +1,30 @@ mod example_config; -use near_crypto::PublicKey; +use near_accounts::Account; +use near_crypto::{InMemorySigner, PublicKey, SecretKey}; +use near_providers::JsonRpcProvider; +use std::sync::Arc; mod utils; +use near_primitives::types::AccountId; #[tokio::main] async fn main() -> Result<(), Box> { env_logger::init(); - let account = example_config::create_account(); + // Get test account and rpc details. + let config = example_config::get_test_config(); + + //Create a signer + let signer_account_id: AccountId = config.near_account.account_id.parse().unwrap(); + let signer_secret_key: SecretKey = config.near_account.secret_key.parse().unwrap(); + let signer = Arc::new(InMemorySigner::from_secret_key( + signer_account_id.clone(), + signer_secret_key, + )); + + //Creat a Provider + let provider = Arc::new(JsonRpcProvider::new(config.rpc_testnet_endpoint.as_str())); + + //Create an Account object + let account = Account::new(signer_account_id, signer, provider.clone()); let public_key: PublicKey = "ed25519:EohEtHT8Dt8jURC3DcJ661hWCx6ExPRtDV82FpT4jfNB".parse::()?; diff --git a/near-accounts/examples/deploy_contract.rs b/near-accounts/examples/deploy_contract.rs index f25ca6a..9f510b0 100644 --- a/near-accounts/examples/deploy_contract.rs +++ b/near-accounts/examples/deploy_contract.rs @@ -1,15 +1,34 @@ -use near_accounts::Account; mod example_config; +use near_accounts::Account; +use near_crypto::{InMemorySigner, SecretKey}; +use near_providers::JsonRpcProvider; +use std::sync::Arc; mod utils; +use near_primitives::types::AccountId; #[tokio::main] async fn main() -> Result<(), Box> { env_logger::init(); - let account: Account = example_config::create_account(); + // Get test account and rpc details. + let config = example_config::get_test_config(); - let wasm_code = example_config::read_wasm_file()?; + //Create a signer + let signer_account_id: AccountId = config.near_account.account_id.parse().unwrap(); + let signer_secret_key: SecretKey = config.near_account.secret_key.parse().unwrap(); + let signer = Arc::new(InMemorySigner::from_secret_key( + signer_account_id.clone(), + signer_secret_key, + )); + //Creat a Provider + let provider = Arc::new(JsonRpcProvider::new(config.rpc_testnet_endpoint.as_str())); + + //Create an Account object + let account = Account::new(signer_account_id, signer, provider.clone()); + + //wasm code to deploy + let wasm_code = example_config::read_wasm_file()?; let response = account.deploy_contract(&wasm_code).await; match response { diff --git a/near-accounts/examples/function_call.rs b/near-accounts/examples/function_call.rs index f0cba84..d8f43bb 100644 --- a/near-accounts/examples/function_call.rs +++ b/near-accounts/examples/function_call.rs @@ -13,29 +13,34 @@ use serde_json::json; async fn main() -> Result<(), Box> { env_logger::init(); + //Read test account details from config file let config = example_config::get_test_config(); let signer_account_id: AccountId = config.near_account.account_id.parse().unwrap(); let signer_secret_key: SecretKey = config.near_account.secret_key.parse().unwrap(); - let contract_id: AccountId = config.contract_account.account_id.parse().unwrap(); - let signer = InMemorySigner::from_secret_key(signer_account_id.clone(), signer_secret_key); - - let gas: Gas = 100_000_000_000_000; // Example amount in yoctoNEAR + //Create a signer + let signer = Arc::new(InMemorySigner::from_secret_key( + signer_account_id.clone(), + signer_secret_key, + )); + //Create a provider let provider = Arc::new(JsonRpcProvider::new(config.rpc_testnet_endpoint.as_str())); - let signer = Arc::new(signer); + //Create an Account let account = Account::new(signer_account_id, signer, provider); - let method_name = "set_status".to_string(); + let contract_id: AccountId = config.contract_account.account_id.parse().unwrap(); + let method_name = "set_status".to_string(); let args_json = json!({"message": "working1"}); + let gas: Gas = 100_000_000_000_000; // Example amount in yoctoNEAR let result = account .function_call(&contract_id, method_name, args_json, gas, 0) .await? .transact() .await; - println!("response: {:#?}", result); + println!("response: {:#?}", result); Ok(()) } diff --git a/near-accounts/examples/multi-thread.rs b/near-accounts/examples/multi-thread.rs index 28d09cb..217a8ca 100644 --- a/near-accounts/examples/multi-thread.rs +++ b/near-accounts/examples/multi-thread.rs @@ -17,7 +17,6 @@ async fn main() -> Result<(), Box> { let signer_secret_key = "ed25519:29nYmQCZMsQeYtztXZzm57ayQt2uBHXdn2SAjK4ccMGSQaNUFNJ7Aoteno81eKTex9cGBbk1FuDuqJRsdzx34xDY".parse::()?; let provider = Arc::new(JsonRpcProvider::new("https://rpc.testnet.near.org")); - //let provider = JsonRpcProvider::new("https://rpc.testnet.near.org"); let signer = Arc::new(InMemorySigner::from_secret_key( signer_account_id.clone(), signer_secret_key, diff --git a/near-accounts/examples/send_money.rs b/near-accounts/examples/send_money.rs index d4f45fe..82cc239 100644 --- a/near-accounts/examples/send_money.rs +++ b/near-accounts/examples/send_money.rs @@ -1,19 +1,39 @@ mod example_config; +use near_accounts::Account; +use near_crypto::{InMemorySigner, SecretKey}; use near_primitives::types::Balance; +use near_providers::JsonRpcProvider; +use std::sync::Arc; mod utils; use near_primitives::types::AccountId; #[tokio::main] async fn main() -> Result<(), Box> { env_logger::init(); + + // Get test account and rpc details. + let config = example_config::get_test_config(); + + //Create a signer + let signer_account_id: AccountId = config.near_account.account_id.parse().unwrap(); + let signer_secret_key: SecretKey = config.near_account.secret_key.parse().unwrap(); + let signer = Arc::new(InMemorySigner::from_secret_key( + signer_account_id.clone(), + signer_secret_key, + )); + + //Creat a Provider + let provider = Arc::new(JsonRpcProvider::new(config.rpc_testnet_endpoint.as_str())); + + //Create an Account object + let account = Account::new(signer_account_id, signer, provider.clone()); + let receiver_account_id: AccountId = utils::input("Enter the account name of receiver account ")?.parse()?; // Amount to transfer to the receiver account let amount: Balance = 10_000_000_000; // Example amount in yoctoNEAR - let account = example_config::create_account(); - // Call create_account let result = account.send_money(&receiver_account_id, amount).await; println!("response: {:#?}", result); diff --git a/near-accounts/src/accounts.rs b/near-accounts/src/accounts.rs index d18c8a6..113e0a4 100644 --- a/near-accounts/src/accounts.rs +++ b/near-accounts/src/accounts.rs @@ -7,13 +7,13 @@ use crate::access_keys::{full_access_key, function_call_access_key}; use crate::transaction_sender::TransactionSender; use near_crypto::{PublicKey, Signer}; use near_primitives::account::AccessKey; -use near_primitives::transaction::SignedTransaction; use near_primitives::types::{AccountId, Balance, BlockReference, Finality, Gas}; use near_primitives::views::{FinalExecutionOutcomeView, QueryRequest}; +use near_transactions::transaction::Transaction; use near_providers::types::query::{QueryResponseKind, RpcQueryResponse}; use near_providers::Provider; -use near_transactions::{ActionBuilder, TransactionBuilder}; +use near_transactions::ActionBuilder; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::ops::{Add, Mul, Sub}; @@ -62,7 +62,7 @@ impl Account { } } - /// Create a `SignedTransaction` for constructing a signed transaction. + /// Create a `Transaction` for constructing a signed transaction later. /// /// # Arguments /// @@ -71,13 +71,14 @@ impl Account { /// /// # Returns /// - /// A result containing a `SignedTransaction` instance or an error if fetching the nonce or block hash failed. - async fn create_signed_transaction( + /// A result containing a `Transaction` instance or an error if fetching the nonce or block hash failed. + async fn create_transaction( &self, receiver_id: &AccountId, - actions: &ActionBuilder, - ) -> Result> { + action_builder: &ActionBuilder, + ) -> Result> { // Fetch the current nonce for the signer account and latest block hash + // cache nonce as well, fetching it everytime is slow. let nonce = self .fetch_nonce(&self.account_id, &self.signer.public_key()) .await?; @@ -87,17 +88,15 @@ impl Account { let block = self.provider.block(block_reference).await?; let block_hash = block.header.hash; - // Use TransactionBuilder to construct the transaction - let signed_transaction: SignedTransaction = TransactionBuilder::new( - self.account_id.clone(), - self.signer.public_key(), - receiver_id.clone(), - nonce + 1, + let tx: Transaction = Transaction { + signer_id: self.account_id.clone(), + public_key: self.signer.public_key(), + nonce: nonce + 1, + receiver_id: receiver_id.clone(), block_hash, - ) - .set_action(&actions.build()) - .sign_transaction(&*self.signer); - Ok(signed_transaction) + actions: action_builder.build(), + }; + Ok(tx) } /// Fetches the current nonce for an account's access key. @@ -156,14 +155,19 @@ impl Account { .transfer(amount) .add_key(public_key.clone(), full_access_key()); - let signed_txn = self - .create_signed_transaction(new_account_id, &action_builder) + let tx = self + .create_transaction(new_account_id, &action_builder) .await?; - // Use TransactionBuilder to construct the transaction + + //Notice the derefencing here. + let signed_tx = tx.sign(&*self.signer); // Send the transaction - let transaction_result = self.provider.send_transaction(signed_txn.clone()).await?; - Ok(transaction_result) + let transaction_result = self.provider.send_transaction(signed_tx.clone()).await; + match transaction_result { + Ok(transaction_result) => Ok(transaction_result), + Err(err) => Err(Box::new(err)), + } } /// Adds a full or function call access key to an account @@ -196,13 +200,14 @@ impl Account { None => full_access_key(), }; - let signed_tx = self - .create_signed_transaction( + let tx = self + .create_transaction( &self.account_id, ActionBuilder::new().add_key(public_key, access_key), ) .await?; - // Use TransactionBuilder to construct the transaction + + let signed_tx = tx.sign(&*self.signer); // Send the transaction let transaction_result = self.provider.send_transaction(signed_tx).await; @@ -225,13 +230,15 @@ impl Account { &self, public_key: PublicKey, ) -> Result> { - let signed_tx = self - .create_signed_transaction( + let tx = self + .create_transaction( &self.account_id, ActionBuilder::new().delete_key(public_key), ) .await?; + let signed_tx = tx.sign(&*self.signer); + // Send the transaction let transaction_result = self.provider.send_transaction(signed_tx).await; match transaction_result { @@ -253,13 +260,15 @@ impl Account { &self, byte_code: &[u8], ) -> Result> { - let signed_tx = self - .create_signed_transaction( + let tx = self + .create_transaction( &self.account_id, ActionBuilder::new().deploy_contract(byte_code), ) .await?; + let signed_tx = tx.sign(&*self.signer); + // Send the transaction let transaction_result = self.provider.send_transaction(signed_tx).await; match transaction_result { @@ -281,12 +290,14 @@ impl Account { &self, beneficiary_id: AccountId, ) -> Result> { - let signed_tx = self - .create_signed_transaction( + let tx = self + .create_transaction( &self.account_id, ActionBuilder::new().delete_account(beneficiary_id), ) .await?; + + let signed_tx = tx.sign(&*self.signer); // Send the transaction let transaction_result = self.provider.send_transaction(signed_tx).await; match transaction_result { @@ -311,9 +322,11 @@ impl Account { receiver_id: &AccountId, amount: Balance, ) -> Result> { - let signed_tx = self - .create_signed_transaction(receiver_id, ActionBuilder::new().transfer(amount)) + let tx = self + .create_transaction(receiver_id, ActionBuilder::new().transfer(amount)) .await?; + + let signed_tx = tx.sign(&*self.signer); // Send the transaction let transaction_result = self.provider.send_transaction(signed_tx).await; match transaction_result { @@ -346,12 +359,14 @@ impl Account { // Serialize the JSON to a Vec let args = serde_json::to_vec(&args)?; - let signed_tx = self - .create_signed_transaction( + let tx = self + .create_transaction( contract_id, ActionBuilder::new().function_call(method_name, args, gas, deposit), ) .await?; + + let signed_tx = tx.sign(&*self.signer); // To-do. Needs error handling here. Ok(TransactionSender::new(signed_tx, self.provider.clone())) } diff --git a/near-transactions/src/lib.rs b/near-transactions/src/lib.rs index a512b37..776f702 100644 --- a/near-transactions/src/lib.rs +++ b/near-transactions/src/lib.rs @@ -29,6 +29,7 @@ pub use crate::action_builder::ActionBuilder; pub use crate::delegate_action::{create_delegate_action, create_signed_delegate_action}; pub use crate::transaction_builder::TransactionBuilder; +pub use near_primitives::transaction; mod action_builder; mod delegate_action;