Oasis ได้นำเสนอเฟรมเวิร์กสำหรับ runtime off-chain logic (ROFL) เพื่อช่วยในการสร้างและเรียกใช้แอปพลิเคชันแบบ off-chain พร้อมทั้งรับประกันความเป็นส่วนตัวและรักษาความไว้วางใจกับ on-chainOasis ได้นำเสนอเฟรมเวิร์กสำหรับ runtime off-chain logic (ROFL) เพื่อช่วยในการสร้างและเรียกใช้แอปพลิเคชันแบบ off-chain พร้อมทั้งรับประกันความเป็นส่วนตัวและรักษาความไว้วางใจกับ on-chain

คู่มือการสร้างคีย์ข้ามเชน (EVM / Base) ด้วย Oasis ROFL

2026/02/20 21:16
8 นาทีในการอ่าน
หากมีข้อเสนอแนะหรือข้อกังวลเกี่ยวกับเนื้อหานี้ โปรดติดต่อเราได้ที่ crypto.news@mexc.com

Oasis ได้แนะนำเฟรมเวิร์กสำหรับ runtime off-chain logic (ROFL) เพื่อช่วยในการสร้างและรันแอปพลิเคชันนอกเชนในขณะที่รับประกันความเป็นส่วนตัวและรักษาความไว้วางใจด้วยการตรวจสอบได้บนเชน มีส่วนประกอบหลายอย่างในการสร้างด้วย ROFL
ในบทช่วยสอนนี้ ผมจะสาธิตวิธีการสร้างแอป TypeScript ขนาดเล็ก สร้างคีย์ secp256k1 ภายใน ROFL จะใช้ @oasisprotocol/rofl-client TypeScript SDK ซึ่งสื่อสารกับ appd REST API ภายใต้พื้นผิว แอป TypeScript จะ ยัง:

จะมีsmoke test แบบง่ายๆ ที่พิมพ์ไปยัง บันทึก

ข้อกำหนดเบื้องต้น

เพื่อทำตามขั้นตอนที่อธิบายไว้ในคู่มือนี้ คุณจะ ต้องมี:

  • Node.js 20+ และ Docker (หรือ Podman)
  • Oasis CLI และโทเค็น TEST อย่างน้อย 120 โทเค็นในกระเป๋าเงินของคุณ (Oasis Testnet faucet)
  • Base Sepiola test ETH จำนวนหนึ่ง (Base Sepiola faucet)

สำหรับรายละเอียดการตั้งค่า โปรดดูเอกสารเกี่ยวกับ Quickstart Prerequisites

Init App

ขั้นตอนแรกคือการเริ่มต้นแอปใหม่โดยใช้ Oasis CLI

oasis rofl init rofl-keygen
cd rofl-keygen

Create App

ในขณะที่สร้างแอปบน Testnet คุณจะต้องฝากโทเค็น กำหนด 100 โทเค็น TEST ใน จุดนี้

oasis rofl create --network testnet

เป็นเอาต์พุต CLI จะสร้าง App ID แสดงด้วย rofl1….

Init โปรเจกต์ Hardhat (TypeScript)

ตอนนี้ คุณพร้อมที่จะเริ่มต้น โปรเจกต์

npx hardhat init

เนื่องจากเรากำลังนำเสนอแอป TypeScript เลือก TypeScript เมื่อได้รับแจ้ง จากนั้นยอมรับค่าเริ่มต้น
ขั้นตอนถัดไปคือการเพิ่ม runtime deps ขนาดเล็กสำหรับใช้งานนอก Hardhat

npm i @oasisprotocol/rofl-client ethers dotenv @types/node
npm i -D tsx

เทมเพลต TypeScript ของ Hardhat สร้าง tsconfig.json โดยอัตโนมัติ เราจำเป็นต้องเพิ่มสคริปต์เล็กๆ เพื่อให้โค้ดแอปสามารถคอมไพล์ไปยัง dist/

// tsconfig.json
{
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist"
},
"include": ["src"]
}

โครงสร้างแอป

ในส่วนนี้ เราจะเพิ่มไฟล์ TS ขนาดเล็กสองสามไฟล์และสัญญา Solidity หนึ่งฉบับ

