diff --git a/docs/glossary.md b/docs/glossary.md index 7ac84f08..78e695ea 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -1,20 +1,32 @@ --- -sidebar_position: 4 +sidebar_position: 7 --- # Glossary -## Dapp +## Endpoint -Decentralized application. +The cross-chain operation entry. An endpoint is a smart contract which is used by Dapps to do cross-chain operations. -## Dapp developer +The endpoint contract is created and deployed by the Dapp developer. After that Dapp can call the endpoint to do cross-chain operations. -Dapp developers include those who develop applications based on the Darwinia Smart Contracts module, as well as those who develop Dapps on public chains, such as blockchain games or Defi applications on platforms such as Ethereum, TRON or EOS. +```js +contract YourEndpoint is MessageEndpoint {...} +``` -## Dapp user +```js +contract YourDapp { + YourEndpoint endpoint; -End users of Dapps. + constructor(YourEndpoint _endpoint) { + endpoint = _endpoint; + } + + function foo() { + YourEndpoint(endpoint).remoteExecute(...); + } +} +``` ## Source Chain @@ -49,11 +61,11 @@ The message is sent by the source chain application layer and finally delivered The intermediary of cross-chain data is responsible for fetching data from one blockchain and then sending the data to the other side. -## Channel +## Channel & Lane -Channels facilitate the transfer of data in one direction. A channel consists of a sender `outbound` of the source chain and a receiver `inbound` of the destination chain. Any user or system that wants to send a message on the bridge must submit it to the channel. The message layer supports multiple channels and provides different deliverability guarantees for messages, such as message replay protection, message order or out-of-order guarantee, etc. +Channels facilitate the transfer of data in one direction. A channel consists of a sender `outboundLane` of the source chain and a receiver `inboundLane` of the target chain. Any user or system that wants to send a message on the bridge must submit it to the `outboundLane`. The message layer supports multiple channels and provides different deliverability guarantees for messages, such as message replay protection, message order or out-of-order guarantee, etc. -Messages sent over the channel are assigned a unique (for this channel) strictly increasing integer value of the nonce. Messages sent over the same channel are guaranteed to be sent to the destination chain in the same order they were sent from the source chain. In other words, messages with nonce `N` will be delivered before nonce `N+1`. +Messages sent over the channel are assigned a unique (for this channel) strictly increasing integer value of the nonce. Messages sent over the same channel are guaranteed to be sent to the target chain in the same order they were sent from the source chain. In other words, messages with nonce `N` will be delivered before nonce `N+1`. A single message channel can be thought of as a transport channel for a single application (on-chain, off-chain, or hybrid). diff --git a/docs/sdk/api-reference.md b/docs/sdk/api-reference.md index bc992bdc..bf16d115 100644 --- a/docs/sdk/api-reference.md +++ b/docs/sdk/api-reference.md @@ -4,139 +4,187 @@ sidebar_position: 1 # API Reference -### Initialization +## _setRemoteEndpoint -Before you start using the SDK, you need to set some globle settings in your initialization function. +Set the remote endpoint. + +This is an internal function to be called by the concrete endpoint functions. ```javascript -contract Demo is SmartChainXApp { - constructor() public { - // Globle settings for Pangolin - dispatchAddress = 0x0000000000000000000000000000000000000019; - callIndexOfSendMessage = 0x2b03; - storageAddress = 0x000000000000000000000000000000000000001a; - callbackSender = 0x6461722f64766D70000000000000000000000000; - } - ... -} +function _setRemoteEndpoint(bytes4 _remoteChainId, address _remoteEndpoint) + internal +``` + +### Params: + +* _remoteChainId: The remote chain id. [The full chain id list](./constants) + +* _remoteEndpoint: The remote endpoint contract address. + +## _remoteDispatch +Dispatch a substrate call of the target chain. + +This is an internal function to be called by the concrete endpoint functions. + +```javascript +function _remoteDispatch( + uint32 tgtSpecVersion, + bytes memory tgtCallEncoded, + uint64 tgtCallWeight +) internal returns (uint256) ``` -If not set, the default is the settings of the Crab Network. +### Params: + +* tgtSpecVersion: The target chain's latest spec version. +* tgtCallEncoded: The call to be dispatched on the target chain. It is a SCALE codec bytes. +* tgtCallWeight: The weight of the call. +### Return: -### (Source Chain) sendMessage +* message id -Send a message through a bridge. +## _remoteTransact + +Transact a evm call of the endpoint contract on the target chain. + +This is an internal function to be called by the concrete endpoint functions. ```javascript -/// @notice Send message over a bridge -/// @param bridge The bridge to send message -/// @param payload The message payload to be sent -/// @return nonce The nonce of the message -function sendMessage(Bridge memory bridge, MessagePayload memory payload) - internal - returns (uint64) +function _remoteTransact( + uint32 tgtSpecVersion, + bytes memory callPayload, + uint256 gasLimit +) internal returns (uint256) + ``` +### Params: + +* tgtSpecVersion: The target chain's latest spec version. +* callPayload: The evm call to be transact on the target chain. It is a ABI codec bytes. +* gasLimit: The gas limit to run the evm call. + +### Return: + +* message id -Define the bridge to send message through: +## _remoteExecute + +Execute a evm call of the target chain. The difference between the `_remoteExecute` and `_remoteTransact` is that `_remoteTransact` can only call functions of the remote endpoint, but `_remoteExecute` can call functions of other contracts as long as you allow. + +This is an internal function to be called by the concrete endpoint functions. ```javascript -struct Bridge { - // The lane id - bytes4 srcOutlaneId; - // The storage key used to get market fee - bytes srcStorageKeyForMarketFee; - // The storage key used to get latest nonce - bytes srcStorageKeyForLatestNonce; -} +function _remoteExecute( + uint32 tgtSpecVersion, + address callReceiver, + bytes calldata callPayload, + uint256 gasLimit +) internal returns (uint256) ``` +### Params: + +* tgtSpecVersion: The target chain's latest spec version. +* callReceiver: The contract address which has the function to be called. +* callPayload: The evm call to be execute on the target chain. It is a ABI codec bytes. +* gasLimit: The gas limit to run the evm call. -Crab's bridges: -1. To Darwinia bridge - ```javascript - Bridge memory bridge = Bridge( - // outlane id, lane to Darwinia - 0, - // storage key for Darwinia market fee - hex"190d00dd4103825c78f55e5b5dbf8bfe2edb70953213f33a6ef6b8a5e3ffcab2", - // storage key for the latest nonce of Darwinia message lane - hex"c9b76e645ba80b6ca47619d64cb5e58d96c246acb9b55077390e3ca723a0ca1f11d2df4e979aa105cf552e9544ebd2b500000000" - ); - ``` - -Pangolin's bridges: -1. To Pangoro bridge - ```javascript - Bridge memory bridge = Bridge( - // outlane id, lane to Darwinia - 0, - // storage key for Darwinia market fee - hex"190d00dd4103825c78f55e5b5dbf8bfe2edb70953213f33a6ef6b8a5e3ffcab2", - // storage key for the latest nonce of Darwinia message lane - hex"c9b76e645ba80b6ca47619d64cb5e58d96c246acb9b55077390e3ca723a0ca1f11d2df4e979aa105cf552e9544ebd2b500000000" - ); - ``` - -### (Source Chain) onMessageDelivered - -You need to implement this virtual function If you want to do something after the message is confirmed. +### Return: + +* message id + +## fee + +Get the estimated cross-chain market fee. ```javascript -/// @notice Callback function for 'send_message' -/// @param lane Lane id -/// @param nonce Nonce of the callback message -/// @param result Dispatch result of cross chain message -function onMessageDelivered( - bytes4 lane, - uint64 nonce, - bool result -) external virtual; +function fee() public view returns (uint256) ``` -If you don't want this function to be wrongly called, you should add a check to the first line of your implemention like this, +### Return: + +* the estimated cross-chain market fee. The decimals is 18. + +## modifier onlyMessageSender + +This modifier is used to restrict a function to be called only by the remote sender. ```javascript -contract RemarkDemo is SmartChainXApp { - ... - - function onMessageDelivered( - bytes4 lane, - uint64 nonce, - bool result - ) external override { - require(msg.sender == callbackSender, "Only pallet address is allowed call 'onMessageDelivered'"); - // TODO: Your code goes here... - } -} +modifier onlyMessageSender() ``` -Callback sender list: +## execute -### (Target Chain) requireSenderOfSourceChain +This external function is called remotely to execute an evm call. -When a function on the target chain is called by source chain, in order to ensure that this function can only be called by the sender on the source chain, this check needs to be added to the first line of the function. +It is the mate of `_remoteExecute`. ```javascript -/// @notice This function is used to check the sender. -/// @param srcChainId The source chain id -/// @param sender The sender of the message on the source chain -function requireSenderOfSourceChain(bytes4 srcChainId, address sender) +function execute(address callReceiver, bytes calldata callPayload) + external + onlyMessageSender +``` + +## _canBeExecuted + +This is an internal virtual function to be overrided. + +It is used to check if a call can be executed on the receiver contract. + +```javascript +function _canBeExecuted(address callReceiver, bytes calldata callPayload) internal + view + virtual + returns (bool) +``` + +## lastDeliveredMessageId + +Get the last delivered **inbound** message id. + +```javascript +function lastDeliveredMessageId() public view returns (uint256) +``` + +### Return: + +* The last delivered inbound message id + +## isMessageDelivered + +Check if an **inbound** message has been delivered. + +```javascript +function isMessageDelivered(uint256 messageId) public view returns (bool) ``` -example: +### Params: + +* messageId: The inbound message id. + +## encodeMessageId + +Build a message by encoding a lane id and a message nonce. ```javascript -contract Demo is SmartChainXApp { - uint256 public number; +function encodeMessageId(bytes4 laneId, uint64 nonce) + public + pure + returns (uint256) +``` + +## decodeMessageId - // this `add` function will be called by `0x6Be02d1d3665660d22FF9624b7BE0551ee1Ac91b` on source chain - function add(uint256 _value) public { - requireSenderOfSourceChain(0, 0x6Be02d1d3665660d22FF9624b7BE0551ee1Ac91b); - number = number + _value; - } +Decode a message into a lane id and a message nonce. + +```javascript +function decodeMessageId(uint256 messageId) + public + pure + returns (bytes4, uint64) +``` - ... -} +### Params: -``` \ No newline at end of file +* messageId: The inbound message id. \ No newline at end of file diff --git a/docs/sdk/constants.md b/docs/sdk/constants.md new file mode 100644 index 00000000..45c7c0f1 --- /dev/null +++ b/docs/sdk/constants.md @@ -0,0 +1,25 @@ +--- +sidebar_position: 6 +--- + +# Constants + +## Chain Identifiers + +The chain identifier is used to uniquely identify the chain and is entered as a parameter when [setting the remote endpoint](./api-reference.md#_setRemoteEndpoint). + + bytes4 constant _DARWINIA_CHAIN_ID = 0x64617277; // darw + bytes4 constant _CRAB_CHAIN_ID = 0x63726162; // crab + bytes4 constant _PANGORO_CHAIN_ID = 0x70616772; // pagr + bytes4 constant _PANGOLIN_CHAIN_ID = 0x7061676c; // pagl + bytes4 constant _PANGOLIN_PARACHAIN_CHAIN_ID = 0x70676c70; // pglp + bytes4 constant _CRAB_PARACHAIN_CHAIN_ID = 0x63726170; // crap + +## Lane Identifiers + +The lane identifier identify the channel used to send and receive messages. Go to [Glossary](../glossary#channel--lane) to see more info. + + bytes4 constant _DARWINIA_CRAB_LANE_ID = 0x00000000; + bytes4 constant _PANGORO_PANGOLIN_LANE_ID = 0x726f6c69; // roli + bytes4 constant _PANGOLIN_PANGOLIN_PARACHAIN_LANE_ID = 0x70616c69; // pali + bytes4 constant _CRAB_CRAB_PARACHAIN_LANE_ID = 0x70616372; // pali \ No newline at end of file diff --git a/docs/sdk/guides/03-build-your-first-cross-chain-dapp.md b/docs/sdk/guides/03-build-your-first-cross-chain-dapp.md deleted file mode 100644 index d4fed7dc..00000000 --- a/docs/sdk/guides/03-build-your-first-cross-chain-dapp.md +++ /dev/null @@ -1,167 +0,0 @@ ---- -sidebar_position: 3 ---- - -# Build your first cross-chain Dapp - -We will use [hardhat](https://hardhat.org/) to build our first cross-chain Dapp. The Dapp will be deployed to the Pangoro Smart Chain, then call the `remark_with_event` on the [Pangolin](https://docs.crab.network/evm-compatible-crab-smart-chain/get-started/darwinia-pangolin) remotely. - -## Preparation - -1. Go to an empty folder, running `npm init` -2. `npm install --save-dev hardhat` -3. `npm install --save @darwinia/contracts-periphery` - -Now, we have a hardhat project with Darwinia SDK. - -## Create Scale Types (Optional) - -If there is no existing Scale Types for the target chain, you need to define the types yourself. You can find the existing types in `@darwinia/contracts-utils`. Here, Darwinia is our target chain. We need to define the remark dispatch call type. - -Create a new file named `PalletSystem.sol` in your contracts folder and copy and paste the code below into it. - -```solidity -library PalletSystem { - struct RemarkCall { - bytes2 callIndex; - bytes remark; - } - - function encodeRemarkCall(RemarkCall memory call) internal pure returns (bytes memory) { - return abi.encodePacked( - call.callIndex, - ScaleCodec.encodeBytes(call.remark) - ); - } -} -``` - -`remark_with_event` is the function(dispatch call) on the Pangolin that will be cross-chain called. The `System.RemarkCall` type is used to encode it. - -Darwinia SDK will provide the most common types for you. If you want to learn more about the Scale Codec, please visit https://docs.substrate.io/v3/advanced/scale-codec/. - -## Create your Dapp - -Darwinia SDK provides a abstract base Dapp contract named `PangoroXApp`. You should extends it to create your Dapp contract. In your contracts folder, create a file `RemarkDemo.sol`. - -```javascript -// SPDX-License-Identifier: MIT - -pragma solidity >=0.6.0; - -import "@darwinia/contracts-periphery/contracts/s2s/SmartChainXApp.sol"; -import "./System.sol"; - -contract RemarkDemo is PangoroXApp { - constructor() public { - init(); - } - - function remark() public payable { - // 1. prepare the call that will be executed on the target chain - System.RemarkCall memory call = System.RemarkCall( - hex"0009", // the call index of remark_with_event - hex"12345678" - ); - bytes memory callEncoded = System.encodeRemarkCall(call); - - // 2. Prepare the message payload - MessagePayload memory payload = MessagePayload( - 1210, // spec version of target chain - 2654000000, // call weight - callEncoded // call encoded bytes - ); - - // 3. Send the message payload to the pangolin through the lane id - bytes4 laneId = 0; - uint64 _messageNonce = sendMessage(toPangolin, laneId, payload); - } -} -``` - -Note: - -1. The spec version of the `MessagePayload` must be correct, you can get the latest spec version from https://pangolin.subscan.io/runtime. - -3. The call weight of the `MessagePayload` must be equal to or greater than the expected weight. you can get it from the target chain's benchmarks. - - -## Run in Remix - -The easiest way to run the code is [Remix](https://remix.ethereum.org/). - -1. [Connect your MetaMask to Pangoro](./01-using-smart-chain-with-metamask.md). -2. Export you Dapp to a single flattened solidity file. - - ```bash - npx hardhat flatten ./contracts/RemarkDemo.sol > ~/Desktop/RemarkDemo.sol - ``` - -3. Copy it to Remix and deploy the `RemarkDemo` contract. -4. Run `systemRemark` with value 200 ethers(here means ORINGs), the value must ≥ the [market fee](../../fee.md). important! - -## Or, Run in your project with hardhat command - -1. Open your `hardhat.config.js` in your project folder, add Crab Network into `networks`. - - ```js - crab: { - url: 'https://pangoro-rpc.darwinia.network', - network_id: "45", - accounts: [PRIVATE_KEY], - gas: 3_000_000, - gasPrice: 53100000000 - } - ``` - PRIVATE_KEY is your private key of Crab Network account with enough(≥ market fee + transaction fee) ORINGs tokens. - -2. Add a script to deploy and run your contract. Inside `scripts/`, create a new file `remarkdemo-script.js` with the following code: - - ```js - const hre = require("hardhat"); - - async function main() { - // We get the contract to deploy - const RemarkDemo = await hre.ethers.getContractFactory("RemarkDemo"); - const demo = await RemarkDemo.deploy(); - await demo.deployed(); - await demo.deployTransaction.wait(); - console.log("Deployed to:", demo.address); - - // Send transaction - const tx = await demo.remark({ - value: BigInt(200000000000000000000), // 200 CRAB, The fee to use the cross-chain service, determined by the Fee Market - }); - await tx.wait(); - console.log("txhash:", tx["hash"]); - } - - main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); - ``` - -3. Run - - In your project's folder, run: - - ```bash - npx hardhat run --network crab scripts/remarkdemo-script.js - ``` - - It will output the txhash of the transaction. You can copy it to [subscan](https://pangoro.subscan.io/) to see the detail of the transaction. - -## Track cross-chain events with subscan - -### MessageAccepted events of Pangoro - -[https://pangoro.subscan.io/event?address=&module=bridgedarwiniamessages&event=all](https://crab.subscan.io/event?address=&module=bridgedarwiniamessages&event=all) - -### MessageDelivered events of Pangolin - -[https://pangolin.subscan.io/event?address=&module=bridgecrabmessages&event=all](https://darwinia.subscan.io/event?address=&module=bridgecrabmessages&event=all) - -[More](./02-know-your-cross-chain-status.md) diff --git a/docs/sdk/guides/03-execute-remote-smart-contract-call.md b/docs/sdk/guides/03-execute-remote-smart-contract-call.md new file mode 100644 index 00000000..4f747589 --- /dev/null +++ b/docs/sdk/guides/03-execute-remote-smart-contract-call.md @@ -0,0 +1,92 @@ +--- +sidebar_position: 3 +--- + +# Execute Remote Smart Contract Call + +This Dapp allows you to call Pangolin Smart Chain's `add(2)` function from Pangoro Smart Chain. + +Pangoro Smart Chain is the testnet of Darwinia Smart Chain. [Pangolin Smart Chain](https://docs.crab.network/evm-compatible-crab-smart-chain/get-started/darwinia-pangolin) is the testnet of Crab Smart Chain. + +## Install deps + +```bash +npm install --save-dev @darwinia/contracts-periphery @darwinia/contracts-utils +``` + +## Prepare your cross-chain endpoints + +We need two endpoints here. One is for Pangoro Smart Chain, and the another one is for Pangolin Smart Chain. We can download them here. + +1. Download [ToPangolinEndpoint.sol](https://raw.githubusercontent.com/darwinia-network/darwinia-messages-sol/master/contracts/periphery/contracts/s2s/examples/ToPangolinEndpoint.sol) for Pangoro. Then deploy it to Pangoro Smart Chain. + +2. Download [ToPangoroEndpoint.sol](https://raw.githubusercontent.com/darwinia-network/darwinia-messages-sol/master/contracts/periphery/contracts/s2s/examples/ToPangoroEndpoint.sol) for Pangolin. Then deploy it to Pangolin Smart Chain. + +3. Call the `setRemoteEndpoint(_remoteChainId, _remoteEndpoint)` of the two endpoints to point to each other. + +The chain id of Pangoro Smart Chain is `0x70616772`. The chain id of Pangolin Smart Chain is `0x7061676c`. + +You can get the full chain id list from [constants](../constants). + +## Create the callee contract on Pangolin + +In your contracts folder, create a file `Callee.sol`. + +```javascript +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.9; + +contract Callee { + uint256 public sum; + + function add(uint256 _value) external { + sum = sum + _value; + } +} +``` + +Deploy it on the Pangolin Smart Chain. + +## Create the caller contract on Pangoro + +In your contracts folder, create a file `ExecuteDemo.sol`. + +```javascript +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.9; + +import "./ToPangolinEndpoint.sol"; + +// Call Pangolin.callee.add(2) from Pangoro +contract ExecuteDemo { + address public endpoint; + + constructor(address _endpoint) { + endpoint = _endpoint; + } + + function remoteAdd(address callee) external payable returns (uint256) { + uint256 messageId = ToPangolinEndpoint(endpoint).remoteExecute( + 28140, // latest spec version of pangolin + callee, + hex"1003e2d20000000000000000000000000000000000000000000000000000000000000002", // add(2) + 120000 // gas limit + ); + + return messageId; + } +} +``` + +Deploy it on the Pangoro Smart Chain. + +Make sure that the endpoint is correct, it must be the ToPangolinEndpoint's address. + +Make sure that the latest spec version of pangolin is correct. You can get it from https://pangolin.subscan.io/runtime. + +## Run + +1. You can get a estimated fee by calling [the `fee` function](../api-reference#fee) of the ToPangolinEndpoint contract. +2. Call the `remoteAdd(callee)` with a value as the market fee. The value should greater than or equal to the estimated fee. \ No newline at end of file diff --git a/docs/sdk/guides/04-call-smart-contract-remotely.md b/docs/sdk/guides/04-call-smart-contract-remotely.md deleted file mode 100644 index 1d56a6bc..00000000 --- a/docs/sdk/guides/04-call-smart-contract-remotely.md +++ /dev/null @@ -1,173 +0,0 @@ ---- -sidebar_position: 4 ---- - -# Call Smart Contract Remotely - -Darwinia supports calling smart contracts on the target chain from the source chain, and currently supports solidity smart contracts. - -Calling a solidity function remotely is similar to calling a normal call, except that 1. You need to deploy a contract to the smart chain of the target chain. 2. Use a specific type of call `PalletEthereum.TransactCall`. - -Here are the steps: - -1. Prepare your smart contract to be deployed on the target chain - - ```javascript - contract TargetContract { - uint256 public number; - - function add(uint256 _value) public { - number = number + _value; - } - } - ``` - - After deployed, the contract address will be used in the next step. - -2. Prepare your smart contract to be deployed on the source chain - - This step is similar to the one in the [previous guide](./build-your-first-cross-chain-dapp), except we are using a specific type of Call. The definition of this call is as follows: - - ```js - contract RemoteTransactDemo is PangoroXApp { - ... - - function callAddOnTargetChain() public payable { - // 1. prepare the call that will be executed on the target chain - PalletEthereum.TransactCall memory call = PalletEthereum.TransactCall( - // the call index of substrate_transact - 0x2902, - // the evm transaction to transact - PalletEthereum.buildTransactionV2( - 0, // evm tx nonce, nonce on the target chain + pending nonce on the source chain + 1 - 1000000000, // gasPrice, get from the target chain - 600000, // gasLimit, get from the target chain - 0x50275d3F95E0F2FCb2cAb2Ec7A231aE188d7319d, // <------------------ the contract address on the target chain - 0, // value, the only allowed value here is 0 - hex"1003e2d20000000000000000000000000000000000000000000000000000000000000002" // the add function bytes that will be called on the target chain, add(2) - ) - ); - bytes memory callEncoded = PalletEthereum.encodeTransactCall(call); - - // 2. Prepare the message payload - MessagePayload memory payload = MessagePayload( - 28110, // spec version of target chain <----------- This may be changed, go to https://pangoro.subscan.io/runtime get the latest spec version - 2654000000, // call weight - callEncoded // call encoded bytes - ); - - // 3. Send the message payload to the pangolin through the lane id - bytes4 laneId = 0; - uint64 _messageNonce = sendMessage(toPangolin, laneId, payload); - } - - ... - } - - ``` - - Note: - - It follows the notes of [previous guide](./build-your-first-cross-chain-dapp#create-your-dapp). And, There are two more rules to note: - - 1. `weight` of MessagePayload >= gas_to_weight(`gasLimit` of buildTransactionV2) - - 2. `gasPrice` of buildTransactionV2 >= WeightToFee::calc(gas_to_weight(1)) * next_fee_multiplier * 10**9 - - 3. `nonce` of buildTransactionV2 is evm tx nonce. - -# Restricted access - -If you want to restrict your method to only be called remotely by your Dapp contract, you need to do something like this: - -1. Prepare your Dapp contract for both source chain and target chain - - ```js - // deploy on the target chain first, then deploy on the source chain - contract RemoteTransactDemo is PangolinXApp { - constructor() public { - init(); - } - - uint256 public number; - - /////////////////////////////////////////// - // used on the source chain - /////////////////////////////////////////// - function callAddOnTheTargetChain() public payable { - // 1. prepare the call that will be executed on the target chain - PalletEthereum.TransactCall memory call = PalletEthereum.TransactCall( - // the call index of substrate_transact - 0x2902, - // the evm transaction to transact - PalletEthereum.buildTransactionV2( - 0, // evm tx nonce, nonce on the target chain + pending nonce on the source chain + 1 - 1000000000, // gasPrice, get from the target chain - 600000, // gasLimit, get from the target chain - 0x50275d3F95E0F2FCb2cAb2Ec7A231aE188d7319d, // <------------------ change to the contract address on the target chain - 0, // value, the only allowed value here is 0 - hex"1003e2d20000000000000000000000000000000000000000000000000000000000000002" // the add function bytes that will be called on the target chain, add(2) - ) - ); - bytes memory callEncoded = PalletEthereum.encodeTransactCall(call); - - // 2. Prepare the message payload - MessagePayload memory payload = MessagePayload( - 28110, // spec version of target chain <----------- This may be changed, go to https://pangoro.subscan.io/runtime get the latest spec version - 2654000000, // call weight - callEncoded // call encoded bytes - ); - - // 3. Send the message payload to the pangolin through the lane id - bytes4 laneId = 0; - uint64 _messageNonce = sendMessage(toPangolin, laneId, payload); - } - - /////////////////////////////////////////// - // used on the target chain - /////////////////////////////////////////// - function add(uint256 _value) public { - // this 'require' makes this function only be called by the dapp contract on the source chain - require( - msg.sender == deriveSenderFromRemote(), - "msg.sender must equal to the address derived from the message sender address on the source chain" - ); - number = number + _value; - } - } - ``` - -2. Deploy the contract to the target chain. - - After deployed, the target chain contract address will be used in the next step. - -3. Deploy the contract to the source chain. - - Before deployment, you need to change the target chain contract address first. - - ```js - PalletEthereum.TransactCall memory call = PalletEthereum.TransactCall( - // the call index of substrate_transact - 0x2902, - // the evm transaction to transact - PalletEthereum.buildTransactionV2( - 0, // evm tx nonce, nonce on the target chain + pending nonce on the source chain + 1 - 1000000000, // gasPrice, get from the target chain - 600000, // gasLimit, get from the target chain - 0x50275d3F95E0F2FCb2cAb2Ec7A231aE188d7319d, // <------------------ the contract address on the target chain - 0, // value, the only allowed value here is 0 - hex"1003e2d20000000000000000000000000000000000000000000000000000000000000002" // the add function bytes that will be called on the target chain, add(2) - ) - ); - ``` - - After deployed, the source chain contract address will be used in the next step. - -4. Call `setMessageSenderOnSrcChain` to update the source chain contract address on the target chain - - ```js - function setMessageSenderOnSrcChain(address _messageSenderOnSrcChain) - ``` - - -After these steps, the `add` function can only be called by `_messageSenderOnSrcChain`. \ No newline at end of file diff --git a/docs/sdk/guides/04-dispatch-remote-substrate-call.md b/docs/sdk/guides/04-dispatch-remote-substrate-call.md new file mode 100644 index 00000000..c017fb41 --- /dev/null +++ b/docs/sdk/guides/04-dispatch-remote-substrate-call.md @@ -0,0 +1,125 @@ +--- +sidebar_position: 4 +--- + +# Dispatch Remote Substrate Call + +This guide helps you to build a small Dapp that will dispatch Pangolin's `remark_with_event` remotely from Pangoro Smart Chain. + +`remark_with_event` is a dispatchable call of the target Substrate based blockchain. A dispatchable call is a public function that can be executed at runtime via a JSON RPC call. More info about [Substrate](https://substrate.io/) and the [dispatchable calls](https://docs.substrate.io/reference/glossary/#dispatch). + +Pangoro Smart Chain is the testnet of Darwinia Smart Chain. [Pangolin](https://docs.crab.network/evm-compatible-crab-smart-chain/get-started/darwinia-pangolin) is the testnet of Crab Chain. + +## Install deps + +```bash +npm install --save-dev @darwinia/contracts-periphery @darwinia/contracts-utils +``` + +## Prepare your cross-chain endpoint + +Extends the `MessageEndpoint` contract to create your own endpoint. In your contracts folder, create a file `ToPangolinEndpoint.sol`. + +```javascript +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.9; + +import "@darwinia/contracts-periphery/contracts/s2s/MessageEndpoint.sol"; + +contract ToPangolinEndpoint is MessageEndpoint { + constructor() { + outboundLaneId = 0x726f6c69; + inboundLaneId = 0x726f6c69; + storageAddress = address(1024); + dispatchAddress = address(1025); + storageKeyForMarketFee = 0x30d35416864cf657db51d3bc8505602f2edb70953213f33a6ef6b8a5e3ffcab2; + storageKeyForLatestNonce = 0xd86d7f611f4d004e041fda08f633f10196c246acb9b55077390e3ca723a0ca1f; + storageKeyForLastDeliveredNonce = 0xd86d7f611f4d004e041fda08f633f101e5f83cf83f2127eb47afdc35d6e43fab; + sendMessageCallIndex = 0x1103; + remoteMessageTransactCallIndex = 0x2901; + remoteSmartChainId = 43; + } + + function _canBeExecuted(address, bytes calldata) + internal + pure + override + returns (bool) + { + return true; + } + + function remoteDispatch( + uint32 pangolinSpecVersion, + bytes memory pangolinCallEncoded, + uint64 pangolinCallWeight + ) external payable returns (uint256) { + return + _remoteDispatch( + pangolinSpecVersion, + pangolinCallEncoded, + pangolinCallWeight + ); + } +} +``` + +Deploy it on the Pangoro Smart Chain. The `remoteDispatch` will be used in the next step. + +You can download the completed [ToPangolinEndpoint.sol](https://raw.githubusercontent.com/darwinia-network/darwinia-messages-sol/master/contracts/periphery/contracts/s2s/examples/ToPangolinEndpoint.sol), and add your own access controls to it if your want to use it in a production environment. + +## Create your Dapp contract + +In your contracts folder, create a file `RemarkDemo.sol`. + +```javascript +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.9; + +import "./ToPangolinEndpoint.sol"; +import "@darwinia/contracts-periphery/contracts/s2s/types/PalletSystem.sol"; + +contract RemarkDemo { + address public endpoint; + + constructor(address _endpoint) { + endpoint = _endpoint; + } + + function remoteRemark(bytes memory _remark) + external + payable + returns (uint256) + { + // 1. Prepare the call and its weight which will be executed on the target chain + PalletSystem.RemarkCall memory call = PalletSystem.RemarkCall( + hex"0009", + _remark + ); + bytes memory encodedCall = PalletSystem.encodeRemarkCall(call); + uint64 weight = uint64(_remark.length * 2_000); + + // 2. Dispatch the call + uint256 messageId = ToPangolinEndpoint(endpoint).remoteDispatch{ + value: msg.value + }( + 28140, // latest spec version of pangolin + encodedCall, + weight + ); + + return messageId; + } +} +``` + +Make sure that the latest spec version of pangolin is correct. You can get it from https://pangolin.subscan.io/runtime. + +Deploy the Dapp contract on the Pangoro Smart Chain. Inject the endpoint address from previous step into the Dapp contract during its initialization. + +## Run + +1. You can get a estimated fee by calling [the `fee` function](../api-reference#fee) of the endpoint contract. +2. Call the `remoteRemark(_remark)` with a value as the market fee. The value should greater than or equal to the estimated fee. diff --git a/src/css/custom.css b/src/css/custom.css index f72e2077..b1d62d36 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -50,7 +50,7 @@ html[data-theme='dark'] { } article { - max-width: 700px; + max-width: 95%; margin-left: auto; margin-right: auto; }