### Install Dependencies and Build Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/examples/encrypted-events/README.md Install project dependencies using pnpm, copy the environment example, and build the project. Optionally, build types for better overload support. ```bash pnpm install cp .env.example .env # paste a 0x‑prefixed private key pnpm build # (Optional) Better typings for overloads: pnpm run build:types # runs hardhat compile + typechain ``` -------------------------------- ### Install Sapphire Python Client Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/clients/py/README.md Install the Sapphire Python client and its development dependencies, then build the project. ```shell pip3 install --user -r requirements.txt pip3 install --user -r requirements.dev.txt make ``` -------------------------------- ### Start Frontend Development Server Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/examples/hardhat-boilerplate/README.md Navigate to the frontend directory and run this command to start the React-based frontend development server. ```sh cd frontend pnpm start ``` -------------------------------- ### Install Dependencies with pnpm Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/examples/hardhat-boilerplate/README.md Run this command in the project directory to install all necessary dependencies using pnpm. ```sh pnpm install ``` -------------------------------- ### Install hardhat-tracer Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/security.md Install the hardhat-tracer plugin to examine EVM state transitions. Add it to your config.ts file. ```shell npm install -D hardhat-tracer ``` ```typescript import "hardhat-tracer" ``` -------------------------------- ### Install Sapphire Viem v2 Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/browser.md Install the Sapphire Viem v2 library and Viem itself using npm or yarn. Ensure you are using Viem version 2.x. ```shell npm install @oasisprotocol/sapphire-viem-v2 viem@2.x ``` -------------------------------- ### Install GSN CLI Package Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/gasless.md Install the Oasis fork of the GSN command line tool. Ensure you are in an empty folder. ```shell npm init npm install -D @oasislabs/opengsn-cli ``` -------------------------------- ### Install Sapphire TypeScript Wrapper Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/browser.md Install the Sapphire TypeScript Wrapper using npm or yarn. This is the first step before wrapping your Ethereum provider. ```shell npm install @oasisprotocol/sapphire-paratime ``` -------------------------------- ### Install OpenGSN Contracts Package Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/gasless.md Install the OpenGSN contracts package for writing GSN-enabled smart contracts. ```shell npm install -D @opengsn/contracts@3.0.0-beta.2 ``` -------------------------------- ### Install Sapphire Contracts Library Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/contracts/README.md Install the Sapphire contracts library using pnpm. This is the first step before importing and using Sapphire contracts in your Solidity projects. ```shell $ pnpm install @oasisprotocol/sapphire-contracts ``` -------------------------------- ### Install Sapphire Wagmi v3 Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/integrations/wagmi-v3/README.md Install the package along with required peer dependencies for Wagmi v3 and Viem. ```bash npm install @oasisprotocol/sapphire-wagmi-v3 wagmi@3.x viem@^2.21.0 ``` -------------------------------- ### Install Rustup and Set Toolchain Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/runtime/README.md Installs Rustup and sets the appropriate Rust toolchain version for the Sapphire ParaTime project. Ensure you are in a Sapphire ParaTime git checkout directory. ```shell curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh source "$HOME/.cargo/env" ``` -------------------------------- ### Install Playwright Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/dappwright.md Install Playwright, with a recommendation for the TypeScript option, after installing dAppwright. ```shell npm init playwright@latest ``` -------------------------------- ### Install Sapphire Ethers v6 Wrapper Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/browser.md Install the Sapphire Ethers v6 library along with ethers v6. This is required for integrating Sapphire with Ethers v6. ```shell npm install 'ethers@6.x' '@oasisprotocol/sapphire-ethers-v6' ``` -------------------------------- ### Install Sapphire Hardhat Plugin Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/testing.md Install the Sapphire Hardhat plugin to enable encrypted transactions when testing with Hardhat. This package should be installed as a development dependency. ```shell npm install -D @oasisprotocol/sapphire-hardhat ``` -------------------------------- ### Install Fortanix SGX Tools Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/runtime/README.md Installs the necessary Fortanix SGX tools using Cargo. This command ensures the tools are installed with locked dependencies. ```shell cargo install --locked fortanix-sgx-tools sgxs-tools ``` -------------------------------- ### Install RainbowKit Dependencies Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/browser.md Install the necessary libraries for RainbowKit integration with Sapphire. Ensure you are using compatible versions of wagmi and viem. ```shell npm install @rainbow-me/rainbowkit wagmi@2.x viem@2.x @tanstack/react-query @oasisprotocol/sapphire-wagmi-v2 ``` -------------------------------- ### Start Sapphire Localnet Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/examples/encrypted-events/README.md Run Sapphire Localnet using Docker. For Apple Silicon, include the --platform flag if the image lacks arm64 support. ```bash docker run -it -p8544-8548:8544-8548 ghcr.io/oasisprotocol/sapphire-localnet # On Apple Silicon, add: --platform linux/x86_64 (if the image lacks arm64) ``` -------------------------------- ### Install Sapphire Ethers Provider Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/testing.md Install the Sapphire Ethers provider package to enable encrypted transactions when working with Ethers.js. This package should be installed as a development dependency. ```shell npm install -D @oasisprotocol/sapphire-paratime ``` -------------------------------- ### Generate API Documentation Locally Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/contracts/README.md Commands to install necessary tools (Foundry, mdbook-pagetoc) and generate API documentation locally. This requires a shell environment. ```shell curl -L https://foundry.paradigm.xyz | bash cargo install mdbook-pagetoc pnpm doc ``` -------------------------------- ### Install Sapphire Wagmi v2 Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/browser.md Install the Sapphire Wagmi v2 library along with Wagmi and Viem. Ensure you are using Wagmi version 2.x and Viem version 2.x. ```shell npm install @oasisprotocol/sapphire-wagmi-v2 wagmi@2.x viem@2.x ``` -------------------------------- ### Install dAppwright Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/dappwright.md Install dAppwright as a development dependency in your frontend application directory. ```shell npm install -D @tenkeylabs/dappwright ``` -------------------------------- ### Install Playwright Dependencies Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/dappwright.md Installs Playwright dependencies for the frontend project using pnpm. ```yaml - name: Install Playwright dependencies run: pnpm test:setup working-directory: frontend ``` -------------------------------- ### Basic Contract Example Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/authentication.md A simple Solidity contract to demonstrate owner verification. Use this to understand basic access control within a contract. ```solidity contract Example { address _owner; constructor () { _owner = msg.sender; } function isOwner() public view returns (bool) { return msg.sender == _owner; } } ``` -------------------------------- ### Add Sapphire Ethers.js Wrapper Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/integrations/ethers-v6/README.md Install the necessary packages for integrating Ethers.js v6 with Oasis Sapphire. ```bash pnpm add 'ethers@6.x' '@oasisprotocol/sapphire-ethers-v6' ``` -------------------------------- ### Build and Test Local Hardhat Project Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/examples/hardhat/README.md Builds the project and runs tests for the Hardhat setup on the Sapphire localnet. ```shell make build make test ``` -------------------------------- ### Start GSN Relay Server Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/gasless.md Start your own GSN relay server. Use the deployed RelayHub address for --relayHubAddress, TestToken address for --managerStakeTokenAddress, and your account address for --owner-address. ```shell npx gsn relayer-run --relayHubAddress 0xc4423AB6133B06e4e60D594Ac49abE53374124b3 --managerStakeTokenAddress 0x6Ed21672c0c26Daa32943F7b1cA1f1d0ABdbac66 --ownerAddress '0xfA3AC9f65C9D75EE3978ab76c6a1105f03156204' --ethereumNodeUrl 'https://testnet.sapphire.oasis.io' --workdir . ``` -------------------------------- ### Spin up Localnet Sapphire Node Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/examples/onchain-signer/README.md Starts a Docker container for a Localnet Sapphire node, exposing necessary ports. ```shell docker run -it -p8545:8545 -p8546:8546 ghcr.io/oasisprotocol/sapphire-localnet -test-mnemonic -n 5 ``` -------------------------------- ### Gas Padding with Conditional Math Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/security.md This example demonstrates padding EVM execution gas using `Sapphire.padGas` within a contract that has a conditional computation path. Note that transaction size gas costs remain public. ```solidity contract GasExample { bytes32 tmp; function constantMath(bool doMath, uint128 padTo) external { if (doMath) { bytes32 x; for (uint256 i = 0; i < 100; i++) { x = keccak256(abi.encodePacked(x, tmp)); } tmp = x; } // Pads EVM execution only; tx size cost remains public. Sapphire.padGas(padTo); } } ``` -------------------------------- ### EIP-7201 Namespaced Storage Example Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/deployment.md Demonstrates an implementation of EIP-7201 for namespaced storage in Solidity, showing how to manage state within a delegatecall context. Includes both the contract defining the namespaced state and a caller contract. ```solidity // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; contract Example7201 { /// @custom:storage-location erc7201:Example7201.state struct State { uint256 counter; } function _stateStorageSlot() private pure returns (bytes32) { return keccak256(abi.encode(uint256(keccak256("Example7201.state")) - 1)) & ~bytes32(uint256(0xff)); } function _getState() private pure returns (State storage state) { bytes32 slot = _stateStorageSlot(); assembly { state.slot := slot } } function increment() public { State storage state = _getState(); state.counter += 1; } function get() public view returns (uint256) { State storage state = _getState(); return state.counter; } } contract ExampleCaller { Example7201 private example; constructor () { example = new Example7201(); } function get() external returns (uint256 counter) { (bool success, bytes memory result ) = address(example).delegatecall(abi.encodeCall(example.get, ())); require(success); counter = abi.decode(result, (uint256)); } function increment() external { (bool success, ) = address(example).delegatecall(abi.encodeCall(example.increment, ())); require(success); } } ``` -------------------------------- ### Padded Contract Example Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/security.md This contract shows how to use `Sapphire.padGas` to equalize execution costs between different code paths, preventing information leakage via fees. ```solidity contract Padded { bytes32 private secret; bytes32 private tmp; function guess(bytes32 candidate) external returns (bool ok) { if (candidate == secret) { for (uint i = 0; i < 10_000; ++i) { tmp = keccak256(abi.encodePacked(tmp, i)); } ok = true; } // Equalize execution cost across branches. Pads execution only // tx size cost stays visible. Sapphire.padGas(100_000); } } ``` -------------------------------- ### Run Playwright Tests Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/dappwright.md Execute the installed Playwright tests using the npx command. This command runs all tests defined in your project. ```shell npx playwright test ``` -------------------------------- ### Create and Navigate to Project Directory Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/quickstart.mdx Initializes a new project directory and changes into it. This is the first step in setting up your dApp environment. ```bash mkdir quickstart && cd quickstart ``` -------------------------------- ### Build and Test Locally Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/clients/go/README.md Run tests to ensure the compatibility library is functioning correctly. Requires Go version 1.22 or later. ```shell go test ``` -------------------------------- ### Initialize Sapphire Web3 Client Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/clients/py/README.md Initialize Web3.py or AsyncWeb3 clients with the Sapphire network provider and set up optional signing accounts for transaction signing and encrypted view calls. ```python from web3 import Web3, AsyncWeb3 from web3.middleware import SignAndSendRawMiddlewareBuilder from eth_account import Account from eth_account.signers.local import LocalAccount from sapphirepy import sapphire w3 = Web3(Web3.HTTPProvider(sapphire.NETWORKS['sapphire-localnet'])) async_w3 = AsyncWeb3( AsyncWeb3.AsyncHTTPProvider( sapphire.NETWORKS['sapphire-localnet'] ) ) # Optional: Setup your Web3 provider with a signing account. # This account is used for signing transactions. account: LocalAccount = ( Account.from_key( # pylint: disable=no-value-for-parameter private_key="" ) ) w3.middleware_onion.add(SignAndSendRawMiddlewareBuilder.build(account)) # Finally, wrap the provider to add Sapphire end-to-end encryption. # Note: Account parameter in the wrap() function is used for signing view # calls and can be different from the account used for singing transactions. w3 = sapphire.wrap(w3, account) # Can provide custom "account" parameter w3.eth.default_account = account.address # Alternatively you can set "from" tx param each time for signed view calls # Wrapper middleware also works with AsyncWeb3 async_w3 = sapphire.wrap(async_w3, account) # Account parameter is optional, to encrypt view calls without signing # we can omit the account parameter w3 = sapphire.wrap(w3) async_w3 = sapphire.wrap(async_w3) # Optionally, query Oasis Web3 Gateway for the gas price. from web3.gas_strategies.rpc import rpc_gas_price_strategy w3.eth.set_gas_price_strategy(rpc_gas_price_strategy) ``` -------------------------------- ### Quick Test: Listener (Testnet) Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/examples/encrypted-events/README.md Terminal A: Set up a listener on the testnet to capture and decrypt events. Ensure the contract address and symmetric key are exported as environment variables. ```bash # First deploy on testnet and copy the address npx hardhat deploy --network sapphire-testnet export ADDR= export KEY=0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f npx hardhat listen --network sapphire-testnet \ --mode key --contract $ADDR --key $KEY ``` -------------------------------- ### Go: Sign View Calls with Wrapped Client Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/authentication.md Wrap your existing Ethereum client using `WrapClient()` and provide signing logic to automatically sign all subsequent view calls. This is useful for backend services that need to interact with contracts without user intervention. ```go import ( "context" "fmt" "github.com/oasisprotocol/sapphire-paratime/clients/go/sapphire" "github.com/ethereum/go-ethereum/ethclient" ) func main() { client, err := ethclient.Dial("YOUR_NODE_URL") if err != nil { panic(err) } // Replace with your actual private key or a secure way to load it privateKey := "0x..." wrappedClient := sapphire.WrapClient(client, privateKey) // Now, any view call made through wrappedClient will be signed // Example: Reading a value from a contract // value, err := contract.SomeViewCall(context.Background()) // if err != nil { // panic(err) // } // fmt.Printf("Value: %s\n", value) } ``` -------------------------------- ### Wrap Go-Ethereum Client with Sapphire Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/clients/go/README.md Instantiate an Ethereum client with the Sapphire gateway URL and wrap it using `sapphire.WrapClient`. This allows contracts using `go-ethereum`'s `abigen` to be used with Sapphire. Provide a custom signing function for confidential transactions. ```go // key := private key client, _ := ethclient.Dial(sapphire.Networks[SapphireChainID.Uint64()].DefaultGateway) backend, _ := sapphire.WrapClient(client, func(digest [32]byte)([]byte, error) { // Pass in a custom signing function to interact with the signer return crypto.Sign(digest[:], key) }) ``` ```go nft, _ := NewNft(addr, backend) ``` -------------------------------- ### Deploying with Proxy using hardhat-deploy Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/deployment.md This script demonstrates how to deploy a contract with proxy support using the hardhat-deploy library. Ensure your configuration specifies `proxy: true` for the deployment. ```typescript module.exports = async ({getNamedAccounts, deployments, getChainId}) => { const {deploy} = deployments; const {deployer} = await getNamedAccounts(); await deploy('Greeter', { from: deployer, proxy: true, }); }; ``` -------------------------------- ### Initialize Hardhat Project Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/quickstart.mdx Initializes a new Hardhat project with TypeScript support. This command sets up the basic structure and configuration for your dApp. ```bash npx hardhat@2 init ``` -------------------------------- ### Import Sapphire Compatibility Library Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/clients/go/README.md Import necessary packages for using the Sapphire compatibility library in your Go project. Ensure you have the latest compatible `go-ethereum` version. ```go import ( "context" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/ethclient" sapphire "github.com/oasisprotocol/sapphire-paratime/clients/go" ) ``` -------------------------------- ### Solidity CommentBox Contract for dApp Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/gasless.md A simple Solidity contract to store comments, used as an example for gasless transaction implementation. ```solidity contract CommentBox { string[] public comments; function comment(string memory commentText) external { comments.push(commentText); } } ``` -------------------------------- ### Create Sapphire-enabled Wallet Client for View-calls Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/integrations/wagmi-v2/README.md Demonstrates how to create a Sapphire-enabled HTTP transport and wrap a Viem wallet client using `wrapWalletClient`. This is essential for handling end-to-end encryption during read-only operations and view calls on Sapphire networks. ```typescript import { sapphireHttpTransport, wrapWalletClient, createSapphireSerializer, sapphireLocalnet } from '@oasisprotocol/sapphire-wagmi-v2'; import { createWalletClient } from 'viem'; // Create a Sapphire-enabled HTTP transport const transport = sapphireHttpTransport(); const client = await wrapWalletClient( createWalletClient({ account, chain: sapphireLocalnet, transport: sapphireHttpTransport() }) ); ``` -------------------------------- ### Leaky Contract Example Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/security.md This contract demonstrates a scenario where differing execution paths based on input can leak information through observable fee differences. ```solidity contract Leaky { bytes32 private secret; bytes32 private tmp; // Returns true on correct guess; success path does extra work // (leaks via fee). function guess(bytes32 candidate) external returns (bool ok) { if (candidate == secret) { for (uint i = 0; i < 10_000; ++i) { tmp = keccak256(abi.encodePacked(tmp, i)); } return true; } return false; } } ``` -------------------------------- ### Build Frontend Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/dappwright.md Builds the frontend application using pnpm. ```yaml - name: Build working-directory: frontend run: pnpm build ``` -------------------------------- ### Go Code Formatting Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/CONTRIBUTING.md Ensure Go code adheres to the `gofumpt` formatting style by running `make fmt` before pushing any code. ```bash make fmt ``` -------------------------------- ### Build Mock SGX Binary for Localnet Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/runtime/README.md Builds the unsafe, mock SGX binary of the Sapphire ParaTime for local development. This binary can be used for testing without actual SGX hardware or attestation. ```shell export OASIS_UNSAFE_SKIP_AVR_VERIFY=1 OASIS_UNSAFE_ALLOW_DEBUG_ENCLAVES=1 OASIS_UNSAFE_USE_LOCALNET_CHAINID=1 cargo build --release --locked --features debug-mock-sgx ``` -------------------------------- ### Deploy Contract to Sapphire Testnet Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/examples/hardhat-boilerplate/README.md Execute this command to deploy your smart contract on the Sapphire Testnet. Ensure you replace 'your_sapphire_private_key_in_hex' with your actual private key. ```sh PRIVATE_KEY=your_sapphire_private_key_in_hex npx hardhat run scripts/deploy.js --network sapphire-testnet ``` -------------------------------- ### Generate Random Number using Sapphire Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/contracts/README.md Example of a Solidity contract that uses the Sapphire library to generate a random number. Ensure your Solidity version is compatible. ```solidity pragma solidity ^0.8.13; import {Sapphire} from "@oasisprotocol/sapphire-contracts/contracts/Sapphire.sol"; contract RandomNumber { function generateNumber() public view returns (uint) { return uint(bytes32(Sapphire.randomBytes(32, ""))); } } ``` -------------------------------- ### Test GSN Relayed Request Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/gasless.md Send a test relayed request to verify your GSN setup. Use the Forwarder address as --to, Paymaster as --paymaster, and your account address as --from. ```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 ``` -------------------------------- ### Confidential Contract with Owner Authorization Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/authentication.md An extended Solidity contract demonstrating owner-only access for setting and getting a secret message. This pattern is crucial for building confidential smart contracts on Sapphire. ```solidity contract MessageBox { address private _owner; string private _message; modifier onlyOwner() { if (msg.sender != _owner) { revert("not allowed"); } _; } constructor() { _owner = msg.sender; } function getSecretMessage() external view onlyOwner returns (string memory) { return _message; } function setSecretMessage(string calldata message) external onlyOwner { _message = message; } } ``` -------------------------------- ### Import Sapphire Chain Configurations Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/integrations/viem-v2/README.md Import chain configurations for local testing and mainnet/testnet. ```typescript import { sapphireLocalnet } from '@oasisprotocol/sapphire-viem-v2'; import { sapphire, sapphireTestnet } from 'viem/chains'; ``` -------------------------------- ### Import Sapphire Hardhat Plugin Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/testing.md Import the Sapphire Hardhat plugin in your `hardhat.config.ts` file. This ensures the provider is wrapped before other plugins are initialized, enabling automatic transaction encryption. ```javascript // ESM import '@oasisprotocol/sapphire-hardhat'; // CommonJS require('@oasisprotocol/sapphire-hardhat'); /** All other plugins must go below this one! **/ ``` -------------------------------- ### Get Secret Message via Signed Query in Go Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/authentication.md Use this Go code to retrieve a secret message from a smart contract using signed queries. Ensure your PRIVATE_KEY environment variable is set. ```go import ( "context" "crypto/ecdsa" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" sapphire "github.com/oasisprotocol/sapphire-paratime/clients/go" messageBox "demo-starter/contracts/message-box" ) func GetC10lMessage() (string, error) { client, err = ethclient.Dial("https://testnet.sapphire.oasis.io") if err != nil { return "", err } sk, err = crypto.HexToECDSA(os.Getenv("PRIVATE_KEY")) addr := crypto.PubkeyToAddress(*sk.Public().(*ecdsa.PublicKey)) wrappedClient, err := sapphire.WrapClient(c.Client, func(digest [32]byte) ([]byte, error) { return crypto.Sign(digest[:], sk) }) if err != nil { return "", fmt.Errorf("unable to wrap backend: %v", err) } mb, err := messageBox.NewMessageBox(common.HexToAddress("0x5FbDB2315678afecb367f032d93F642f64180aa3"), wrappedClient) if err != nil { return "", fmt.Errorf("Unable to get instance of contract: %v", err) } msg, err := mb.GetSecretMessage(&bind.CallOpts{From: addr}) // Don't forget to pass callOpts! if err != nil { return "", fmt.Errorf("failed to retrieve message: %v", err) } return msg, nil } ``` -------------------------------- ### Configure Playwright for Local Dev Server Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/dappwright.md Configure Playwright to start a local development server before running tests. This ensures a consistent state for iteration. The server is set to restart if not reusing an existing one, and output is piped. ```typescript import { defineConfig } from '@playwright/test'; export default defineConfig({ // highlight-start /* Run your local dev server before starting the tests */ webServer: { command: 'pnpm dev', url: process.env.FRONTEND_URL || 'http://localhost:8080/', reuseExistingServer: !process.env.CI, stdout: 'pipe', stderr: 'pipe', }, // highlight-end }); ``` -------------------------------- ### Wrap Viem WalletClient with Sapphire Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/browser.md Wrap an existing Viem WalletClient with `wrapWalletClient` to enable Sapphire's encrypted transaction capabilities. This requires importing necessary components from the Sapphire Viem v2 library. ```typescript import { createWalletClient } from 'viem' import { english, generateMnemonic, mnemonicToAccount } from 'viem/accounts'; import { sapphireLocalnet, sapphireHttpTransport, wrapWalletClient } from '@oasisprotocol/sapphire-viem-v2'; const account = mnemonicToAccount(generateMnemonic(english)); const walletClient = await wrapWalletClient(createWalletClient({ account, chain: sapphireLocalnet, transport: sapphireHttpTransport() })); ``` -------------------------------- ### Pack and Sign Ethereum Transactions for Sapphire Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/clients/go/README.md Package an existing Ethereum transaction for Sapphire using `sapphire.PackTx` and then sign it using your usual signer. This method is used when you want to send a transaction with a normal, not-wrapped `ethclient.Client` instance. ```go sapphireTestnetChainId := 0x5aff // Sapphire Testnet. packedTx := sapphire.PackTx(tx, sapphire.NewCipher(sapphireTestnetChainId)) signedTx := sign(packedTx) // Using your usual signer. ``` ```go _ = c.SendTransaction(ctx, signedTx) ``` -------------------------------- ### Get Secret Message via Signed Query in Python Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/authentication.md Use this Python code to retrieve a secret message from a smart contract using signed queries. Ensure your PRIVATE_KEY environment variable is set and the 'MessageBox_compiled.json' file is available. ```python from web3 import Web3 from web3.middleware import construct_sign_and_send_raw_middleware from eth_account.signers.local import LocalAccount from eth_account import Account from sapphirepy import sapphire def get_c10l_message(address: str, network_name: Optional[str] = "sapphire-localnet") -> str: w3 = Web3(Web3.HTTPProvider(sapphire.NETWORKS[network_name])) account: LocalAccount = Account.from_key(os.environ.get("PRIVATE_KEY")) w3.middleware_onion.add(construct_sign_and_send_raw_middleware(account)) w3 = sapphire.wrap(w3, account) compiled_contract = json.load("MessageBox_compiled.json") contract_data = compiled_contract["contracts"]["MessageBox.sol"]["MessageBox"] message_box = w3.eth.contract(address=address, abi=contract_data["abi"]) return message_box.functions.message().call() ``` -------------------------------- ### Flow A: Deploy Contract Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/examples/encrypted-events/README.md Deploy the contract to the Sapphire localnet. The output will provide the contract address, which should be stored in the $ADDR environment variable. ```bash # Deploy npx hardhat deploy --network sapphire-localnet # copy printed address to $ADDR (this is the CONTRACT address, not a tx hash) ``` -------------------------------- ### Build SGX Binary Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/runtime/README.md Builds the SGX binary of the Sapphire ParaTime using the specified target and then converts it to the .sgxs format. ```shell cargo build --release --target x86_64-fortanix-unknown-sgx --locked cargo elf2sgxs --release ``` -------------------------------- ### Configure Hardhat for Sapphire Testnet Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/quickstart.mdx Configures the Hardhat project to include the Sapphire Testnet. Importing the sapphire-hardhat plugin automatically enhances network configurations for Sapphire. ```typescript import "@oasisprotocol/sapphire-hardhat"; import { HardhatUserConfig } from "hardhat/config"; const config: HardhatUserConfig = { solidity: "0.8.20", networks: { // ... other networks sapphireTestnet: { url: process.env.SAPPHIRE_TESTNET_RPC_URL || "https://testnet.sapphire.oasis.dev", accounts: process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [], }, }, }; export default config; ``` -------------------------------- ### Configure Wagmi for Multi-Chain Support with Wrapped MetaMask Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/integrations/wagmi-v3/README.md Use `wrapConnectorWithSapphire` to create a single wrapped connector that automatically detects the chain and applies encryption only when connected to Sapphire networks. This allows for seamless support of both Sapphire and non-Sapphire chains. ```typescript import { createConfig, http } from "wagmi"; import { mainnet } from "wagmi/chains"; import { metaMask } from "wagmi/connectors"; import { sapphire, sapphireLocalnet, wrapConnectorWithSapphire, sapphireHttpTransport } from "@oasisprotocol/sapphire-wagmi-v3"; export const wagmiConfig = createConfig({ chains: [sapphire, sapphireLocalnet, mainnet], connectors: [ // Sapphire-wrapped aware MetaMask for Sapphire chains, unwrapped for other chains wrapConnectorWithSapphire( metaMask, { id: 'metamask-sapphire', name: 'MetaMask (Sapphire)', } ), ], transports: { [sapphire.id]: sapphireHttpTransport(), [sapphireLocalnet.id]: sapphireHttpTransport(), [mainnet.id]: http(), }, }); ``` -------------------------------- ### Import Sapphire Chain Definitions Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/integrations/wagmi-v3/README.md Import pre-configured chain definitions for Sapphire networks. ```typescript import { sapphire, sapphireTestnet, sapphireLocalnet } from "@oasisprotocol/sapphire-wagmi-v3"; ``` -------------------------------- ### Configure Hardhat for Deterministic Deployment Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/deployment.md Specify the Safe Singleton factory and deployer addresses in your `hardhat.config.ts` file for deterministic deployment on specific network chain IDs. ```typescript deterministicDeployment: { "97": { factory: '0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7', deployer: '0xE1CB04A0fA36DdD16a06ea828007E35e1a3cBC37', funding: '2000000', signedTx: '', }, "23295": { factory: '0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7', deployer: '0xE1CB04A0fA36DdD16a06ea828007E35e1a3cBC37', funding: '2000000', signedTx: '', } }, ``` -------------------------------- ### Perform Confidential Queries with Wrapped Client Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/clients/go/README.md Execute confidential queries on a Sapphire-wrapped contract. Pass `bind.CallOpts` with your `From` address to ensure the query is signed with your account's key. ```go balance := nft.BalanceOf(&bind.CallOpts{From: "0xYOUR_ADDRESS"}, common.HexToAddress("0xDce075E1C39b1ae0b75D554558b6451A226ffe00")) ``` -------------------------------- ### Configure Wagmi for Single Chain Sapphire with MetaMask and WalletConnect Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/integrations/wagmi-v2/README.md Set up a Wagmi configuration using `wrapConnectorWithSapphire` to integrate MetaMask and WalletConnect connectors for the Sapphire chain. This allows for custom connector IDs and names to differentiate them. ```typescript import { createConfig } from "wagmi"; import { sapphire, sapphireTestnet } from "wagmi/chains"; import { metaMask, walletConnect } from "@wagmi/connectors"; import { wrapConnectorWithSapphire, sapphireHttpTransport } from "@oasisprotocol/sapphire-wagmi-v2"; export const wagmiConfig = createConfig({ chains: [sapphire, sapphireTestnet], connectors: [ wrapConnectorWithSapphire( metaMask, { id: 'metamask-sapphire', name: 'MetaMask (Sapphire)', } ), wrapConnectorWithSapphire( () => walletConnect({ projectId: 'your-project-id' }), { id: 'walletconnect-sapphire', name: 'WalletConnect (Sapphire)', } ), ], transports: { [sapphire.id]: sapphireHttpTransport(), [sapphireTestnet.id]: sapphireHttpTransport(), [sapphireLocalnet.id]: sapphireHttpTransport(), }, }); ``` -------------------------------- ### Deploy Backend and Capture Contract Address Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/dappwright.md This step deploys the backend smart contracts and captures the deployed contract address, saving it to GITHUB_OUTPUT for subsequent steps. ```yaml - name: Deploy backend working-directory: backend id: deploy run: | echo "message_box_address=$(pnpm hardhat deploy localhost --network sapphire-localnet | grep -o '0x.*')" >> $GITHUB_OUTPUT ``` -------------------------------- ### Quick Test: Emitter (Testnet) Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/examples/encrypted-events/README.md Terminal B: Emit an encrypted event to the deployed contract on the testnet using the specified symmetric key. The listener in Terminal A should then decrypt and display the message. ```bash npx hardhat emit --network sapphire-testnet \ --mode key --contract $ADDR --message "secret" --key $KEY ``` -------------------------------- ### Integrate Sapphire Hardhat Plugin Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/clients/js/README.md Include the '@oasisprotocol/sapphire-hardhat' plugin at the top of your `hardhat.config.ts` file to easily integrate Sapphire ParaTime support into your Hardhat development environment. Ensure this import comes before any other Hardhat plugins. ```javascript import '@oasisprotocol/sapphire-hardhat'; // All other Hardhat plugins must come below. ``` -------------------------------- ### Configure Wagmi with Wrapped MetaMask and WalletConnect Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/integrations/wagmi-v3/README.md Wrap MetaMask and WalletConnect connectors with `wrapConnectorWithSapphire` for Sapphire network integration. It's recommended to modify connector names to differentiate them from unwrapped connectors. ```typescript import { createConfig } from "wagmi"; import { metaMask, walletConnect } from "wagmi/connectors"; import { sapphire, sapphireTestnet, wrapConnectorWithSapphire, sapphireHttpTransport } from "@oasisprotocol/sapphire-wagmi-v3"; export const wagmiConfig = createConfig({ chains: [sapphire, sapphireTestnet], connectors: [ wrapConnectorWithSapphire( metaMask, { id: 'metamask-sapphire', name: 'MetaMask (Sapphire)', } ), wrapConnectorWithSapphire( () => walletConnect({ projectId: 'your-project-id' }), { id: 'walletconnect-sapphire', name: 'WalletConnect (Sapphire)', } ), ], transports: { [sapphire.id]: sapphireHttpTransport(), [sapphireTestnet.id]: sapphireHttpTransport(), }, }); ``` -------------------------------- ### Deploying Contract with SIWE Domain Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/authentication.md When deploying your contract, provide the domain where your dApp's web content will be hosted. MetaMask verifies this domain against the SIWE message to prevent phishing attacks. Include the host chunk and optional port, but exclude scheme and path. ```solidity contract MyContract is SiweAuth { constructor(string memory _domain) SiweAuth(_domain) { // ... } } ``` -------------------------------- ### Configure Wagmi with Sapphire Connector Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/browser.md Wrap Wagmi connectors with `wrapConnectorWithSapphire` to integrate Sapphire networks. This allows seamless encryption for Sapphire chains while using standard transports for others. ```typescript import { createConfig } from "wagmi"; import { sapphire, mainnet } from "wagmi/chains"; import { metaMask } from "@wagmi/connectors"; import { wrapConnectorWithSapphire, sapphireHttpTransport, sapphireLocalnet } from "@oasisprotocol/sapphire-wagmi-v2"; import { http } from "wagmi"; export const wagmiConfig = createConfig({ chains: [sapphire, sapphireLocalnet, mainnet], connectors: [ // Sapphire-wrapped aware MetaMask for Sapphire chains, unwrapped for other chains wrapConnectorWithSapphire( metaMask(), { id: 'metamask-sapphire', name: 'MetaMask (Sapphire)', } ), ], transports: { [sapphire.id]: sapphireHttpTransport(), [sapphireLocalnet.id]: sapphireHttpTransport(), [mainnet.id]: http(), }, }); ``` -------------------------------- ### Wrap MetaMask Wallet for RainbowKit with Sapphire Source: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/develop/browser.md This TypeScript code demonstrates how to wrap the MetaMask wallet connector with Sapphire support for use in RainbowKit. It defines a helper function to apply Sapphire options to wallet connectors. ```typescript import { connectorsForWallets, Wallet } from "@rainbow-me/rainbowkit"; import { wrapConnectorWithSapphire } from "@oasisprotocol/sapphire-wagmi-v2"; import { metaMaskWallet } from "@rainbow-me/rainbowkit/wallets"; const wrapRainbowKitWalletWithSapphire = ( walletFn: (options: { projectId: string }) => Wallet, sapphireOptions: { id: string; name: string }, ) => (options: { projectId: string }): Wallet => { const wallet = walletFn(options); return { ...wallet, id: sapphireOptions.id, name: sapphireOptions.name, createConnector: (walletDetails) => { const originalConnector = wallet.createConnector(walletDetails); return (config) => { const baseConnector = originalConnector(config); const wrappedConnector = wrapConnectorWithSapphire( (_) => baseConnector, sapphireOptions, ); return wrappedConnector(config); }; }, }; }; const connectors = connectorsForWallets( [ { groupName: "Recommended", wallets: [ wrapRainbowKitWalletWithSapphire( metaMaskWallet, { id: "metamask-sapphire-rk", name: "MetaMask (Sapphire)", }, ) ], }, ], { appName: "Wagmi v2 Example", projectId: /*PROJECT_ID*/, }, ); ```