src/
├── appd.ts # thin wrapper over @oasisprotocol/rofl-client
├── evm.ts # ethers helpers (provider, wallet, tx, deploy)
├── keys.ts # tiny helpers (checksum)
└── scripts/
├── deploy-contract.ts # generic deploy script for compiled artifacts
└── smoke-test.ts # end-to-end demo (logs)
contracts/
└── Counter.sol # sample contract

  1. src/appd.ts — thin wrapper over the SDK ที่นี่ คุณจะต้องใช้ไคลเอนต์อย่างเป็นทางการเพื่อสื่อสารกับ appd (UNIX socket) เราจะต้องเก็บ local‑dev fallback อย่างชัดเจนเมื่อรันนอก ROFL

src/appd.ts

import {existsSync} from 'node:fs';
import {
RoflClient,
KeyKind,
ROFL_SOCKET_PATH
} from '@oasisprotocol/rofl-client';
const client = new RoflClient(); // UDS: /run/rofl-appd.sock
export async function getAppId(): Promise<string> {
return client.getAppId();
}
/**
* Generates (or deterministically re-derives) a secp256k1 key inside ROFL and
* returns it as a 0x-prefixed hex string (for ethers.js Wallet).
*
* Local development ONLY (outside ROFL): If the socket is missing and you set
* ALLOW_LOCAL_DEV=true and LOCAL_DEV_SK=0x<64-hex>, that value is used.
*/
export async function getEvmSecretKey(keyId: string): Promise<string> {
if (existsSync(ROFL_SOCKET_PATH)) {
const hex = await client.generateKey(keyId, KeyKind.SECP256K1);
return hex.startsWith('0x') ? hex : `0x${hex}`;
}
const allow = process.env.ALLOW_LOCAL_DEV === 'true';
const pk = process.env.LOCAL_DEV_SK;
if (allow && pk && /^0x[0-9a-fA-F]{64}$/.test(pk)) return pk;
throw new Error(
'rofl-appd socket not found and no LOCAL_DEV_SK provided (dev only).'
);
}

2. src/evm.ts — ethers helpers

import {
JsonRpcProvider,
Wallet,
parseEther,
type TransactionReceipt,
ContractFactory
} from "ethers";
export function makeProvider(rpcUrl: string, chainId: number) {
return new JsonRpcProvider(rpcUrl, chainId);
}
export function connectWallet(
skHex: string,
rpcUrl: string,
chainId: number
): Wallet {
const w = new Wallet(skHex);
return w.connect(makeProvider(rpcUrl, chainId));
}
export async function signPersonalMessage(wallet: Wallet, msg: string) {
return wallet.signMessage(msg);
}
export async function sendEth(
wallet: Wallet,
to: string,
amountEth: string
): Promise<TransactionReceipt> {
const tx = await wallet.sendTransaction({
to,
value: parseEther(amountEth)
});
const receipt = await tx.wait();
if (receipt == null) {
throw new Error("Transaction dropped or replaced before confirmation");
}
return receipt;
}
export async function deployContract(
wallet: Wallet,
abi: any[],
bytecode: string,
args: unknown[] = []
): Promise<{ address: string; receipt: TransactionReceipt }> {
const factory = new ContractFactory(abi, bytecode, wallet);
const contract = await factory.deploy(...args);
const deployTx = contract.deploymentTransaction();
const receipt = await deployTx?.wait();
await contract.waitForDeployment();
if (!receipt) {
throw new Error("Deployment TX not mined");
}
return { address: contract.target as string, receipt };
}

3. src/keys.ts — tiny helpers

import { Wallet, getAddress } from "ethers";
export function secretKeyToWallet(skHex: string): Wallet {
return new Wallet(skHex);
}
export function checksumAddress(addr: string): string {
return getAddress(addr);
}

4. src/scripts/smoke-test.ts — single end‑to‑end flow

นี่เป็นขั้นตอนที่สำคัญเนื่องจากสคริปต์นี้มีหลายฟังก์ชัน:

  • พิมพ์ App ID (ภายใน ROFL), แอดเดรส และ ข้อความที่ลงนาม
  • รอการ เติมเงิน
  • ปรับใช้ counter contract

