kernel-eth/src/index.ts

229 lines
5.4 KiB
TypeScript

import { ActiveQuery, addHandler, handleMessage } from "libkmodule";
import { createClient, RpcNetwork } from "@lumeweb/kernel-rpc-client";
import init, { Client } from "../wasm/helios_ts.js";
// @ts-ignore
import wasm from "../wasm/helios_ts_bg.wasm?base64";
import { Buffer } from "buffer";
import { RPCResponse } from "@lumeweb/interface-relay";
import { ConsensusRequest, ExecutionRequest } from "./types.js";
const CHECKPOINT =
"0x694433ba78dd08280df68d3713c0f79d668dbee9e0922ec2346fcceb1dc3daa9";
onmessage = handleMessage;
let moduleReadyResolve: Function;
let moduleReady: Promise<void> = new Promise((resolve) => {
moduleReadyResolve = resolve;
});
let client: Client;
let rpc: RpcNetwork;
addHandler("presentSeed", handlePresentSeed);
addHandler("ready", handleReady);
[
"eth_accounts",
"eth_requestAccounts",
"eth_getBalance",
"eth_chainId",
"eth_blockNumber",
"eth_getTransactionByHash",
"eth_getTransactionCount",
"eth_getBlockTransactionCountByHash",
"eth_getBlockTransactionCountByNumber",
"eth_getCode",
"eth_call",
"eth_estimateGas",
"eth_gasPrice",
"eth_maxPriorityFeePerGas",
"eth_sendRawTransaction",
"eth_getTransactionReceipt",
"eth_getLogs",
"net_version",
].forEach((rpcMethod) => {
addHandler(rpcMethod, async (aq: ActiveQuery) => {
aq.callerInput = {
params: aq.callerInput || {},
method: rpcMethod,
};
aq.respond(await handleRpcMethod(aq));
});
});
async function handlePresentSeed() {
await setup();
moduleReadyResolve();
}
async function handleRpcMethod(aq: ActiveQuery) {
await moduleReady;
switch (aq.callerInput?.method) {
case "eth_accounts":
case "eth_requestAccounts": {
return [];
}
case "eth_getBalance": {
return client.get_balance(
aq.callerInput?.params[0],
aq.callerInput?.params[1]
);
}
case "eth_chainId": {
return client.chain_id();
}
case "eth_blockNumber": {
return client.get_block_number();
}
case "eth_getTransactionByHash": {
let tx = await client.get_transaction_by_hash(aq.callerInput?.params[0]);
return mapToObj(tx);
}
case "eth_getTransactionCount": {
return client.get_transaction_count(
aq.callerInput?.params[0],
aq.callerInput?.params[1]
);
}
case "eth_getBlockTransactionCountByHash": {
return client.get_block_transaction_count_by_hash(
aq.callerInput?.params[0]
);
}
case "eth_getBlockTransactionCountByNumber": {
return client.get_block_transaction_count_by_number(
aq.callerInput?.params[0]
);
}
case "eth_getCode": {
return client.get_code(
aq.callerInput?.params[0],
aq.callerInput?.params[1]
);
}
case "eth_call": {
return client.call(aq.callerInput?.params[0], aq.callerInput?.params[1]);
}
case "eth_estimateGas": {
return client.estimate_gas(aq.callerInput?.params[0]);
}
case "eth_gasPrice": {
return client.gas_price();
}
case "eth_maxPriorityFeePerGas": {
return client.max_priority_fee_per_gas();
}
case "eth_sendRawTransaction": {
return client.send_raw_transaction(aq.callerInput?.params[0]);
}
case "eth_getTransactionReceipt": {
return client.get_transaction_receipt(aq.callerInput?.params[0]);
}
case "eth_getLogs": {
return client.get_logs(aq.callerInput?.params[0]);
}
case "net_version": {
return client.chain_id();
}
}
}
async function setup() {
rpc = createClient();
// @ts-ignore
await (
await rpc.ready
)();
await init(URL.createObjectURL(new Blob([Buffer.from(wasm, "base64")])));
(self as any).consensus_rpc_handler = async (
data: Map<string, string | any>
) => {
const method = data.get("method");
const path = data.get("path");
let query;
let ret: RPCResponse;
while (true) {
query = await rpc.simpleQuery({
query: {
module: "eth",
method: "consensus_request",
data: {
method,
path,
} as ConsensusRequest,
},
options: {
relayTimeout: 10,
queryTimeout: 10,
},
});
ret = await query.result;
if (ret?.data) {
break;
}
}
if (path.startsWith("/eth/v1/beacon/light_client/updates")) {
return JSON.stringify(ret.data);
}
return JSON.stringify({ data: ret.data });
};
(self as any).execution_rpc_handler = async (
data: Map<string, string | any>
) => {
const method = data.get("method");
let params = data.get("params");
params = JSON.parse(params);
let query;
let ret: RPCResponse;
while (true) {
query = await rpc.simpleQuery({
query: {
module: "eth",
method: "execution_request",
data: {
method,
params,
} as ExecutionRequest,
},
});
ret = await query.result;
if (ret?.data) {
break;
}
}
return JSON.stringify(ret.data);
};
client = new Client(CHECKPOINT);
await client.sync();
}
async function handleReady(aq: ActiveQuery) {
await moduleReady;
aq.respond();
}
function mapToObj(map: Map<any, any> | undefined): Object | undefined {
if (!map) return undefined;
return Array.from(map).reduce((obj: any, [key, value]) => {
obj[key] = value;
return obj;
}, {});
}