跳到主要内容

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_callmsg.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 });

下一步