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 @@
+
\ 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',
+ ],
+ },
],
},
{