diff --git a/docs/dapp/images/tools/sourcify1.png b/docs/dapp/images/tools/sourcify1.png
new file mode 100644
index 0000000000..b6dba793af
Binary files /dev/null and b/docs/dapp/images/tools/sourcify1.png differ
diff --git a/docs/dapp/images/tools/sourcify2.png b/docs/dapp/images/tools/sourcify2.png
new file mode 100644
index 0000000000..42b201dfe2
Binary files /dev/null and b/docs/dapp/images/tools/sourcify2.png differ
diff --git a/docs/dapp/images/tools/sourcify3.png b/docs/dapp/images/tools/sourcify3.png
new file mode 100644
index 0000000000..ac32713f68
Binary files /dev/null and b/docs/dapp/images/tools/sourcify3.png differ
diff --git a/docs/dapp/tools/README.mdx b/docs/dapp/tools/README.mdx
index 4c5596ded3..ffb1be2e34 100644
--- a/docs/dapp/tools/README.mdx
+++ b/docs/dapp/tools/README.mdx
@@ -9,8 +9,8 @@ developers using [Remix] (*unencrypted transactions only*), [Sourcify],
[Discord][discord] if you are using a tool that has problems integrating with
Oasis.
-[Remix]: remix.md
-[Sourcify]: /dapp/sapphire/verification
+[Remix]: ./remix.md
+[Sourcify]: ./verification.md
[localnet]: ./localnet.mdx
[Band]: ./band.md
[discord]: https://oasis.io/discord
@@ -18,9 +18,9 @@ Oasis.
## See also
diff --git a/docs/dapp/tools/abi-playground.md b/docs/dapp/tools/abi-playground.md
index 8150f3d0f7..4c1919b197 100644
--- a/docs/dapp/tools/abi-playground.md
+++ b/docs/dapp/tools/abi-playground.md
@@ -82,6 +82,6 @@ Should you have any other problems or questions, do not hesitate to share them w
[abi-playground]: https://abi-playground.oasis.io/
[Explorer]: https://explorer.oasis.io/
-[localnet]: /dapp/tools/localnet
-[verification]: /dapp/sapphire/verification
+[localnet]: ./localnet.mdx
+[verification]: ./verification.md
[discord]: https://oasis.io/discord
diff --git a/docs/dapp/tools/verification.md b/docs/dapp/tools/verification.md
new file mode 100644
index 0000000000..79acf902f7
--- /dev/null
+++ b/docs/dapp/tools/verification.md
@@ -0,0 +1,143 @@
+---
+description: Verifying deployed contracts
+---
+
+# Contract Verification
+
+[Sourcify] is the preferred service for the [verification of smart
+contracts][ethereum-contract-verify] deployed on Sapphire. Make sure you have
+the **address of each deployed contract** available (your deployment scripts
+should report those) and the **contracts JSON metadata file** generated when
+compiling contracts (Hardhat stores it inside the `artifacts/build-info` folder
+and names it as a 32-digit hex number). If your project contains multiple
+contracts, you will need to verify each contract separately.
+
+:::warning Contract deployment encryption
+
+**Do not deploy your contract with an encrypted contract deployment transaction,
+if you want to verify it.** For example, if your `hardhat.config.ts`
+or deployment script contains `import '@oasisprotocol/sapphire-hardhat'` or
+`import '@oasisprotocol/sapphire-paratime'` lines at the beginning, you should
+comment those out for the deployment.
+
+Verification services will try to match the contract deployment transaction code
+with the one in the provided contract's metadata. Because the transaction was
+encrypted with an ephemeral ParaTime key, the verification service will not be
+able to decrypt it. Some services may extract the contract's bytecode from the
+chain directly by calling `eth_getCode` RPC, but this will not work correctly
+for contracts with immutable variables.
+
+:::
+
+## Verification with Hardhat
+
+If you use Hardhat to deploy your contracts, consider using the
+[hardhat-verify] plugin.
+
+To configure it, add the following to your `hardhat.config.ts` file:
+
+```js title="hardhat.config.ts"
+ etherscan: {
+ // Enabled by default (not supported on Sapphire)
+ enabled: false
+ },
+ sourcify: {
+ // Disabled by default
+ // Doesn't need an API key
+ enabled: true
+ }
+```
+
+Now you can use the `verify` task:
+
+```shell
+pnpm hardhat verify --network sapphire-testnet DEPLOYED_CONTRACT_ADDRESS "Constructor argument 1"
+```
+
+[hardhat-verify]: https://hardhat.org/hardhat-runner/plugins/nomicfoundation-hardhat-verify
+
+## Verification with Foundry
+
+[Foundry] natively supports Sourcify verification. To use **Sourcify** as a
+provider, specify it with the `--verifier` option.
+
+Example:
+
+```shell
+forge verify-contract
src/MyToken.sol:MyToken --verifier sourcify
+```
+
+:::info
+To see all available options and more examples visit the
+**[verify-contract page of foundry][foundry-verify]** or the **[sourcify docs]**
+:::
+
+[Foundry]: https://book.getfoundry.sh
+[foundry-verify]: https://book.getfoundry.sh/reference/forge/forge-verify-contract
+[sourcify docs]: https://docs.sourcify.dev/docs/how-to-verify/#foundry
+
+## Verification on Sourcify
+
+To verify maunally a contract deployed on Sapphire Mainnet or Testnet on Sourcify:
+
+1. Visit the [Sourcify] website and hit the "VERIFY CONTRACT" button.
+
+ ![Sourcify website](../images/tools/sourcify1.png)
+
+2. Upload the contracts JSON metadata file. (Sourcify can parse the Hardhat
+ .json output file under `artifacts/build-info`)
+
+ ![Sourcify: Upload metadata JSON file](../images/tools//sourcify2.png)
+
+ :::tip Store your metadata files
+
+ For production deployments, it is generally a good idea to **archive your
+ contract metadata JSON file** since it is not only useful for the
+ verification, but contains a copy of all the source files, produced bytecode,
+ an ABI, compiler and other relevant contract-related settings that may be
+ useful in the future. Sourcify will store the metadata file for you and will
+ even make it available via IPFS, but it is still a good idea to store it
+ yourself.
+
+ :::
+
+3. Sourcify will decode the metadata and prepare a list of included contracts on
+ the right. Enter the address of the specific contract and select the "Oasis
+ Sapphire" or "Oasis Sapphire Testnet" chain for Mainnet or Testnet
+ accordingly. If your contract assigns any immutable variables in the
+ constructor, you will also need to correctly fill those out under the "More
+ Inputs (optional)" panel. Finally, click on the "Verify" button.
+
+ ![Sourcify: Verify contract](../images/tools/sourcify3.png)
+
+4. If everything goes well, you will get a *Perfect match* notice. Your
+ contract is now verified. Congratulations!
+
+In case of a *Partial match*, the contracts metadata JSON differs from the one
+used for deployment although the compiled contract bytecode matched. Make sure
+the source code `.sol` file of the contract is the same as the one used during the
+deployment (including the comments, variable names and source code file
+names) and use the same version of Hardhat and solc compiler.
+
+:::info
+You can also explore all verification methods on Sourcify by reading the
+[official Sourcify contract verification instructions][sourcify-contract-verify].
+:::
+
+[Sourcify]: https://sourcify.dev/
+[sourcify-contract-verify]: https://docs.sourcify.dev/docs/how-to-verify/
+[ethereum-contract-verify]: https://ethereum.org/en/developers/docs/smart-contracts/verifying/
+
+## Troubleshooting
+
+### Etherscan error with hardhat-verify
+
+ - **Cause**: hardhat-verify tries to verify a contract on Etherscan for an unsupported network.
+ - **Solution**: Disable Etherscan verification with
+ ```
+ etherscan: {
+ // Enabled by default (not supported on Sapphire)
+ enabled: false
+ },
+ ```
+
diff --git a/redirects.ts b/redirects.ts
index 29a6cf214c..7251c9cf3b 100644
--- a/redirects.ts
+++ b/redirects.ts
@@ -284,6 +284,10 @@ export const redirectsOptions: Options = {
{
to: '/dapp/tools/band',
from: '/dapp/emerald/integrating-band-oracle-smart-contract', // #907 Move Band oracle to Tools section
+ },
+ {
+ to: '/dapp/tools/verification',
+ from: '/dapp/sapphire/verification', // #1054 Move Sapphire verification to Tools section
}
],
createRedirects(existingPath) {
diff --git a/sidebarDapp.ts b/sidebarDapp.ts
index 871b91165d..782f3a36af 100644
--- a/sidebarDapp.ts
+++ b/sidebarDapp.ts
@@ -20,7 +20,6 @@ export const sidebarDapp: SidebarsConfig = {
'dapp/sapphire/guide',
'dapp/sapphire/browser',
'dapp/sapphire/authentication',
- 'dapp/sapphire/verification',
'dapp/sapphire/gasless',
'dapp/sapphire/addresses',
'dapp/sapphire/deployment',
@@ -123,9 +122,10 @@ export const sidebarDapp: SidebarsConfig = {
},
items: [
'dapp/tools/abi-playground',
+ 'dapp/tools/verification',
'dapp/tools/band',
'dapp/tools/localnet',
- 'dapp/tools/remix'
+ 'dapp/tools/remix',
],
},
],