diff --git a/docs/dapp/diagrams/opl-router-pingpong-flow.mmd b/docs/dapp/diagrams/opl-router-pingpong-flow.mmd new file mode 100644 index 0000000000..80c6582de2 --- /dev/null +++ b/docs/dapp/diagrams/opl-router-pingpong-flow.mmd @@ -0,0 +1,16 @@ +sequenceDiagram + participant User + participant P-A as PingPong.sol
(Chain A) + participant G-A as Gateway
(Chain A) + participant Relayer as Router Chain
& Relayer + participant G-B as Gateway
(Chain B) + participant P-B as PingPong.sol
(Chain B) + User->>+P-A: iPing() + P-A->>+G-A: iSend() + G-A-->>Relayer: Listens to event + Relayer-->>+G-B: Relays message + G-B->>+P-B: iReceive() + P-B->>-G-B: Response from
iReceive() + G-B-->>-Relayer: Listens to event + Relayer-->>G-A: Relays message + G-A->>-P-A: iAck() diff --git a/docs/dapp/diagrams/opl-router-pingpong-flow.mmd.svg b/docs/dapp/diagrams/opl-router-pingpong-flow.mmd.svg new file mode 100644 index 0000000000..25ebb53476 --- /dev/null +++ b/docs/dapp/diagrams/opl-router-pingpong-flow.mmd.svg @@ -0,0 +1 @@ +PingPong.sol(Chain B)Gateway(Chain B)Router Chain& RelayerGateway(Chain A)PingPong.sol(Chain A)UserPingPong.sol(Chain B)Gateway(Chain B)Router Chain& RelayerGateway(Chain A)PingPong.sol(Chain A)UseriPing()iSend()Listens to eventRelays messageiReceive()Response fromiReceive()Listens to eventRelays messageiAck() \ No newline at end of file diff --git a/docs/dapp/images/opl/compile-pingpong.png b/docs/dapp/images/opl/compile-pingpong.png new file mode 100644 index 0000000000..dbfd8b3251 Binary files /dev/null and b/docs/dapp/images/opl/compile-pingpong.png differ diff --git a/docs/dapp/images/opl/router-approve.png b/docs/dapp/images/opl/router-approve.png new file mode 100644 index 0000000000..eb733d9a39 Binary files /dev/null and b/docs/dapp/images/opl/router-approve.png differ diff --git a/docs/dapp/images/opl/router-architecture.png b/docs/dapp/images/opl/router-architecture.png new file mode 100644 index 0000000000..faead296be Binary files /dev/null and b/docs/dapp/images/opl/router-architecture.png differ diff --git a/docs/dapp/images/opl/router-deploy-pingpong-amoy.png b/docs/dapp/images/opl/router-deploy-pingpong-amoy.png new file mode 100644 index 0000000000..e122c0a2d2 Binary files /dev/null and b/docs/dapp/images/opl/router-deploy-pingpong-amoy.png differ diff --git a/docs/dapp/images/opl/router-deploy-pingpong-sapphire.png b/docs/dapp/images/opl/router-deploy-pingpong-sapphire.png new file mode 100644 index 0000000000..0bbec04052 Binary files /dev/null and b/docs/dapp/images/opl/router-deploy-pingpong-sapphire.png differ diff --git a/docs/dapp/images/opl/router-faucet.png b/docs/dapp/images/opl/router-faucet.png new file mode 100644 index 0000000000..12bb7d629e Binary files /dev/null and b/docs/dapp/images/opl/router-faucet.png differ diff --git a/docs/dapp/images/opl/router-iping.png b/docs/dapp/images/opl/router-iping.png new file mode 100644 index 0000000000..532654e797 Binary files /dev/null and b/docs/dapp/images/opl/router-iping.png differ diff --git a/docs/dapp/images/opl/router-metadata.png b/docs/dapp/images/opl/router-metadata.png new file mode 100644 index 0000000000..1999b5cfdb Binary files /dev/null and b/docs/dapp/images/opl/router-metadata.png differ diff --git a/docs/dapp/opl/router-protocol/README.md b/docs/dapp/opl/router-protocol/README.md new file mode 100644 index 0000000000..92c51a3464 --- /dev/null +++ b/docs/dapp/opl/router-protocol/README.md @@ -0,0 +1,94 @@ +--- +description: Cross-chain dApps with Router Protocol +--- + +# Router Protocol + +Router Protocol offers two frameworks for cross-chain interactions: + +- **Router CrossTalk**: Enables stateless and stateful cross-chain messaging +- **Router Nitro**: Facilitates native cross-chain asset transfers + +For guidance on choosing the appropriate framework, refer to Router's [guide]. + +This documentation focuses on **Router CrossTalk**. If you're primarily +interested in asset transfers, please consult the [Router Nitro documentation]. + +[Router Nitro documentation]: https://docs.routerprotocol.com/develop/category/asset-transfer-via-nitro +[guide]: https://docs.routerprotocol.com/overview/choosing-the-right-framework + +## Router CrossTalk + +Router CrossTalk is designed to enable cross-chain interactions, allowing +developers to create decentralized applications (dApps) that operate across +multiple blockchain networks. This framework supports both stateless and +stateful operations, providing flexible and efficient communication between +contracts on different chains. + +### Architecture + +![Router Architecture](../../images/opl/router-architecture.png) +*High-level architecture diagram for Router CrossTalk[^1]* + +[^1]: The CrossTalk high-level architecture diagram is courtesy of [Router documentation][router-architecture]. + +[router-architecture]: https://docs.routerprotocol.com/develop/message-transfer-via-crosstalk/key-concepts/high-level-architecture + +The **CrossTalk** infrastructure consists of three main components: + +- **Gateway** contracts on source and destination chains +- **Orchestrators** on the Router chain +- **Relayers** that forward messages to the Router Gateway contracts + +The process flow is as follows: + +1. The dApp contract calls the iSend function on the source chain's Gateway + contract. +2. Orchestrators monitor events emitted by the Gateway contract. +3. A Relayer picks up the transaction signed by the orchestrator and forwards + the message to the destination chain's Router Gateway contract. +4. Gateway contract on the destination chain calls the dApp contract's + `iReceive` function. +5. For acknowledgment, the process is reversed, and the Relayer calls the + `iAck` function on the dApp contract on the source chain. + +### Fees + +Fees in the cross-chain messaging process are paid by two parties: + +- The dApp **user** pays when initiating the transaction on the source chain. +- The dApp **fee payer** pre-pays the Relayers for calling the Router Gateway + contract. + +To ensure the correct **fee payer** is used, the dApp's contract must register +the fee payer address as metadata with the Router Gateway. Additionally, the +**fee payer** needs to approve the conntract on the Router chain, which can be +done through the [Router Explorer]. + +For more info about the [fee management], consult the Router documentation. + +[Router Explorer]: https://testnet.routerscan.io/feePayer +[fee management]: https://docs.routerprotocol.com/develop/message-transfer-via-crosstalk/key-concepts/fee-management + + +### Examples + +:::note Example: PingPong + +Explore our [PingPong example] to see Router CrossTalk in action. + +::: + +[PingPong example]: ./pingpong-example.md + +For more examples, refer to the [Router Protocol documentation]: + +- [Cross-Chain NFT] +- [Cross-Chain Read Request] + +and in the Router Protocol [CrossTalk sample repository]. + +[Router Protocol documentation]: https://docs.routerprotocol.com/develop/message-transfer-via-crosstalk +[Cross-Chain NFT]: https://docs.routerprotocol.com/develop/message-transfer-via-crosstalk/evm-guides/your-first-crosschain-nft-contract +[Cross-Chain Read Request]: https://docs.routerprotocol.com/develop/message-transfer-via-crosstalk/evm-guides/cross-chain-read-requests +[CrossTalk sample repository]: https://github.com/router-protocol/new-crosstalk-sample/ diff --git a/docs/dapp/opl/router-protocol/approve.md b/docs/dapp/opl/router-protocol/approve.md new file mode 100644 index 0000000000..7f1a22c618 --- /dev/null +++ b/docs/dapp/opl/router-protocol/approve.md @@ -0,0 +1,60 @@ +--- +description: Approving fee payer in Router Protcol +--- + +# Approving the Fee Payer + +According to Router Protocol's [fee management] system, cross-chain requests +initiated by a dApp are paid for by the dApp's corresponding fee payer account +on the Router Chain. This fee payer is registered by calling the +`setDappMetadata` function on the gateway contract. + +## Obtaining Test Tokens + +To interact with the Router Protocol testnet, you'll need `ROUTE` test tokens. +Follow these steps to obtain them from the Router Faucet: + +1. Visit the [Router Faucet] website. +2. Connect your MetaMask wallet. +3. Add the Router Test Network to your MetaMask if prompted. +4. Enter your account address in the provided field. +5. Click the `Get Test Tokens` button. + +![Router Test Faucet](../../images/opl/router-faucet.png) + +## Approving Contracts in Router Explorer + +After deploying your contracts, you need to approve the fee payer for each of +them. Here's how to do it using the Router Explorer: + +1. Navigate to the [Router Explorer]. +2. Connect your wallet by clicking the "Connect Wallet" button. +3. Once connected, you'll see a list of pending approvals for your deployed +contracts. + +![Router Approvals](../../images/opl/router-approve.png) + +4. For each contract listed, click the `Approve` button. +5. Follow the prompts in your wallet to sign the approval message. + +:::info + +If you don't see your deployed contracts in the list, it's possible you used an +incorrect gateway address for the chain during deployment. Verify the current +gateway addresses in the [Router Protocol documentation]. + +::: + +## Troubleshooting + +If you encounter any issues during the approval process, consider the following: + +- Ensure you have sufficient ROUTE test tokens in your wallet. +- Verify that you're connected to the correct network in MetaMask. +- Double-check that the contracts were deployed with the correct gateway + addresses. + +[fee management]: https://docs.routerprotocol.com/develop/message-transfer-via-crosstalk/key-concepts/fee-management +[Router Faucet]: https://faucet.routerprotocol.com/ +[Router Explorer]: https://testnet.routerscan.io/feePayer +[Router Protocol documentation]: https://docs.routerprotocol.com/networks/supported-chains#for-testnet diff --git a/docs/dapp/opl/router-protocol/interface.md b/docs/dapp/opl/router-protocol/interface.md new file mode 100644 index 0000000000..3ff2900394 --- /dev/null +++ b/docs/dapp/opl/router-protocol/interface.md @@ -0,0 +1,164 @@ +--- +description: Router Protocol Interfaces +--- + +# Router Interfaces + +Router Protocol provides the `evm-gateway-contracts` library to facilitate the +development of cross-chain dApps. + +## Installation + +### Using Remix + +If you're using [Remix], you can import the contracts directly as shown in the +examples below. + +[Remix]: https://remix.ethereum.org/ + +### Using Hardhat + +For Hardhat projects, install the package via npm, yarn or pnpm: + +```shell npm2yarn +npm install @routerprotocol/evm-gateway-contracts +``` + +## Gateway + +The Router Gateway is deployed on all chains supported by Router Protocol and +serves as the central communication point between chains. + +### IGateway + +```solidity +import "@routerprotocol/evm-gateway-contracts/contracts/IGateway.sol"; +``` +To develop cross-chain contracts, you should generally: + +1. Import the `IGateway.sol` interface into all cross-chain contracts +2. Create a variable to store the Gateway contract address +3. Initialize it with the corresponding Gateway address of the given chain + +This setup will be used later to call the `iSend` function. + +### iSend() + +```solidity +function iSend( + uint256 version, + uint256 routeAmount, + string calldata routeRecipient, + string calldata destChainId, + bytes calldata requestMetadata, + bytes calldata requestPacket +) external payable returns (uint256); +``` + +`iSend` is the function you'll call on the Gateway of the source chain to +initiate a cross-chain message. Every contract that wants to make a cross-chain +call needs to call it. + +For a detailed description of each parameter, refer to the Router Protocol +[iSend documentation]. + +[iSend documentation]: https://docs.routerprotocol.com/develop/message-transfer-via-crosstalk/evm-guides/iDapp-functions/iSend + +### getRequestMetaData() + +```solidity + function getRequestMetadata( + uint64 destGasLimit, + uint64 destGasPrice, + uint64 ackGasLimit, + uint64 ackGasPrice, + uint128 relayerFees, + uint8 ackType, + bool isReadCall, + string memory asmAddress +) public pure returns (bytes memory) { + bytes memory requestMetadata = abi.encodePacked( + destGasLimit, + destGasPrice, + ackGasLimit, + ackGasPrice, + relayerFees, + ackType, + isReadCall, + asmAddress + ); + return requestMetadata; +} +``` +The `getRequestMetadata` function helps create the `requestMetadata` bytes +object required for the `iSend` function call. + +Here's an overview of the arguments: + +| Argument | Example Value | Description | +| ------------ | ------------- | ------------------------------------------ | +| destGasLimit | 300000 | Gas limit on destination chain | +| destGasPrice | 100000000000 | Gas price on destination chain | +| ackGasLimit | 300000 | Gas limit on source chain for ack | +| ackGasPrice | 100000000000 | Gas price on source chain for ack | +| relayerFees | 10000000000 | Relayer fees on Router chain | +| ackType | 3 | Acknowledge type | +| isReadCall | false | If the call is read-only | +| asmAddress | "0x" | Address for the additional security module | + +Alternatively, you can use `ethers.js` to encode the metadata: +```js +const metadata = ethers.utils.solidityPack( + ['uint64', 'uint64', 'uint64', 'uint64', 'uint128', 'uint8', 'bool', 'string'], + [destGasLimit, destGasPrice, ackGasLimit, ackGasPrice, relayerFees, ackType, isReadCall, asmAddress] +); +``` + +For more information about encoding and the request metadata, see the Router +[metadata documentation]. + +[metadata documentation]: https://docs.routerprotocol.com/develop/message-transfer-via-crosstalk/evm-guides/iDapp-functions/iSend#5-requestmetadata + +## IDapp + +```solidity +import "@routerprotocol/evm-gateway-contracts/contracts/IDapp.sol"; +``` + +The IDapp interface consists of two main functions: + +1. `iReceive`: The entry point for the cross-chain message on the destination + chain +2. `iAck`: The entry point on the source chain to receive the acknowledgment + +### iReceive() + +```solidity +function iReceive( + string memory requestSender, + bytes memory packet, + string memory srcChainId + ) external returns (bytes memory) +``` + +`iReceive` is called by the Gateway on the destination chain. + +For more information about `iReceive`, see the Router [iReceive documentation]. + +[iReceive documentation]: https://docs.routerprotocol.com/develop/message-transfer-via-crosstalk/evm-guides/iDapp-functions/iReceive + +### iAck() + +```solidity +function iAck( + uint256 requestIdentifier, + bool execFlag, + bytes memory execData + ) external +``` + +`iAck` is called by the Gateway on the source chain. + +For more information about `iAck`, see the Router [iAck documentation]. + +[iAck documentation]: https://docs.routerprotocol.com/develop/message-transfer-via-crosstalk/evm-guides/iDapp-functions/iAck diff --git a/docs/dapp/opl/router-protocol/pingpong-example.md b/docs/dapp/opl/router-protocol/pingpong-example.md new file mode 100644 index 0000000000..2f7a48db2e --- /dev/null +++ b/docs/dapp/opl/router-protocol/pingpong-example.md @@ -0,0 +1,351 @@ +--- +description: Router Protocol PingPong example +--- + +# PingPong Example + +This tutorial demonstrates how to send a cross-chain message using Router +Protocol's [CrossTalk]. + +You'll learn how to: + + - Deploy Router-compatible contracts + - Approve the feePayer for your contracts + - Prepare metadata for cross-chain calls + - Send cross-chain messages + +We recommend using [Remix] for an easy-to-follow experience. +The only prerequisite is a set-up Metamask account. + +:::info + +If you're new to Remix, follow our basic guide for using Remix +[here][dapp-remix]. + +[dapp-remix]: /dapp/emerald/writing-dapps-on-emerald#create-dapp-on-emerald-with-remix---ethereum-ide + +::: + + +## Overview PingPong + +In this example, you'll deploy the same contract on two different chains. +You'll then send a `ping` from chain A to chain B, facilitated by Router +Protocol's [CrossTalk]. The contract on chain B will receive the `ping` and +respond back to Router Protocol. Finally, Router Protocol will send an +acknowledgment message back to the contract on chain A. + +![PingPong Flow](../../diagrams/opl-router-pingpong-flow.mmd.svg) + +[CrossTalk]: https://docs.routerprotocol.com/develop/message-transfer-via-crosstalk + +## Contract Setup + +1. Open [Remix] and create a new file called `PingPong.sol` +2. Paste the following contract into it: + +
+ PingPong.sol Contract + + ```solidity title="PingPong.sol" showLineNumbers + //SPDX-License-Identifier: UNLICENSED + pragma solidity >=0.8.0 <0.9.0; + + import "@routerprotocol/evm-gateway-contracts/contracts/IGateway.sol"; + + /// @title PingPong + /// @author Yashika Goyal + /// @notice This is a cross-chain ping pong smart contract to demonstrate how one can + /// utilise Router CrossTalk for cross-chain transactions. + contract PingPong { + address public owner; + uint64 public currentRequestId; + + // srcChainId + requestId => pingFromSource + mapping(string => mapping(uint64 => string)) public pingFromSource; + // requestId => ackMessage + mapping(uint64 => string) public ackFromDestination; + + // instance of the Router's gateway contract + IGateway public gatewayContract; + + // custom error so that we can emit a custom error message + error CustomError(string message); + + // event we will emit while sending a ping to destination chain + event PingFromSource( + string indexed srcChainId, + uint64 indexed requestId, + string message + ); + event NewPing(uint64 indexed requestId); + + // events we will emit while handling acknowledgement + event ExecutionStatus(uint256 indexed eventIdentifier, bool isSuccess); + event AckFromDestination(uint64 indexed requestId, string ackMessage); + + constructor(address payable gatewayAddress, string memory feePayerAddress) { + owner = msg.sender; + + gatewayContract = IGateway(gatewayAddress); + + gatewayContract.setDappMetadata(feePayerAddress); + } + + /// @notice function to set the fee payer address on Router Chain. + /// @param feePayerAddress address of the fee payer on Router Chain. + function setDappMetadata(string memory feePayerAddress) external { + require(msg.sender == owner, "only owner"); + gatewayContract.setDappMetadata(feePayerAddress); + } + + /// @notice function to set the Router Gateway Contract. + /// @param gateway address of the gateway contract. + function setGateway(address gateway) external { + require(msg.sender == owner, "only owner"); + gatewayContract = IGateway(gateway); + } + + /// @notice function to generate a cross-chain request to ping a destination chain contract. + /// @param destChainId chain ID of the destination chain in string. + /// @param destinationContractAddress contract address of the contract that will handle this + /// @param str string to be pinged to destination + /// @param requestMetadata abi-encoded metadata according to source and destination chains + function iPing( + string calldata destChainId, + string calldata destinationContractAddress, + string calldata str, + bytes calldata requestMetadata + ) public payable { + currentRequestId++; + + bytes memory packet = abi.encode(currentRequestId, str); + bytes memory requestPacket = abi.encode(destinationContractAddress, packet); + gatewayContract.iSend{ value: msg.value }( + 1, + 0, + string(""), + destChainId, + requestMetadata, + requestPacket + ); + emit NewPing(currentRequestId); + } + + /// @notice function to get the request metadata to be used while initiating cross-chain request + /// @return requestMetadata abi-encoded metadata according to source and destination chains + function getRequestMetadata( + uint64 destGasLimit, + uint64 destGasPrice, + uint64 ackGasLimit, + uint64 ackGasPrice, + uint128 relayerFees, + uint8 ackType, + bool isReadCall, + string memory asmAddress + ) public pure returns (bytes memory) { + bytes memory requestMetadata = abi.encodePacked( + destGasLimit, + destGasPrice, + ackGasLimit, + ackGasPrice, + relayerFees, + ackType, + isReadCall, + asmAddress + ); + return requestMetadata; + } + + /// @notice function to handle the cross-chain request received from some other chain. + /// @param packet the payload sent by the source chain contract when the request was created. + /// @param srcChainId chain ID of the source chain in string. + function iReceive( + string memory, //requestSender, + bytes memory packet, + string memory srcChainId + ) external returns (uint64, string memory) { + require(msg.sender == address(gatewayContract), "only gateway"); + (uint64 requestId, string memory sampleStr) = abi.decode( + packet, + (uint64, string) + ); + if ( + keccak256(abi.encodePacked(sampleStr)) == keccak256(abi.encodePacked("")) + ) { + revert CustomError("String should not be empty"); + } + pingFromSource[srcChainId][requestId] = sampleStr; + + emit PingFromSource(srcChainId, requestId, sampleStr); + + return (requestId, sampleStr); + } + + /// @notice function to handle the acknowledgement received from the destination chain + /// back on the source chain. + /// @param requestIdentifier event nonce which is received when we create a cross-chain request + /// We can use it to keep a mapping of which nonces have been executed and which did not. + /// @param execFlag a boolean value suggesting whether the call was successfully + /// executed on the destination chain. + /// @param execData returning the data returned from the handleRequestFromSource + /// function of the destination chain. + function iAck( + uint256 requestIdentifier, + bool execFlag, + bytes memory execData + ) external { + (uint64 requestId, string memory ackMessage) = abi.decode( + execData, + (uint64, string) + ); + + ackFromDestination[requestId] = ackMessage; + + emit ExecutionStatus(requestIdentifier, execFlag); + emit AckFromDestination(requestId, ackMessage); + } + } + ``` +
+ +### Key Contract Functions + +- `iPing`: Initiates the cross-chain message by calling Router's + `IGateway.iSend`. +- `iReceive`: Serves as the entry point on the destination contract. +- `iAck`: Handles the acknowledgment in a bidirectional cross-chain message on + the source contract. + +### Compiling the Contract + +For compatibility with Sapphire, compile the contract using Solidity version +**`0.8.24`** or older. + +### Deploying the Contract + +Deploy the PingPong contract on two different chains: `Sapphire Testnet` and +`Polygon Amoy`. + +#### Deploying on Sapphire Testnet + +1. Obtain TEST tokens for `Sapphire Testnet` from the [Oasis faucet]. +2. In Metamask, switch to the `Sapphire Testnet` network and select + `Injected Provider - MetaMask` as the environment in Remix +3. Fill in the deployment parameters: + +- **`gatewayAddress`**: `0xfbe6d1e711cc2bc241dfa682cbbff6d68bf62e67` + (current Sapphire Testnet Gateway) +- **`feePayerAddress`**: Your current account address + (copy from MetaMask or Remix) + +4. Deploy the contract on Sapphire Testnet + +
+ Remix Example + +![Deploy Sapphire](../../images/opl/router-deploy-pingpong-sapphire.png) +
+ +[Oasis Faucet]: https://faucet.testnet.oasis.io/ + +#### Deploying on Polygon Amoy + +1. Obtain POL tokens for `Polygon Amoy` Testnet from the [Polygon faucet]. +2. Switch to the `Polygon Amoy` network in Metamask. +3. Fill in the deployment parameters: + +- **`gatewayAddress`**: `0x778a1f43459a05accd8b57007119f103c249f929` + (current Polygon Amoy Gateway) +- **`feePayerAddress`**: Your current account address + (copy from MetaMask or Remix) + +4. Deploy the contract on Polygon Amoy + +
+ Remix Example + +![Deploy Polygon Amoy](../../images/opl/router-deploy-pingpong-amoy.png) +
+ +[Polygon Faucet]: https://faucet.polygon.technology/ + +## Approving the Fee Payer + +After deploying the contracts, approve the **fee payer** on the Router Chain: + +1. Obtain Router test tokens from the [Router faucet]. +2. Approve the contracts on the [Router Explorer][feepayer]. + +For detailed instructions on fee payer approval, see our [approval guide]. + +[Router faucet]: https://faucet.routerprotocol.com/ +[feepayer]: https://testnet.routerscan.io/feePayer +[approval guide]: /dapp/opl/router-protocol/approve + +## Executing PingPong + +Now that you've deployed the contracts and approved the fee payer, you can play +**PingPong**. This process involves two steps: + +1. Obtaining the Request Metadata +2. Executing the iPing function + +### Step 1: Obtaining Request Metadata + +Call the `getRequestMetadata()` function with the following parameters: + +| Argument | Example Value | Description | +| ------------ | ------------- | ------------------------------------------ | +| destGasLimit | 300000 | Gas limit on destination chain | +| destGasPrice | 100000000000 | Gas price on destination chain | +| ackGasLimit | 300000 | Gas limit on source chain for ack | +| ackGasPrice | 100000000000 | Gas price on source chain for ack | +| relayerFees | 10000000000 | Relayer fees on Router chain | +| ackType | 3 | Acknowledge type | +| isReadCall | false | Weather the call is read-only | +| asmAddress | "0x" | Address for the additional security module | + +
+ Remix Example + + ![Router getRequestMetadata](../../images/opl/router-metadata.png) +
+ +:::info + +You will need the bytes answer in the next step, so copy it! + +::: + +For more information about request metadata, see the +[Router documentation][metadata]. + +[metadata]: https://docs.routerprotocol.com/develop/message-transfer-via-crosstalk/evm-guides/iDapp-functions/iSend#5-requestmetadata + +### Step 2: Executing iPing() + +To initiate the cross-chain message, call `iPing` with these parameters: + +| Argument | Value | Description | +| -------------------------- | ----------------------- | ------------------------------------------------- | +| destChainId | 23295 | Destination Chain ID (e.g. Sapphire) | +| destinationContractAddress | 0x<your-contract> | Contract address on the destination chain | +| str | "Hello" | Message to include in the ping | +| requestMetadata | <bytes string> | Bytes response from the getRequestMetada call | + + +After sending the transaction, you can monitor its status on the +[Router Explorer]. + +
+ Remix Example + +![Router iPing](../../images/opl/router-iping.png) +
+ +This completes the PingPong example, demonstrating cross-chain messaging using +Router Protocol's CrossTalk framework. + +[Router Explorer]: https://testnet.routerscan.io/crosschain +[Remix]: https://remix.ethereum.org/ diff --git a/docs/dapp/opl/secret-ballot-example/README.md b/docs/dapp/opl/secret-ballot-example/README.md new file mode 100644 index 0000000000..92726cb797 --- /dev/null +++ b/docs/dapp/opl/secret-ballot-example/README.md @@ -0,0 +1,38 @@ +--- +description: How to build your first dApp on OPL +--- + +# Secret Ballot Example + +## Overview + +On-chain voting is the basis for any decentralized autonomous organization +(DAO) that would like to foster bottom-up decision making. +In this tutorial, you will create a [secret ballot](https://en.wikipedia.org/wiki/Secret_ballot) +dApp that can only be built with the Oasis Privacy Layer. + +Why is this important? [Privacy](https://en.wikipedia.org/wiki/Secret_ballot) +protects the voter (DAO token holder) from intimidation and bullying when +exercising their right of participation, such as on a protocol. +Vote organizers can encourage participation with ballots not only by protecting +the identity of voters, but also by sealing the results of an ongoing +vote, giving the same weight to the first and last votes. + +## Getting Started + +If you have made a dApp before, then you likely know most if not all of +the tools covered here! But even if you haven't used all of these tools listed, +you should still keep going! We will do this together. + +By the end of this tutorial, we will have: + +- written smart contracts using the OPL [library](https://github.com/oasisprotocol/sapphire-paratime/blob/main/contracts/contracts/OPL.sol) +- used [Hardhat](https://hardhat.org/docs) development environment for OPL +- used [Hardhat Deploy](https://github.com/wighawag/hardhat-deploy) to deploy +smarts contracts to a testnet. +- used [Pinata](https://www.pinata.cloud) +to store simple JSON data. Not everything has to go on a blockchain. +- used [Celer](https://im-docs.celer.network/developer/celer-im-overview) to +pass messages cross multiple chains +- built a simple [Vue.JS](https://vuejs.org/guide/introduction.html) +app to interact with our dApp through [MetaMask](https://docs.metamask.io/wallet). diff --git a/docs/dapp/opl/build.md b/docs/dapp/opl/secret-ballot-example/build.md similarity index 98% rename from docs/dapp/opl/build.md rename to docs/dapp/opl/secret-ballot-example/build.md index c5920ed90a..0d918e08d5 100644 --- a/docs/dapp/opl/build.md +++ b/docs/dapp/opl/secret-ballot-example/build.md @@ -127,7 +127,7 @@ We will use these addresses in our frontend application. #### Testnet -We can likewise deploy to [Testnet](../../dapp/sapphire/guide#testnet-and-mainnet) +We can likewise deploy to [Testnet](../../sapphire/guide.mdx#testnet-and-mainnet) with Sapphire. In this case, we should prepare a wallet with Testnet tokens on both BNB Smart diff --git a/docs/dapp/opl/enclave.md b/docs/dapp/opl/secret-ballot-example/enclave.md similarity index 100% rename from docs/dapp/opl/enclave.md rename to docs/dapp/opl/secret-ballot-example/enclave.md diff --git a/docs/dapp/opl/frontend.md b/docs/dapp/opl/secret-ballot-example/frontend.md similarity index 92% rename from docs/dapp/opl/frontend.md rename to docs/dapp/opl/secret-ballot-example/frontend.md index 6fe8afad7c..c697c5f25a 100644 --- a/docs/dapp/opl/frontend.md +++ b/docs/dapp/opl/secret-ballot-example/frontend.md @@ -83,20 +83,20 @@ configuration. You should be able to navigate to [http://localhost:5173](http://localhost:5173) and create a new poll. -![Create a poll](../images/opl/create-poll.png) +![Create a poll](../../images/opl/create-poll.png) Confirm and sign a transaction to create a new poll (issues a request against the Host contract). -![Confirm new poll](../images/opl/confirm-new-poll.png) +![Confirm new poll](../../images/opl/confirm-new-poll.png) Voting on a ballot issues a request to the *enclave* contract. -![Vote on ballot](../images/opl/vote-on-ballot.png) +![Vote on ballot](../../images/opl/vote-on-ballot.png) You should be able to see results from past polls. -![See past proposals](../images/opl/past-dao-proposals.png) +![See past proposals](../../images/opl/past-dao-proposals.png) If you were able to get to this point, congrats! You have created an OPL dApp! diff --git a/docs/dapp/opl/host.md b/docs/dapp/opl/secret-ballot-example/host.md similarity index 99% rename from docs/dapp/opl/host.md rename to docs/dapp/opl/secret-ballot-example/host.md index d308ba8134..76623b8d26 100644 --- a/docs/dapp/opl/host.md +++ b/docs/dapp/opl/secret-ballot-example/host.md @@ -108,7 +108,7 @@ Our very simple DAO contract creates proposals and manages them, allowing both active and past proposals to be queried externally through methods `getActiveProposals` and `getPastProposals`. This would be sufficient on a single chain, and it is possible to develop confidential applications without -bridges, relying solely on [Sapphire](../sapphire/README.mdx). However, we will proceed +bridges, relying solely on [Sapphire](../../sapphire/README.mdx). However, we will proceed to demonstrate the cross-chain capabilities of OPL. ## What is different with OPL? diff --git a/docs/dapp/opl/introduction.md b/docs/dapp/opl/secret-ballot-example/introduction.md similarity index 100% rename from docs/dapp/opl/introduction.md rename to docs/dapp/opl/secret-ballot-example/introduction.md diff --git a/docs/dapp/opl/setup.md b/docs/dapp/opl/secret-ballot-example/setup.md similarity index 99% rename from docs/dapp/opl/setup.md rename to docs/dapp/opl/secret-ballot-example/setup.md index 6ee6b8bc28..4d75d88c99 100644 --- a/docs/dapp/opl/setup.md +++ b/docs/dapp/opl/secret-ballot-example/setup.md @@ -50,6 +50,7 @@ We would like to set `@oasisprotocol/secret-ballot-backend` as the package name inside `package.json` at `version` of `1.0.0`. Finally, we need to install the following dependencies: + - `@oasisprotocol/sapphire-contracts` contains the OPL Solidity smart contracts. - `@oasisprotocol/sapphire-hardhat` integrates Sapphire using the Hardhat config file. diff --git a/sidebarDapp.ts b/sidebarDapp.ts index 3b62b9a88f..0d7a1ef282 100644 --- a/sidebarDapp.ts +++ b/sidebarDapp.ts @@ -45,12 +45,34 @@ export const sidebarDapp: SidebarsConfig = { id: 'dapp/opl/README', }, items: [ - 'dapp/opl/introduction', - 'dapp/opl/setup', - 'dapp/opl/host', - 'dapp/opl/enclave', - 'dapp/opl/build', - 'dapp/opl/frontend', + { + type: 'category', + label: 'Router Protocol', + link: { + type: 'doc', + id: 'dapp/opl/router-protocol/README', + }, + items: [ + 'dapp/opl/router-protocol/pingpong-example', + 'dapp/opl/router-protocol/interface', + 'dapp/opl/router-protocol/approve', + ], + }, + { + type: 'category', + label: 'Secret Ballot Example', + link: { + type: 'doc', + id: 'dapp/opl/secret-ballot-example/README', + }, + items: [ + 'dapp/opl/secret-ballot-example/setup', + 'dapp/opl/secret-ballot-example/host', + 'dapp/opl/secret-ballot-example/enclave', + 'dapp/opl/secret-ballot-example/build', + 'dapp/opl/secret-ballot-example/frontend', + ], + }, ], }, {