import "dotenv/config";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { getAppId, getEvmSecretKey } from "../appd.js";
import { secretKeyToWallet, checksumAddress } from "../keys.js";
import { makeProvider, signPersonalMessage, sendEth, deployContract } from "../evm.js";
import { formatEther, JsonRpcProvider } from "ethers";
const RPC_URL = process.env.BASE_RPC_URL ?? "https://sepolia.base.org";
const CHAIN_ID = Number(process.env.BASE_CHAIN_ID ?? "84532");
const KEY_ID = process.env.KEY_ID ?? "evm:base:sepolia";
function sleep(ms: number): Promise<void> {
return new Promise((r) => setTimeout(r, ms));
}
async function waitForFunding(
provider: JsonRpcProvider,
addr: string,
minWei: bigint = 1n,
timeoutMs = 15 * 60 * 1000,
pollMs = 5_000
): Promise<bigint> {
const start = Date.now();
while (Date.now() - start < timeoutMs) {
const bal = await provider.getBalance(addr);
if (bal >= minWei) return bal;
console.log(`Waiting for funding... current balance=${formatEther(bal)} ETH`);
await sleep(pollMs);
}
throw new Error("Timed out waiting for funding.");
}
async function main() {
const appId = await getAppId().catch(() => null);
console.log(`ROFL App ID: ${appId ?? "(unavailable outside ROFL)"}`);
const sk = await getEvmSecretKey(KEY_ID);
// NOTE: This demo trusts the configured RPC provider. For production, prefer a
// light client (for example, Helios) so you can verify remote chain state.
const wallet = secretKeyToWallet(sk).connect(makeProvider(RPC_URL, CHAIN_ID));
const addr = checksumAddress(await wallet.getAddress());
console.log(`EVM address (Base Sepolia): ${addr}`);
const msg = "hello from rofl";
const sig = await signPersonalMessage(wallet, msg);
console.log(`Signed message: "${msg}"`);
console.log(`Signature: ${sig}`);
const provider = wallet.provider as JsonRpcProvider;
let bal = await provider.getBalance(addr);
if (bal === 0n) {
console.log("Please fund the above address with Base Sepolia ETH to continue.");
bal = await waitForFunding(provider, addr);
}
console.log(`Balance detected: ${formatEther(bal)} ETH`);
const artifactPath = join(process.cwd(), "artifacts", "contracts", "Counter.sol", "Counter.json");
const artifact = JSON.parse(readFileSync(artifactPath, "utf8"));
if (!artifact?.abi || !artifact?.bytecode) {
throw new Error("Counter artifact missing abi/bytecode");
}
const { address: contractAddress, receipt: deployRcpt } =
await deployContract(wallet, artifact.abi, artifact.bytecode, []);
console.log(`Deployed Counter at ${contractAddress} (tx=${deployRcpt.hash})`);
console.log("Smoke test completed successfully!");
}
main().catch((e) => {
console.error(e);
process.exit(1);
});

5. contracts/Counter.sol — minimal sample

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
contract Counter {
uint256 private _value;
event Incremented(uint256 v);
event Set(uint256 v);
function current() external view returns (uint256) { return _value; }
function inc() external { unchecked { _value += 1; } emit Incremented(_value); }
function set(uint256 v) external { _value = v; emit Set(v); }
}

6. src/scripts/deploy-contract.ts — generic deployer

