diff --git a/docs/basics/cross-contract-calling.md b/docs/basics/cross-contract-calling.md index 415c1c505a..589e2eb108 100644 --- a/docs/basics/cross-contract-calling.md +++ b/docs/basics/cross-contract-calling.md @@ -14,50 +14,64 @@ on-chain contracts. There are a few approaches to performing these cross-contract calls in ink!: 1. Contract references (i.e `ContractRef`) 2. Builders (i.e `CreateBuilder` and `CallBuilder`) -3. Calling Solidity ABI Encoded Contracts with `CallBuilder` -Contract references can only be used for cross-contract calls to other ink! contracts. -Builders can be used to issue cross-contract calls to any RISC-V contract, such as those -written in ink! or Solidity. +:::note +In general, contract references should be preferred over builders +because they provide higher-level type-safe interfaces. +Only use builders if you need to manipulate low-level call parameters. +::: ## Contract References -Contract references refer to structs that are generated by the ink! code generation for the -purposes of cross-contract calls. +Contract references are wrapper types that can be used for interacting with an on-chain/"callee" contract +using a high-level type-safe interface. -They give developers a type-safe way of interacting with a contract. +They are either statically generated by the ink! code generation (for contract dependencies), +or they can be manually defined as dynamic interfaces using the [`#[ink::contract_ref]` attribute][contract-ref-attr]. -A downside to using them is that you need to import the contract you want to call as a -dependency of your own contract. +[contract-ref-attr]: ../macros-attributes/contract_ref.md -If you want to interact with a contract that is already on-chain you will need to use the -[`Builders`](#builders) approach instead. +### Manually defined contract references -:::note -In ["all" ABI mode][abi-all], contract references are generated for both ink!/native -and Solidity ABI calling conventions, with the Solidity ABI specific contract reference -named with an additional `Sol` suffix -(e.g. `ContractRefSol` for a contract named `Contract`). +See our section on using the [`#[ink::contract_ref]` attribute][contract-ref-attr] +for a detailed description and examples of how to manually define the dynamic interface +for an on-chain/"callee" contract, and use the generated contract reference +for calling the on-chain/"callee" contract in a type-safe manner. -Note that, this `Sol` suffix is not necessary in ["sol" ABI mode][abi-sol] -(i.e. for a contract named `Contract`, `ContractRef` will use the Solidity ABI -calling conventions in "sol" ABI mode). +:::caution +A downside to manually defined contract references is that mistakes +in the interface definition are not caught at compile-time. + +It's therefore important to make sure such interfaces are properly tested +using [end-to-end testing][e2e-test] before contracts are deployed on-chain. ::: -[abi-all]: ./abi/all.md -[abi-sol]: ./abi/solidity.md +[e2e-test]: ../testing/e2e.md + +### Statically generated contract references -### `BasicContractRef` walkthrough +To use statically generated contract references, you need to import the contract +you want to call as a dependency of your own contract. -We will walk through the [`cross-contract-calls`](https://github.com/use-ink/ink-examples/tree/master/cross-contract-calls) -example in order to demonstrate how cross-contract calls using contract references work. +This means that this approach cannot be used if you want to interact with a contract +that is either built in another language (e.g. Solidity), or has no publicly available package/crate. +For those cases, you will need to use either [manually defined contract references](#manually-defined-contract-references) +using the [`#[ink::contract_ref]` attribute][contract-ref-attr] (recommended), +or the [`Builders`](#builders) approach instead. + +#### `BasicContractRef` walkthrough + +We will walk through the [`cross-contract-calls`][example] example in order +to demonstrate how cross-contract calls using contract references work. The general workflow will be: 1. Prepare `OtherContract` to be imported to other contracts -1. Import `OtherContract` into `BasicContractRef` -1. Upload `OtherContract` on-chain -1. Instantiate `OtherContract` using `BasicContractRef` -1. Call `OtherContract` using `BasicContractRef` +2. Import `OtherContract` into `BasicContractRef` +3. Upload `OtherContract` on-chain +4. Instantiate `OtherContract` using `BasicContractRef` +5. Call `OtherContract` using `BasicContractRef` + +[example]: https://github.com/use-ink/ink-examples/tree/master/cross-contract-calls #### Prepping `OtherContract` @@ -70,6 +84,24 @@ We do this by re-exporting the contract reference as follows: pub use self::other_contract::OtherContractRef; ``` +:::info +We intend to automatically generate this re-export in future releases of ink! v6. +::: + +:::note +In ["all" ABI mode][abi-all], contract references are generated for both ink!/native +and Solidity ABI calling conventions, with the Solidity ABI specific contract reference +named with an additional `Sol` suffix +(e.g. `ContractRefSol` for a contract named `Contract`). + +Note that, this `Sol` suffix is not necessary in ["sol" ABI mode][abi-sol] +(i.e. for a contract named `Contract`, `ContractRef` will use the Solidity ABI +calling conventions in "sol" ABI mode). +::: + +[abi-all]: ./abi/all.md +[abi-sol]: ./abi/solidity.md + #### Importing `OtherContract` Next, we need to import `OtherContract` to our `BasicContractRef` contract. @@ -206,23 +238,30 @@ Data Ok(true) ``` ## Builders -The -[`CreateBuilder`](https://use-ink.github.io/ink/ink_env/call/struct.CreateBuilder.html) -and -[`CallBuilder`](https://use-ink.github.io/ink/ink_env/call/struct.CallBuilder.html) -offer low-level, flexible interfaces for performing cross-contract calls. The -`CreateBuilder` allows you to instantiate already uploaded contracts, and the -`CallBuilder` allows you to call messages on instantiated contracts. +The [`CreateBuilder`][create-builder] and [`CallBuilder`][call-builder] +offer low-level, flexible interfaces for performing cross-contract calls. +The `CreateBuilder` allows you to instantiate already uploaded contracts, +and the `CallBuilder` allows you to call messages on instantiated contracts. + +[create-builder]: https://use-ink.github.io/ink/ink_env/call/struct.CreateBuilder.html +[call-builder]: https://use-ink.github.io/ink/ink_env/call/struct.CallBuilder.html + +:::caution +A downside to low-level `CreateBuilder`s and `CallBuilder`s is that mistakes +in the generated calls (e.g. wrong selectors, wrong order and/or types of arguments e.t.c) +are not caught at compile-time. + +It's therefore important to make sure such calls are properly tested +using [end-to-end testing][e2e-test] before contracts are deployed on-chain. +::: ### CreateBuilder -The `CreateBuilder` offers an an easy way for you to **instantiate** a contract. Note -that you'll still need this contract to have been previously uploaded. +The `CreateBuilder` offers an easy way for you to **instantiate** a contract. +Note that you'll still need this contract to have been previously uploaded. :::note - For a refresher on the difference between `upload` and `instantiate` [see here](../getting-started/deploying.md). - ::: In order to instantiate a contract you need a reference to your contract, just like in @@ -243,9 +282,8 @@ Below is an example of how to instantiate a contract using the `CreateBuilder`. ```rust use contract::MyContractRef; let my_contract: MyContractRef = build_create::() - .instantiate_v1() .code_hash(Hash::from([0x42; 32])) - .gas_limit(0) + .ref_time_limit(0) .endowment(10) .exec_input( ExecutionInput::new(Selector::new(ink::selector_bytes!("new"))) @@ -299,8 +337,7 @@ Below is an example of how to call a contract using the `CallBuilder`. We will: ```rust let my_return_value = build_call::() .call(H160::from([0x42; 20])) - .call_v1() - .gas_limit(0) + .ref_time_limit(0) .transferred_value(10) .exec_input( ExecutionInput::new(Selector::new(ink::selector_bytes!("flip"))) @@ -312,14 +349,15 @@ let my_return_value = build_call::() .invoke(); ``` -Note: - +:::caution Message arguments will be encoded in the order in which they are provided to the `CallBuilder`. This means that they should match the order (and type) they appear in the function signature. -You will not be able to get any feedback about this at compile time. You will only -find out your call failed at runtime! +You will not get any feedback about this at compile-time, +so it's important to make sure such calls are properly tested +with [end-to-end testing][e2e-test] before contracts are deployed on-chain. +::: :::note To call messages from a contract that uses a different [ABI][abi] than your contract, diff --git a/docs/macros-attributes/contract_ref.md b/docs/macros-attributes/contract_ref.md index 84e9f8506c..92e5b9acad 100644 --- a/docs/macros-attributes/contract_ref.md +++ b/docs/macros-attributes/contract_ref.md @@ -75,6 +75,16 @@ mod erc20_caller { } ``` +:::caution +A downside to manually defined contract references is that mistakes +in the interface definition are not caught at compile-time. + +It's therefore important to make sure such interfaces are properly tested +using [end-to-end testing][e2e-test] before contracts are deployed on-chain. +::: + +[e2e-test]: ../testing/e2e.md + ## Header Arguments The `#[ink::contract_ref]` macro can be provided with some additional @@ -125,6 +135,7 @@ pub struct MyEnvironment; impl ink_env::Environment for MyEnvironment { const NATIVE_TO_ETH_RATIO: u32 = 100_000_000; + type AccountId = [u8; 16]; type Balance = u128; type Hash = [u8; 32];