TypeScript / JavaScript SDK
OXN 的 TypeScript SDK 封装标准 ethers.js 与 viem 客户端,自动加密外发交易、解密响应。本页是两种封装模式的参考。
安装
npm install ethers@^6.16.0 @oasisprotocol/sapphire-ethers-v6@^6.0.1
依赖:
- ethers v6.16 或更新
- Node 18 或更新,或现代浏览器
旧的 ethers v5 不再积极支持;如果你还在 v5,迁移到 v6。
封装 Provider(读)
import { ethers } from "ethers";
import { wrapEthersProvider } from "@oasisprotocol/sapphire-ethers-v6";
const raw = new ethers.JsonRpcProvider("https://rpc.bout.network");
const provider = wrapEthersProvider(raw);
// 经这个 provider 的所有 eth_call 请求都会加密
const network = await provider.getNetwork();
console.log("Chain ID:", network.chainId); // 186n
Wrapped provider 透 明加密 eth_call 并解密响应。标准 ethers.js API 照常工作。
封装 Signer(交易)
import { ethers } from "ethers";
import { wrapEthersSigner } from "@oasisprotocol/sapphire-ethers-v6";
const raw = new ethers.JsonRpcProvider("https://rpc.bout.network");
const signer = wrapEthersSigner(new ethers.Wallet(process.env.PRIVATE_KEY!, raw));
// 经这个 signer 发出的所有交易都加密
const tx = await signer.sendTransaction({
to: "0xRecipientAddress",
value: ethers.parseEther("0.1"),
gasLimit: 100_000n
});
await tx.wait();
给 wrapEthersSigner 传未 wrap 的 Wallet。Wrapper 会在内部处理 provider wrap。
使用合约
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));
const abi = [ /* 你的 ABI */ ];
// 用于读(view 函数、加密 eth_call)
const readOnly = new ethers.Contract(address, abi, provider);
const value = await readOnly.someView();
// 用于写与签名查询
const writable = new ethers.Contract(address, abi, signer);
await writable.someWrite(arg, { gasLimit: 500_000n });
// 签名查询——传 signer 让 view 函数内 msg.sender 被填充
const myBalance = await writable.myBalance();
经验法则:想让 view 函数内 msg.sender 有值时用绑定 signer 的合约。 绑定 provider 的合约做的是明文 eth_call(msg.sender = 0x0)。
部署合约
const factory = new ethers.ContractFactory(abi, bytecode, signer);
const contract = await factory.deploy(
...constructorArgs,
{ gasLimit: 3_000_000n }
);
await contract.waitForDeployment();
console.log("Deployed at:", await contract.getAddress());
事件监听
OXN 上的事件用调用者的会话密钥加密。用 wrapped provider 监听才能收到解密后的事件:
const contract = new ethers.Contract(address, abi, provider);
contract.on("Transfer", (from, to, amount, event) => {
console.log("decrypted event:", from, to, amount);
});
只有你自己触发的调用产生的事件才能解密。注意事项见机密性模型。
viem 集成
如果你用 viem 而不是 ethers,有对应的 wrapper。安装与 API 略不同但封装模型一样。最新 API 见 Oasis SDK for viem 文档。
常见模式
检测 signer 是否已 wrap:
import { wrapEthersSigner } from "@oasisprotocol/sapphire-ethers-v6";
// 通过检查 wrapped 内部状态存在——实现相关。
// 更简单:始终防御性 wrap;wrap-of-wrapped 是 no-op。
需要明文时强制明文:
const raw = new ethers.JsonRpcProvider("https://rpc.bout.network");
const rawSigner = new ethers.Wallet(PRIVATE_KEY, raw); // 未 wrap
// 通过 rawSigner 的调用是明文——用于公开读或调试。
验证一笔交易是加密的:
const tx = await raw.getTransaction(txHash);
const firstByte = parseInt(tx!.data.slice(2, 4), 16);
if ((firstByte & 0xe0) !== 0xa0) {
throw new Error("Transaction was sent unencrypted — check wrapping");
}
TypeChain 类型安全
TypeChain 从 ABI 生成带类型的合约绑定:
npm install --save-dev typechain @typechain/ethers-v6
npx typechain --target ethers-v6 --out-dir types 'artifacts/**/*.json'
生成的类型与 wrapped signer 无差别使用:
import { MyToken__factory } from "./types";
const token = MyToken__factory.connect(address, signer);
await token.transfer(recipient, amount, { gasLimit: 500_000n });