Skip to content

Commit

Permalink
Implement balance_transfer_keep_alive (#775)
Browse files Browse the repository at this point in the history
* Implement functionality

* Create test for balance_transfer_keep_alive

* Incorporate review feedback
  • Loading branch information
Niederb authored Jul 10, 2024
1 parent 96771a8 commit 59b0f4d
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 7 deletions.
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();
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);
}

0 comments on commit 59b0f4d

Please sign in to comment.