Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement balance_transfer_keep_alive #775

Merged
merged 3 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/extrinsic/balances.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,15 @@ use codec::{Compact, Encode};

pub const BALANCES_MODULE: &str = "Balances";
pub const TRANSFER_ALLOW_DEATH: &str = "transfer_allow_death";
pub const TRANSFER_KEEP_ALIVE: &str = "transfer_keep_alive";
pub const FORCE_SET_BALANCE: &str = "force_set_balance";

/// Call for a balance transfer.
pub type TransferAllowDeathCall<Address, Balance> = (CallIndex, Address, Compact<Balance>);

/// Call for a balance transfer.
pub type TransferKeepAliveCall<Address, Balance> = (CallIndex, Address, Compact<Balance>);

/// Call to the balance of an account.
pub type ForceSetBalanceCall<Address, Balance> = (CallIndex, Address, Compact<Balance>);

Expand All @@ -52,6 +56,14 @@ pub trait BalancesExtrinsics {
amount: Self::Balance,
) -> Option<Self::Extrinsic<TransferAllowDeathCall<Self::Address, Self::Balance>>>;

/// Transfer some liquid free balance to another account.
#[allow(clippy::type_complexity)]
async fn balance_transfer_keep_alive(
&self,
to: Self::Address,
amount: Self::Balance,
) -> Option<Self::Extrinsic<TransferKeepAliveCall<Self::Address, Self::Balance>>>;

/// Set the balances of a given account.
#[allow(clippy::type_complexity)]
async fn balance_force_set_balance(
Expand Down Expand Up @@ -85,6 +97,15 @@ where
compose_extrinsic!(self, BALANCES_MODULE, TRANSFER_ALLOW_DEATH, to, Compact(amount))
}

#[allow(clippy::type_complexity)]
async fn balance_transfer_keep_alive(
&self,
to: Self::Address,
amount: Self::Balance,
) -> Option<Self::Extrinsic<TransferKeepAliveCall<Self::Address, Self::Balance>>> {
compose_extrinsic!(self, BALANCES_MODULE, TRANSFER_KEEP_ALIVE, to, Compact(amount))
}

async fn balance_force_set_balance(
&self,
who: Self::Address,
Expand Down
12 changes: 8 additions & 4 deletions testing/async/examples/dispatch_errors_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use sp_keyring::AccountKeyring;
use sp_runtime::MultiAddress;
use substrate_api_client::{
ac_primitives::AssetRuntimeConfig, extrinsic::BalancesExtrinsics, rpc::JsonrpseeClient, Api,
Error, GetAccountInformation, SubmitAndWatch, XtStatus,
Error, GetAccountInformation, GetBalance, SubmitAndWatch, XtStatus,
};

#[tokio::main]
Expand All @@ -33,7 +33,7 @@ async fn main() {

let alice = AccountKeyring::Alice.to_account_id();
let balance_of_alice = api.get_account_data(&alice).await.unwrap().unwrap().free;
println!("[+] Alice's Free Balance is is {}\n", balance_of_alice);
println!("[+] Alice's Free Balance is {}\n", balance_of_alice);

let bob = AccountKeyring::Bob.to_account_id();
let balance_of_bob = api.get_account_data(&bob).await.unwrap().unwrap_or_default().free;
Expand All @@ -59,10 +59,10 @@ async fn main() {
assert!(report.block_hash.is_some());
assert!(report.events.is_some());
assert!(format!("{dispatch_error:?}").contains("BadOrigin"));
println!("[+] BadOrigin error: Bob can't force set balance");
},
_ => panic!("Expected Failed Extrinisc Error"),
}
println!("[+] BadOrigin error: Bob can't force set balance");

//BelowMinimum
api.set_signer(alice_signer.into());
Expand All @@ -80,5 +80,9 @@ async fn main() {
},
_ => panic!("Expected Failed Extrinisc Error"),
}
println!("[+] BelowMinimum error: balance (999999) is below the existential deposit");
let existential_deposit = api.get_existential_deposit().await.unwrap();
println!(
"[+] BelowMinimum error: balance (999999) is below the existential deposit ({})",
&existential_deposit
);
}
56 changes: 53 additions & 3 deletions testing/async/examples/pallet_balances_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,65 @@

