Migrating an Ethereum dApp to OXN
Most Ethereum dApps run on OXN with minimal changes: two lines of compiler config, one Hardhat plugin, and explicit gas limits in your scripts. This page walks through the checklist and highlights the differences.
Overview
The migration is essentially:
- Compile with
evmVersion: "paris". - Install the OXN SDK.
- Wrap providers and signers for encrypted transactions.
- Set gas limits explicitly.
- Redeploy.
Nothing about your Solidity source has to change. Standard ERC-20, ERC-721, ERC-1155, DEX, and lending patterns all work.
1. Compiler settings
Update hardhat.config.js:
module.exports = {
solidity: {
version: "0.8.24",
settings: {
evmVersion: "paris", // required for OXN
optimizer: { enabled: true, runs: 200 },
},
},
networks: {
oxn_testnet: {
url: "https://rpc.bout.network",
chainId: 186,
accounts: [process.env.PRIVATE_KEY],
},
},
};
For Foundry, foundry.toml:
[profile.default]
evm_version = "paris"
2. Install the SDK
npm install --save-dev @oasisprotocol/sapphire-hardhat
npm install ethers@^6.16.0 @oasisprotocol/sapphire-ethers-v6
Add to hardhat.config.js:
require("@nomicfoundation/hardhat-toolbox");
require("@oasisprotocol/sapphire-hardhat"); // AFTER toolbox
3. Wrap providers and signers
In deployment and interaction scripts, wrap for encryption:
import { ethers } from "ethers";
import { wrapEthersProvider, wrapEthersSigner } from "@oasisprotocol/sapphire-ethers-v6";
const raw = new ethers.JsonRpcProvider("https://rpc.bout.network");
const provider = wrapEthersProvider(raw);
const signer = wrapEthersSigner(new ethers.Wallet(PRIVATE_KEY, raw));
For Hardhat scripts using hre.ethers, the Hardhat plugin does this automatically — no code change needed.
4. Set gas limits explicitly
Every transaction needs an explicit gasLimit:
// Before (Ethereum): estimateGas handled implicitly
await token.transfer(recipient, amount);
// After (OXN): explicit gasLimit
await token.transfer(recipient, amount, { gasLimit: 500_000n });
Common values: see Gas and Fees.
5. Redeploy
Contract addresses on Ethereum do not carry over to OXN. Deploy fresh:
npx hardhat run scripts/deploy.js --network oxn_testnet
Persist addresses in a deployments/ folder — see Deploying Contracts.
Confidentiality — what changes for users
For a straight port (no confidentiality features added), your users get:
- Encrypted calldata — the arguments to their transactions are no longer public.
- Encrypted events — logs from their transactions are only visible to them.
- Same UX otherwise — MetaMask flow, gas costs, transaction times are nearly identical to Ethereum.
To take advantage of confidentiality, add features that only make sense in a confidential setting:
- Hide balances in an ERC-20 (see Building a Confidential ERC-20).
- Add sealed-bid auction mechanics.
- Use
Sapphire.randomBytesfor fair randomness in games.
Testing
Local Hardhat tests continue to work for logic. Add integration tests against OXN Testnet for encryption paths — see Testing Confidential Behavior.
What breaks (unlikely but check for)
- Contracts using
PUSH0— fix withevmVersion: "paris". - Contracts using
TLOAD/TSTORE— Cancun-era transient storage isn't supported. Refactor to normal storage. - Contracts using
BLOBHASH— not supported; OXN has no blobs. - Contracts relying on
PREVRANDAOfor randomness — replace withSapphire.randomBytes. - Scripts using
estimateGas— replace with explicitgasLimit.
Migration checklist
-
evmVersion: "paris"in compiler config -
@oasisprotocol/sapphire-hardhatinstalled and required in order - Deployment scripts have explicit
gasLimit - Interaction scripts wrap providers/signers
- Integration tests hit real OXN, not Hardhat network
- Contracts redeployed
- Addresses persisted in
deployments/ - Frontend chain ID updated to
186(or parameterized) - Frontend RPC URL updated to
https://rpc.bout.network - Wallet integration tested with MetaMask + OXN Testnet
Next steps
- Deploying Contracts — production deployment patterns
- Confidentiality Model — what your users get automatically
- Building a Confidential ERC-20 — the natural next step for token dApps