Browser Support
This page provides guidance for developers looking to build confidential dApps on Sapphire that work across different web browsers and integrate with wallets, including Metamask. It covers supported libraries, best practices for secure transactions, and quick steps for using the libraries.
Supported Libraries
Choosing the Right Library
Many browser-based dApps can use the lightweight Sapphire TypeScript wrapper if they rely entirely on the injected EIP-1193 wallet provider (e.g. window.ethereum) to communicate with and sign transactions on Sapphire. If you already use an EVM-frontend library, use our library-specific packages for Ethers, Viem or Wagmi.
Example: Starter project
If your project includes both a smart contract backend and a web frontend, you can explore our demo-starter repository. It provides a working example using React as well as a Vue branch.
Transaction encryption
When using the supported libraries, ensure that all transactions containing sensitive information are encrypted. Encryption is essential to safeguard user data and ensure privacy. To verify that a transaction is encrypted, you can check the transaction details on the Oasis Block Explorer for the corresponding network (Localnet, Testnet, or Mainnet). Look for a green lock icon next to the transaction, which indicates that it is securely encrypted.
You can check programmatically if calldata is encrypted by using
isCalldataEnveloped(), which is part of @oasisprotocol/sapphire-paratime.
For authenticated view calls, make sure to visit the View-Call Authentication chapter to learn about the proper authentication procedures.
Lightweight Sapphire TypeScript Wrapper
This shows a quick way to use Sapphire TypeScript Wrapper to encrypt
transactions, for more info see
@oasisprotocol/sapphire-paratime.
Usage
Install the library with your favorite package manager
- npm
- pnpm
- Yarn
npm install @oasisprotocol/sapphire-paratime
pnpm add @oasisprotocol/sapphire-paratime
yarn add @oasisprotocol/sapphire-paratime
After installing the library, find your Ethereum provider and wrap it using
wrapEthereumProvider.
import { wrapEthereumProvider } from '@oasisprotocol/sapphire-paratime';
const provider = wrapEthereumProvider(window.ethereum);
Example: Hardhat boilerplate
Our maintained Hardhat boilerplate uses the Sapphire TypeScript Wrapper to enable confidential transactions in development. Find the code in the Sapphire ParaTime examples repository.
Ethers v6
This shows a quick way to use Ethers v6 to encrypt transactions, for more info see @oasisprotocol/sapphire-ethers-v6.
Usage
Install the library with your favorite package manager
- npm
- pnpm
- Yarn
npm install 'ethers@6.x' '@oasisprotocol/sapphire-ethers-v6'
pnpm add 'ethers@6.x' '@oasisprotocol/sapphire-ethers-v6'
yarn add 'ethers@6.x' '@oasisprotocol/sapphire-ethers-v6'
After installing the library, find your Ethereum provider and wrap it using
wrapEthersSigner.
import { BrowserProvider } from 'ethers';
import { wrapEthersSigner } from '@oasisprotocol/sapphire-ethers-v6';
const signer = wrapEthersSigner(
new BrowserProvider(window.ethereum).getSigner()
);
Viem
This shows a quick way to use Viem to encrypt transactions, for more info see @oasisprotocol/sapphire-viem-v2.
Usage
Install the library with your favorite package manager
- npm
- pnpm
- Yarn
npm install @oasisprotocol/sapphire-viem-v2 viem@2.x
pnpm add @oasisprotocol/sapphire-viem-v2 viem@2.x
yarn add @oasisprotocol/sapphire-viem-v2 viem@2.x
After installing the library, wrap the WalletClient with wrapWalletClient.
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()
}));
Viem Example
You can find more example code demonstrating how to use the library in our Hardhat-Viem example.
Wagmi
This shows a quick way to use Wagmi to encrypt transactions, for more info see @oasisprotocol/sapphire-wagmi-v2.
Usage
Install the library with your favorite package manager
- npm
- pnpm
- Yarn
npm install @oasisprotocol/sapphire-wagmi-v2 wagmi@2.x viem@2.x
pnpm add @oasisprotocol/sapphire-wagmi-v2 wagmi@2.x viem@2.x
yarn add @oasisprotocol/sapphire-wagmi-v2 wagmi@2.x viem@2.x
Use this library by wrapping existing Wagmi connectors with
wrapConnectorWithSapphire(). This works with any connector type
(MetaMask, WalletConnect, Coinbase Wallet, etc.) and provides seamless
integration with Sapphire networks:
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(),
},
});
For applications supporting both Sapphire and non-Sapphire networks,
wrapConnectorWithSapphire() automatically detects the chain and only applies
encryption when connected to Sapphire networks.
WalletConnect Integration
Using wrapConnectorWithSapphire() with WalletConnect is similar to the
MetaMask example above, just by wrapping the connector returned by
walletConnect().
import { walletConnect } from "@wagmi/connectors";
import { wrapConnectorWithSapphire } from "@oasisprotocol/sapphire-wagmi-v2";
// ...
connectors: [
wrapConnectorWithSapphire(
walletConnect({
projectId: /*PROJECT_ID*/,
}),
{
id: "walletConnect-sapphire",
name: "WalletConnect (Sapphire)",
},
),
]
// ...
For a complete example of how to use this library, please refer to our Wagmi example.
RainbowKit
This shows a quick way to use RainbowKit to encrypt transactions, for more info see usage example. RainbowKit is a React library that depends on Wagmi and Viem.
Usage
Install the library with your favorite package manager
- npm
- pnpm
- Yarn
npm install @rainbow-me/rainbowkit wagmi@2.x viem@2.x @tanstack/react-query @oasisprotocol/sapphire-wagmi-v2
pnpm add @rainbow-me/rainbowkit wagmi@2.x viem@2.x @tanstack/react-query @oasisprotocol/sapphire-wagmi-v2
yarn add @rainbow-me/rainbowkit wagmi@2.x viem@2.x @tanstack/react-query @oasisprotocol/sapphire-wagmi-v2
Wrap wallet connectors with Sapphire support by applying
wrapConnectorWithSapphire() to the connector returned by the library's wallet
creation function.
Here's a simplified example for RainbowKit:
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*/,
},
);
WalletConnect Integration
import { connectorsForWallets, Wallet } from "@rainbow-me/rainbowkit";
import { wrapConnectorWithSapphire } from "@oasisprotocol/sapphire-wagmi-v2";
import { walletConnectWallet } from "@rainbow-me/rainbowkit/wallets";
const createWalletConnectWallet = (options: { projectId: string }): Wallet => {
const walletOptions = {
id: 'walletConnect-sapphire-rk',
name: 'WalletConnect (Sapphire)',
};
const baseWallet = walletConnectWallet(options);
return {
...baseWallet,
...walletOptions,
mobile: baseWallet.mobile || {
getUri: (uri: string) => uri,
},
desktop: baseWallet.desktop || {
getUri: (uri: string) => uri,
},
qrCode: baseWallet.qrCode || {
getUri: (uri: string) => uri,
},
createConnector: (walletDetails) => {
const originalConnector = baseWallet.createConnector(walletDetails);
return (config) => {
const baseConnector = originalConnector(config);
const wrappedConnector = wrapConnectorWithSapphire(
(_) => baseConnector,
walletOptions,
);
return wrappedConnector(config);
};
},
};
};
const connectors = connectorsForWallets(
[
{
groupName: "Recommended",
wallets: [createWalletConnectWallet],
},
],
{
appName: 'Wagmi v2 Example',
projectId: /*PROJECT_ID*/,
},
);
Rainbowkit Example
You can find more example code demonstrating how to use the library in our RainbowKit example.