//! Tests for the pallet balances interface functions.

use sp_keyring::AccountKeyring;
use substrate_api_client::{
ac_primitives::AssetRuntimeConfig, rpc::JsonrpseeClient, Api, GetBalance,
ac_primitives::AssetRuntimeConfig, extrinsic::BalancesExtrinsics, rpc::JsonrpseeClient, Api,
GetAccountInformation, GetBalance, SubmitAndWatch, XtStatus,
};

#[tokio::main]
async fn main() {
// Setup
let client = JsonrpseeClient::with_default_url().await.unwrap();
let api = Api::<AssetRuntimeConfig, _>::new(client).await.unwrap();
let mut api = Api::<AssetRuntimeConfig, _>::new(client).await.unwrap();

let _ed = api.get_existential_deposit().await.unwrap();
let ed = api.get_existential_deposit().await.unwrap();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the focus of this test is sending funds, therefore I would make a new test-file with a fitting name. Also I would test a successfull transaction with balance_transfer_keep_alive.

I would create 2 new tests:

  1. "send_funds_test.rs" that tests a successfull balance_transfer_keep_alive transaction
  2. "send_funds_with_death_test.rs" for the test case here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with adding a successful transaction for balance_transfer_keep_alive but I would keep it all in the same file it is now. The reason is that so far the testing examples are more organized by pallet/subsystem (frame_system_tests.rs, pallet_contract_tests.rs, chain_tests.rs, ....) and not by a specific test case.

println!("[+] Existential deposit is {}\n", ed);

let alice = AccountKeyring::Alice.to_account_id();
let alice_signer = AccountKeyring::Alice.pair();
api.set_signer(alice_signer.into());
let balance_of_alice = api.get_account_data(&alice).await.unwrap().unwrap().free;
println!("[+] Alice's Free Balance is {}\n", balance_of_alice);

let bob = AccountKeyring::Bob.to_account_id();
let balance_of_bob = api.get_account_data(&bob).await.unwrap().unwrap_or_default().free;
println!("[+] Bob's Free Balance is {}\n", balance_of_bob);

// Rough estimate of fees for three transactions
let fee_estimate = 3 * 2000000000000;

let xt = api
.balance_transfer_keep_alive(bob.clone().into(), balance_of_alice / 2 - fee_estimate)
.await
.unwrap();
let report = api.submit_and_watch_extrinsic_until(xt, XtStatus::Finalized).await;
// This call should succeed as alice has enough money
assert!(report.is_ok());

let balance_of_alice = api.get_account_data(&alice).await.unwrap().unwrap().free;
println!("[+] Alice's Free Balance is {}\n", balance_of_alice);

let xt = api
.balance_transfer_keep_alive(bob.clone().into(), balance_of_alice - fee_estimate)
.await
.unwrap();
let report = api.submit_and_watch_extrinsic_until(xt, XtStatus::Finalized).await;
// This call should fail as alice would fall below the existential deposit
assert!(report.is_err());

let xt = api
.balance_transfer_allow_death(bob.clone().into(), balance_of_alice - fee_estimate)
.await
.unwrap();
let result = api.submit_and_watch_extrinsic_until(xt, XtStatus::Finalized).await;
// With allow_death the call should succeed
assert!(result.is_ok());

let alice_account = api.get_account_data(&alice).await.unwrap();
// Alice account should not exist anymore so we excpect an error
assert!(alice_account.is_none());

let balance_of_bob = api.get_account_data(&bob).await.unwrap().unwrap_or_default().free;
println!("[+] Bob's Free Balance is {}\n", balance_of_bob);
}