diff --git a/docs/README.mdx b/docs/README.mdx
index 8a17612dab..a72ef5893d 100644
--- a/docs/README.mdx
+++ b/docs/README.mdx
@@ -37,7 +37,7 @@ The part also covers other ParaTimes such as the non-confidential
]} />
[Oasis Sapphire]: dapp/sapphire/README.mdx
-[Oasis Privacy Layer]: dapp/opl/README.md
+[Oasis Privacy Layer]: dapp/opl/README.mdx
[Oasis Emerald]: dapp/emerald/README.mdx
[Oasis Cipher]: dapp/emerald/README.mdx
diff --git a/docs/dapp/README.mdx b/docs/dapp/README.mdx
index 5a247ccd26..4b2ba6a52a 100644
--- a/docs/dapp/README.mdx
+++ b/docs/dapp/README.mdx
@@ -29,7 +29,7 @@ ParaTimes, you can also add privacy to your existing dApps running on other,
non-confidential chains, and only use Oasis as a *privacy service*.
The Oasis team prepared a rich cross-chain toolkit called the
-[**Oasis Privacy Layer**](opl/README.md). It consists of solidity precompiles
+[**Oasis Privacy Layer**](opl/README.mdx). It consists of solidity precompiles
integrating bridges and wrapping the OpenZeppelin ERC2771 contract on top of
Sapphire, demonstrates best practices for writing confidential smart contracts
and working examples for you to explore and extend.
diff --git a/docs/dapp/diagrams/opl-contract-flow.mmd b/docs/dapp/diagrams/opl-contract-flow.mmd
index 9b40c259e1..aa394bea2a 100644
--- a/docs/dapp/diagrams/opl-contract-flow.mmd
+++ b/docs/dapp/diagrams/opl-contract-flow.mmd
@@ -1,5 +1,10 @@
sequenceDiagram
- User ->> Home Contract: Transaction
- Home Contract -->> SGN: SGN Watches Events
+ participant User
+ participant Home as Home Contract (Host)
+ participant SGN
+ participant Executor
+ participant Sapphire as Sapphire Contract (Enclave)
+ User ->> Home: Transaction
+ Home -->> SGN: SGN Watches Events
SGN -->> Executor: Waits for SGN Approval
- Executor ->> Sapphire Contract: Submit Proof Tx
+ Executor ->> Sapphire: Submit Proof Tx
diff --git a/docs/dapp/diagrams/opl-contract-flow.mmd.svg b/docs/dapp/diagrams/opl-contract-flow.mmd.svg
index 81940a489e..8f0461a148 100644
--- a/docs/dapp/diagrams/opl-contract-flow.mmd.svg
+++ b/docs/dapp/diagrams/opl-contract-flow.mmd.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/dapp/images/opl/celer-architecture.png b/docs/dapp/images/opl/celer-architecture.png
new file mode 100644
index 0000000000..41557621a7
Binary files /dev/null and b/docs/dapp/images/opl/celer-architecture.png differ
diff --git a/docs/dapp/images/opl/celer-e2e.png b/docs/dapp/images/opl/celer-e2e.png
new file mode 100644
index 0000000000..879647585c
Binary files /dev/null and b/docs/dapp/images/opl/celer-e2e.png differ
diff --git a/docs/dapp/opl/README.md b/docs/dapp/opl/README.md
deleted file mode 100644
index 7901fe16bc..0000000000
--- a/docs/dapp/opl/README.md
+++ /dev/null
@@ -1,162 +0,0 @@
-# Oasis Privacy Layer
-
-The Oasis Privacy Layer (OPL) is an EVM-compatible privacy solution that
-empowers developers to add new functionality to smart contracts on the most
-popular EVM networks like Ethereum, BNB Chain, and Polygon with encrypted
-transactions and confidential state.
-
-By using [a Solidity library][sapphire-contracts]
-that integrates [Sapphire] into your existing and future Web3 applications,
-developers on any supported network can seamlessly add confidential state and
-selective disclosures to their dApps when using contract a deployed on Sapphire
-without leaving their existing networks.
-
-For more information about OPL and to catch the latest news, please visit the
-[official OPL page].
-
-![Oasis Privacy Layer diagram](../images/opl/privacy-layer-diagram.png)
-
-The user submits a transaction on the Home network to a contract which uses
-`postMessage` to notify the SGN that it should approve the cross-chain message.
-The Executor waits, when the SGN approves the message the Executor submits a
-transaction to the target contract on Sapphire.
-
-![Transaction Flow](../diagrams/opl-contract-flow.mmd.svg)
-
-The Home Contract pays the SGN to watch and approve the message, but the
-Executor needs to be run by somebody willing to pay for the gas to submit
-transactions to the destination chain.
-
-[sapphire-contracts]: https://www.npmjs.com/package/@oasisprotocol/sapphire-contracts
-[Sapphire]: https://oasisprotocol.org/sapphire
-[official OPL page]: https://oasisprotocol.org/opl
-
-## Quickstart
-
-A pair of contracts are linked bidirectionally 1-1 to each other across chains,
-with one end on Sapphire and the other on a supported EVM-compatible chain (the
-Home Network). They can post and receive messages to & from each other using the
-message-passing bridge, but must register endpoints to define which messages
-they handle from each other.
-
-Start by adding the [`@oasisprotocol/sapphire-contracts`] NPM package to your
-Hardhat project so you can import `OPL.sol`:
-
-```shell npm2yarn
-npm install @oasisprotocol/sapphire-contracts
-```
-
-[`@oasisprotocol/sapphire-contracts`]: http://npmjs.com/package/@oasisprotocol/sapphire-contracts
-
-Then define the two contracts, starting with a contract on Sapphire which runs
-inside the confidential enclave and can be called via the `secretExample`
-handler. Use the constructor to provide the Sapphire contract with the location
-(address and chain) of the contract on the Home network:
-
-```solidity
-import {Enclave, Result, autoswitch} from "@oasisprotocol/sapphire-contracts/contracts/OPL.sol";
-
-contract SapphireContract is Enclave {
- constructor(address otherEnd, string chain) Enclave(otherEnd, autoswitch(chain)) {
- registerEndpoint("secretExample", on_example);
- }
- function on_example(bytes calldata _args) internal returns (Result) {
- (uint256 a, bool b) = abi.decode(args, (uint256, bool));
- // TODO: do confidential things here
- return Result.Success;
- }
-}
-```
-
-Then on the other chain, define your contract which can be called via
-`triggerExample` to send a message to the contract on Sapphire using the
-`postMessage` interface.
-
-```solidity
-import {Host, Result} from "@oasisprotocol/sapphire-contracts/contracts/OPL.sol";
-
-contract HomeContract is Host {
- constructor(address otherEnd) Host(otherEnd) {
- }
- function triggerExample (uint256 a, bool b) external payable {
- postMessage("secretExample", abi.encode(a, b));
- }
-}
-```
-
-After a few minutes the bridge will detect and then the executor will invoke the
-`SapphireContract.on_example` method.
-
-## Monitoring
-
-The Celer IM Scan API can be used to retrieve status and message details by
-providing the globally unique transaction ID from the chain which originated the
-message.
-
- https://api.celerscan.com/scan/searchByTxHash?tx=0x...
-
-For details of the response format, see the [Query IM Tx Status] page of the
-Celer Inter-Chain Message (IM) documentation. Using this API lets you to check
-if messages have been delivered.
-
-[Query IM Tx Status]: https://im-docs.celer.network/developer/development-guide/query-im-tx-status
-
-## Mainnet Deployment
-
-It is necessary to run a [Message Executor] which monitors the Celer *State
-Guardian Network* (SGN) for cross-chain messages and then submits the proof
-on-chain to deliver them to the target contract.
-
-If you are participating in a Hackathon or Grant, [please fill out the relay
-request form](https://form.typeform.com/to/RsiUR9Xz) to be allowed to use the
-shared Message Executor.
-
-[Message Executor]: https://im-docs.celer.network/developer/development-guide/message-executor
-
-## Supported Networks
-
-### Mainnets
-
-| Name | Int ID | Hex ID | autoswitch name |
-| ---- | ------ | ------ | --------------- |
-| Ape | 16350 | 0x3fde | ape |
-| Arbitrum Nova | 42170 | 0xa4ba | arbitrum-nova |
-| Arbitrum One | 42161 | a4b1 | arbitrum-one |
-| Astar | 592 | 0x250 | astar |
-| Aurora | 1313161554 | 0x4e454152 | aurora |
-| Avalanche | 43114 | 0xa86a | avalanche |
-| Binance Smart Chain | 56 | 0x38 | bsc |
-| Ethereum | 1 | 0x1 | ethereum |
-| Fantom | 250 | 0xfa | fantom |
-| Filecoin | 314 | 0x13a | filecoin |
-| Milkomeda C1 | 2001 | 0x7d1 | milkomeda |
-| Moonriver | 1285 | 0x505 | moonriver |
-| Polygon | 137 | 0x89 | polygon |
-| Sapphire | 23294 | 0x5afe | sapphire |
-| Syscoin | 57 | 0x39 | syscoin |
-| Polygon zkEVM | 1101 | 0x44d | polygon-zkevm |
-| Optimism | 10 | 0xa | optimism |
-| zkSync Era | 324 | 0x144 | zksync-era |
-
-### Testnets
-
-| Name | Int ID | Hex ID | autoswitch name |
-| ---- | ------ | ------ | --------------- |
-| Arbitrum Testnet | 421611 | 0x66eeb | arbitrum-testnet |
-| Avalanche C-Chain Fuji Testnet | 43113 | 0xa869 | avalanche-fuji |
-| BSC Testnet | 97 | 0x61 | bsc-testnet |
-| ConsenSys zkEVM Testnet | 59140 | 0xe704 | zkevm-testnet |
-| Dexalot Testnet | 432201 | 0x69849 | dexalot-testnet |
-| Fantom Testnet | 4002 | 0xfa2 | fantom-testnet |
-| Filecoin Hyperspace Testnet | 3141 | 0xc45 | filecoin-testnet |
-| FNCY Testnet | 923018 | 0xe158a | fncy-testnet |
-| Godwoken Testnet | 71401 | 0x116e9 | godwoken-testnet |
-| Goerli Testnet | 5 | 0x5 | goerli |
-| Polygon Mumbai Testnet | 80001 | 0x13881 | polygon-mumbai |
-| Polygon zkEVM Testnet | 1442 | 0x5a2 | zkevm-testnet |
-| Sapphire Testnet | 23295 | 0x5aff | sapphire-testnet |
-| Scroll Alpha Testnet | 534353 | 0x82751 | scroll-testnet |
-| Shibuya Testnet | 81 | 0x51 | shibuya-testnet |
-
-In the following sections we will look at a concrete example on how to build a
-confidential, cross-chain DAO-voting dApp from scratch using the Oasis Privacy Layer!
diff --git a/docs/dapp/opl/README.mdx b/docs/dapp/opl/README.mdx
new file mode 100644
index 0000000000..d4463d5757
--- /dev/null
+++ b/docs/dapp/opl/README.mdx
@@ -0,0 +1,76 @@
+---
+description: Integrating privacy into dApps on other blockchains
+---
+
+import DocCard from '@theme/DocCard';
+import DocCardList from '@theme/DocCardList';
+import {findSidebarItem} from '@site/src/sidebarUtils';
+
+# Oasis Privacy Layer (OPL)
+
+The Oasis Privacy Layer (OPL) is a powerful solution that enables developers
+to integrate privacy features into their decentralized applications (dApps)
+across multiple EVM-compatible networks.
+
+- **Privacy-First**: OPL leverages the [Sapphire]'s privacy features to ensure
+that contract data and computation remains confidential.
+- **Cross-Chain Compatibility**: OPL is compatible with multiple blockchains
+through message bridging protocols, making it easy to integrate privacy
+regardless of the chain your dApp is built on.
+
+For more information about OPL and to catch the latest news, please visit the
+[official OPL page].
+
+[official OPL page]: https://oasisprotocol.org/opl
+[Sapphire]: ../sapphire/README.mdx
+
+## How OPL Works
+
+The OPL is made possible through message bridges, which enable secure
+communication between OPL-enabled contracts on Sapphire and smart contracts on
+other chains. This allows dApps to access privacy-preserving capabilities while
+keeping their main logic on their primary chain.
+
+![Oasis Privacy Layer diagram](../images/opl/privacy-layer-diagram.png)
+
+:::info
+
+For how to use use signed messages with the GSN to trigger a cross-chain
+messages, please visit our [Gasless Transactions chapter].
+
+:::
+
+[Gasless Transactions chapter]: ../sapphire/gasless.md
+
+## Message Bridges
+
+You can integrate message bridges into your dApps using one of these three
+methods:
+
+- **[OPL SDK]**: A wrapper provided by the Oasis Protocol that simplifies the
+integration of message bridging with Oasis’s privacy features.
+- **[Celer Inter-Chain Messaging (IM)][celer]**: A generalized message bridging solution
+by Celer, which lets you build more complex solutions.
+- **[Router Protocol CrossTalk][router]**: An extensible cross-chain framework that
+enables seamless state transitions across multiple chains.
+
+### Comparison
+
+| Protocol | Validator Network | Relayer | Fees |
+| --------------- | ----------------- | ------ | ------ |
+| **[OPL SDK]** | SGN (Celer) | Executor (self-hosted or hosted service by Celer) | SGN Fee: Paid via `msg.value` Executor Fee: Charged externally (Free on testnet) |
+| **[Celer IM][celer]** | SGN (Celer) | Executor (self-hosted or hosted service by Celer) | SGN Fee: Paid via `msg.value` Executor Fee: Charged externally (Free on testnet) |
+| **[Router Protocol][router]** | Orchestrators (Router Chain) | Relayer (run by 3rd party) | Paid by the approved feepayer on the Routerchain |
+
+## Examples
+
+
+
+[OPL SDK]: ./opl-sdk/README.md
+[celer]: ./celer/README.md
+[router]: ./router-protocol/README.md
diff --git a/docs/dapp/opl/celer/README.md b/docs/dapp/opl/celer/README.md
new file mode 100644
index 0000000000..5e28a5838a
--- /dev/null
+++ b/docs/dapp/opl/celer/README.md
@@ -0,0 +1,107 @@
+---
+description: A message bridge by Celer
+---
+
+# Celer Inter-Chain Messaging (IM)
+
+**Celer Inter-Chain Messaging (IM)** is a message passing protocol that
+facilitates the seamless transfer of any type of generic message, including
+function calls, across multiple blockchains via a single source-chain
+transaction. Celer IM currently supports message passing between Oasis Sapphire
+and all other IM-supported chains. The message-passing support enables
+developers to build entirely new privacy-centric dApps or add confidentiality
+to existing dApps on popular EVM networks using Sapphire as a privacy layer.
+
+**Celer IM** offers two design patterns:
+
+- Cross-chain logic execution without fund transfer
+- Cross-chain logic execution with accompanying fund transfer
+
+This documentation focuses on cross-chain logic execution **without** fund
+transfer. For information on using Celer IM with fund transfer, please refer
+to the [Celer IM documentation].
+
+[Celer IM documentation]: https://im-docs.celer.network/
+
+## Architecture
+
+![Celer IM Architecture](../../images/opl/celer-e2e.png)
+*Architecture diagram for Celer IM[^1]*
+
+[^1]: The Celer IM architecture diagram is courtesy of [Celer documentation][celer-architecture].
+
+[celer-architecture]: https://im-docs.celer.network/developer/architecture-walkthrough/end-to-end-workflow
+
+Celer IM’s architecture is composed of several core components that work
+together to facilitate secure and reliable cross-chain messaging:
+
+- **MessageBus**: The primary component managing message transmission between
+ source and destination blockchains. It ensures proper formatting and routing
+ of messages through the Celer network.
+- **State Guardian Network (SGN)**: A decentralized network of validators that
+ manage the state of cross-chain messages. SGN validators sign off on messages
+ and coordinate their secure delivery, providing security and availability for
+ cross-chain interactions.
+- **[Executor](#executor)**: An off-chain component that listens to the SGN for
+ validated messages ready for execution on the destination chain. Once a
+ message is verified, the Executor sends transactions to the MessageBus on the
+ destination chain, triggering the execution of the specified logic.
+
+## Executor
+
+The [Executor][Message Executor] is a crucial part of the Celer IM framework.
+It performs two main functions:
+
+- Monitors the Celer State Guardian Network (SGN) for messages ready to be
+ submitted (with sufficient validator signatures).
+- Submits message execution transactions to the MessageBus contract on the
+ destination chain.
+
+It is necessary a [Message Executor] runs for you dapp. To set up an executor,
+you have two options
+
+- Follow the [documentation] to set up your own executor.
+- Fill out this [form][celer-form] for Celer to set up a hosted executor
+ service for you.
+
+For Hackathon or Grant participants, we recommend filling out the
+[relay request form][celer-form] to use the shared Message Executor.
+
+In most cases, Celer advises dApp developers to use the shared executor
+services provided by the Celer Network team to avoid server configuration and
+operation concerns.
+
+:::info
+
+Oasis is running an executor for the Sapphire Testnet, which is okay to rely on
+for a test, for a faster execution it's recommended to run your own or use the
+hosted service.
+
+:::
+
+[Message Executor]: https://im-docs.celer.network/developer/development-guide/message-executor
+[documentation]: https://im-docs.celer.network/developer/development-guide/message-executor/integration-guide
+[celer-form]:https://form.typeform.com/to/RsiUR9Xz
+
+## Fees
+
+The cross-chain messaging process involves fees paid to two parties:
+
+- **SGN Fee**: Paid as `msg.value` to the *MessageBus* contract by
+ the entity calling `sendMessge`.
+- **Executor Fee**: Charged by the Executor for submitting execute message
+ transactions.
+
+## Monitoring
+
+The Celer IM Scan API can be used to retrieve status and message details by
+providing the globally unique transaction ID from the chain which originated the
+message.
+
+ https://api.celerscan.com/scan/searchByTxHash?tx=0x...
+
+For details of the response format, see the [Query IM Tx Status] page of the
+Celer-IM documentation. Using this API lets you to check if messages have been
+delivered.
+
+[Query IM Tx Status]: https://im-docs.celer.network/developer/development-guide/query-im-tx-status
diff --git a/docs/dapp/opl/celer/networks.md b/docs/dapp/opl/celer/networks.md
new file mode 100644
index 0000000000..ab50f37001
--- /dev/null
+++ b/docs/dapp/opl/celer/networks.md
@@ -0,0 +1,54 @@
+---
+description: Supported networks on Celer IM
+---
+
+# Supported Networks
+
+## Mainnets
+
+| Name | Int ID | Hex ID | autoswitch name |
+| ---- | ------ | ------ | --------------- |
+| Ape | 16350 | 0x3fde | ape |
+| Arbitrum Nova | 42170 | 0xa4ba | arbitrum-nova |
+| Arbitrum One | 42161 | a4b1 | arbitrum-one |
+| Astar | 592 | 0x250 | astar |
+| Aurora | 1313161554 | 0x4e454152 | aurora |
+| Avalanche | 43114 | 0xa86a | avalanche |
+| Binance Smart Chain | 56 | 0x38 | bsc |
+| Ethereum | 1 | 0x1 | ethereum |
+| Fantom | 250 | 0xfa | fantom |
+| Filecoin | 314 | 0x13a | filecoin |
+| Milkomeda C1 | 2001 | 0x7d1 | milkomeda |
+| Moonriver | 1285 | 0x505 | moonriver |
+| Polygon | 137 | 0x89 | polygon |
+| Sapphire | 23294 | 0x5afe | sapphire |
+| Syscoin | 57 | 0x39 | syscoin |
+| Polygon zkEVM | 1101 | 0x44d | polygon-zkevm |
+| Optimism | 10 | 0xa | optimism |
+| zkSync Era | 324 | 0x144 | zksync-era |
+
+## Testnets
+
+| Name | Int ID | Hex ID | autoswitch name |
+| ---- | ------ | ------ | --------------- |
+| Arbitrum Testnet | 421611 | 0x66eeb | arbitrum-testnet |
+| Avalanche C-Chain Fuji Testnet | 43113 | 0xa869 | avalanche-fuji |
+| BSC Testnet | 97 | 0x61 | bsc-testnet |
+| ConsenSys zkEVM Testnet | 59140 | 0xe704 | zkevm-testnet |
+| Dexalot Testnet | 432201 | 0x69849 | dexalot-testnet |
+| Fantom Testnet | 4002 | 0xfa2 | fantom-testnet |
+| Filecoin Hyperspace Testnet | 3141 | 0xc45 | filecoin-testnet |
+| FNCY Testnet | 923018 | 0xe158a | fncy-testnet |
+| Godwoken Testnet | 71401 | 0x116e9 | godwoken-testnet |
+| Goerli Testnet | 5 | 0x5 | goerli |
+| Polygon Mumbai Testnet | 80001 | 0x13881 | polygon-mumbai |
+| Polygon zkEVM Testnet | 1442 | 0x5a2 | zkevm-testnet |
+| Sapphire Testnet | 23295 | 0x5aff | sapphire-testnet |
+| Scroll Alpha Testnet | 534353 | 0x82751 | scroll-testnet |
+| Shibuya Testnet | 81 | 0x51 | shibuya-testnet |
+
+In the following sections we will look at a concrete example on how to build a
+confidential, cross-chain DAO-voting dApp from scratch using the Oasis Privacy Layer!
+
+
+
diff --git a/docs/dapp/opl/celer/ping-example.md b/docs/dapp/opl/celer/ping-example.md
new file mode 100644
index 0000000000..548532bcfa
--- /dev/null
+++ b/docs/dapp/opl/celer/ping-example.md
@@ -0,0 +1,198 @@
+---
+description: Ping example with Celer IM
+---
+
+# Ping Example
+
+This tutorial demonstrates how to send a cross-chain message using
+[Celer's Inter-Chain Messaging (IM)].
+
+[Celer's Inter-Chain Messaging (IM)]: https://im-docs.celer.network/
+
+You'll learn how to:
+
+ - Deploy MessageBus-compatible contracts
+ - 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 Ping
+
+In this example, you'll deploy the same contract on two different chains.
+You'll then send a `ping` from *BSC Testnet* to *Saphhire Testnet*, facilitated
+by Celer-IM.
+The contract on *Sapphire Testnet* will receive the `ping` and emits an event
+with the message which was received.
+
+## Contract Setup
+
+1. Open [Remix] and create a new file called `Ping.sol`
+2. Paste the following contract and interface into it:
+
+
+ Ping.sol Contract
+
+ ```solidity title="Ping.sol" showLineNumbers
+ // SPDX-License-Identifier: MIT
+ pragma solidity ^0.8.0;
+
+ interface IMessageBus {
+ function sendMessage(
+ address _receiver,
+ uint256 _dstChainId,
+ bytes calldata _message
+ ) external payable;
+ }
+
+
+ contract Ping {
+ address public messageBus;
+
+ event MessageReceived(
+ address srcContract,
+ uint64 srcChainId,
+ address sender,
+ bytes message
+ );
+
+
+ enum ExecutionStatus {
+ Fail, // execution failed, finalized
+ Success, // execution succeeded, finalized
+ Retry // execution rejected, can retry later
+ }
+
+ constructor(address _messageBus) {
+ messageBus = _messageBus;
+ }
+
+ modifier onlyMessageBus() {
+ require(msg.sender == messageBus, "caller is not message bus");
+ _;
+ }
+
+
+ function sendPing(
+ address _dstContract,
+ uint64 _dstChainId,
+ bytes calldata _message
+ ) external payable {
+ bytes memory message = abi.encode(msg.sender, _message);
+ IMessageBus(messageBus).sendMessage{value: msg.value}(_dstContract, _dstChainId, message);
+ }
+
+ function executeMessage(
+ address _srcContract,
+ uint64 _srcChainId,
+ bytes calldata _message,
+ address // executor
+ ) external payable onlyMessageBus returns (ExecutionStatus) {
+ (address sender, bytes memory message) = abi.decode(
+ (_message),
+ (address, bytes)
+ );
+ emit MessageReceived(_srcContract, _srcChainId, sender, message);
+ return ExecutionStatus.Success;
+ }
+ }
+ ```
+
+
+### Key points
+
+- `messageBus`: Celer's MessageBus contract on the respective chain.
+- `sendPing`: Initiates the cross-chain my calling Celers MessageBus.
+- `executeMessage`: Called by Celer's MessageBus on the destination chian.
+
+## Compiling the Contract
+
+For compatibility with Sapphire, compile the contract using compiler version
+**`0.8.24`** and evm version **`paris`** (under advanced configuration).
+
+:::info
+
+You can also use Celer's framework contracts and interfaces by importing them
+
+```solidity
+import "sgn-v2-contracts/contracts/message/framework/MessageBusAddress.sol";
+import "sgn-v2-contracts/contracts/message/framework/MessageReceiverApp.sol";
+import "sgn-v2-contracts/contracts/message/interfaces/IMessageBus.sol";
+```
+
+but this will limit you to use only Solidity version **`0.8.9`**.
+
+:::
+
+## Deploying the Contract
+
+Deploy the Ping contract on two different chains: `BSC Testnet` and
+`Sapphire Testnet`.
+
+### Deploying on BSC Testnet
+
+1. Obtain BNB test token for `BSC Testnet` from the [BNB faucet] or their
+ discord.
+2. In MetaMask, switch to the `BSC Testnet` network and select
+ `Injected Provider - MetaMask` as the environment in Remix.
+3. Fill in the messageBus address for BSC Testnet:
+ `0xAd204986D6cB67A5Bc76a3CB8974823F43Cb9AAA`.
+4. Deploy the contract on `BSC Testnet`.
+
+
+[BNB faucet]: https://www.bnbchain.org/en/testnet-faucet
+
+### 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 messageBus address for BSC Testnet:
+ `0x9Bb46D5100d2Db4608112026951c9C965b233f4D`.
+4. Deploy the contract on Sapphire Testnet
+
+[Oasis Faucet]: https://faucet.testnet.oasis.io/
+
+## Executing Ping
+
+Now that you've deployed the contacts, you can send the ping message
+cross-chain.
+
+You'll need the following three parameters:
+
+- `_dstContract`: The contract address of the reveiving contract on the
+ destination chain which you just deployed.
+- `_dstChainId`: The chain id of the the destination chain. Which is in our
+ example `Sapphire Testnet` - `23295`.
+- `message`: The encoded message. e.g. "Hello from BSC" -
+ `0x48656c6c6f2066726f6d20425343000000000000000000000000000000000000`.
+
+Additionally you'll have to pay a fee which you send as value. For sending the
+ping 0.001 tBNB (1000000 gwei) will be enough.
+
+:::info
+
+For the `Sapphire Testnet` an executor is running to relay the messages every
+few mintues. If you deploy on mainnet please refer to the [Executor chapter].
+:::
+
+[Executor chapter]: ./README.md#executor
+
+## Checking execution
+
+To see if you successfully send a ping message cross-chain you can watch for
+new transactions at the [MessageBus address] from Celer or your deployed
+contract Sapphire Testnet.
+
+[MessageBus address]: https://explorer.oasis.io/testnet/sapphire/address/0x9Bb46D5100d2Db4608112026951c9C965b233f4D
+
+[Remix]: https://remix.ethereum.org/
diff --git a/docs/dapp/opl/opl-sdk/README.md b/docs/dapp/opl/opl-sdk/README.md
new file mode 100644
index 0000000000..51ac192382
--- /dev/null
+++ b/docs/dapp/opl/opl-sdk/README.md
@@ -0,0 +1,110 @@
+---
+description: OPL sdk
+---
+
+# OPL SDK
+
+
+The OPL SDK is available in our [Solidity library][sapphire-contracts].
+The SDK wraps the Celer Inter-Chain Message (IM) and makes it easy and straight
+forward to integrate [Sapphire] and its privacy features into your existing or
+future Web3 applications.
+
+[sapphire-contracts]: https://www.npmjs.com/package/@oasisprotocol/sapphire-contracts
+[Sapphire]: https://oasisprotocol.org/sapphire
+
+## Overview
+
+![Transaction Flow](../../diagrams/opl-contract-flow.mmd.svg)
+
+1. The **user** submits a transaction on the Home network to a contract which uses
+ `postMessage` to emit an event about a the cross-chain message.
+2. The **Celer *State Guardian Network* (SGN)** monitors for transactions which
+ trigger a cross-chain message event and create attestation.
+3. The **Executor** waits, when the SGN approves the message the Executor submits a
+ transaction to the target contract on Sapphire.
+
+## Fees
+
+The Home Contract pays the SGN to watch and approve the message, but the
+Executor needs to be run by somebody willing to pay for the gas to submit
+transactions to the destination chain.
+
+More details to the Celer Executor you can find [here][celer-executor].
+
+
+## Quickstart
+
+A pair of contracts are linked bidirectionally 1-1 to each other across chains,
+with one end on Sapphire and the other on a supported EVM-compatible chain (the
+Home Network). They can post and receive messages to & from each other using the
+message-passing bridge, but must register endpoints to define which messages
+they handle from each other.
+
+### Setup
+
+Start by adding the [`@oasisprotocol/sapphire-contracts`] NPM package to your
+Hardhat project so you can import `OPL.sol`:
+
+```shell npm2yarn
+npm install @oasisprotocol/sapphire-contracts
+```
+
+[`@oasisprotocol/sapphire-contracts`]: http://npmjs.com/package/@oasisprotocol/sapphire-contracts
+
+Now define the two contracts:
+
+- A contract on **Sapphire** which runs inside the confidential `enclave`
+- A contract on the **home chain** as a `host` which triggers the example
+
+### Sapphire Contract
+
+On Sapphire use the constructor to provide the Sapphire contract with the
+location (address and chain) of the contract on the Home chain and register an
+endpoint called `secretExample`.
+
+```solidity
+import {Enclave, Result, autoswitch} from "@oasisprotocol/sapphire-contracts/contracts/OPL.sol";
+
+contract SapphireContract is Enclave {
+ constructor(address otherEnd, string chain) Enclave(otherEnd, autoswitch(chain)) {
+ registerEndpoint("secretExample", on_example);
+ }
+ function on_example(bytes calldata _args) internal returns (Result) {
+ (uint256 a, bool b) = abi.decode(args, (uint256, bool));
+ // TODO: do confidential things here
+ return Result.Success;
+ }
+}
+```
+
+### Home Contract
+
+On the other chain, define your contract which can be called via
+`triggerExample` to send a message to the contract on Sapphire using the
+`postMessage` interface.
+
+```solidity
+import {Host, Result} from "@oasisprotocol/sapphire-contracts/contracts/OPL.sol";
+
+contract HomeContract is Host {
+ constructor(address otherEnd) Host(otherEnd) {
+ }
+ function triggerExample (uint256 a, bool b) external payable {
+ postMessage("secretExample", abi.encode(a, b));
+ }
+}
+```
+
+After a few minutes the bridge will detect and then the executor will invoke the
+`SapphireContract.on_example` method.
+
+:::info
+
+As noted in the [fees](#fees) section, an exectuor needs to relay your messages.
+Please refer to the Celer [Executor][celer-executor] section on how to get on
+the shared Message Executor or how to set up your own executor.
+
+:::
+
+[celer-executor]: ../celer/README.md#executor
\ No newline at end of file
diff --git a/docs/dapp/opl/opl-sdk/ping-example.md b/docs/dapp/opl/opl-sdk/ping-example.md
new file mode 100644
index 0000000000..b2073f4031
--- /dev/null
+++ b/docs/dapp/opl/opl-sdk/ping-example.md
@@ -0,0 +1,224 @@
+---
+description: Ping example with OPL SDK
+---
+
+# Ping Example
+
+This tutorial demonstrates how to send a cross-chain message using
+[Oasis OPL].
+
+[Oasis OPL]: ./README.md
+
+You'll learn how to:
+
+ - Deploy a Host contract
+ - Deploy a Enclave contract
+ - Send a cross-chain message
+
+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 Ping
+
+In this example, you'll deploy a `host` contract on *BSC Testnet* and a `enclave`
+contract on *Sapphire Testnet*.
+You'll then send a `ping` from the host contract to the enclave contract,
+facilitated by the OPL SDK.
+The enclave contract will receive the `ping` and emits an event with the
+message which was received.
+
+## Contract Setup
+
+1. Open [Remix] and create a new file called `Ping.sol`
+2. Paste the following Ping host contract into it:
+
+
+ Ping.sol Contract
+
+ ```solidity title="Ping.sol" showLineNumbers
+ // SPDX-License-Identifier: MIT
+ pragma solidity ^0.8.0;
+
+ import {Host, Result} from "@oasisprotocol/sapphire-contracts/contracts/OPL.sol";
+
+ contract Ping is Host {
+ event MessageReceived(bytes message);
+
+ constructor(address pong) Host(pong) {
+ registerEndpoint("pongMessage", _pongMessage);
+ }
+
+ function startPing (bytes calldata _message) external payable {
+ postMessage("ping", abi.encode(_message));
+ }
+
+ function _pongMessage(bytes calldata _args) internal returns (Result) {
+ (bytes memory message) = abi.decode((_args), (bytes));
+ emit MessageReceived(message);
+ return Result.Success;
+ }
+ }
+ ```
+
+
+3. Create a new file called `Pong.sol`
+4. Paste the following Pong enclave contract into it:
+
+
+ Pong.sol Contract
+
+ ```solidity title="Pong.sol" showLineNumbers
+ // SPDX-License-Identifier: MIT
+ pragma solidity ^0.8.0;
+
+ import {Enclave, Result, autoswitch} from "@oasisprotocol/sapphire-contracts/contracts/OPL.sol";
+
+ contract Pong is Enclave {
+ event MessageReceived(bytes message);
+
+ constructor(uint nonce, bytes32 chain) Enclave(computeAddress(msg.sender, nonce), autoswitch(chain)) {
+ registerEndpoint("ping", _pingMessage);
+ }
+
+ function _pingMessage(bytes calldata _args) internal returns (Result) {
+ (bytes memory message) = abi.decode((_args), (bytes));
+ emit MessageReceived(message);
+ return Result.Success;
+ }
+
+ function computeAddress(address _origin, uint _nonce) public pure returns (address) {
+ if (_nonce == 0x00) {
+ return address(uint160(uint256(keccak256(abi.encodePacked(
+ bytes1(0xd6), bytes1(0x94), _origin, bytes1(0x80)
+ )))));
+ }
+ if (_nonce <= 0x7f) {
+ return address(uint160(uint256(keccak256(abi.encodePacked(
+ bytes1(0xd6), bytes1(0x94), _origin, bytes1(uint8(_nonce))
+ )))));
+ }
+ if (_nonce <= 0xff) {
+ return address(uint160(uint256(keccak256(abi.encodePacked(
+ bytes1(0xd7), bytes1(0x94), _origin, bytes1(0x81), uint8(_nonce)
+ )))));
+ }
+ if (_nonce <= 0xffff) {
+ return address(uint160(uint256(keccak256(abi.encodePacked(
+ bytes1(0xd8), bytes1(0x94), _origin, bytes1(0x82), uint16(_nonce)
+ )))));
+ }
+ if (_nonce <= 0xffffff) {
+ return address(uint160(uint256(keccak256(abi.encodePacked(
+ bytes1(0xd9), bytes1(0x94), _origin, bytes1(0x83), uint24(_nonce)
+ )))));
+ }
+ return address(uint160(uint256(keccak256(abi.encodePacked(
+ bytes1(0xda), bytes1(0x94), _origin, bytes1(0x84), uint32(_nonce)
+ )))));
+ }
+ }
+ ```
+
+
+### Key points
+
+- `Host`: OPL wrapper for outside contract.
+- `Enclave`: OPL wrapper for the contract on Sapphire side.
+- `registerEndpoint`: Registers endpoints in an OPL managed map.
+- `postMessage`: Call registered endpoints.
+- `autoswitch`: Finds correct MessageBus address via chain name.
+
+## Compiling the Contract
+
+For compatibility with Sapphire, compile the contract using compiler version
+**`0.8.24`** and evm version **`paris`** (under advanced configuration).
+
+## Deploying the Contract
+
+Deploy the Ping contract on `BSC Testnet` and the Pong.sol contract on
+`Sapphire Testnet`.
+
+### Deploying Pong.sol on Sapphire Testnet
+
+You'll deploy the contract on `Sapphire Testnet` first to avoid switching chains
+back and forth.
+
+1. Obtain TEST tokens for `Sapphire Testnet` from the [Oasis faucet].
+2. Get next nonce of your account from `BSC Testnet`
+ 1. If you didn't do anything on *BSC Testnet* yet this will `0`.
+ 2. Else you need to get your last nonce, e.g. by checking your account
+address on [bscscan](https://testnet.bscscan.com/) and and inspect the details
+of your latest transaction, and then add 1.
+3. In Metamask, switch to the `Sapphire Testnet` network and select
+ `Injected Provider - MetaMask` as the environment in Remix.
+4. Select the `Pong.sol` contract.
+5. Fill in the deployment parameters:
+
+- **`nonce`**: `0` (or next nonce as written above)
+- **`chain`**: `0x6273630000000000000000000000000000000000000000000000000000000000`
+ (bytes encoded `"bsc"`)
+
+6. Deploy the contract on `Sapphire Testnet`
+
+:::info
+
+Copy the address of the deployed contract, you'll need it for the next step as
+Remix will remove the contract from the UI if you change the chain.
+
+:::
+
+[Oasis Faucet]: https://faucet.testnet.oasis.io/
+
+### Deploying Ping.sol on BSC Testnet
+
+1. Obtain BNB test token for `BSC Testnet` from the [BNB faucet] or their
+ discord.
+2. In MetaMask, switch to the `BSC Testnet` network and select
+ `Injected Provider - MetaMask` as the environment in Remix.
+3. Select the `Ping.sol` contract.
+4. Fill in the contract addess you just have deployed on `Sapphire Testnet`.
+5. Deploy the contract on `BSC Testnet`.
+
+[BNB faucet]: https://www.bnbchain.org/en/testnet-faucet
+
+## Executing Ping
+
+Now that you've deployed the contacts, you can send the ping message
+cross-chain.
+
+You'll need the following parameter for `startPing`:
+
+- `_message`: The encoded message. e.g. "Hello from BSC" -
+ `0x48656c6c6f2066726f6d20425343000000000000000000000000000000000000`.
+
+Additionally you'll have to pay a fee which you send as `value`. For sending the
+ping 0.001 tBNB (1000000 gwei) will be enough.
+
+Finally execute the function `startPing`.
+
+:::info
+
+For the `Sapphire Testnet` an executor is running to relay the messages every
+few mintues. If you deploy on mainnet please refer to the [Executor chapter].
+:::
+
+[Executor chapter]: ../celer/README.md#executor
+
+## Checking execution
+
+To see if you successfully send a ping message cross-chain you can watch for
+new transactions at the [MessageBus address] from Celer or your deployed
+contract on `Sapphire Testnet`.
+
+[MessageBus address]: https://explorer.oasis.io/testnet/sapphire/address/0x9Bb46D5100d2Db4608112026951c9C965b233f4D
+
+[Remix]: https://remix.ethereum.org/
diff --git a/docs/dapp/opl/router-protocol/pingpong-example.md b/docs/dapp/opl/router-protocol/pingpong-example.md
index 2f7a48db2e..1c208f749f 100644
--- a/docs/dapp/opl/router-protocol/pingpong-example.md
+++ b/docs/dapp/opl/router-protocol/pingpong-example.md
@@ -1,5 +1,5 @@
---
-description: Router Protocol PingPong example
+description: PingPong example with Router CrossTalk
---
# PingPong Example
@@ -217,17 +217,17 @@ acknowledgment message back to the contract on chain A.
- `iAck`: Handles the acknowledgment in a bidirectional cross-chain message on
the source contract.
-### Compiling the Contract
+## Compiling the Contract
-For compatibility with Sapphire, compile the contract using Solidity version
-**`0.8.24`** or older.
+For compatibility with Sapphire, compile the contract using compiler version
+**`0.8.24`** and evm version **`paris`** (under advanced configuration).
-### Deploying the Contract
+## Deploying the Contract
Deploy the PingPong contract on two different chains: `Sapphire Testnet` and
`Polygon Amoy`.
-#### Deploying on Sapphire Testnet
+### 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
@@ -249,7 +249,7 @@ Deploy the PingPong contract on two different chains: `Sapphire Testnet` and
[Oasis Faucet]: https://faucet.testnet.oasis.io/
-#### Deploying on Polygon Amoy
+### 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.
diff --git a/docs/dapp/opl/secret-ballot-example/README.md b/docs/dapp/opl/secret-ballot-example/README.md
index 92726cb797..41ece083a4 100644
--- a/docs/dapp/opl/secret-ballot-example/README.md
+++ b/docs/dapp/opl/secret-ballot-example/README.md
@@ -4,6 +4,10 @@ description: How to build your first dApp on OPL
# Secret Ballot Example
+This example is an extensive tutorial on how to use [OPL] for confidential voting.
+
+[OPL]: ../opl-sdk/README.md
+
## Overview
On-chain voting is the basis for any decentralized autonomous organization
diff --git a/sidebarDapp.ts b/sidebarDapp.ts
index 7f8b137e19..e1ac747b41 100644
--- a/sidebarDapp.ts
+++ b/sidebarDapp.ts
@@ -47,30 +47,53 @@ export const sidebarDapp: SidebarsConfig = {
items: [
{
type: 'category',
- label: 'Router Protocol',
+ label: 'OPL SDK',
link: {
type: 'doc',
- id: 'dapp/opl/router-protocol/README',
+ id: 'dapp/opl/opl-sdk/README',
},
items: [
- 'dapp/opl/router-protocol/pingpong-example',
- 'dapp/opl/router-protocol/interface',
- 'dapp/opl/router-protocol/approve',
+ 'dapp/opl/opl-sdk/ping-example',
+ {
+ 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',
+ ],
+ }
+ ],
+ },
+ {
+ type: 'category',
+ label: 'Celer Inter-Chain Messaging',
+ link: {
+ type: 'doc',
+ id: 'dapp/opl/celer/README',
+ },
+ items: [
+ 'dapp/opl/celer/ping-example',
+ 'dapp/opl/celer/networks',
],
},
{
type: 'category',
- label: 'Secret Ballot Example',
+ label: 'Router Protocol',
link: {
type: 'doc',
- id: 'dapp/opl/secret-ballot-example/README',
+ id: 'dapp/opl/router-protocol/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',
+ 'dapp/opl/router-protocol/pingpong-example',
+ 'dapp/opl/router-protocol/interface',
+ 'dapp/opl/router-protocol/approve',
],
},
],