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

Transfer integration test #95

Merged
merged 5 commits into from
Sep 14, 2022
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
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ members = [
"frc42_dispatch/hasher",
"frc42_dispatch/macros",
"frc42_dispatch/macros/example",
"frcxx_nft",
"fvm_dispatch_tools",
"fil_fungible_token",
"testing/fil_token_integration",
"testing/fil_token_integration/actors/basic_token_actor",
"testing/fil_token_integration/actors/basic_receiving_actor",
"testing/fil_token_integration/actors/basic_nft_actor",
"frcxx_nft"
"testing/fil_token_integration/actors/basic_transfer_actor"
]
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ test-coverage: install-toolchain
--exclude fil_token_integration_tests \
--exclude basic_token_actor \
--exclude basic_receiving_actor \
--exclude basic_nft_actor
--exclude basic_nft_actor \
--exclude basic_transfer_actor

# separate actor testing stage to run from CI without coverage support
test-actors: install-toolchain
Expand Down
9 changes: 6 additions & 3 deletions testing/fil_token_integration/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ fvm_integration_tests = "2.0.0-alpha.1"
fvm_ipld_blockstore = "0.1.1"
fvm_ipld_encoding = "0.2.2"
fvm_shared = { version = "2.0.0-alpha.2" }
serde = { version = "1.0", features = ["derive"] }
serde_tuple = { version = "0.5.0" }

