Integration Tests
Learn how to write and run integration tests for NEAR smart contracts using Sandbox testing and realistic blockchain environments.
Integration tests deploy your contract to a local NEAR Sandbox and create test accounts to interact with it. This lets you test contract behavior in a realistic blockchain environment.
Moreover, when using the local sandbox you gain complete control of the network:
- Create test
Accountsand manipulate theirStateandBalance. - Simulate errors on callbacks.
- Control the time flow and fast-forward into the future.
Use Rust near-sandbox to start and control a local NEAR node, and near-api to create accounts, deploy contracts, send transactions, and read their outcomes. The examples below use these libraries directly.
[!NOTE]
Sandbox Testing
Run automated integration tests locally with
near-sandbox. Each test can create its own accounts, deploy contracts built from source, and inspect transaction and receipt outcomes. Usenear-apito interact with the node through its local RPC endpoint.
In-process alternative: near-mock
When a full sandbox node is more than you need — fast local iteration, hermetic CI, or replaying a specific mainnet account — near-mock executes unmodified contract wasm in-process via wasmtime: no node, Docker, RPC, or fees. Contracts only see host calls, state, and gas, so existing binaries (vaults, DEXes, oracles) run as-is and complete in milliseconds.
cargo install near-mock
It implements the production host surface (~140 functions: storage, registers, promise batch actions, validators, epochs), protocol-accurate gas metering (V155 fee table), and the real crypto precompiles (ecrecover, p256_verify, alt_bn128, keccak512, BLS12-381) using the same crates as nearcore.
Two patterns where it complements sandbox testing:
[tab] Deterministic CI
Pin randomness, time, and epoch height via environment variables, so every CI run replays identically:
NEAR_MOCK_SEED=abc123 NEAR_MOCK_NOW=1700000000 near-mock state.bin call my-contract my_method '{}'
[tab] Mainnet forensics
Snapshot a live contract's state at a pinned block and probe it offline — call views, script cross-contract flows, or diff behavior across upgrades without touching the network:
near-mock snapshot pyth-oracle.near pyth.bin near-mock pyth.bin --view some_view_method '{}'
[!TIP]
For workflows that need the full node stack — wallets, indexers, network behavior — keep using near-sandbox. Use
near-mockwhen you want chain semantics without the chain.
Create Accounts
Account
let sandbox = near_sandbox::Sandbox::start_sandbox().await?;
let alice_account_id: AccountId = "alice.test.sandbox".parse().unwrap();
sandbox
.create_account(alice_account_id.clone())
.initial_balance(NearToken::from_near(1))
.send()
.await?;
let alice_account = Account(alice_account_id.clone());
println!("alice_account: {:#?}", alice_account);
// alice_account: Account(
// AccountId(
// "alice.test.sandbox",
// ),
// )
Using Secret Key
let sandbox = near_sandbox::Sandbox::start_sandbox().await?;
let account_id: AccountId = "test.sandbox".parse().unwrap();
let secret_key = SecretKey::from_random(KeyType::ED25519);
sandbox
.create_account(account_id.clone())
.public_key(secret_key.public_key().to_string())
.initial_balance(NearToken::from_near(1))
.send()
.await?;
let account = Account(account_id.clone());
println!("account: {:#?}", account);
// account: Account(
// AccountId(
// "test.sandbox",
// ),
// )
Using Credentials From File
let sandbox = near_sandbox::Sandbox::start_sandbox().await?;
let signer = near_crypto::InMemorySigner::from_file(Path::new("./tests/credentials.json"))
.map_err(|err| anyhow::Error::new(err))
.unwrap();
let account_id = signer.get_account_id();
let secret_key = match signer {
near_crypto::Signer::Empty(_) => panic!("Empty signer"),
near_crypto::Signer::InMemory(signer) => signer.secret_key,
};
sandbox
.create_account(account_id.clone())
.public_key(secret_key.public_key().to_string())
.initial_balance(NearToken::from_near(1))
.send()
.await?;
let account = Account(account_id.clone());
println!("account: {:#?}", account);
// account: Account(
// AccountId(
// "test-ac-1719933221123-3.testnet",
// ),
// )
WASM Files
Compile Contract Code
let compiling_wasm_result = cargo_near_build::build_with_cli(Default::default());
assert!(compiling_wasm_result.is_ok());
let _contract_wasm = std::fs::read(compiling_wasm_result.unwrap())
.expect("Could not process WASM file after compiling");
[!TIP]
You don't need to assert compiling process everytime. You can use
?operator to get the result asVec<u8>without dealing withResult<Vec<u8>>, Error>type. That way you can directly use this vector to deploy the wasm file into account. Your test will still fail if compiling process fails.let contract_wasm_path = cargo_near_build::build_with_cli(Default::default())?;
Loading From File
let artifact_path = "target/near/hello_rs.wasm";
let reading_file_result = std::fs::read(artifact_path);
assert!(reading_file_result.is_ok());
let _contract_wasm = reading_file_result
.expect(format!("Could not read WASM file from {}", artifact_path).as_str());
[!TIP]
The same as in the case of compilation wasm from code, you don't need to assert reading file process everytime. You can use
expectmethod to get the reading file result asVec<u8>and provide error message as a parameter. Your test will still fail if compiling process fails.let contract_wasm = std::fs::read(artifact_path) .expect(format!("Could not read WASM file from {}", artifact_path).as_str());
Deploy Contracts
Deploy To Account
let sandbox = near_sandbox::Sandbox::start_sandbox().await?;
let sandbox_network =
near_api::NetworkConfig::from_rpc_url("sandbox", sandbox.rpc_addr.parse()?);
let contract_wasm_path = cargo_near_build::build_with_cli(Default::default())?;
let contract_wasm = std::fs::read(contract_wasm_path)?;
let contract_account_id: AccountId = "contract.sandbox".parse().unwrap();
sandbox
.create_account(contract_account_id.clone())
.initial_balance(NearToken::from_near(10))
.send()
.await?;
let contract = Account(contract_account_id).as_contract();
// Initialize signer for the contract deployment
let signer = near_api::Signer::from_secret_key(
near_sandbox::config::DEFAULT_GENESIS_ACCOUNT_PRIVATE_KEY
.parse()
.unwrap(),
)?;
// Deploy the contract
near_api::Contract::deploy(contract.account_id().clone())
.use_code(contract_wasm)
.without_init_call()
.with_signer(signer.clone())
.send_to(&sandbox_network)
.await?
.assert_success();
println!("contract: {:#?}", contract);
// contract: Contract(
// AccountId(
// "contract.sandbox",
// ),
// )
Logs
Show contract's logs.
You can use println or dbg! when you want to see information from your code.
let alice_account = Account(alice_account_id.clone());
println!("alice_account: {:#?}", alice_account);
// alice_account: Account(
// AccountId(
// "alice.test.sandbox",
// ),
// )
In Rust, the output from your code is captured by default and not displayed in the terminal. In order to see the output, you have to use the --nocapture flag
eg. cargo test -- --nocapture
If you want to access the contracts logs, you can find them in the tx_outcome.logs() Vec.
let tx_outcome = contract
.call_function("set_greeting", json!({"greeting": "Hello World!"}))
.transaction()
.gas(Gas::from_tgas(100))
.with_signer(contract.account_id().clone(), signer.clone())
.send_to(&sandbox_network)
.await?;
assert!(tx_outcome.is_success());
dbg!(tx_outcome.logs());
// [tests/test_basics.rs:29:5] tx_outcome.logs() = [
// "Saving greeting: Hello World!",
// ]
Account Balance
let sandbox = near_sandbox::Sandbox::start_sandbox().await?;
let sandbox_network =
near_api::NetworkConfig::from_rpc_url("sandbox", sandbox.rpc_addr.parse()?);
let alice_account_id: AccountId = "alice.test.sandbox".parse().unwrap();
sandbox
.create_account(alice_account_id.clone())
.initial_balance(NearToken::from_near(1))
.send()
.await?;
let alice_account = Account(alice_account_id.clone());
let account_near_balance = alice_account
.tokens()
.near_balance()
.fetch_from(&sandbox_network)
.await
.expect("Account has to have some balance");
println!("account_near_balance: {:#?}", account_near_balance);
// account_near_balance: UserBalance {
// total: NearToken {
// inner: 1000000000000000000000000,
// },
// storage_locked: NearToken {
// inner: 1820000000000000000000,
// },
// storage_usage: 182,
// locked: NearToken {
// inner: 0,
// },
// }
Transactions
Call
let sandbox = near_sandbox::Sandbox::start_sandbox().await?;
let sandbox_network =
near_api::NetworkConfig::from_rpc_url("sandbox", sandbox.rpc_addr.parse()?);
let contract_wasm_path = cargo_near_build::build_with_cli(
cargo_near_build::BuildOpts::builder()
.manifest_path("./tests/contracts/ft/Cargo.toml")
.build(),
)?;
let contract_wasm = std::fs::read(contract_wasm_path)?;
let contract_account_id: AccountId = "contract.sandbox".parse().unwrap();
sandbox
.create_account(contract_account_id.clone())
.initial_balance(NearToken::from_near(10))
.send()
.await?;
let contract = Account(contract_account_id).as_contract();
// Initialize signer for the contract deployment
let signer = near_api::Signer::from_secret_key(
near_sandbox::config::DEFAULT_GENESIS_ACCOUNT_PRIVATE_KEY
.parse()
.unwrap(),
)?;
// Deploy the contract
near_api::Contract::deploy(contract.account_id().clone())
.use_code(contract_wasm)
.without_init_call()
.with_signer(signer.clone())
.send_to(&sandbox_network)
.await?
.assert_success();
let initialize_result = contract
.call_function("new_default_meta", json!({"owner_id": contract.account_id().clone(), "name": "token", "symbol": "tt", "total_supply": "1000000000000000000000000" }))
.transaction()
.gas(Gas::from_tgas(100))
.with_signer(contract.account_id().clone(), signer.clone())
.send_to(&sandbox_network)
.await?;
println!("initialize_result: {:#?}", initialize_result);
// initialize_result: ExecutionFinalResult {
// total_gas_burnt: NearGas {
// inner: 2422860822219,
// },
// transaction: SignedTransaction {
// ...
View
let sandbox = near_sandbox::Sandbox::start_sandbox().await?;
let sandbox_network =
near_api::NetworkConfig::from_rpc_url("sandbox", sandbox.rpc_addr.parse()?);
let contract_wasm_path = cargo_near_build::build_with_cli(
cargo_near_build::BuildOpts::builder()
.manifest_path("./tests/contracts/ft/Cargo.toml")
.build(),
)?;
let contract_wasm = std::fs::read(contract_wasm_path)?;
let contract_account_id: AccountId = "contract.sandbox".parse().unwrap();
sandbox
.create_account(contract_account_id.clone())
.initial_balance(NearToken::from_near(10))
.send()
.await?;
let contract = Account(contract_account_id).as_contract();
// Initialize signer for the contract deployment
let signer = near_api::Signer::from_secret_key(
near_sandbox::config::DEFAULT_GENESIS_ACCOUNT_PRIVATE_KEY
.parse()
.unwrap(),
)?;
// Deploy the contract
near_api::Contract::deploy(contract.account_id().clone())
.use_code(contract_wasm)
.without_init_call()
.with_signer(signer.clone())
.send_to(&sandbox_network)
.await?
.assert_success();
contract
.call_function("new_default_meta", json!({"owner_id": contract.account_id().clone(), "name": "token", "symbol": "tt", "total_supply": "1000000000000000000000000" }))
.transaction()
.gas(Gas::from_tgas(100))
.with_signer(contract.account_id().clone(), signer.clone())
.send_to(&sandbox_network)
.await?
.assert_success();
let account_balance: NearToken = contract
.call_function(
"ft_balance_of",
json!({"account_id": contract.account_id().clone()}),
)
.read_only()
.fetch_from(&sandbox_network)
.await?
.data;
println!("account_balance: {:#?}", account_balance);
// account_balance: NearToken {
// inner: 1000000000000000000000000,
// }
Patch State on the Fly
In Sandbox-mode, you can add or modify any contract state, contract code, account or access key with patchState.
You can alter contract code, accounts, and access keys using normal transactions via the DeployContract, CreateAccount, and AddKey actions. But this limits you to altering your own account or sub-account. patchState allows you to perform these operations on any account.
let sandbox = near_sandbox::Sandbox::start_sandbox().await?;
let sandbox_network =
near_api::NetworkConfig::from_rpc_url("sandbox", sandbox.rpc_addr.parse()?);
let contract_wasm_path = cargo_near_build::build_with_cli(Default::default())?;
let contract_wasm = std::fs::read(contract_wasm_path)?;
let contract_account_id: AccountId = "contract.sandbox".parse().unwrap();
sandbox
.create_account(contract_account_id.clone())
.initial_balance(NearToken::from_near(10))
.send()
.await?;
let contract = Account(contract_account_id).as_contract();
// Initialize signer for the contract deployment
let signer = near_api::Signer::from_secret_key(
near_sandbox::config::DEFAULT_GENESIS_ACCOUNT_PRIVATE_KEY
.parse()
.unwrap(),
)?;
// Deploy the contract
near_api::Contract::deploy(contract.account_id().clone())
.use_code(contract_wasm)
.without_init_call()
.with_signer(signer.clone())
.send_to(&sandbox_network)
.await?
.assert_success();
let new_greeting = "Howdy";
let state_key = general_purpose::STANDARD.encode("STATE");
let state_value =
general_purpose::STANDARD.encode(borsh::to_vec(new_greeting.as_bytes()).unwrap());
let patch_state_result = sandbox
.patch_state(contract.account_id().clone())
.storage(state_key, state_value)
.send()
.await?;
println!("patch_state_result: {:#?}", patch_state_result);
let current_greeting: String = contract
.call_function("get_greeting", json!({}))
.read_only()
.fetch_from(&sandbox_network)
.await?
.data;
println!("current_greeting: {:#?}", current_greeting);
// current_greeting: "Howdy"
[!NOTE]
As an alternative to
patchState, you can stop the node, dump state at genesis, edit the genesis, and restart the node. This approach is more complex to do and also cannot be performed without restarting the node.
Time Traveling
sandbox offers support for forwarding the state of the blockchain to the future. This means contracts which require time sensitive data do not need to sit and wait the same amount of time for blocks on the sandbox to be produced. We can simply just call sandbox.fast_forward to get us further in time:
let sandbox = near_sandbox::Sandbox::start_sandbox().await?;
let sandbox_network =
near_api::NetworkConfig::from_rpc_url("sandbox", sandbox.rpc_addr.parse()?);
let contract_wasm_path = cargo_near_build::build_with_cli(
cargo_near_build::BuildOpts::builder()
.manifest_path("./tests/contracts/simple-contract/Cargo.toml")
.build(),
)?;
let contract_wasm = std::fs::read(contract_wasm_path)?;
let contract_account_id: AccountId = "contract.sandbox".parse().unwrap();
sandbox
.create_account(contract_account_id.clone())
.initial_balance(NearToken::from_near(10))
.send()
.await?;
let contract = Account(contract_account_id).as_contract();
// Initialize signer for the contract deployment
let signer = near_api::Signer::from_secret_key(
near_sandbox::config::DEFAULT_GENESIS_ACCOUNT_PRIVATE_KEY
.parse()
.unwrap(),
)?;
// Deploy the contract
near_api::Contract::deploy(contract.account_id().clone())
.use_code(contract_wasm)
.without_init_call()
.with_signer(signer.clone())
.send_to(&sandbox_network)
.await?
.assert_success();
let height = near_api::Chain::block_number()
.fetch_from(&sandbox_network)
.await
.unwrap();
println!("BlockInfo pre-fast_forward {:#?}", height);
// Call into fast_forward. This will take a bit of time to invoke, but is
// faster than manually waiting for the same amounts of blocks to be produced
sandbox.fast_forward(1000).await.unwrap();
let new_height = near_api::Chain::block_number()
.fetch_from(&sandbox_network)
.await
.unwrap();
println!("BlockInfo post-fast_forward {:#?}", new_height);
See the full Sandbox timeout test.
Using Testnet
For checks against deployed testnet contracts, use near-api with NetworkConfig::testnet() and funded testnet accounts. Sandbox runs a separate local node; its account creation, state patching, and fast-forward controls apply to that local node. Testnet checks can help you:
- Check your contract against a public network
- Exercise interactions with existing testnet contracts
- Compare results with your local Sandbox tests
let testnet_network = near_api::NetworkConfig::testnet();
let account_id: AccountId = "test-ac-1719933221123-3.testnet".parse().unwrap();
let secret_key: near_api::SecretKey = "ed25519:5QEPBcxrYJroypA4zFCofGR2VxXwyHuc6it7hzX3Er6uYfSJyR4ppq7cZ9MBUqErJG6xzHvqtB5TCTo5b2X1Uoze".parse().unwrap();
let signer = near_api::Signer::from_secret_key(secret_key)?;
let hello_near_contract = Account("hello.near-examples.testnet".parse().unwrap()).as_contract();
let current_greeting_on_testnet: String = hello_near_contract
.call_function("get_greeting", json!({}))
.read_only()
.fetch_from(&testnet_network)
.await?
.data;
println!(
"current_greeting_on_testnet: {:#?}",
current_greeting_on_testnet
);
// current_greeting_on_testnet: "defi is cool"
let new_greeting = format!("{current_greeting_on_testnet} updated");
let _ = hello_near_contract
.call_function("set_greeting", json!({"greeting": new_greeting}))
.transaction()
.max_gas()
.with_signer(account_id, signer.clone())
.send_to(&testnet_network)
.await?
.assert_success();
let updated_greeting_on_testnet: String = hello_near_contract
.call_function("get_greeting", json!({}))
.read_only()
.fetch_from(&testnet_network)
.await?
.data;
println!(
"updated_greeting_on_testnet: {:#?}",
updated_greeting_on_testnet
);
[!TIP]
If you can create a new account on each iteration as well.
Spooning Contracts
Spooning a blockchain is copying the data from one network into a different network. NEAR Sandbox makes it easy to copy data from Mainnet or Testnet contracts into your local Sandbox environment:
Specify the contract name from testnet you want to be pulling, and a specific block ID referencing back to a specific time. (Just in case the contract you're referencing has been changed or updated)
Create a function called pull_contract which will pull the contract's .wasm file from the chain and deploy it onto your local sandbox. You'll have to re-initialize it with all the data to run tests. This is because the contract's data is too big for the RPC service to pull down. (limits are set to 50Mb)
let sandbox = near_sandbox::Sandbox::start_sandbox().await?;
let sandbox_network =
near_api::NetworkConfig::from_rpc_url("sandbox", sandbox.rpc_addr.parse()?);
let testnet_network = near_api::NetworkConfig::testnet();
let hello_near_contract = Account("hello.near-examples.testnet".parse().unwrap()).as_contract();
sandbox
.patch_state(hello_near_contract.account_id().clone())
.fetch_from(RPCEndpoint::testnet().url, FetchData::ALL)
.await
.unwrap()
.initial_balance(NearToken::from_near(666))
.send()
.await
.unwrap();
let greeting_from_testnet: String = hello_near_contract
.call_function("get_greeting", json!({}))
.read_only()
.fetch_from(&testnet_network)
.await?
.data;
let greeting: String = hello_near_contract
.call_function("get_greeting", json!({}))
.read_only()
.fetch_from(&sandbox_network)
.await?
.data;
assert_eq!(greeting_from_testnet, greeting);
Snippets
Snippet I: Testing Hello NEAR
Lets take a look at the test of our Quickstart Project 👋 Hello NEAR, where we deploy the contract on an account and test it correctly retrieves and sets the greeting.
use near_api::{AccountId, NearToken};
use near_sdk::serde_json::json;
#[tokio::test]
async fn test_contract_is_operational() -> testresult::TestResult<()> {
// Build the contract wasm file
let contract_wasm_path = cargo_near_build::build_with_cli(Default::default())?;
let contract_wasm = std::fs::read(contract_wasm_path)?;
// Initialize the sandbox
let sandbox = near_sandbox::Sandbox::start_sandbox().await?;
let sandbox_network =
near_api::NetworkConfig::from_rpc_url("sandbox", sandbox.rpc_addr.parse()?);
// Create accounts
let user_account = create_subaccount(&sandbox, "user.sandbox").await?;
let contract = create_subaccount(&sandbox, "contract.sandbox")
.await?
.as_contract();
// Initialize signer for the contract deployment
let signer = near_api::Signer::from_secret_key(
near_sandbox::config::DEFAULT_GENESIS_ACCOUNT_PRIVATE_KEY
.parse()
.unwrap(),
)?;
// Deploy the contract with the init call
near_api::Contract::deploy(contract.account_id().clone())
.use_code(contract_wasm)
.without_init_call()
.with_signer(signer.clone())
.send_to(&sandbox_network)
.await?
.assert_success();
// Change the greeting
contract
.call_function("set_greeting", json!({"greeting": "Hello World!"}))
.transaction()
.with_signer(user_account.account_id().clone(), signer.clone())
.send_to(&sandbox_network)
.await?
.assert_success();
// Get the greeting and assert it is the same as the one set by the user
let user_message_outcome: String = contract
.call_function("get_greeting", ())
.read_only()
.fetch_from(&sandbox_network)
.await?
.data;
assert_eq!(user_message_outcome, "Hello World!");
Ok(())
}
async fn create_subaccount(
sandbox: &near_sandbox::Sandbox,
name: &str,
) -> testresult::TestResult<near_api::Account> {
let account_id: AccountId = name.parse().unwrap();
sandbox
.create_account(account_id.clone())
.initial_balance(NearToken::from_near(10))
.send()
.await?;
Ok(near_api::Account(account_id))
}
Snippet II: Testing Donations
In most cases we will want to test complex methods involving multiple users and money transfers. A perfect example for this is our Donation Example, which enables users to donate money to a beneficiary. Lets see its integration tests
use near_api::types::{AccountId, NearToken};
use near_sdk::json_types::{U128, U64};
use serde_json::json;
const ONE_NEAR: NearToken = NearToken::from_near(1);
const STORAGE_COST: NearToken = NearToken::from_millinear(1);
#[tokio::test]
async fn test_contract_is_operational() -> testresult::TestResult<()> {
// Initialize the sandbox
let sandbox = near_sandbox::Sandbox::start_sandbox().await?;
let sandbox_network =
near_api::NetworkConfig::from_rpc_url("sandbox", sandbox.rpc_addr.parse()?);
// Build the contract
let contract_wasm_path = cargo_near_build::build_with_cli(Default::default())?;
let contract_wasm = std::fs::read(contract_wasm_path)?;
// Create accounts
let alice = create_subaccount(&sandbox, "alice.sandbox").await?;
let bob = create_subaccount(&sandbox, "bob.sandbox").await?;
let beneficiary = create_subaccount(&sandbox, "beneficiary.sandbox").await?;
let contract = create_subaccount(&sandbox, "donation.sandbox")
.await?
.as_contract();
// Initialize signer for the contract deployment
let signer = near_api::Signer::from_secret_key(
near_sandbox::config::DEFAULT_GENESIS_ACCOUNT_PRIVATE_KEY
.parse()
.unwrap(),
)?;
// Deploy the contract and initialize it with the beneficiary
near_api::Contract::deploy(contract.account_id().clone())
.use_code(contract_wasm)
.with_init_call(
"init",
json!({ "beneficiary": beneficiary.account_id() }),
)?
.with_signer(signer.clone())
.send_to(&sandbox_network)
.await?
.assert_success();
let initial_balance = near_api::Account(beneficiary.account_id().clone())
.view()
.fetch_from(&sandbox_network)
.await?
.data
.amount;
// Alice and Bob donate
for (donor, amount) in [
(&alice, ONE_NEAR),
(&bob, ONE_NEAR),
(&alice, ONE_NEAR.saturating_mul(3)),
] {
contract
.call_function("donate", json!({}))
.transaction()
.deposit(amount)
.with_signer(donor.account_id().clone(), signer.clone())
.send_to(&sandbox_network)
.await?
.assert_success();
}
// There are two donors
let number_of_donors: U64 = contract
.call_function("number_of_donors", json!({}))
.read_only()
.fetch_from(&sandbox_network)
.await?
.data;
assert_eq!(number_of_donors, U64::from(2));
#[derive(near_sdk::serde::Serialize, near_sdk::serde::Deserialize, Debug, PartialEq)]
#[serde(crate = "near_sdk::serde")]
struct Donation {
account_id: String,
total_amount: U128,
}
// Alice's donations are accumulated
let donation: Donation = contract
.call_function("get_donation_for_account", json!({"account_id": alice.account_id()}))
.read_only()
.fetch_from(&sandbox_network)
.await?
.data;
assert_eq!(
u128::from(donation.total_amount),
NearToken::from_near(4).as_yoctonear()
);
// All donations are recorded
let donations: Vec<Donation> = contract
.call_function("get_donations", json!({}))
.read_only()
.fetch_from(&sandbox_network)
.await?
.data;
assert_eq!(
donations,
vec![
Donation {
account_id: alice.account_id().to_string(),
total_amount: U128::from(NearToken::from_near(4).as_yoctonear()),
},
Donation {
account_id: bob.account_id().to_string(),
total_amount: U128::from(NearToken::from_near(1).as_yoctonear()),
},
]
);
// The beneficiary received the donations, minus the storage costs
let donation_amount = NearToken::from_near(5).saturating_sub(STORAGE_COST.saturating_mul(2));
let final_balance = near_api::Account(beneficiary.account_id().clone())
.view()
.fetch_from(&sandbox_network)
.await?
.data
.amount;
assert_eq!(final_balance, initial_balance.saturating_add(donation_amount));
Ok(())
}
async fn create_subaccount(
sandbox: &near_sandbox::Sandbox,
name: &str,
) -> testresult::TestResult<near_api::Account> {
let account_id: AccountId = name.parse().unwrap();
sandbox
.create_account(account_id.clone())
.initial_balance(NearToken::from_near(10))
.send()
.await?;
Ok(near_api::Account(account_id))
}