Skip to content

Commit

Permalink
Add Transaction wrapper and move transaction_sender from accounts.
Browse files Browse the repository at this point in the history
  • Loading branch information
ckshitij committed Jun 7, 2024
1 parent 43abccf commit 3b5f743
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 134 deletions.
175 changes: 41 additions & 134 deletions near-accounts/src/accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,114 +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::action::Action;
use near_primitives::hash::CryptoHash;
use near_primitives::transaction::{SignedTransaction, Transaction};
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::ActionBuilder;
use near_transactions::{ActionBuilder, TransactionWrapper};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::ops::{Add, Mul, Sub};
use std::sync::Arc;

type ArcProviderSendSync = Arc<dyn Provider + Send + Sync>;
type ArcSignerSendSync = Arc<dyn Signer + Send + Sync>;

///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<RpcTransactionResponse, Box<dyn std::error::Error>> {
self.provider
.send_tx(self.signed_transaction, TxExecutionStatus::default())
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)
}

///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<RpcTransactionResponse, Box<dyn std::error::Error>> {
self.provider
.send_tx(self.signed_transaction, TxExecutionStatus::None)
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)
}

///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<RpcTransactionResponse, Box<dyn std::error::Error>> {
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<dyn std::error::Error>)
}

/// Returns transaction hash for a given signed transaction
pub fn get_transaction_hash(self) -> Result<CryptoHash, Box<dyn std::error::Error>> {
Ok(self.signed_transaction.get_hash())
}
}
pub type ArcProviderSendSync = Arc<dyn Provider + Send + Sync>;
pub type ArcSignerSendSync = Arc<dyn Signer + Send + Sync>;

/// Represents a NEAR account, encapsulating account ID, signer, and provider for blockchain interaction.
pub struct Account {
Expand All @@ -129,36 +38,6 @@ pub struct AccountBalance {
pub available: String,
}

#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct TransactionRequest {
pub receiver_id: AccountId,
pub actions: Vec<Action>,
pub nonce: Option<u64>,
pub block_hash: Option<CryptoHash>,
}

impl Default for TransactionRequest {
fn default() -> Self {
TransactionRequest {
receiver_id: "".parse().unwrap(),
actions: Vec::new(),
nonce: None,
block_hash: None,
}
}
}

impl TransactionRequest {
fn new(account_id: AccountId, actions: Vec<Action>) -> Self {
TransactionRequest {
receiver_id: account_id,
actions,
nonce: None,
block_hash: None,
}
}
}

impl Account {
/// Constructs a new `Account` instance.
///
Expand Down Expand Up @@ -194,7 +73,7 @@ impl Account {
/// A result containing a `TransactionBuilder` instance or an error if fetching the nonce or block hash failed.
async fn build_signed_transaction(
&self,
txn_request: TransactionRequest,
txn_request: TransactionWrapper,
) -> Result<SignedTransaction, Box<dyn std::error::Error>> {
// Fetch the current nonce for the signer account and latest block hash
let nonce = match txn_request.nonce {
Expand Down Expand Up @@ -288,9 +167,13 @@ impl Account {
.add_key(public_key.clone(), full_access_key());

let signed_txn = self
.build_signed_transaction(TransactionRequest::new(
.build_signed_transaction(TransactionWrapper::new(
new_account_id.clone(),
actions.build(),
None,
None,
None,
None,
))
.await?;
// Use TransactionBuilder to construct the transaction
Expand Down Expand Up @@ -331,9 +214,13 @@ impl Account {
};

let signed_tx = self
.build_signed_transaction(TransactionRequest::new(
.build_signed_transaction(TransactionWrapper::new(
self.account_id.clone(),
ActionBuilder::new().add_key(public_key, access_key).build(),
None,
None,
None,
None,
))
.await?; // Sign the transaction

Expand All @@ -359,9 +246,13 @@ impl Account {
public_key: PublicKey,
) -> Result<FinalExecutionOutcomeView, Box<dyn std::error::Error>> {
let signed_tx = self
.build_signed_transaction(TransactionRequest::new(
.build_signed_transaction(TransactionWrapper::new(
self.account_id.clone(),
ActionBuilder::new().delete_key(public_key).build(),
None,
None,
None,
None,
))
.await?; // Sign the transaction

Expand All @@ -387,9 +278,13 @@ impl Account {
byte_code: &[u8],
) -> Result<FinalExecutionOutcomeView, Box<dyn std::error::Error>> {
let signed_tx = self
.build_signed_transaction(TransactionRequest::new(
.build_signed_transaction(TransactionWrapper::new(
self.account_id.clone(),
ActionBuilder::new().deploy_contract(byte_code).build(),
None,
None,
None,
None,
))
.await?; // Sign the transaction

Expand All @@ -415,9 +310,13 @@ impl Account {
beneficiary_id: AccountId,
) -> Result<FinalExecutionOutcomeView, Box<dyn std::error::Error>> {
let signed_tx = self
.build_signed_transaction(TransactionRequest::new(
.build_signed_transaction(TransactionWrapper::new(
self.account_id.clone(),
ActionBuilder::new().delete_account(beneficiary_id).build(),
None,
None,
None,
None,
))
.await?; // Sign the transaction

Expand Down Expand Up @@ -446,9 +345,13 @@ impl Account {
amount: Balance,
) -> Result<FinalExecutionOutcomeView, Box<dyn std::error::Error>> {
let signed_tx = self
.build_signed_transaction(TransactionRequest::new(
.build_signed_transaction(TransactionWrapper::new(
receiver_id.clone(),
ActionBuilder::new().transfer(amount).build(),
None,
None,
None,
None,
))
.await?; // Sign the transaction

Expand Down Expand Up @@ -485,11 +388,15 @@ impl Account {
let args = serde_json::to_vec(&args)?;

let signed_tx = self
.build_signed_transaction(TransactionRequest::new(
.build_signed_transaction(TransactionWrapper::new(
contract_id.clone(),
ActionBuilder::new()
.function_call(method_name, args, gas, deposit)
.build(),
None,
None,
None,
None,
))
.await?; // Sign the transaction

Expand Down
1 change: 1 addition & 0 deletions near-accounts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,4 @@ pub use crate::accounts::Account;

mod access_keys;
pub mod accounts;
mod transaction_sender;
94 changes: 94 additions & 0 deletions near-accounts/src/transaction_sender.rs
Original file line number Diff line number Diff line change
@@ -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<RpcTransactionResponse, Box<dyn std::error::Error>> {
self.provider
.send_tx(self.signed_transaction, TxExecutionStatus::default())
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)
}

///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<RpcTransactionResponse, Box<dyn std::error::Error>> {
self.provider
.send_tx(self.signed_transaction, TxExecutionStatus::None)
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)
}

///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<RpcTransactionResponse, Box<dyn std::error::Error>> {
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<dyn std::error::Error>)
}

/// Returns transaction hash for a given signed transaction
pub fn get_transaction_hash(self) -> Result<CryptoHash, Box<dyn std::error::Error>> {
Ok(self.signed_transaction.get_hash())
}
}
2 changes: 2 additions & 0 deletions near-transactions/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@

pub use crate::action_builder::ActionBuilder;
pub use crate::transaction_builder::TransactionBuilder;
pub use crate::transaction_wrapper::TransactionWrapper;

mod action_builder;
mod transaction_builder;
mod transaction_wrapper;
Loading

0 comments on commit 3b5f743

Please sign in to comment.