import "dotenv/config";
import { readFileSync } from "node:fs";
import { getEvmSecretKey } from "../appd.js";
import { secretKeyToWallet } from "../keys.js";
import { makeProvider, deployContract } from "../evm.js";
const KEY_ID = process.env.KEY_ID ?? "evm:base:sepolia";
const RPC_URL = process.env.BASE_RPC_URL ?? "https://sepolia.base.org";
const CHAIN_ID = Number(process.env.BASE_CHAIN_ID ?? "84532");
/**
* Usage:
* npm run deploy-contract -- ./artifacts/MyContract.json '[arg0, arg1]'
* The artifact must contain { abi, bytecode }.
*/
async function main() {
const [artifactPath, ctorJson = "[]"] = process.argv.slice(2);
if (!artifactPath) {
console.error("Usage: npm run deploy-contract -- <artifact.json> '[constructorArgsJson]'");
process.exit(2);
}
const artifactRaw = readFileSync(artifactPath, "utf8");
const artifact = JSON.parse(artifactRaw);
const { abi, bytecode } = artifact ?? {};
if (!abi || !bytecode) {
throw new Error("Artifact must contain { abi, bytecode }");
}
let args: unknown[];
try {
args = JSON.parse(ctorJson);
if (!Array.isArray(args)) throw new Error("constructor args must be a JSON array");
} catch (e) {
throw new Error(`Failed to parse constructor args JSON: ${String(e)}`);
}
const sk = await getEvmSecretKey(KEY_ID);
// NOTE: This demo trusts the configured RPC provider. For production, prefer a
// light client (for example, Helios) so you can verify remote chain state.
const wallet = secretKeyToWallet(sk).connect(makeProvider(RPC_URL, CHAIN_ID));
const { address, receipt } = await deployContract(wallet, abi, bytecode, args);
console.log(JSON.stringify({ contractAddress: address, txHash: receipt.hash, status: receipt.status }, null, 2));
}
main().catch((e) => {
console.error(e);
process.exit(1);
});

Hardhat (contracts only)

ในขั้นตอนนี้ เราจะต้องใช้การกำหนดค่าขั้นต่ำในการคอมไพล์ Counter.sol

hardhat.config.ts

import type { HardhatUserConfig } from "hardhat/config";
const config: HardhatUserConfig = {
solidity: {
version: "0.8.24",
settings: {
optimizer: { enabled: true, runs: 200 }
}
},
paths: {
sources: "./contracts",
artifacts: "./artifacts",
cache: "./cache"
}
};
export default config;

จุดที่ควรสังเกตคือการคอมไพล์ในเครื่องเป็นตัวเลือก ดังนั้นคุณสามารถข้ามได้หากต้องการ ขั้นตอนถัดไปคือทางเลือก — ลบไฟล์ contracts/Lock.sol ที่มีอยู่หรือคุณสามารถอัปเดตเป็น Solidity version 0.8.24

npx hardhat compile

Containerize

นี่เป็นขั้นตอนที่สำคัญ ที่นี่ คุณต้องใช้ Dockerfile ที่สร้าง TS และคอมไพล์สัญญา ไฟล์จะรัน smoke test ครั้งเดียว จากนั้นจะพักรอในขณะที่คุณตรวจสอบ บันทึก

Dockerfile

FROM node:20-alpine
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm ci
COPY tsconfig.json ./
COPY src ./src
COPY contracts ./contracts
COPY hardhat.config.ts ./
RUN npm run build && npx hardhat compile && npm prune --omit=dev
ENV NODE_ENV=production
CMD ["sh", "-c", "node dist/scripts/smoke-test.js || true; tail -f /dev/null"]

ถัดไป คุณต้องเมานต์ appd socket ที่จัดเตรียมโดย ROFL มั่นใจได้ว่าไม่มีพอร์ตสาธารณะถูกเปิดเผยใน กระบวนการ

compose.yaml

