Skip to content

Commit

Permalink
Merge pull request #85 from CoinFabrik/67-issue-21-transfer
Browse files Browse the repository at this point in the history
67 issue 21 transfer
  • Loading branch information
aon authored Sep 20, 2023
2 parents 759969c + 4ef99e2 commit 2852159
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 0 deletions.
32 changes: 32 additions & 0 deletions test-cases/transfer/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[package]
name = "transfer"
version = "0.1.0"
edition = "2021"
authors = ["[your_name] <[your_email]>"]

[lib]
path = "lib.rs"

[features]
default = ["std"]
std = [
"ink/std",
"scale/std",
"scale-info/std",
]
ink-as-dependency = []
e2e-tests = []

[dependencies]
ink = { version = "4.3.0", default-features = false }
scale = { package = "parity-scale-codec", version = "=3.6.5", default-features = false, features = ["derive"] }
scale-info = { version = "2.6", default-features = false, features = ["derive"], optional = true }

[dev-dependencies]
ink_e2e = "4.3.0"

[profile.dev]
overflow-checks = false

[profile.release]
overflow-checks = false
40 changes: 40 additions & 0 deletions test-cases/transfer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Function `transfer`

```rust
pub fn transfer<E>(destination: E::AccountId, value: E::Balance) -> Result<()>
```

## Description

The `transfer` function serves as the core of this contract. When invoked, it initiates a balance transfer of the specified `value` from the contract's balance to the `destination` account. This function is especially useful in scenarios where users or other contracts need to transfer funds.

## Related ink! functions

- [`transfer`](https://paritytech.github.io/ink/ink_env/fn.transfer.html)

## Test case

The contract's functionality can be demonstrated with a simple test:

```rust
let contract = Transfer::new();
contract.transfer(1000);
```

This test showcases the process of instantiating the contract and requesting a balance transfer. In real-world scenarios, the results would also account for transaction fees and other network considerations.

## Comparison Integration vs E2E

Ensuring the reliability and security of balance transfers is crucial. As such, rigorous testing is performed on both integration and end-to-end levels:

- Integration Tests: In the integration test `transfer_works`, a testing environment is set up with a specific account balance. The `transfer` function is then invoked to transfer a specified value from the contract's balance to the caller. After the transfer, the test asserts that the balances of the accounts have changed as expected.

- E2E Tests: In the end-to-end test `transfer_works`, the contract is set up with a given balance. The `transfer` function is then called to transfer a value of `1` to the caller. The test then verifies that the contract's balance has decreased by the transferred amount.

| \# | Test | Integration | E2E |
| --- | ------------------------------------- | :---------: | :-: |
| 1 | Transfers funds to the caller account |||

## Result

This functionality works as expected.
116 changes: 116 additions & 0 deletions test-cases/transfer/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#![cfg_attr(not(feature = "std"), no_std, no_main)]

#[ink::contract]
mod transfer {

#[ink(storage)]
pub struct Transfer {}

impl Transfer {
#[ink(constructor, payable)]
pub fn new() -> Self {
Self {}
}

#[ink(message)]
pub fn transfer(&self, value: Balance) {
self.env()
.transfer(self.env().caller(), value)
.unwrap_or_else(|err| panic!("transfer failed: {:?}", err));
}
}

impl Default for Transfer {
fn default() -> Self {
Self::new()
}
}

#[cfg(test)]
mod tests {
use super::*;
use ink::env::{
test::{get_account_balance, set_account_balance, set_callee, set_caller},
DefaultEnvironment,
};

#[ink::test]
fn transfer_works() {
// Given
let contract = Transfer::new();
let callee_account_id = AccountId::from([0x01; 32]);
let caller_account_id = AccountId::from([0x02; 32]);
set_callee::<DefaultEnvironment>(callee_account_id);
set_caller::<DefaultEnvironment>(caller_account_id);
set_account_balance::<DefaultEnvironment>(callee_account_id, 1000);
set_account_balance::<DefaultEnvironment>(caller_account_id, 0);

// When
contract.transfer(100);

// Then
let callee_balance =
get_account_balance::<DefaultEnvironment>(AccountId::from([0x34; 32]))
.expect("Cannot get account balance");
let caller_balance = get_account_balance::<DefaultEnvironment>(caller_account_id)
.expect("Cannot get account balance");
assert_eq!(callee_balance, 900);
assert_eq!(caller_balance, 100);
}
}

#[cfg(all(test, feature = "e2e-tests"))]
mod e2e_tests {
use ink_e2e::{build_message, AccountKeyring};

use super::*;

type E2EResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;

#[ink_e2e::test]
async fn transfer_works(mut client: Client<C, E>) -> E2EResult<()> {
// Given
let constructor = TransferRef::new();

let callee_account_id = client
.instantiate("transfer", &ink_e2e::bob(), constructor, 1000, None)
.await
.expect("instantiate failed")
.account_id;
let caller_account_id = AccountKeyring::Bob.to_raw_public().into();

let initial_callee_balance = client
.balance(callee_account_id)
.await
.expect("Failed to get account balance");
let initial_caller_balance = client
.balance(caller_account_id)
.await
.expect("Failed to get account balance");

// When
let transfer_call = build_message::<TransferRef>(callee_account_id)
.call(|contract| contract.transfer(1));

let _transfer_call_res = client
.call(&ink_e2e::bob(), transfer_call, 0, None)
.await
.expect("transfer_call failed");

// Then
let callee_balance = client
.balance(callee_account_id)
.await
.expect("Failed to get account balance");
let caller_balance = client
.balance(caller_account_id)
.await
.expect("Failed to get account balance");

assert_eq!(callee_balance, initial_callee_balance - 1);
assert!(caller_balance < initial_caller_balance + 1);

Ok(())
}
}
}

0 comments on commit 2852159

Please sign in to comment.