[dev-dependencies]
basic_token_actor = {path = "actors/basic_token_actor"}
basic_receiving_actor = {path = "actors/basic_receiving_actor"}
actors-v10 = { package = "fil_builtin_actors_bundle", git = "https://github.com/filecoin-project/builtin-actors", branch = "next", features = ["m2-native"] }
basic_nft_actor = {path = "actors/basic_nft_actor"}
actors-v10 = { package = "fil_builtin_actors_bundle", git = "https://github.com/filecoin-project/builtin-actors", branch = "next", features = ["m2-native"] }
basic_receiving_actor = { path = "actors/basic_receiving_actor" }
basic_token_actor = { path = "actors/basic_token_actor" }
basic_transfer_actor = { path = "actors/basic_transfer_actor" }
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,14 @@ pub fn invoke(params: u32) -> u32 {
401872942 => {
// TransferFrom
let params = deserialize_params(params);
let res = token_actor.transfer_from(params).unwrap();
let cid = token_actor.util.flush().unwrap();
sdk::sself::set_root(&cid).unwrap();
return_ipld(&res).unwrap()
}
1303003700 => {
// Transfer
let params = deserialize_params(params);
let res = token_actor.transfer(params).unwrap();
let cid = token_actor.util.flush().unwrap();
sdk::sself::set_root(&cid).unwrap();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "basic_transfer_actor"
version = "0.1.0"
edition = "2021"

[dependencies]
cid = { version = "0.8.5", default-features = false }
fil_fungible_token = { version = "0.1.0", path = "../../../../fil_fungible_token" }
frc42_dispatch = { path = "../../../../frc42_dispatch" }
fvm_ipld_blockstore = { version = "0.1.1" }
fvm_ipld_encoding = { version = "0.2.2" }
fvm_sdk = { version = "2.0.0-alpha.2" }
fvm_shared = { version = "2.0.0-alpha.2" }
serde = { version = "1.0.136", features = ["derive"] }
serde_tuple = { version = "0.5.0" }

[build-dependencies]
wasm-builder = "3.0"
12 changes: 12 additions & 0 deletions testing/fil_token_integration/actors/basic_transfer_actor/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
fn main() {
use wasm_builder::WasmBuilder;
WasmBuilder::new()
.with_current_project()
.import_memory()
.append_to_rust_flags("-Ctarget-feature=+crt-static")
.append_to_rust_flags("-Cpanic=abort")
.append_to_rust_flags("-Coverflow-checks=true")
.append_to_rust_flags("-Clto=true")
.append_to_rust_flags("-Copt-level=z")
.build()
}
146 changes: 146 additions & 0 deletions testing/fil_token_integration/actors/basic_transfer_actor/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
use cid::{multihash::Code, Cid};
use fil_fungible_token::{
receiver::types::{FRC46TokenReceived, UniversalReceiverParams, FRC46_TOKEN_TYPE},
token::types::TransferParams,
};
use frc42_dispatch::{match_method, method_hash};
use fvm_ipld_blockstore::Block;
use fvm_ipld_encoding::tuple::{Deserialize_tuple, Serialize_tuple};
use fvm_ipld_encoding::{de::DeserializeOwned, RawBytes, DAG_CBOR};
use fvm_sdk as sdk;
use fvm_shared::{address::Address, bigint::Zero, econ::TokenAmount, error::ExitCode};
use sdk::NO_DATA_BLOCK_ID;

/// Grab the incoming parameters and convert from RawBytes to deserialized struct
pub fn deserialize_params<O: DeserializeOwned>(params: u32) -> O {
let params = sdk::message::params_raw(params).unwrap().1;
let params = RawBytes::new(params);
params.deserialize().unwrap()
}

#[derive(Serialize_tuple, Deserialize_tuple)]
struct TransferActorState {
operator_address: Option<Address>,
token_address: Option<Address>,
}

impl TransferActorState {
fn load(cid: &Cid) -> Self {
let data = sdk::ipld::get(cid).unwrap();
fvm_ipld_encoding::from_slice::<Self>(&data).unwrap()
}

fn save(&self) -> Cid {
let serialized = fvm_ipld_encoding::to_vec(self).unwrap();
let block = Block { codec: DAG_CBOR, data: serialized };
sdk::ipld::put(Code::Blake2b256.into(), 32, block.codec, block.data.as_ref()).unwrap()
}
}

/// Implements a simple actor that can hold and transfer tokens
///
/// First operator to send it tokens will be saved and tokens from other operators will be rejected
///
/// Address of the token actor is also saved as this identifies the token type
///
/// After receiving some tokens, it does nothing until the Forward method is called by the initial operator
/// When Forward method is invoked, it will transfer the entire balance it holds to a given address
///
/// Forward requires the same operator to initiate transfer and will abort if the operator address doesn't match,
/// or if the receiver hook rejects the transfer
#[no_mangle]
fn invoke(input: u32) -> u32 {
Copy link
Member

Choose a reason for hiding this comment

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

Could you describe the basic behaviour that this implements?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a comment describing how the actor works

std::panic::set_hook(Box::new(|info| {
sdk::vm::abort(ExitCode::USR_ASSERTION_FAILED.value(), Some(&format!("{}", info)))
}));

let method_num = sdk::message::method_number();
match_method!(method_num, {
"Constructor" => {
let initial_state = TransferActorState { operator_address: None, token_address: None };
let cid = initial_state.save();
sdk::sself::set_root(&cid).unwrap();

NO_DATA_BLOCK_ID
},
"Receive" => {
let mut state = TransferActorState::load(&sdk::sself::root().unwrap());
// Received is passed a TokenReceivedParams
let params: UniversalReceiverParams = deserialize_params(input);

// reject if not an FRC46 token
// we don't know how to inspect other payloads here
if params.type_ != FRC46_TOKEN_TYPE {
panic!("invalid token type, rejecting transfer");
}

// get token transfer data
let token_params: FRC46TokenReceived = params.payload.deserialize().unwrap();

// check the address, we'll remember the first operator and reject others later
match state.operator_address {
Some(operator) => {
let actor_id = sdk::actor::resolve_address(&operator).unwrap();
if actor_id != token_params.operator {
panic!("cannot accept from this operator");
}
}
None => {
state.operator_address = Some(Address::new_id(token_params.operator));
state.token_address = Some(Address::new_id(sdk::message::caller()));
let cid = state.save();
sdk::sself::set_root(&cid).unwrap();
}
}

// all good, don't need to return anything
NO_DATA_BLOCK_ID
},
"Forward" => {
let state = TransferActorState::load(&sdk::sself::root().unwrap());

let target: Address = deserialize_params(input);

// match sender address to the one who operated the last transfer
// if there's no address set, abort because we're expecting a transfer first
match state.operator_address {
Some(operator) => {
let actor_id = sdk::actor::resolve_address(&operator).unwrap();
if actor_id != sdk::message::caller() {
panic!("cannot accept from this operator");
}
}
None => panic!("no operator id set"),
}

// get our balance
let self_address = Address::new_id(sdk::message::receiver());
let balance_receipt = sdk::send::send(&state.token_address.unwrap(), method_hash!("BalanceOf"), RawBytes::serialize(self_address).unwrap(), TokenAmount::zero()).unwrap();
if !balance_receipt.exit_code.is_success() {
panic!("unable to get balance");
}
let balance = balance_receipt.return_data.deserialize::<TokenAmount>().unwrap();

// transfer to target address
let params = TransferParams {
to: target,
amount: balance, // send everything
operator_data: RawBytes::default(),
};
let transfer_receipt = sdk::send::send(&state.token_address.unwrap(), method_hash!("Transfer"), RawBytes::serialize(&params).unwrap(), TokenAmount::zero()).unwrap();
if !transfer_receipt.exit_code.is_success() {
panic!("transfer call failed");
}

// we could return the balance sent or something like that
// but the test we run from is checking that already so no need to do it here
NO_DATA_BLOCK_ID
}
_ => {
sdk::vm::abort(
ExitCode::USR_UNHANDLED_MESSAGE.value(),
Some("Unknown method number"),
);
}
})
}
Loading