services:
demo:
image: docker.io/YOURUSER/rofl-keygen:0.1.0
platform: linux/amd64
environment:
- KEY_ID=${KEY_ID:-evm:base:sepolia}
- BASE_RPC_URL=${BASE_RPC_URL:-https://sepolia.base.org}
- BASE_CHAIN_ID=${BASE_CHAIN_ID:-84532}
volumes:
- /run/rofl-appd.sock:/run/rofl-appd.sock

Build the image

สิ่งสำคัญที่ต้องจำไว้คือ ROFL รันได้เฉพาะบนฮาร์ดแวร์ที่เปิดใช้งาน Intel TDX เท่านั้น ดังนั้น หากคุณกำลังคอมไพล์อิมเมจบนโฮสต์อื่น เช่น macOS การส่งพารามิเตอร์ — platform linux/amd64 เป็นขั้นตอนเพิ่มเติมที่ จำเป็น

docker buildx build --platform linux/amd64 \
-t docker.io/YOURUSER/rofl-keygen:0.1.0 --push .

ประเด็นที่น่าสนใจที่ควรทราบคือคุณสามารถเลือกความปลอดภัยและการตรวจสอบได้เพิ่มเติม คุณเพียงแค่ต้องปักหมุด digest และใช้ image: …@sha256:… ใน compose.yaml

Build ROFL bundle

มีขั้นตอนที่คุณต้องทำก่อนรันคำสั่ง oasis rofl build เนื่องจากการสร้างเซกเมนต์อิมเมจมาหลังจากการสร้างคอนเทนเนอร์ คุณจะต้องอัปเดต services.demo.image ใน compose.yaml เป็นอิมเมจที่คุณสร้าง
สำหรับโปรเจกต์ TypeScript แบบง่ายๆ เช่นนี้ บางครั้งอาจมีความเป็นไปได้ว่าขนาดอิมเมจจะใหญ่กว่าที่คาดไว้ ดังนั้นจึงแนะนำให้อัปเดตส่วน resources ของ rofl.yaml เป็นอย่างน้อย: memory: 1024 และ storage.size: 4096
ตอนนี้ คุณ พร้อมแล้ว

oasis rofl build

คุณสามารถเผยแพร่ enclave identities และ config ต่อไป

oasis rofl update

Deploy

นี่เป็นขั้นตอนที่ค่อนข้างง่ายซึ่งคุณปรับใช้ไปยังผู้ให้บริการ Testnet

oasis rofl deploy

End‑to‑end (Base Sepolia)

นี่เป็นกระบวนการ 2 ขั้นตอน แม้ว่าขั้นตอนที่สองจะเป็นตัวเลือก
ก่อนอื่น คุณดู smoke‑test logs

oasis rofl machine logs

หากคุณทำตามขั้นตอนทั้งหมดจนถึงตอนนี้อย่างถูกต้อง คุณจะเห็นใน เอาต์พุต:

  • App ID
  • แอดเดรส EVM และ ข้อความที่ลงนาม
  • แจ้งให้เติมเงินไปยัง แอดเดรส
  • เมื่อการเติมเงินเสร็จสิ้น การปรับใช้ Counter.sol

ถัดไป local dev ที่นี่ คุณต้องรัน npm run build:all เพื่อคอมไพล์โค้ด TypeScript และสัญญา Solidity ข้ามขั้นตอนนี้หากไม่ จำเป็น

export ALLOW_LOCAL_DEV=true
export LOCAL_DEV_SK=0x<64-hex-dev-secret-key> # DO NOT USE IN PROD
npm run smoke-test

ความปลอดภัย & หมายเหตุที่ควร จำ

  • บันทึกของผู้ให้บริการไม่ได้เข้ารหัสลับขณะพัก ดังนั้น ห้าม บันทึกคีย์ ลับ
  • appd socket /run/rofl-appd.sock มีอยู่ เฉพาะภายใน ROFL
  • อาจมีขอบเขตอัตราใน RPC สาธารณะ ดังนั้นจึงแนะนำให้เลือกใช้ Base RPC URL เฉพาะ

มี key generation demo ใน Oasis GitHub ซึ่งคุณสามารถอ้างอิงเป็นตัวอย่างของบทช่วยสอนนี้ https://github.com/oasisprotocol/demo-rofl-keygen

ตอนนี้ที่คุณสร้างคีย์ใน ROFL ด้วย appd ลงนามข้อความ ปรับใช้สัญญา และย้าย ETH บน Base Sepolia สำเร็จแล้ว แจ้งให้เราทราบในส่วนความคิดเห็นเกี่ยวกับความคิดเห็นของคุณ สำหรับการแชทอย่างรวดเร็วกับทีมวิศวกร Oasis เพื่อขอความช่วยเหลือเกี่ยวกับปัญหาเฉพาะ คุณสามารถโพสต์ความคิดเห็นของคุณใน dev-central channel ใน Discord อย่างเป็นทางการ

เผยแพร่ครั้งแรกที่ https://dev.to เมื่อวันที่ 20 กุมภาพันธ์ 2026


Guide To Cross-Chain Key Generation (EVM / Base) With Oasis ROFL ได้รับการเผยแพร่ครั้งแรกใน Coinmonks บน Medium ซึ่งผู้คนกำลังสนทนาต่อโดยการเน้นและตอบสนองต่อเรื่องราวนี้

โอกาสทางการตลาด
CROSS โลโก้
ราคา CROSS(CROSS)
$0.06045
$0.06045$0.06045
-0.86%
USD
CROSS (CROSS) กราฟราคาสด
ข้อจำกัดความรับผิดชอบ: บทความที่โพสต์ซ้ำในไซต์นี้มาจากแพลตฟอร์มสาธารณะและมีไว้เพื่อจุดประสงค์ในการให้ข้อมูลเท่านั้น ซึ่งไม่ได้สะท้อนถึงมุมมองของ MEXC แต่อย่างใด ลิขสิทธิ์ทั้งหมดยังคงเป็นของผู้เขียนดั้งเดิม หากคุณเชื่อว่าเนื้อหาใดละเมิดสิทธิของบุคคลที่สาม โปรดติดต่อ crypto.news@mexc.com เพื่อลบออก MEXC ไม่รับประกันความถูกต้อง ความสมบูรณ์ หรือความทันเวลาของเนื้อหาใดๆ และไม่รับผิดชอบต่อการดำเนินการใดๆ ที่เกิดขึ้นตามข้อมูลที่ให้มา เนื้อหานี้ไม่ถือเป็นคำแนะนำทางการเงิน กฎหมาย หรือคำแนะนำจากผู้เชี่ยวชาญอื่นๆ และไม่ถือว่าเป็นคำแนะนำหรือการรับรองจาก MEXC

คุณอาจชอบเช่นกัน

ไม่จำเป็นต้องเปลี่ยนแปลงฉันทามติ: CPO ของ Starkware สร้างธุรกรรม Bitcoin ที่ปลอดภัยจากควอนตัมจากกฎที่มีอยู่ – ข่าว Bitcoin เด่น

ไม่จำเป็นต้องเปลี่ยนแปลงฉันทามติ: CPO ของ Starkware สร้างธุรกรรม Bitcoin ที่ปลอดภัยจากควอนตัมจากกฎที่มีอยู่ – ข่าว Bitcoin เด่น

โพสต์ No Consensus Changes Needed: Starkware CPO Builds Quantum-Safe Bitcoin Transactions From Existing Rules – Featured Bitcoin News ปรากฏบน BitcoinEthereumNews
แชร์
BitcoinEthereumNews2026/04/10 13:40
เวียดนามเข้มงวดควบคุมคริปโตด้วยโครงการแลกเปลี่ยนในประเทศใหม่

เวียดนามเข้มงวดควบคุมคริปโตด้วยโครงการแลกเปลี่ยนในประเทศใหม่

เวียดนามเสริมสร้างการกำกับดูแลคริปโตด้วยการเปิดตัว CAEX มุ่งควบคุมการซื้อขาย เพิ่มความปลอดภัย และจำกัดการครอบงำของตลาดแลกเปลี่ยนเงินตราต่างประเทศ เวียดนาม
แชร์
LiveBitcoinNews2026/04/10 13:45
รัฐบาลญี่ปุ่นอนุมัติให้สินทรัพย์คริปโตเป็นผลิตภัณฑ์ทางการเงินอย่างเป็นทางการ! นี่คือรายละเอียด

รัฐบาลญี่ปุ่นอนุมัติให้สินทรัพย์คริปโตเป็นผลิตภัณฑ์ทางการเงินอย่างเป็นทางการ! นี่คือรายละเอียด

รัฐมนตรีว่าการกระทรวงการคลังญี่ปุ่น Satsuki Katayama เน้นย้ำว่าการเปลี่ยนแปลงนี้มีเป้าหมายเพื่อให้แน่ใจว่าตลาดมีความยุติธรรมและโปร่งใส อ่านต่อ: รัฐบาลญี่ปุ่น
แชร์
Bitcoinsistemi2026/04/10 15:37

ข่าวสดตลอด 24/7

มากกว่า