diff --git a/.gitmodules b/.gitmodules
index b1c953dd35..510d75b006 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -11,3 +11,6 @@
[submodule "external/cli"]
path = external/cli
url = https://github.com/oasisprotocol/cli
+[submodule "external/sapphire-paratime"]
+ path = external/sapphire-paratime
+ url = https://github.com/oasisprotocol/sapphire-paratime
diff --git a/README.md b/README.md
index 5abf8856b4..6b8cf79550 100644
--- a/README.md
+++ b/README.md
@@ -179,23 +179,24 @@ number in the comment which added this redirection for future reference, if
major rewrite is to happen and the developers would need more context around
the redirection.
-### Diagrams
+### Images
-There are three kinds of image assets currently used in the docs.
+There are three kinds of image assets used in the docs.
-1. Screenshots, photos, non-technical figures go into `images/` folder of the
- respected top-level chapter.
+1. Screenshots, photos, non-technical figures go into `images/` folder
+ on the part-level (i.e. `docs/dapp/images`). External repositories may use
+ own images in their respective folder.
2. [Mermaid](https://mermaid-js.github.io) diagrams (preferred tool for
sequence diagrams, flowcharts and other technical material) live in
- `diagrams/` folder of the respected top-level chapter. Diagram sources
- reside in the .mmd files. To generate .svg which can be used in the
+ `diagrams/` folder of the respective part or the external repository. Diagram
+ sources reside in the .mmd files. To generate .svg which can be used in the
markdown run `yarn diagrams`. Both .svg and .mmd files should be stored in
git. CI will check that they are always in sync.
3. Other diagrams which cannot be designed with Mermaid are drawn in
[diagrams.net](https://diagrams.net) and then stored locally inside
`images/` folder along other images. When exporting the diagram, don't
forget to tick the "Include a copy of my diagram" checkbox so that the
- diagram source will be stored along in the .svg file so you will
+ diagram source will be stored along in the .svg file and you will
be able to edit it in the future.
## Vocabulary
diff --git a/docs/dapp/images/sapphire/gasless-gsn-flow.jpg b/docs/dapp/images/sapphire/gasless-gsn-flow.jpg
deleted file mode 100644
index 4a3683560e..0000000000
Binary files a/docs/dapp/images/sapphire/gasless-gsn-flow.jpg and /dev/null differ
diff --git a/docs/dapp/images/sapphire/gasless-on-chain-signer.svg b/docs/dapp/images/sapphire/gasless-on-chain-signer.svg
deleted file mode 100644
index 6ca4a197c6..0000000000
--- a/docs/dapp/images/sapphire/gasless-on-chain-signer.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/docs/dapp/images/sapphire/hardhat-boilerplate-frontend1.png b/docs/dapp/images/sapphire/hardhat-boilerplate-frontend1.png
deleted file mode 100644
index 8d08559908..0000000000
Binary files a/docs/dapp/images/sapphire/hardhat-boilerplate-frontend1.png and /dev/null differ
diff --git a/docs/dapp/images/sapphire/hardhat-boilerplate-frontend2.png b/docs/dapp/images/sapphire/hardhat-boilerplate-frontend2.png
deleted file mode 100644
index 49a293bba2..0000000000
Binary files a/docs/dapp/images/sapphire/hardhat-boilerplate-frontend2.png and /dev/null differ
diff --git a/docs/dapp/images/sapphire/hardhat-boilerplate-frontend3.png b/docs/dapp/images/sapphire/hardhat-boilerplate-frontend3.png
deleted file mode 100644
index 16c4f944fb..0000000000
Binary files a/docs/dapp/images/sapphire/hardhat-boilerplate-frontend3.png and /dev/null differ
diff --git a/docs/dapp/images/sapphire/sourcify1.png b/docs/dapp/images/sapphire/sourcify1.png
deleted file mode 100644
index b6dba793af..0000000000
Binary files a/docs/dapp/images/sapphire/sourcify1.png and /dev/null differ
diff --git a/docs/dapp/images/sapphire/sourcify2.png b/docs/dapp/images/sapphire/sourcify2.png
deleted file mode 100644
index 42b201dfe2..0000000000
Binary files a/docs/dapp/images/sapphire/sourcify2.png and /dev/null differ
diff --git a/docs/dapp/images/sapphire/sourcify3.png b/docs/dapp/images/sapphire/sourcify3.png
deleted file mode 100644
index ac32713f68..0000000000
Binary files a/docs/dapp/images/sapphire/sourcify3.png and /dev/null differ
diff --git a/docs/dapp/sapphire/addresses.md b/docs/dapp/sapphire/addresses.md
deleted file mode 100644
index 6fd24eb2bf..0000000000
--- a/docs/dapp/sapphire/addresses.md
+++ /dev/null
@@ -1,18 +0,0 @@
----
-description: List of Standard Contract Addresses
----
-
-# Standard Contract Addresses
-
-| Name | Mainnet Address | Testnet Address | Verify | Source |
-|--------------|--------------------------------------------|--------------------------------------------|------------------------------------------------------------------|---------------------------------|
-| [Multicall V3][multicall] | `0xcA11bde05977b3631167028862bE2a173976CA11` | - | [Mainnet][multicall-verify-mainnet] | [Multicall3.sol][multicall-source] |
-| Wrapped ROSE | `0x8Bc2B030b299964eEfb5e1e0b36991352E56D2D3` | `0xB759a0fbc1dA517aF257D5Cf039aB4D86dFB3b94` | [Mainnet][wrose-verify-mainnet], [Testnet][wrose-verify-testnet] | [WrappedROSE.sol][wrose-source] |
-
-[multicall-source]: https://github.com/mds1/multicall/blob/main/src/Multicall3.sol
-[multicall-verify-mainnet]: https://sourcify.dev/#/lookup/0xcA11bde05977b3631167028862bE2a173976CA11
-[multicall]: https://multicall3.com/
-
-[wrose-source]: https://github.com/oasisprotocol/sapphire-paratime/blob/main/contracts/contracts/WrappedROSE.sol
-[wrose-verify-mainnet]: https://sourcify.dev/#/lookup/0x8Bc2B030b299964eEfb5e1e0b36991352E56D2D3
-[wrose-verify-testnet]: https://sourcify.dev/#/lookup/0xB759a0fbc1dA517aF257D5Cf039aB4D86dFB3b94
diff --git a/docs/dapp/sapphire/addresses.md b/docs/dapp/sapphire/addresses.md
new file mode 120000
index 0000000000..8ef4fd10cf
--- /dev/null
+++ b/docs/dapp/sapphire/addresses.md
@@ -0,0 +1 @@
+../../../external/sapphire-paratime/docs/addresses.md
\ No newline at end of file
diff --git a/docs/dapp/sapphire/authentication.md b/docs/dapp/sapphire/authentication.md
deleted file mode 100644
index 341f1b2b50..0000000000
--- a/docs/dapp/sapphire/authentication.md
+++ /dev/null
@@ -1,209 +0,0 @@
----
-description: Authenticate users with your confidential contracts
----
-
-# View-Call Authentication
-
-User impersonation on Ethereum and other "Transparent EVMs" isn't a problem
-because **everybody** can see **all** data however the Sapphire confidential
-EVM prevents contracts from revealing confidential information to the wrong
-party (account or contract) - for this reason we cannot allow arbitrary
-impersonation of any `msg.sender`.
-
-In Sapphire, there are four types of contract calls:
-
- 1. Contract to contract calls (also known as *internal calls*)
- 2. Unauthenticted view calls (queries using `eth_call`)
- 3. Authenticated view calls (signed queries)
- 4. Transactions (authenticated by signature)
-
-Intra-contract calls always set `msg.sender` appropriately, if a contract calls
-another contract in a way which could reveal sensitive information, the calling
-contract must implement access control or authentication.
-
-By default all `eth_call` queries used to invoke contract functions have the
-`msg.sender` parameter set to `address(0x0)`. In contrast, authenticated calls are
-signed by a keypair and will have the `msg.sender` parameter correctly initialized
-(more on that later). Also, when a transaction is
-submitted it is signed by a keypair (thus costs gas and can make state updates)
-and the `msg.sender` will be set to the signing account.
-
-## Sapphire Wrapper
-
-The [@oasisprotocol/sapphire-paratime][sp-npm] Ethereum provider wrapper
-`sapphire.wrap` function will **automatically end-to-end encrypt calldata** when
-interacting with contracts on Sapphire, this is an easy way to ensure the
-calldata of your dApp transactions remain confidential - although the `from`,
-`to`, and `gasprice` parameters are not encrypted.
-
-[sp-npm]: https://www.npmjs.com/package/@oasisprotocol/sapphire-paratime
-
-:::tip Unauthenticated calls and Encryption
-
-Although the calls may be unauthenticated, they can still be encrypted!
-
-:::
-
-However, if the Sapphire wrapper has been attached to a signer then subsequent
-view calls via `eth_call` will request that the user sign them (e.g. a
-MetaMask popup), these are called **signed queries** meaning `msg.sender` will be
-set to the signing account and can be used for authentication or to implement
-access control. This may add friction to the end-user experience and can result
-in frequent pop-ups requesting they sign queries which wouldn't normally require
-any interaction on Transparent EVMs.
-
-Let's see how Sapphire interprets different contract calls. Suppose the
-following solidity code:
-
-```solidity
-contract Example {
- address owner;
- constructor () {
- owner = msg.sender;
- }
- function isOwner () public view returns (bool) {
- return msg.sender == owner;
- }
-}
-```
-
-In the sample above, assuming we're calling from the same contract or account
-which created the contract, calling `isOwner` will return:
-
- * `false`, for `eth_call`
- * `false`, with `sapphire.wrap` but without an attached signer
- * `true`, with `sapphire.wrap` and an attached signer
- * `true`, if called via the contract which created it
-* `true`, if called via transaction
-
-## Caching Signed Queries
-
-When using signed queries the blockchain will be queried each time, however
-the Sapphire wrapper will cache signatures for signed queries with the same
-parameters to avoid asking the user to sign the same thing multiple times.
-
-Behind the scenes the signed queries use a "leash" to specify validity conditions
-so the query can only be performed within a block and account `nonce` range.
-These parameters are visible in the EIP-712 popup signed by the user. Queries
-with the same parameters will use the same leash.
-
-## Daily Sign-In with EIP-712
-
-One strategy which can be used to reduce the number of transaction signing
-prompts when a user interacts with contracts via a dApp is to use
-[EIP-712][eip-712] to "sign-in" once per day (or per-session), in combination
-with using two wrapped providers:
-
-[eip-712]: https://eips.ethereum.org/EIPS/eip-712
-
- 1. Provider to perform encrypted but unauthenticated view calls
- 2. Another provider to perform encrypted and authenticated transactions (or view calls)
- - The user will be prompted to sign each action.
-
-The two-provider pattern, in conjunction with a daily EIP-712 sign-in prompt
-ensures all transactions are end-to-end encrypted and the contract can
-authenticate users in view calls without frequent annoying popups.
-
-The code sample below uses an `authenticated` modifier to verify the sign-in:
-
-```solidity
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.0;
-
-struct SignatureRSV {
- bytes32 r;
- bytes32 s;
- uint256 v;
-}
-
-contract SignInExample {
- bytes32 public constant EIP712_DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
- string public constant SIGNIN_TYPE = "SignIn(address user,uint32 time)";
- bytes32 public constant SIGNIN_TYPEHASH = keccak256(bytes(SIGNIN_TYPE));
- bytes32 public immutable DOMAIN_SEPARATOR;
-
- constructor () {
- DOMAIN_SEPARATOR = keccak256(abi.encode(
- EIP712_DOMAIN_TYPEHASH,
- keccak256("SignInExample.SignIn"),
- keccak256("1"),
- block.chainid,
- address(this)
- ));
- }
-
- struct SignIn {
- address user;
- uint32 time;
- SignatureRSV rsv;
- }
-
- modifier authenticated(SignIn calldata auth)
- {
- // Must be signed within 24 hours ago.
- require( auth.time > (block.timestamp - (60*60*24)) );
-
- // Validate EIP-712 sign-in authentication.
- bytes32 authdataDigest = keccak256(abi.encodePacked(
- "\x19\x01",
- DOMAIN_SEPARATOR,
- keccak256(abi.encode(
- SIGNIN_TYPEHASH,
- auth.user,
- auth.time
- ))
- ));
-
- address recovered_address = ecrecover(
- authdataDigest, uint8(auth.rsv.v), auth.rsv.r, auth.rsv.s);
-
- require( auth.user == recovered_address, "Invalid Sign-In" );
-
- _;
- }
-
- function authenticatedViewCall(
- SignIn calldata auth,
- ... args
- )
- external view
- authenticated(auth)
- returns (bytes memory output)
- {
- // Use `auth.user` instead of `msg.sender`!
- }
-}
-```
-
-With the above contract code deployed, let's look at the frontend dApp and how
-it can request the user to sign-in using EIP-712. You may wish to add additional
-parameters which are authenticated such as the domain name. The following code
-example uses Ethers:
-
-```typescript
-const time = new Date().getTime();
-const user = await eth.signer.getAddress();
-
-// Ask user to "Sign-In" every 24 hours.
-const signature = await eth.signer._signTypedData({
- name: "SignInExample.SignIn",
- version: "1",
- chainId: import.meta.env.CHAINID,
- verifyingContract: contract.address
-}, {
- SignIn: [
- { name: 'user', type: "address" },
- { name: 'time', type: 'uint32' },
- ]
-}, {
- user,
- time: time
-});
-const rsv = ethers.utils.splitSignature(signature);
-const auth = {user, time, rsv};
-// The `auth` variable can then be cached.
-
-// Then in the future, authenticated view calls can be performed by
-// passing auth without further user interaction authenticated data.
-await contract.authenticatedViewCall(auth, ...args);
-```
diff --git a/docs/dapp/sapphire/authentication.md b/docs/dapp/sapphire/authentication.md
new file mode 120000
index 0000000000..912a6e5c27
--- /dev/null
+++ b/docs/dapp/sapphire/authentication.md
@@ -0,0 +1 @@
+../../../external/sapphire-paratime/docs/authentication.md
\ No newline at end of file
diff --git a/docs/dapp/sapphire/browser.md b/docs/dapp/sapphire/browser.md
deleted file mode 100644
index 20ab2f9a1a..0000000000
--- a/docs/dapp/sapphire/browser.md
+++ /dev/null
@@ -1,208 +0,0 @@
----
-description: Writing Sapphire dApp for browser and Metamask
----
-
-# Browser Support
-
-Confidential Sapphire dApps work in web browsers by wrapping the
-Ethereum provider such as Metamask to enable signing and encrypting
-calls and transactions.
-
-Let's begin with the [Hardhat boilerplate]. As mentioned on their website
-the boilerplate provides the following:
-
-- The Solidity contract implementing an ERC-20 token
-- Tests for the entire functionality of the contract
-- A minimal React front-end to interact with the contract using ethers.js
-
-Go ahead and clone the original [Hardhat boilerplate repo]. Move to the checked
-out folder and **apply the Sapphire-specific changes** to `hardhat.config.js`
-as [described in the quickstart][quickstart].
-
-Next, install dependencies. The boilerplate project uses
-[pnpm], but `yarn` and `npm` will also work with some modifications
-around workspaces:
-
-```shell npm2yarn
-npm install
-npm install -D @oasisprotocol/sapphire-paratime
-```
-
-Now, you can deploy the contract on the Testnet with the private key of the
-account holding some [TEST tokens]:
-
-```shell
-PRIVATE_KEY="0x..." npx hardhat run scripts/deploy.js --network sapphire-testnet
-```
-
-This will compile the contract and deploy it on the Testnet. In addition
-to the quickstart steps, **the contract address and ABI will also automatically
-be copied over to the `frontend/src/contracts` folder so that the frontend can
-access them!**
-
-:::warning
-
-The contract in the Hardhat boilerplate is ERC-20-compatible and emits the
-`transfer` event. If your wish to preserve confidentiality, you can comment
-out [line 66]. Read [the guide](guide.mdx#contract-logs) to learn more.
-
-:::
-
-## Signing Sapphire Calls and Transactions in Browser
-
-Now, let's explore the frontend of our dApp. Begin by moving into the
-`frontend` folder and install dependencies:
-
-```shell npm2yarn
-npm install
-npm install -D @oasisprotocol/sapphire-paratime
-```
-
-The main frontend logic is stored in `frontend/src/components/Dapp.js`. Apply
-the following changes:
-
-```diff title="frontend/src/components/Dapp.js"
---- a/hardhat-boilerplate/frontend/src/components/Dapp.js
-+++ b/hardhat-boilerplate/frontend/src/components/Dapp.js
-@@ -2,6 +2,7 @@
-
- // We'll use ethers to interact with the Ethereum network and our contract
- import { ethers } from "ethers";
-+import * as sapphire from '@oasisprotocol/sapphire-paratime';
-
- // We import the contract's artifacts and address here, as we are going to be
- // using them with ethers
-@@ -22,7 +23,7 @@
- // This is the Hardhat Network id that we set in our hardhat.config.js.
- // Here's a list of network ids https://docs.metamask.io/guide/ethereum-provider.html#properties
- // to use when deploying to other networks.
--const HARDHAT_NETWORK_ID = '1337';
-+const HARDHAT_NETWORK_ID = '23295'; // Sapphire Testnet
-
- // This is an error code that indicates that the user canceled a transaction
- const ERROR_CODE_TX_REJECTED_BY_USER = 4001;
-@@ -225,14 +226,20 @@
-
- async _initializeEthers() {
- // We first initialize ethers by creating a provider using window.ethereum
-- this._provider = new ethers.providers.Web3Provider(window.ethereum);
-+ this._provider = sapphire.wrap(new ethers.providers.Web3Provider(window.ethereum));
-
-- // Then, we initialize the contract using that provider and the token's
-- // artifact. You can do this same thing with your contracts.
-+ // Then, we initialize two contract instances:
-+ // - _token: Used for eth_calls (e.g. balanceOf, name, symbol)
-+ // - _tokenWrite: Used for on-chain transactions (e.g. transfer)
- this._token = new ethers.Contract(
- contractAddress.Token,
- TokenArtifact.abi,
-- this._provider.getSigner(0)
-+ this._provider,
-+ );
-+ this._tokenWrite = new ethers.Contract(
-+ contractAddress.Token,
-+ TokenArtifact.abi,
-+ sapphire.wrap(new ethers.providers.Web3Provider(window.ethereum).getSigner())
- );
- }
-
-@@ -294,7 +301,7 @@
-
- // We send the transaction, and save its hash in the Dapp's state. This
- // way we can indicate that we are waiting for it to be mined.
-- const tx = await this._token.transfer(to, amount);
-+ const tx = await this._tokenWrite.transfer(to, amount);
- this.setState({ txBeingSent: tx.hash });
-
- // We use .wait() to wait for the transaction to be mined. This method
-@@ -360,8 +367,8 @@
- return true;
- }
-
-- this.setState({
-- networkError: 'Please connect Metamask to Localhost:8545'
-+ this.setState({
-+ networkError: 'Please connect to Sapphire ParaTime Testnet'
- });
-
- return false;
-```
-
-Beside the obvious change to the chain ID and wrapping ethers.js objects with the
-Sapphire wrapper you can notice that we initialized **two** contract
-instances:
-
-- `this._token` object will be used for unsigned eth calls. This is the
- Hardhat method of [setting `from` transaction field to all zeros]
- [guide-transaction-calls] in order to avoid Metamask signature popups.
- Although the call is unsigned, **it is still encrypted** with the
- corresponding runtime key to preserve confidentiality.
-- `this._tokenWrite` object will be used for signed calls and transactions. The
- user will be prompted by Metamask for the signature. Both β calls and
- transactions β will be encrypted.
-
-## Trying it
-
-Start the frontend by typing:
-
-```shell npm2yarn
-npm run start
-```
-
-If all goes well the web server will spin up and your browser should
-automatically open `http://localhost:3000`.
-
-![Hardhat boilerplate frontend](../images/sapphire/hardhat-boilerplate-frontend1.png)
-
-Go ahead and connect the wallet. If you haven't done it yet, you will have
-to add the [Sapphire ParaTime Testnet network to your Metamask]
-[sapphire-testnet]. Once connected, the frontend will make an unsigned call to
-the `balanceOf` view and show you the amount of `MHT`s in your selected
-Metamask account.
-
-![MHT balance of your account](../images/sapphire/hardhat-boilerplate-frontend2.png)
-
-Next, let's transfer some `MHT`s. Fill in the amount, the address and hit the
-*Transfer* button. Metamask will show you the popup to sign and submit the
-transfer transaction. Once confirmed, Metamask will both **sign and encrypt** the transaction.
-
-![Sign and encrypt the transfer transaction](../images/sapphire/hardhat-boilerplate-frontend3.png)
-
-Once the transaction is processed, you will get a notification from Metamask
-and the balance in the dApp will be updated.
-
-:::tip
-
-If you commented out `emit Transfer(...)`, the transfer of `MHT`s would have
-been completely confidential. In the example above, the [following transaction]
-[block explorer] was generated. Go ahead and check your transaction on the
-block explorer too, to make sure no sensitive data was leaked!
-
-:::
-
-Congratulations, you successfully implemented your first truly confidential
-dApp which runs in the browser and wraps Metamask to both sign and encrypt the
-transactions!
-
-Should you have any questions or ideas to share, feel free to reach out to us
-on [discord and other social media channels][social-media].
-
-:::info Example
-
-You can download a full working example from the [Sapphire ParaTime examples]
-repository.
-
-:::
-
-[block explorer]: https://testnet.explorer.sapphire.oasis.dev/tx/0x3303dea5d48291d1564cad573f21fc71fcbdc2b862e17e056287fd9207e3bc53
-[guide-transaction-calls]: guide.mdx#transactions--calls
-[Hardhat boilerplate repo]: https://github.com/NomicFoundation/hardhat-boilerplate
-[Hardhat boilerplate]: https://hardhat.org/tutorial/boilerplate-project
-[Hardhat tutorial]: https://hardhat.org/tutorial
-[line 66]: https://github.com/NomicFoundation/hardhat-boilerplate/blob/13bd712c1285b2de572f14d20e6a750ae08565c0/contracts/Token.sol#L66
-[quickstart]: quickstart.mdx#add-the-sapphire-testnet-to-hardhat
-[sapphire-testnet]: ./README.mdx#testnet
-[Sapphire ParaTime examples]: https://github.com/oasisprotocol/sapphire-paratime/tree/main/examples/hardhat-boilerplate
-[social-media]: ../../get-involved/README.md#social-media-channels
-[pnpm]: https://pnpm.io
-[TEST tokens]: quickstart.mdx#get-some-sapphire-testnet-tokens
diff --git a/docs/dapp/sapphire/browser.md b/docs/dapp/sapphire/browser.md
new file mode 120000
index 0000000000..1b1dfca35c
--- /dev/null
+++ b/docs/dapp/sapphire/browser.md
@@ -0,0 +1 @@
+../../../external/sapphire-paratime/docs/browser.md
\ No newline at end of file
diff --git a/docs/dapp/sapphire/gasless.md b/docs/dapp/sapphire/gasless.md
deleted file mode 100644
index 775690d504..0000000000
--- a/docs/dapp/sapphire/gasless.md
+++ /dev/null
@@ -1,388 +0,0 @@
----
-description: Submitting transactions without paying for fees
----
-
-# Gasless Transactions
-
-When you submit a transaction to a blockchain, you need to pay certain fee
-(called *gas* in Ethereum jargon). Since only the transactions with the highest
-fee will be included in the block, this mechanism effectively prevents denial
-of service attacks on the network. On the other hand, paying for gas requires
-from the user that they have certain amount of blockchain-native tokens
-available in their wallet which may not be feasible.
-
-In this chapter we will learn how the user signs and sends their transaction to
-a *relayer*. The relayer then wraps the original signed transaction into a new
-*meta-transaction* (see [ERC-2771] for details), signs it and pays for the
-necessary transaction fees. When the transaction is submitted the on-chain
-recipient contract decodes the meta-transaction, verifies both signatures and
-executes the original transaction.
-
-Oasis Sapphire supports two transaction relaying methods: The **on-chain
-signer** exposes the Oasis-specific contract state encryption functionality
-while the **gas station network** method is a standardized approach known in
-other blockchains as well.
-
-:::caution
-
-The gas station network implementation on Sapphire is still in early beta. Some
-features such as the browser support are not fully implemented yet.
-
-:::
-
-[ERC-2771]: https://eips.ethereum.org/EIPS/eip-2771
-
-## On-Chain Signer
-
-The on-chain signer is a smart contract which receives the user's transaction,
-checks whether the transaction is valid, wraps it into a meta-transaction
-(which includes paying for the transaction fee) and returns it back to the user
-in [EIP-155] format. These steps are executed as a confidential call. Finally,
-the user submits the generated transaction to the network.
-
-![Diagram of the On-Chain Signing](../images/sapphire/gasless-on-chain-signer.svg)
-
-### EIP155Signer
-
-To sign a transaction, the Sapphire's `EIP155Signer` library bundled along the
-`@oasisprotocol/sapphire-contract` package comes with the following helper which
-returns a raw, RLP-encoded, signed transaction ready to be broadcast:
-
-```solidity
-function sign(address publicAddress, bytes32 secretKey, EthTx memory transaction) internal view returns (bytes memory);
-```
-
-`publicAddress` and `secretKey` are the signer's address and their private key
-used to sign a meta-transaction (and pay for the fees). We will store these
-sensitive data inside the encrypted smart contract state together with the
-signer's `nonce` field in the following struct:
-
-```solidity
-struct EthereumKeypair {
- address addr;
- bytes32 secret;
- uint64 nonce;
-}
-```
-
-The last `transaction` parameter in the `sign()` function is the transaction
-encoded in a format based on [EIP-155]. This can either be the original user's
-transaction or a meta-transaction.
-
-### Gasless Proxy Contract
-
-The following snippet is a complete *Gasless* contract for wrapping the user's
-transactions (`makeProxyTx()`) and executing them (`proxy()`). The signer's
-private key containing enough balance to cover transaction fees should be
-provided in the constructor.
-
-```solidity
-import {EIP155Signer} from "@oasisprotocol/sapphire-contracts/contracts/EIP155Signer.sol";
-
-struct EthereumKeypair {
- address addr;
- bytes32 secret;
- uint64 nonce;
-}
-
-struct EthTx {
- uint64 nonce;
- uint256 gasPrice;
- uint64 gasLimit;
- address to;
- uint256 value;
- bytes data;
- uint256 chainId;
-}
-
-// Proxy for gasless transaction.
-contract Gasless {
- EthereumKeypair private kp;
-
- function setKeypair(EthereumKeypair memory keypair) external payable {
- kp = keypair;
- }
-
- function makeProxyTx(address innercallAddr, bytes memory innercall)
- external
- view
- returns (bytes memory output)
- {
- bytes memory data = abi.encode(innercallAddr, innercall);
-
- // Call will invoke proxy().
- return
- EIP155Signer.sign(
- kp.addr,
- kp.secret,
- EIP155Signer.EthTx({
- nonce: kp.nonce,
- gasPrice: 100_000_000_000,
- gasLimit: 250000,
- to: address(this),
- value: 0,
- data: abi.encodeCall(this.proxy, data),
- chainId: block.chainid
- })
- );
- }
-
- function proxy(bytes memory data) external payable {
- (address addr, bytes memory subcallData) = abi.decode(
- data,
- (address, bytes)
- );
- (bool success, bytes memory outData) = addr.call{value: msg.value}(
- subcallData
- );
- if (!success) {
- // Add inner-transaction meaningful data in case of error.
- assembly {
- revert(add(outData, 32), mload(outData))
- }
- }
- kp.nonce += 1;
- }
-}
-```
-
-:::tip
-
-The snippet above only runs on Sapphire Mainnet, Testnet or Localnet.
-`EIP155Signer.sign()` is not supported on other EVM chains.
-
-:::
-
-### Simple Gasless Commenting dApp
-
-Let's see how we can implement on-chain signer for a gasless commenting dApp
-like this:
-
-```solidity
-contract CommentBox {
- string[] public comments;
-
- function comment(string memory commentText) external {
- comments.push(commentText);
- }
-}
-```
-
-Then, the TypeScript code on a client side for submitting a comment in a gasless
-fashion would look like this:
-
-```typescript
-const CommentBox = await ethers.getContractFactory("CommentBox");
-const commentBox = await CommentBox.deploy();
-const Gasless = await ethers.getContractFactory("Gasless");
-const gasless = await Gasless.deploy();
-
-// Set the keypair used to sign the meta-transaction.
-await gasless.setKeypair({
- addr: "70997970C51812dc3A010C7d01b50e0d17dc79C8",
- secret: Uint8Array.from(Buffer.from("59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d", 'hex')),
- nonce: 0,
-});
-
-const innercall = commentBox.interface.encodeFunctionData('comment', ['Hello, free world!']);
-const tx = await gasless.makeProxyTx(commentBox.address, innercall);
-
-const plainProvider = new ethers.providers.JsonRpcProvider(ethers.provider.connection);
-const plainResp = await plainProvider.sendTransaction(tx);
-
-const receipt = await ethers.provider.waitForTransaction(plainResp.hash);
-if (!receipt || receipt.status != 1) throw new Error('tx failed');
-```
-
-:::info Example
-
-You can download a complete on-chain signer example based on the above snippets
-from the [Sapphire ParaTime examples] repository.
-
-:::
-
-[Sapphire ParaTime examples]:
- https://github.com/oasisprotocol/sapphire-paratime/tree/main/examples/onchain-signer
-
-### Gasless Proxy in Production
-
-The snippets above have shown how the on-chain signer can generate and sign a
-meta-transaction for arbitrary transaction. In production environment however,
-you must consider the following:
-
-#### Confidentiality
-
-Both the inner- and the meta-transaction are stored on-chain unencrypted. Use
-`Sapphire.encrypt()` and `Sapphire.decrypt()` call on the inner-transaction with
-an encryption key generated and stored inside a confidential contract state.
-
-#### Gas Cost and Gas Limit
-
-The gas cost and the gas limit in our snippet were hardcoded inside the
-contract. Ideally the gas cost should be dynamically adjusted by an oracle and
-the gas limit determined based on the type of transactions. **Never let gas cost
-and limit to be freely defined by the user, since they can drain your relayer's
-account.**
-
-#### Allowed Transactions
-
-Your relayer will probably be used for transactions of a specific contract only.
-One approach is to store the allowed address of the target contract and **only
-allow calls to this contract address**.
-
-#### Access Control
-
-You can either whitelist specific addresses of the users in the relayer contract
-or implement the access control in the target contract. In the latter case, the
-relayer's `makeProxyTx()` should simulate the execution of the inner-transaction
-and generate the meta-transaction only if it inner-transaction succeeded.
-
-#### Multiple Signers
-
-Only one transaction per block can be relayed by the same signer since the order
-of the transactions is not deterministic and nonces could mismatch. To overcome
-this, relayer can randomly pick a signer from the **pool of signers**. When the
-transaction is relayed, don't forget to reimburse the signer of the transaction!
-
-:::info Example
-
-All the above points are considered in the [Demo Voting dApp][demo-voting].
-You can explore the code and also try out a deployed gasless version of the
-voting dApp on the [Oasis Playground site][demo-voting-playground]. The access
-control list is configured so that anyone can vote on any poll and only poll
-creators can close the poll.
-
-:::
-
-[demo-voting]: https://github.com/oasisprotocol/demo-voting
-[demo-voting-playground]: https://playground.oasis.io/demo-voting
-[dao-opl]: ../opl/host.md
-[EIP-155]: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md
-
-## Gas Station Network
-
-[Gas Station Network](https://docs.opengsn.org) (GSN) was adapted to work with
-Sapphire in a forked `@oasislabs/opengsn-cli` package. The diagram below
-illustrates a flow for signing a transaction by using a GSN[^1].
-
-![Diagram of the Gas Station Network Flow](../images/sapphire/gasless-gsn-flow.jpg)
-
-[^1]: The GSN flow diagram is courtesy of [OpenGSN documentation][opengsn-docs].
-
-[opengsn-docs]: https://github.com/opengsn/docs
-
-### Package Install
-
-Starting with an empty folder, let us install the
-[Oasis fork of the GSN command line tool](https://github.com/oasislabs/gsn) by
-using the following commands:
-
-```shell npm2yarn
-npm init
-npm install -D @oasislabs/opengsn-cli
-```
-
-Next, we will export our hex-encoded private key (**without** the leading `0x`)
-for deploying the gas station network as an environment variable:
-
-```shell
-export PRIVATE_KEY=...
-```
-
-### Deploy GSN
-
-Deploy GSN relaying contracts along with the test paymaster using a
-test token. Use the address of your account as `--burnAddress` and
-`--devAddress` parameters:
-
-```shell
-npx gsn deploy --network sapphire-testnet --burnAddress 0xfA3AC9f65C9D75EE3978ab76c6a1105f03156204 --devAddress 0xfA3AC9f65C9D75EE3978ab76c6a1105f03156204 --testToken true --testPaymaster true --yes --privateKeyHex $PRIVATE_KEY
-```
-
-After the command finishes successfully, you should find the addreses of
-deployed contracts at the end:
-
-```
- Deployed TestRecipient at address 0x594cd6354b23A5200a57355072E2A5B15354ee21
-
- RelayHub: 0xc4423AB6133B06e4e60D594Ac49abE53374124b3
- RelayRegistrar: 0x196036FBeC1dA841C60145Ce12b0c66078e141E6
- StakeManager: 0x6763c3fede9EBBCFbE4FEe6a4DE6C326ECCdacFc
- Penalizer: 0xA58A0D302e470490c064EEd5f752Df4095d3A002
- Forwarder: 0x59001d07a1Cd4836D22868fcc0dAf3732E93be81
- TestToken (test only): 0x6Ed21672c0c26Daa32943F7b1cA1f1d0ABdbac66
- Paymaster (Default): 0x8C06261f58a024C958d42df89be7195c8690008d
-```
-
-
-### Start GSN Relay Server
-
-Now we are ready to start our own relay server by using the following command.
-Use the newly deployed:
-
-- `RelayHub` address for `--relayHubAddress`,
-- `TestToken` address for `--managerStakeTokenAddress`,
-- address of your account for `--owner-address`
-
-```shell
-npx gsn relayer-run --relayHubAddress 0xc4423AB6133B06e4e60D594Ac49abE53374124b3 --managerStakeTokenAddress 0x6Ed21672c0c26Daa32943F7b1cA1f1d0ABdbac66 --ownerAddress '0xfA3AC9f65C9D75EE3978ab76c6a1105f03156204' --ethereumNodeUrl 'https://testnet.sapphire.oasis.dev' --workdir .
-```
-
-### Fund and Register GSN Relay Server
-
-The first thing is to fund your relay server so that it has enough native
-tokens to pay for others' transactions. Let's fund the paymaster with
-**5 tokens**. Use the `RelayHub` and `Paymaster` addresses for `--hub`
-and `--paymaster` values:
-
-```shell
-npx gsn paymaster-fund --network sapphire-testnet --hub 0xc4423AB6133B06e4e60D594Ac49abE53374124b3 --paymaster 0x8C06261f58a024C958d42df89be7195c8690008d --privateKeyHex $PRIVATE_KEY --amount 5000000000000000000
-```
-
-You can check the balance of the paymaster by running:
-
-```shell
-npx gsn paymaster-balance --network sapphire-testnet --hub 0xc4423AB6133B06e4e60D594Ac49abE53374124b3 --paymaster 0x8C06261f58a024C958d42df89be7195c8690008d
-```
-
-Next, we need to register the relay server with the your desired `relayUrl` by
-staking the `token` the relayHub requires.
-
-```shell
-npx gsn relayer-register --network sapphire-testnet --relayUrl 'http://localhost:8090' --token 0x6Ed21672c0c26Daa32943F7b1cA1f1d0ABdbac66 --wrap true --privateKeyHex $PRIVATE_KEY
-```
-
-After this step, your relay server should be ready to take incoming relay
-requests and forward them to the relay hub on Sapphire Testnet.
-
-### Send Testing Relayed Requests:
-
-We can test whether a relayed request can be forwarded and processed correctly.
-Scroll up to find the GSN deployment response and use the following parameters:
-- `Forwarder` as `--to`,
-- `Paymaster` as `--paymaster`,
-- your account address as `--from`
-
-Parameters matching our deployment would be:
-
-```shell
-npx gsn send-request --network sapphire-testnet --abiFile 'node_modules/@oasislabs/opengsn-cli/dist/compiled/TestRecipient.json' --method emitMessage --methodParams 'hello world!' --to 0x594cd6354b23A5200a57355072E2A5B15354ee21 --paymaster 0x8C06261f58a024C958d42df89be7195c8690008d --privateKeyHex $PRIVATE_KEY --from 0xfA3AC9f65C9D75EE3978ab76c6a1105f03156204 --gasLimit 150000 --gasPrice 100
-```
-
-:::info
-
-More detailed explanations of these GSN commands and parameters can be found on
-the [upstream OpenGSN website](https://docs.opengsn.org/javascript-client/gsn-helpers.html).
-
-:::
-
-### Writing a GSN-enabled Smart Contract
-
-First, install the OpenGSN contracts package:
-
-```shell npm2yarn
-npm install -D @opengsn/contracts@3.0.0-beta.2
-```
-
-Then follow the remainder of the steps from the
-[upstream OpenGSN docs](https://docs.opengsn.org/contracts/#receiving-a-relayed-call).
diff --git a/docs/dapp/sapphire/gasless.md b/docs/dapp/sapphire/gasless.md
new file mode 120000
index 0000000000..1ae10d0a76
--- /dev/null
+++ b/docs/dapp/sapphire/gasless.md
@@ -0,0 +1 @@
+../../../external/sapphire-paratime/docs/gasless.md
\ No newline at end of file
diff --git a/docs/dapp/sapphire/guide.mdx b/docs/dapp/sapphire/guide.mdx
deleted file mode 100644
index b7689b5aae..0000000000
--- a/docs/dapp/sapphire/guide.mdx
+++ /dev/null
@@ -1,390 +0,0 @@
----
-description: Guide to creating secure dApps on Sapphire
----
-
-import DocCard from '@theme/DocCard';
-import {findSidebarItem} from '@site/src/sidebarUtils';
-
-# Guide
-
-This page mainly describes the differences between Sapphire and Ethereum
-since there are a number of excellent tutorials on developing for Ethereum.
-If you don't know where to begin, the [Hardhat tutorial], [Solidity docs], and
-[Emerald dApp tutorial] are great places to start. You can continue following
-this guide once you've set up your development environment and have deployed
-your contract to a non-confidential EVM network (e.g., Ropsten, Emerald).
-
-
-[Hardhat tutorial]: https://hardhat.org/tutorial
-[Solidity docs]: https://docs.soliditylang.org/en/v0.8.15/solidity-by-example.html
-[Emerald dApp tutorial]: ../emerald/writing-dapps-on-emerald.mdx
-
-## Oasis Consensus Layer and Sapphire ParaTime
-
-The Oasis Network consists of the consensus layer and a number of ParaTimes.
-ParaTimes are independent replicated state machines that settle transactions
-using the consensus layer (to learn more, check the [Oasis Network Overview]
-[overview chapter]). Sapphire is a ParaTime which implements the Ethereum
-Virtual Machine (EVM).
-
-The minimum and also expected block time in Sapphire is **6 seconds**. Any
-Sapphire transaction will require at least this amount of time to be executed,
-and probably no more.
-
-ParaTimes, Sapphire included, are not allowed to directly access your tokens stored
-in consensus layer accounts. You will need to _deposit_ tokens from your consensus
-account to Sapphire. Consult the [How to transfer ROSE into an EVM ParaTime]
-[how-to-deposit-rose] chapter to learn more.
-
-
-[overview chapter]: ../../general/oasis-network/README.mdx
-[how-to-deposit-rose]: ../../general/manage-tokens/how-to-transfer-rose-into-paratime.mdx
-[Testnet faucet]: https://faucet.testnet.oasis.dev/
-
-## Testnet and Mainnet
-
-Sapphire is deployed on both Testnet and Mainnet. The Testnet should be
-considered unstable software and may have its state wiped at any time. As
-the name implies, only use the Testnet for testing unless you're testing how
-angry your users get when state is wiped.
-
-:::danger Never deploy production services on Testnet
-
-Because Testnet state can be wiped in the future, you should **never** deploy a
-production service on the Testnet! Just don't do it!
-
-Also note that while the Testnet does use actual TEEs, due to experimental
-software and different security parameters, **confidentiality of Sapphire on the
-Testnet is not guaranteed** -- all transactions and state published on the
-Sapphire Testnet should be considered public.
-
-:::
-
-:::tip
-
-For testing purposes, visit our [Testnet faucet] to obtain some TEST which you
-can then use on the Sapphire Testnet to pay for gas fees. The faucet supports
-sending TEST both to your consensus layer address or to your address inside the
-ParaTime.
-
-:::
-
-[network-parameters]: ../../node/mainnet/README.md
-[Testnet]: ../../node/testnet/README.md
-
-## Sapphire vs Ethereum
-
-Sapphire is generally compatible with Ethereum, the EVM, and all of the
-user and developer tooling that you already use. There are a few breaking changes,
-but we think that you'll like them:
-
-* Contract state is only visible to the contract that wrote it. With respect
- to the contract API, it's as if all state variables are declared as `private`, but
- with the further restriction that not even full nodes can read the values. Public or
- access-controlled values are provided instead through explicit getters.
-* Transactions and calls are end-to-end encrypted into the contract. Only the caller
- and the contract can see the data sent to/received from the ParaTime. This ends up
- defeating most of the utility of block explorers, however.
-* The `from` address using of calls is derived from a signature attached to the call.
- Unsigned calls have their sender set to the zero address. This allows contract authors
- to write getters that release secrets to authenticated callers, but without
- requiring a transaction to be posted on-chain.
-
-In addition to confidentiality, you get a few extra benefits including the ability to generate private
-entropy, and make signatures on-chain. An example of a dApp that uses both is a HSM contract
-that generates an Ethereum wallet and signs transactions sent to it via transactions.
-
-Otherwise Sapphire is like Emerald, which is like a fast, cheap Ethereum.
-
-## Integrating Sapphire
-
-Once ROSE tokens are [deposited into Sapphire], it should be painless for users to begin
-using dApps. To achieve this ideal user experience, we have to modify the dApp a little,
-but it's made simple by our compatibility library, [@oasisprotocol/sapphire-paratime].
-
-There are compatibility layers in other languages, which may be found in [the repo].
-
-
-[deposited into Sapphire]: ../../general/manage-tokens/how-to-transfer-rose-into-paratime.mdx
-[@oasisprotocol/sapphire-paratime]: https://www.npmjs.com/package/@oasisprotocol/sapphire-paratime
-[the repo]: https://github.com/oasisprotocol/sapphire-paratime/tree/main/clients
-
-## Writing Secure dApps
-
-### Wallets
-
-Sapphire is compatible with popular self-custodial wallets including MetaMask,
-Ledger, Brave, and so forth. You can also use libraries like Web3.js and Ethers
-to create programmatic wallets. In general, if it generates secp256k1 signatures,
-it'll work just fine.
-
-### Languages & Frameworks
-
-Sapphire is programmable using any language that targets the EVM, such as Solidity
-or Vyper. If you prefer to use an Ethereum framework like Hardhat or Foundry, you
-can also use those with Sapphire; all you need to do is set your Web3 gateway URL.
-You can find the details of the Oasis Sapphire Web3 gateway
-[here](/dapp/sapphire#web3-gateway).
-
-### Transactions & Calls
-
-![Client, Key Manager, Compute Node diagram](../../general/images/architecture/client-km-compute.svg)
-
-The figure above illustrates the flow of a confidential smart contract
-*transaction* executed on the Sapphire ParaTime.
-
-Transactions and calls must be encrypted and signed for maximum security.
-You can use the [@oasisprotocol/sapphire-paratime] JS package to make your life
-easy. It'll handle cryptography and signing for you.
-
-You should be aware that taking actions based on the value of private data may
-leak the private data through side channels like time spent and gas use. If you
-need to branch on private data, you should in most cases ensure that both
-branches exhibit similar time/gas and storage patterns.
-
-You can also make confidential smart contract *calls* on Sapphire. If you
-use `msg.sender` for access control in your contract, the call **must be
-signed**, otherwise `msg.sender` will be zeroed. On the other hand, set the
-`from` address to all zeros, if you want to avoid annoying signature popups in
-the user's wallet for calls that do not need to be signed. The JS library will
-do this for you.
-
-:::note
-
-Inside the smart contract code, there is no way of knowing whether the
-client's call data were originally encrypted or not.
-
-:::
-
-Detailed confidential smart contract transaction flow on Sapphire
- Detailed confidential smart contract call flow on Sapphire
-
- -
- -In this tutorial, you will build and deploy a unique dApp that requires -confidentiality to work. By the end of the tutorial, you should feel -comfortable setting up your Eth development environment to target Sapphire, -and know how and when to use confidentiality. - -The expected completion time of this tutorial is 15 minutes. - -:::info Sunsetting Truffle - -Per Consensys [announcement], Oasis will no longer support Truffle as of -2023-10-05 and encourage immediate [migration] to Hardhat. Please see our -repository for the archived Truffle [tutorial] and the deprecated [example]. - -::: - -[announcement]: https://consensys.io/blog/consensys-announces-the-sunset-of-truffle-and-ganache-and-new-hardhat -[migration]: https://trufflesuite.com/docs/truffle/how-to/migrate-to-hardhat/ -[tutorial]: https://github.com/oasisprotocol/docs/blob/2f4a1a3c217b82687ab9440bf051762ae369ed45/docs/dapp/sapphire/quickstart.mdx -[example]: https://github.com/oasisprotocol/sapphire-paratime/tree/3a85e42e6c1cc090c28a521cf7df6353aa8a30c8/examples/truffle - - -## Create a Sapphire-Native dApp - -Porting an existing Eth app is cool, and will provide benefits such as -protection against MEV. -However, starting from scratch with confidentiality in mind can unlock some -really novel dApps and provide a [higher level of security]. - -One simple-but-useful dApp that takes advantage of confidentiality is a -[dead person's switch] that reveals a secret (let's say the encryption key to a -data trove) if the operator fails to re-up before too long. -Let's make it happen! - -[higher level of security]: guide.mdx#writing-secure-dapps -[dead person's switch]: https://en.wikipedia.org/wiki/Dead_man%27s_switch - -### Init a new Hardhat project - -We're going to use Hardhat, but Sapphire should be compatible with your dev -environment of choice. Let us know if things are not as expected! - -1. Make & enter a new directory -2. `npx hardhat@~2.16.0 init` then create a TypeScript project. -3. Add [`@oasisprotocol/sapphire-hardhat`] as dependency: - - ```shell npm2yarn - npm install -D @oasisprotocol/sapphire-hardhat - ``` - -4. Install `@nomicfoundation/hardhat-toolbox`, TypeScript and other peer - dependencies required by HardHat. - -### Add the Sapphire Testnet to Hardhat - -Open up your `hardhat.config.ts` and drop in these lines. - -```diff -diff --git a/hardhat.config.ts b/hardhat.config.ts -index 414e974..49c95f9 100644 ---- a/hardhat.config.ts -+++ b/hardhat.config.ts -@@ -1,8 +1,19 @@ - import { HardhatUserConfig } from "hardhat/config"; -+import '@oasisprotocol/sapphire-hardhat'; - import "@nomicfoundation/hardhat-toolbox"; - - const config: HardhatUserConfig = { - solidity: "0.8.17", -+ networks: { -+ 'sapphire-testnet': { -+ // This is Testnet! If you want Mainnet, add a new network config item. -+ url: "https://testnet.sapphire.oasis.dev", -+ accounts: process.env.PRIVATE_KEY -+ ? [process.env.PRIVATE_KEY] -+ : [], -+ chainId: 0x5aff, -+ }, -+ }, - }; - - export default config; -``` - -By importing `@oasisprotocol/sapphire-hardhat` at the top of the config file, -**any network config entry corresponding to the Sapphire's chain ID will -automatically be wrapped with Sapphire specifics for encrypting and signing the -transactions**. - -### Get some Sapphire Testnet tokens - -Now for the fun part. We need to configure the Sapphire network and get some tokens. -Hit up the one and only [Oasis Testnet faucet] and select "Sapphire". -Submit the form and be on your way. - -[Oasis Testnet faucet]: https://faucet.testnet.oasis.dev - -### Get the Contract - -This is a Sapphire tutorial and you're already a Solidity expert, so let's not -bore you with explaining the gritty details of the contract. -Start by pasting [Vigil.sol] into `contracts/Vigil.sol`. - -While you're there, also place [run-vigil.ts] into `scripts/run-vigil.ts`. -We'll need that later. - -[Vigil.sol]: https://github.com/oasisprotocol/sapphire-paratime/blob/main/examples/hardhat/contracts/Vigil.sol -[run-vigil.ts]: https://github.com/oasisprotocol/sapphire-paratime/blob/main/examples/hardhat/scripts/run-vigil.ts - -#### Vigil.sol, the interesting parts - -The key state variables are: - -```solidity - SecretMetadata[] public _metas; - bytes[] private _secrets; -``` - -* `_metas` is marked with `public` visibility, so despite the state itself being - encrypted and not readable directly, Solidity will generate a getter that will - do the decryption for you. -* `_secrets` is `private` and therefore truly secret; only the contract can - access the data contained in this mapping. - -And the methods we'll care most about are - -* `createSecret`, which adds an entry to both `_metas` and `_secrets`. -* `revealSecret`, which acts as an access-controlled getter for the data - contained with `_secrets`. Due to trusted execution and confidentiality, the - only way that the secret will get revealed is if execution proceeds all the - way to the end of the function and does not revert. - -The rest of the methods are useful if you actually intended to use the contract, -but they demonstrate that developing for Sapphire is essentially the same as for -Ethereum. -You can even write tests against the Hardhat network and use Hardhat plugins. - -### Run the Contract - -And to wrap things up, we'll put `Vigil` through its paces. -First, let's see what's actually going on. - -After deploying the contract, we can create a secret, check that it's not -readable, wait a bit, and then check that it has become readable. -Pretty cool if you ask me! - -Anyway, make it happen by running - -```shell -PRIVATE_KEY="0x..." npx hardhat run scripts/run-vigil.ts --network sapphire-testnet -``` - -And if you see something like the following, you'll know you're well on the road -to deploying confidential dApps on Sapphire. - -``` -Vigil deployed to: 0x74dC4879B152FDD1DDe834E9ba187b3e14f462f1 -Storing a secret in 0x13125d868f5fb3cbc501466df26055ea063a90014b5ccc8dfd5164dc1dd67543 -Checking the secret -failed to fetch secret: reverted: not expired -Waiting... -Checking the secret again -The secret ingredient is brussels sprouts -``` - -## All done! - -Congratulations, you made it through the Sapphire tutorial! If you have any -questions, please check out the [guide] and join the discussion on the -[#sapphire-paratime Discord channel][social-media]. - -Best of luck on your future forays into confidentiality! - -:::info Example - -Visit the Sapphire ParaTime repository to download the [Hardhat][hardhat-example] -example of this quickstart. - -::: - -:::info Example - -If your project involves building a web frontend, we recommend that you check -out the official [Oasis starter] files. - -[Oasis starter]: https://github.com/oasisprotocol/demo-starter - -::: - -## See also - -