diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 88c61f478..4e7df72ca 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -320,6 +320,9 @@ jobs: echo "$(pwd)/bin" >> $GITHUB_PATH - run: npm install working-directory: ./integration/substrate + - name: Build ink! contracts + run: npm run build-ink + working-directory: ./integration/substrate - name: Build Solang contracts run: npm run build working-directory: ./integration/substrate diff --git a/integration/substrate/ink/caller/.gitignore b/integration/substrate/ink/caller/.gitignore new file mode 100755 index 000000000..8de8f877e --- /dev/null +++ b/integration/substrate/ink/caller/.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 diff --git a/integration/substrate/ink/caller/Cargo.toml b/integration/substrate/ink/caller/Cargo.toml new file mode 100755 index 000000000..b51681763 --- /dev/null +++ b/integration/substrate/ink/caller/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "caller" +version = "0.1.0" +authors = ["Cyrill Leutwiler "] +edition = "2021" + +[dependencies] +ink = { version = "4.0.0-beta", default-features = false } + +scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } +scale-info = { version = "2.3", default-features = false, features = ["derive"], optional = true } + +[lib] +name = "caller" +path = "lib.rs" +crate-type = [ + # Used for normal contract Wasm blobs. + "cdylib", +] + +[features] +default = ["std"] +std = [ + "ink/std", + "scale/std", + "scale-info/std", +] +ink-as-dependency = [] + +[workspace] diff --git a/integration/substrate/ink/caller/lib.rs b/integration/substrate/ink/caller/lib.rs new file mode 100755 index 000000000..4434ef247 --- /dev/null +++ b/integration/substrate/ink/caller/lib.rs @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: Apache-2.0 + +#![cfg_attr(not(feature = "std"), no_std)] + +#[ink::contract] +mod caller { + use ink::env::{ + call::{build_call, Call, ExecutionInput, Selector}, + DefaultEnvironment, + }; + + #[ink(storage)] + #[derive(Default)] + pub struct Caller {} + + impl Caller { + #[ink(constructor)] + pub fn new() -> Self { + Default::default() + } + + /// Do a proxy call to `callee` and return its result. + /// The message under `selector` should have exactly one `u32` arguemnt and return a `u32`. + #[ink(message)] + pub fn u32_proxy( + &self, + callee: AccountId, + selector: [u8; 4], + arg: u32, + max_gas: Option, + transfer_value: Option, + ) -> u32 { + let my_return_value = build_call::() + .call_type( + Call::new() + .callee(callee) + .gas_limit(max_gas.unwrap_or_default()), + ) + .transferred_value(transfer_value.unwrap_or_default()) + .exec_input(ExecutionInput::new(Selector::new(selector)).push_arg(arg)) + .returns::() // FIXME: This should be Result to respect LanguageError + .fire(); + my_return_value.unwrap() + } + } +} diff --git a/integration/substrate/ink_cross_calls.spec.ts b/integration/substrate/ink_cross_calls.spec.ts new file mode 100644 index 000000000..8ce0486fa --- /dev/null +++ b/integration/substrate/ink_cross_calls.spec.ts @@ -0,0 +1,44 @@ +import expect from 'expect'; +import { weight, createConnection, deploy, transaction, aliceKeypair, query, } from './index'; +import { ContractPromise } from '@polkadot/api-contract'; +import { ApiPromise } from '@polkadot/api'; +import { KeyringPair } from '@polkadot/keyring/types'; + +describe('Test cross contract calls between ink and solidity', () => { + let conn: ApiPromise; + let alice: KeyringPair; + + let ink_contract: ContractPromise; + + let sol_contract: ContractPromise; + let inkee_echo = [1, 2, 3, 4]; + + before(async function () { + conn = await createConnection(); + alice = aliceKeypair(); + + let ink_deployment = await deploy(conn, alice, 'ink/caller/target/ink/caller.contract', 0n); + ink_contract = new ContractPromise(conn, ink_deployment.abi, ink_deployment.address); + + let sol_deployment = await deploy(conn, alice, 'Inkee.contract', 0n); + sol_contract = new ContractPromise(conn, sol_deployment.abi, sol_deployment.address); + }); + + it('calls solidity from ink', async function () { + this.timeout(50000); + + async function proxy(goes_in: number) { + const comes_out = await query(conn, alice, ink_contract, "u32_proxy", [sol_contract.address, inkee_echo, goes_in, null, null]); + expect(comes_out.output?.toJSON()).toEqual({ "ok": goes_in }); + } + + await proxy(0); + await proxy(1); + await proxy(1337); + await proxy(0xffffffff); + }); + + after(async function () { + await conn.disconnect(); + }); +}); diff --git a/integration/substrate/inkee.sol b/integration/substrate/inkee.sol new file mode 100644 index 000000000..bb9fd286a --- /dev/null +++ b/integration/substrate/inkee.sol @@ -0,0 +1,5 @@ +contract Inkee { + function echo(uint32 v) selector=hex"01020304" public pure returns (uint32) { + return v; + } +} diff --git a/integration/substrate/package.json b/integration/substrate/package.json index a24236939..bc9b4c759 100644 --- a/integration/substrate/package.json +++ b/integration/substrate/package.json @@ -5,7 +5,8 @@ "main": "index.js", "scripts": { "test": "tsc; ts-mocha -t 20000 *.spec.ts", - "build": "parallel solang compile -v --target substrate ::: *.sol test/*.sol" + "build": "parallel solang compile -v --target substrate ::: *.sol test/*.sol", + "build-ink": "docker run --rm -v $(pwd)/ink/caller:/opt/contract paritytech/contracts-ci-linux:latest cargo contract build --manifest-path /opt/contract/Cargo.toml" }, "contributors": [ {