difftreelog
Merge pull request #8 from usetech-llc/feature/NFTPAR-94
in: master
Feature/nftpar-94 SIT: Balances can be transferred using smart contract.
17 files changed
tests/ink-types-node-runtime/.gitignorediffbeforeafterboth--- /dev/null
+++ b/tests/ink-types-node-runtime/.gitignore
@@ -0,0 +1,16 @@
+# Ignore build artifacts
+/target/
+/examples/**/target/
+
+# Ignore backup files creates by cargo fmt.
+**/*.rs.bk
+
+# Remove Cargo.lock when creating an executable, leave it for libraries
+# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
+Cargo.lock
+
+# Ignore VS Code artifacts.
+**/.vscode/**
+
+# Ignore history files.
+**/.history/**
\ No newline at end of file
tests/ink-types-node-runtime/Cargo.tomldiffbeforeafterboth--- /dev/null
+++ b/tests/ink-types-node-runtime/Cargo.toml
@@ -0,0 +1,54 @@
+[package]
+name = "ink_types_node_runtime"
+version = "0.1.0"
+authors = ["Parity Technologies <admin@parity.io>"]
+edition = "2018"
+
+license = "GPL-3.0"
+readme = "README.md"
+
+repository = "https://github.com/paritytech/ink"
+documentation = "https://github.com/paritytech/ink/wiki"
+homepage = "https://www.parity.io/"
+
+description = "[ink!] Rust based eDSL for writing smart contracts for Substrate"
+keywords = ["wasm", "parity", "webassembly", "blockchain", "edsl"]
+categories = ["no-std", "embedded"]
+
+include = ["/Cargo.toml", "src/**/*.rs", "/README.md", "/LICENSE"]
+
+[dependencies]
+ink_core = { version = "2", git = "https://github.com/paritytech/ink", tag = "latest-v2", package = "ink_core", default-features = false }
+frame-system = { git = "https://github.com/paritytech/substrate/", package = "frame-system", default-features = false }
+pallet-indices = { git = "https://github.com/paritytech/substrate/", package = "pallet-indices", default-features = false }
+sp-core = { git = "https://github.com/paritytech/substrate/", package = "sp-core", default-features = false }
+sp-io = { git = "https://github.com/paritytech/substrate/", package = "sp-io", default-features = false, features = ["disable_panic_handler", "disable_oom", "disable_allocator"] }
+sp-runtime = { git = "https://github.com/paritytech/substrate/", package = "sp-runtime", default-features = false }
+scale = { package = "parity-scale-codec", version = "1.3", default-features = false, features = ["derive"] }
+
+[dependencies.type-metadata]
+git = "https://github.com/type-metadata/type-metadata.git"
+rev = "02eae9f35c40c943b56af5b60616219f2b72b47d"
+default-features = false
+features = ["derive"]
+optional = true
+
+
+[dev-dependencies]
+node-runtime = { git = "https://github.com/paritytech/substrate/", package = "node-runtime", features = ["std"] }
+
+[features]
+default = ["std"]
+std = [
+ "ink_core/std",
+ "frame-system/std",
+ "pallet-indices/std",
+ "sp-core/std",
+ "sp-io/std",
+ "sp-runtime/std",
+]
+ink-generate-abi = [
+ "std",
+ "type-metadata",
+ "ink_core/ink-generate-abi",
+]
tests/ink-types-node-runtime/README.mddiffbeforeafterboth--- /dev/null
+++ b/tests/ink-types-node-runtime/README.md
@@ -0,0 +1,33 @@
+# Node runtime types for `ink!`
+
+Defines types for [ink!](https://github.com/paritytech/ink) smart contracts targeting [Substrate's `node-runtime`](https://github.com/paritytech/substrate/blob/master/bin/node/runtime/src/lib.rs).
+
+Supplies an implementation of the [ink! `EnvTypes` trait](https://github.com/paritytech/ink/blob/master/core/src/env/types.rs#L128).
+
+See `ink!` [examples](./examples) for usage.
+
+## Requirements
+```
+rustup component add rust-src --toolchain nightly
+rustup target add wasm32-unknown-unknown --toolchain nightly
+rustup target add wasm32-unknown-unknown --toolchain stable
+cargo install cargo-contract --vers 0.6.1 --force
+```
+
+## Build
+
+### Runtime Dependencies
+```
+cargo +nightly build --release
+```
+
+### ink! Smart Contract
+```
+cargo +nightly contract build
+cargo +nightly contract generate-metadata
+```
+
+## Test
+```
+cargo +nightly test
+```
\ No newline at end of file
tests/ink-types-node-runtime/examples/calls/.gitignorediffbeforeafterboth--- /dev/null
+++ b/tests/ink-types-node-runtime/examples/calls/.gitignore
@@ -0,0 +1,9 @@
+# Ignore build artifacts from the local tests sub-crate.
+/target/
+
+# Ignore backup files creates by cargo fmt.
+**/*.rs.bk
+
+# Remove Cargo.lock when creating an executable, leave it for libraries
+# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
+Cargo.lock
\ No newline at end of file
tests/ink-types-node-runtime/examples/calls/.ink/abi_gen/Cargo.tomldiffbeforeafterboth--- /dev/null
+++ b/tests/ink-types-node-runtime/examples/calls/.ink/abi_gen/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "abi-gen"
+version = "0.1.0"
+authors = ["Parity Technologies <admin@parity.io>"]
+edition = "2018"
+publish = false
+
+[[bin]]
+name = "abi-gen"
+path = "main.rs"
+
+[dependencies]
+contract = { path = "../..", package = "calls", default-features = false, features = ["ink-generate-abi"] }
+ink_lang = { version = "2", git = "https://github.com/paritytech/ink", tag = "latest-v2", package = "ink_lang", default-features = false, features = ["ink-generate-abi"] }
+serde = "1.0.101"
+serde_json = "1.0.55"
tests/ink-types-node-runtime/examples/calls/.ink/abi_gen/main.rsdiffbeforeafterboth--- /dev/null
+++ b/tests/ink-types-node-runtime/examples/calls/.ink/abi_gen/main.rs
@@ -0,0 +1,7 @@
+fn main() -> Result<(), std::io::Error> {
+ let abi = <contract::Calls as ink_lang::GenerateAbi>::generate_abi();
+ let contents = serde_json::to_string_pretty(&abi)?;
+ std::fs::create_dir("target").ok();
+ std::fs::write("target/metadata.json", contents)?;
+ Ok(())
+}
tests/ink-types-node-runtime/examples/calls/Cargo.tomldiffbeforeafterbothno changes
tests/ink-types-node-runtime/examples/calls/lib.rsdiffbeforeafterboth--- /dev/null
+++ b/tests/ink-types-node-runtime/examples/calls/lib.rs
@@ -0,0 +1,48 @@
+#![cfg_attr(not(feature = "std"), no_std)]
+
+use ink_lang as ink;
+
+#[ink::contract(version = "0.1.0", env = NodeRuntimeTypes)]
+mod calls {
+ use ink_core::env;
+ use ink_prelude::*;
+ use ink_types_node_runtime::{calls as runtime_calls, NodeRuntimeTypes};
+
+ /// This simple dummy contract dispatches substrate runtime calls
+ #[ink(storage)]
+ struct Calls {}
+
+ impl Calls {
+ #[ink(constructor)]
+ fn new(&mut self) {}
+
+ /// Dispatches a `transfer` call to the Balances srml module
+ #[ink(message)]
+ fn balance_transfer(&self, dest: AccountId, value: Balance) {
+ // create the Balances::transfer Call
+ let transfer_call = runtime_calls::transfer_balance(dest, value);
+
+ // dispatch the call to the runtime
+ let result = self.env().invoke_runtime(&transfer_call);
+
+ // report result to console
+ // NOTE: println should only be used on a development chain)
+ env::println(&format!("Balance transfer invoke_runtime result {:?}", result));
+ }
+ }
+
+ #[cfg(test)]
+ mod tests {
+ use super::*;
+ use sp_keyring::AccountKeyring;
+
+ #[test]
+ fn dispatches_balances_call() {
+ let calls = Calls::new();
+ let alice = AccountId::from(AccountKeyring::Alice.to_account_id());
+ // assert_eq!(calls.env().dispatched_calls().into_iter().count(), 0);
+ calls.balance_transfer(alice, 10000);
+ // assert_eq!(calls.env().dispatched_calls().into_iter().count(), 1);
+ }
+ }
+}
tests/ink-types-node-runtime/src/calls.rsdiffbeforeafterboth--- /dev/null
+++ b/tests/ink-types-node-runtime/src/calls.rs
@@ -0,0 +1,100 @@
+// Copyright 2019 Parity Technologies (UK) Ltd.
+// This file is part of ink!.
+//
+// ink! is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// ink! is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ink!. If not, see <http://www.gnu.org/licenses/>.
+
+use ink_core::env::EnvTypes;
+use scale::{Codec, Decode, Encode};
+use pallet_indices::address::Address;
+use sp_runtime::traits::Member;
+use crate::{AccountId, AccountIndex, Balance, NodeRuntimeTypes};
+
+/// Default runtime Call type, a subset of the runtime Call module variants
+///
+/// The codec indices of the modules *MUST* match those in the concrete runtime.
+#[derive(Encode, Decode)]
+#[cfg_attr(feature = "std", derive(Clone, PartialEq, Eq))]
+pub enum Call {
+ #[codec(index = "5")]
+ Balances(Balances<NodeRuntimeTypes, AccountIndex>),
+}
+
+impl From<Balances<NodeRuntimeTypes, AccountIndex>> for Call {
+ fn from(balances_call: Balances<NodeRuntimeTypes, AccountIndex>) -> Call {
+ Call::Balances(balances_call)
+ }
+}
+/// Generic Balance Call, could be used with other runtimes
+#[derive(Encode, Decode, Clone, PartialEq, Eq)]
+pub enum Balances<T, AccountIndex>
+where
+ T: EnvTypes,
+ T::AccountId: Member + Codec,
+ AccountIndex: Member + Codec,
+{
+ #[allow(non_camel_case_types)]
+ #[codec(index = "3")]
+ transfer(T::AccountId, #[codec(compact)] T::Balance),
+ #[allow(non_camel_case_types)]
+ #[codec(index = "6")]
+ set_balance(
+ Address<T::AccountId, AccountIndex>,
+ #[codec(compact)] T::Balance,
+ #[codec(compact)] T::Balance,
+ ),
+}
+
+/// Construct a `Balances::transfer` call
+pub fn transfer_balance(account: AccountId, balance: Balance) -> Call {
+ Balances::<NodeRuntimeTypes, AccountIndex>::transfer(account, balance).into()
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::{calls, AccountIndex, NodeRuntimeTypes};
+ use super::Call;
+
+ use node_runtime::{self, Runtime};
+ use pallet_indices::address;
+ use scale::{Decode, Encode};
+
+
+ #[test]
+ fn call_balance_transfer() {
+ let balance = 10_000;
+ let account_index = 0;
+
+ let contract_address = calls::Address::Index(account_index);
+ let contract_transfer =
+ calls::Balances::<NodeRuntimeTypes, AccountIndex>::transfer(contract_address, balance);
+ let contract_call = Call::Balances(contract_transfer);
+
+ let srml_address = address::Address::Index(account_index);
+ let srml_transfer = node_runtime::BalancesCall::<Runtime>::transfer(srml_address, balance);
+ let srml_call = node_runtime::Call::Balances(srml_transfer);
+
+ let contract_call_encoded = contract_call.encode();
+ let srml_call_encoded = srml_call.encode();
+
+ assert_eq!(srml_call_encoded, contract_call_encoded);
+
+ let srml_call_decoded: node_runtime::Call =
+ Decode::decode(&mut contract_call_encoded.as_slice())
+ .expect("Balances transfer call decodes to srml type");
+ let srml_call_encoded = srml_call_decoded.encode();
+ let contract_call_decoded: Call = Decode::decode(&mut srml_call_encoded.as_slice())
+ .expect("Balances transfer call decodes back to contract type");
+ assert!(contract_call == contract_call_decoded);
+ }
+}
tests/ink-types-node-runtime/src/lib.rsdiffbeforeafterboth--- /dev/null
+++ b/tests/ink-types-node-runtime/src/lib.rs
@@ -0,0 +1,122 @@
+// Copyright 2018-2019 Parity Technologies (UK) Ltd.
+// This file is part of ink!.
+//
+// ink! is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// ink! is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with ink!. If not, see <http://www.gnu.org/licenses/>.
+
+//! Definitions for environment types for contracts targeted at a
+//! substrate chain with the default `node-runtime` configuration.
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+use core::{array::TryFromSliceError, convert::TryFrom};
+use ink_core::env::Clear;
+use scale::{Decode, Encode};
+use sp_core::crypto::AccountId32;
+#[cfg(feature = "ink-generate-abi")]
+use type_metadata::{HasTypeId, HasTypeDef, Metadata, MetaType, TypeId, TypeDef, TypeIdArray};
+
+pub mod calls;
+
+/// Contract environment types defined in substrate node-runtime
+#[cfg_attr(feature = "ink-generate-abi", derive(Metadata))]
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum NodeRuntimeTypes {}
+
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Encode, Decode)]
+pub struct AccountId (AccountId32);
+
+impl From<AccountId32> for AccountId {
+ fn from(account: AccountId32) -> Self {
+ AccountId(account)
+ }
+}
+
+#[cfg(feature = "ink-generate-abi")]
+impl HasTypeId for AccountId {
+ fn type_id() -> TypeId {
+ TypeIdArray::new(32, MetaType::new::<u8>()).into()
+ }
+}
+
+#[cfg(feature = "ink-generate-abi")]
+impl HasTypeDef for AccountId {
+ fn type_def() -> TypeDef {
+ TypeDef::builtin()
+ }
+}
+
+/// The default SRML balance type.
+pub type Balance = u128;
+
+/// The default SRML hash type.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Encode, Decode)]
+pub struct Hash([u8; 32]);
+
+impl From<[u8; 32]> for Hash {
+ fn from(hash: [u8; 32]) -> Hash {
+ Hash(hash)
+ }
+}
+
+impl<'a> TryFrom<&'a [u8]> for Hash {
+ type Error = TryFromSliceError;
+
+ fn try_from(bytes: &'a [u8]) -> Result<Hash, TryFromSliceError> {
+ let hash = <[u8; 32]>::try_from(bytes)?;
+ Ok(Hash(hash))
+ }
+}
+
+impl AsRef<[u8]> for Hash {
+ fn as_ref(&self) -> &[u8] {
+ &self.0[..]
+ }
+}
+
+impl AsMut<[u8]> for Hash {
+ fn as_mut(&mut self) -> &mut [u8] {
+ &mut self.0[..]
+ }
+}
+
+impl Clear for Hash {
+ fn is_clear(&self) -> bool {
+ self.as_ref().iter().all(|&byte| byte == 0x00)
+ }
+
+ fn clear() -> Self {
+ Self([0x00; 32])
+ }
+}
+
+/// The default SRML moment type.
+pub type Moment = u64;
+
+/// The default SRML blocknumber type.
+pub type BlockNumber = u64;
+
+/// The default SRML AccountIndex type.
+pub type AccountIndex = u32;
+
+/// The default timestamp type.
+pub type Timestamp = u64;
+
+impl ink_core::env::EnvTypes for NodeRuntimeTypes {
+ type AccountId = AccountId;
+ type Balance = Balance;
+ type Hash = Hash;
+ type Timestamp = Timestamp;
+ type BlockNumber = BlockNumber;
+ type Call = calls::Call;
+}
tests/src/balance-transfer-contract/calls.wasmdiffbeforeafterbothbinary blob — no preview
tests/src/balance-transfer-contract/metadata.jsondiffbeforeafterboth--- /dev/null
+++ b/tests/src/balance-transfer-contract/metadata.json
@@ -0,0 +1,96 @@
+{
+ "registry": {
+ "strings": [
+ "Storage",
+ "calls",
+ "__ink_private",
+ "__ink_storage",
+ "new",
+ "balance_transfer",
+ "dest",
+ "AccountId",
+ "value",
+ "Balance"
+ ],
+ "types": [
+ {
+ "id": {
+ "custom.name": 1,
+ "custom.namespace": [
+ 2,
+ 2,
+ 3,
+ 4
+ ],
+ "custom.params": []
+ },
+ "def": {
+ "struct.fields": []
+ }
+ },
+ {
+ "id": {
+ "array.len": 32,
+ "array.type": 3
+ },
+ "def": "builtin"
+ },
+ {
+ "id": "u8",
+ "def": "builtin"
+ },
+ {
+ "id": "u128",
+ "def": "builtin"
+ }
+ ]
+ },
+ "storage": {
+ "struct.type": 1,
+ "struct.fields": []
+ },
+ "contract": {
+ "name": 2,
+ "constructors": [
+ {
+ "name": 5,
+ "selector": "[\"0x5E\",\"0xBD\",\"0x88\",\"0xD6\"]",
+ "args": [],
+ "docs": []
+ }
+ ],
+ "messages": [
+ {
+ "name": 6,
+ "selector": "[\"0xEC\",\"0x9F\",\"0xA4\",\"0xF0\"]",
+ "mutates": false,
+ "args": [
+ {
+ "name": 7,
+ "type": {
+ "ty": 2,
+ "display_name": [
+ 8
+ ]
+ }
+ },
+ {
+ "name": 9,
+ "type": {
+ "ty": 4,
+ "display_name": [
+ 10
+ ]
+ }
+ }
+ ],
+ "return_type": null,
+ "docs": [
+ "Dispatches a `transfer` call to the Balances srml module"
+ ]
+ }
+ ],
+ "events": [],
+ "docs": []
+ }
+}
\ No newline at end of file
tests/src/contracts.test.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/contracts.test.ts
@@ -0,0 +1,160 @@
+import { expect } from "chai";
+import usingApi from "./substrate/substrate-api";
+import promisifySubstrate from "./substrate/promisify-substrate";
+import fs from "fs";
+import privateKey from "./substrate/privateKey";
+import { compactAddLength, u8aToU8a } from '@polkadot/util';
+import { Hash, AccountId } from "@polkadot/types/interfaces";
+import { Abi, PromiseContract } from "@polkadot/api-contract";
+import { ISubmittableResult, IKeyringPair } from "@polkadot/types/types";
+import BN from "bn.js";
+import { alicesPublicKey, bobsPublicKey } from "./accounts";
+import { ApiPromise } from "@polkadot/api";
+import { GenericAccountId, u128 } from "@polkadot/types";
+import getBalance from "./substrate/get-balance";
+
+
+function deployContract(api: ApiPromise, contract: Uint8Array, privateKey: IKeyringPair): Promise<Hash> {
+ return promisifySubstrate(api, () => {
+ return new Promise<Hash>(async (resolve, reject) => {
+ const unsubscribe = api.tx.contracts.putCode(contract).signAndSend(privateKey, async result => {
+ if(!result.isInBlock){
+ return;
+ }
+
+ const record = result.findRecord('contracts', 'CodeStored');
+
+ if(record) {
+ (await unsubscribe)();
+ resolve(record.event.data[0] as unknown as Hash);
+ } else {
+ reject('Failed to find contract hash in putCode transaction result.');
+ }
+ });
+ })
+ })();
+}
+
+function instantiateContract(api: ApiPromise, contractHash: Hash, args: Uint8Array, privateKey: IKeyringPair): Promise<AccountId> {
+ return promisifySubstrate(api, () => {
+ return new Promise<AccountId>((resolve, reject) => {
+ const unsubscribe = api.tx.contracts.instantiate(1000000000000000n, 1000000000000n, contractHash, args)
+ .signAndSend(privateKey, async (result:ISubmittableResult) => {
+ if(!result.isInBlock) {
+ return;
+ }
+ const record = result.findRecord('contracts', 'Instantiated');
+ if(record) {
+ (await unsubscribe)();
+ expect(alicesPublicKey).to.be.equal(record.event.data[0].toString());
+ resolve(record.event.data[1] as AccountId);
+ } else {
+ reject('Failed to find instantiated event.');
+ }
+ });
+ });
+ })();
+}
+
+function txContractCall(api: ApiPromise, privateKey: IKeyringPair, address: string, call: Uint8Array): Promise<void> {
+ return promisifySubstrate(api, async () => {
+ return new Promise<void>(async (resolve, reject) => {
+ api.tx.contracts.call(address, 0, 1000000000000n, call)
+ .signAndSend(privateKey, async result => {
+ if(!result.isInBlock) {
+ return;
+ }
+
+ if(result.findRecord('system', 'ExtrinsicSuccess')) {
+ resolve();
+ }
+ else {
+ reject('Failed to execute tx call.');
+ }
+ })
+ });
+ })();
+}
+
+describe('Contracts', () => {
+ it(`Can deploy smart contract Flipper, instantiate it and call it's get and flip messages.`, async () => {
+ await usingApi(async api => {
+ const wasm = fs.readFileSync('./src/flipper/flipper.wasm');
+ const contract = compactAddLength(u8aToU8a(wasm));
+
+ const metadata = JSON.parse(fs.readFileSync('./src/flipper/metadata.json').toString('utf-8'));
+ const abi = new Abi(api.registry as any, metadata);
+
+ const alicesPrivateKey = privateKey('//Alice');
+
+ const contractHash = await deployContract(api, contract, alicesPrivateKey);
+
+ const args = abi.constructors[0](true);
+ const instanceAccountId = await instantiateContract(api, contractHash, args, alicesPrivateKey);
+
+ const contractInstance = new PromiseContract(api, abi, instanceAccountId);
+ const getFlipValue = async () => {
+ return await promisifySubstrate(api, async () => {
+ const result = await contractInstance.call('rpc', 'get', 0, new BN('1000000000000'))
+ .send(alicesPublicKey);
+ if(!result.isSuccess) {
+ throw 'Failed to get flipper value';
+ }
+ return result.output && result.output.valueOf && result.output.valueOf();
+ })();
+ }
+
+ const initialGetResponse = await getFlipValue();
+ expect(initialGetResponse).to.be.true;
+
+ await promisifySubstrate(api, async () => {
+ return new Promise<void>(async (resolve, reject) => {
+ api.tx.contracts.call(contractInstance.address.toString(), 0, 1000000000000n, contractInstance.getMessage('flip').fn())
+ .signAndSend(alicesPrivateKey, async result => {
+ if(!result.isInBlock) {
+ return;
+ }
+
+ if(result.findRecord('system', 'ExtrinsicSuccess')) {
+ resolve();
+ }
+ else {
+ reject('Failed to flip value.');
+ }
+ })
+ });
+ })();
+
+ const afterFlipGetResponse = await getFlipValue();
+
+ expect(afterFlipGetResponse).to.be.false;
+ });
+ });
+
+ it('Can transfer balance using smart contract.', async () => {
+ await usingApi(async api => {
+ const [alicesBalanceBefore, bobsBalanceBefore] = await getBalance(api, [alicesPublicKey, bobsPublicKey]);
+ const wasm = fs.readFileSync('./src/balance-transfer-contract/calls.wasm');
+ const contract = compactAddLength(u8aToU8a(wasm));
+
+ const metadata = JSON.parse(fs.readFileSync('./src/balance-transfer-contract/metadata.json').toString('utf-8'));
+ const abi = new Abi(api.registry as any, metadata);
+
+ const alicesPrivateKey = privateKey('//Alice');
+
+ const contractHash = await deployContract(api, contract, alicesPrivateKey);
+
+ const args = abi.constructors[0]();
+ const instanceAccountId = await instantiateContract(api, contractHash, args, alicesPrivateKey);
+ const contractInstance = new PromiseContract(api, abi, instanceAccountId);
+ const bob = new GenericAccountId(api.registry, bobsPublicKey);
+ const call = contractInstance.getMessage('balance_transfer').fn(bob, new u128(api.registry, 1000000));
+ await txContractCall(api, alicesPrivateKey, contractInstance.address.toString(), call);
+
+ const [alicesBalanceAfter, bobsBalanceAfter] = await getBalance(api, [alicesPublicKey, bobsPublicKey]);
+
+ expect(alicesBalanceAfter < alicesBalanceBefore).to.be.true;
+ expect(bobsBalanceAfter > bobsBalanceBefore).to.be.true;
+ });
+ })
+});
tests/src/deploy-flipper.test.tsdiffbeforeafterboth--- a/tests/src/deploy-flipper.test.ts
+++ /dev/null
@@ -1,102 +0,0 @@
-import { expect } from "chai";
-import usingApi from "./substrate/substrate-api";
-import promisifySubstrate from "./substrate/promisify-substrate";
-import fs from "fs";
-import privateKey from "./substrate/privateKey";
-import { compactAddLength, u8aToU8a } from '@polkadot/util';
-import { Hash, AccountId } from "@polkadot/types/interfaces";
-import { Abi, PromiseContract } from "@polkadot/api-contract";
-import { ISubmittableResult } from "@polkadot/types/types";
-import BN from "bn.js";
-import { alicesPublicKey } from "./accounts";
-
-describe('Flipper', () => {
- it(`Can deploy smart contract Flipper, instantiate it and call it's get and flip messages.`, async () => {
- await usingApi(async api => {
- const wasm = fs.readFileSync('./src/flipper/flipper.wasm');
- const contract = compactAddLength(u8aToU8a(wasm));
-
- const metadata = JSON.parse(fs.readFileSync('./src/flipper/metadata.json').toString('utf-8'));
- const abi = new Abi(api.registry as any, metadata);
-
- const alicesPrivateKey = privateKey('//Alice');
-
- const contractHash = await promisifySubstrate(api, () => {
- return new Promise<Hash>(async (resolve, reject) => {
- const unsubscribe = api.tx.contracts.putCode(contract).signAndSend(alicesPrivateKey, async result => {
- if(!result.isInBlock){
- return;
- }
-
- const record = result.findRecord('contracts', 'CodeStored');
-
- if(record) {
- (await unsubscribe)();
- resolve(record.event.data[0] as unknown as Hash);
- } else {
- reject('Failed to find contract hash in putCode transaction result.');
- }
- });
- })
- })();
-
-
- const instanceAccountId = await promisifySubstrate(api, () => {
- return new Promise<AccountId>((resolve, reject) => {
- const args = abi.constructors[0](true);
- const unsubscribe = api.tx.contracts.instantiate(1000000000000000n, 1000000000000n, contractHash, args)
- .signAndSend(alicesPrivateKey, async (result:ISubmittableResult) => {
- if(!result.isInBlock) {
- return;
- }
- const record = result.findRecord('contracts', 'Instantiated');
- if(record) {
- (await unsubscribe)();
- expect(alicesPublicKey).to.be.equal(record.event.data[0].toString());
- resolve(record.event.data[1] as AccountId);
- } else {
- reject('Failed to find instantiated event,');
- }
- });
- });
- })();
-
- const contractInstance = new PromiseContract(api, abi, instanceAccountId);
- const getFlipValue = async () => {
- return await promisifySubstrate(api, async () => {
- const result = await contractInstance.call('rpc', 'get', 0, new BN('1000000000000'))
- .send(alicesPublicKey);
- if(!result.isSuccess) {
- throw 'Failed to get flipper value';
- }
- return result.output && result.output.valueOf && result.output.valueOf();
- })();
- }
-
- const initialGetResponse = await getFlipValue();
- expect(initialGetResponse).to.be.true;
-
- await promisifySubstrate(api, async () => {
- return new Promise<void>(async (resolve, reject) => {
- api.tx.contracts.call(contractInstance.address.toString(), 0, 1000000000000n, contractInstance.getMessage('flip').fn())
- .signAndSend(alicesPrivateKey, async result => {
- if(!result.isInBlock) {
- return;
- }
-
- if(result.findRecord('system', 'ExtrinsicSuccess')) {
- resolve();
- }
- else {
- reject('Failed to flip value.');
- }
- })
- });
- })();
-
- const afterFlipGetResponse = await getFlipValue();
-
- expect(afterFlipGetResponse).to.be.false;
- });
- });
-});
tests/src/substrate/get-balance.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/substrate/get-balance.ts
@@ -0,0 +1,9 @@
+import { ApiPromise } from "@polkadot/api";
+import promisifySubstrate from "./promisify-substrate";
+import {AccountInfo} from "@polkadot/types/interfaces/system";
+
+export default async function getBalance(api: ApiPromise, accounts: string[]): Promise<bigint[]> {
+ const balance = promisifySubstrate(api, (accounts: string[]) => api.query.system.account.multi(accounts));
+ const responce = await balance(accounts) as unknown as AccountInfo[];
+ return responce.map(r => r.data.free.toBigInt().valueOf());
+}
tests/src/substrate/privateKey.tsdiffbeforeafterboth--- a/tests/src/substrate/privateKey.ts
+++ b/tests/src/substrate/privateKey.ts
@@ -1,6 +1,7 @@
import { Keyring } from "@polkadot/api";
+import { IKeyringPair } from "@polkadot/types/types";
-export default function privateKey(account: string) {
+export default function privateKey(account: string): IKeyringPair {
const keyring = new Keyring({ type: 'sr25519' });
return keyring.addFromUri(account);
tests/src/transfer.test.tsdiffbeforeafterboth--- a/tests/src/transfer.test.ts
+++ b/tests/src/transfer.test.ts
@@ -1,18 +1,10 @@
-import createSubstrateApi from "./substrate/substrate-api";
-import { Keyring, ApiPromise } from "@polkadot/api";
-import {AccountInfo} from "@polkadot/types/interfaces/system";
import { expect } from "chai";
import usingApi from "./substrate/substrate-api";
import promisifySubstrate from "./substrate/promisify-substrate";
import waitNewBlocks from "./substrate/wait-new-blocks";
import { alicesPublicKey, bobsPublicKey } from "./accounts";
import privateKey from "./substrate/privateKey";
-
-async function getBalance(api: ApiPromise, accounts: string[]): Promise<bigint[]> {
- const balance = promisifySubstrate(api, (accounts: string[]) => api.query.system.account.multi(accounts));
- const responce = await balance(accounts) as unknown as AccountInfo[];
- return responce.map(r => r.data.free.toBigInt().valueOf());
-}
+import getBalance from "./substrate/get-balance";
describe('Transfer', () => {
it('Balance transfers', async () => {
@@ -27,7 +19,7 @@
await waitNewBlocks(api);
- const [alicesBalanceAfter, bobsBalanceAfter] = await getBalance(api, [alicesPublicKey, bobsPublicKey]);
+ const [alicesBalanceAfter, bobsBalanceAfter] = await getBalance(api, [alicesPublicKey, bobsPublicKey]);
expect(alicesBalanceAfter < alicesBalanceBefore).to.be.true;
expect(bobsBalanceAfter > bobsBalanceBefore).to.be.true;