From 688302305596184c8da932b7202bb290fd5ded27 Mon Sep 17 00:00:00 2001 From: Jonathan Gimeno Date: Wed, 28 Jan 2026 10:21:16 +0100 Subject: [PATCH 1/2] client impl part 1 --- clients/evnode-viem.ts | 622 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 622 insertions(+) create mode 100644 clients/evnode-viem.ts diff --git a/clients/evnode-viem.ts b/clients/evnode-viem.ts new file mode 100644 index 0000000..2991ff2 --- /dev/null +++ b/clients/evnode-viem.ts @@ -0,0 +1,622 @@ +import { + type AccessList, + type Address, + type Client, + type Hex, + type Signature, + bytesToHex, + concat, + defineTransaction, + fromRlp, + hexToBigInt, + hexToBytes, + hexToSignature, + isHex, + keccak256, + recoverAddress, + toHex, + toRlp, +} from 'viem'; + +export const EVNODE_TX_TYPE = 0x76; +export const EVNODE_EXECUTOR_DOMAIN = 0x76; +export const EVNODE_SPONSOR_DOMAIN = 0x78; + +const EMPTY_BYTES = '0x' as const; +const TX_TYPE_HEX = toHex(EVNODE_TX_TYPE, { size: 1 }); +const EXECUTOR_DOMAIN_HEX = toHex(EVNODE_EXECUTOR_DOMAIN, { size: 1 }); +const SPONSOR_DOMAIN_HEX = toHex(EVNODE_SPONSOR_DOMAIN, { size: 1 }); + +type RlpValue = Hex | RlpValue[]; + +export interface Call { + to: Address | null; + value: bigint; + data: Hex; +} + +export interface EvNodeTransaction { + chainId: bigint; + nonce: bigint; + maxPriorityFeePerGas: bigint; + maxFeePerGas: bigint; + gasLimit: bigint; + calls: Call[]; + accessList: AccessList; + feePayerSignature?: Signature; +} + +export interface EvNodeSignedTransaction { + transaction: EvNodeTransaction; + executorSignature: Signature; +} + +export interface SponsorableIntent { + tx: EvNodeTransaction; + executorSignature: Signature; + executorAddress: Address; +} + +export interface HashSigner { + address: Address; + // Must sign the raw 32-byte hash without EIP-191 prefixing. + signHash: (hash: Hex) => Promise; +} + +export interface EvnodeClientOptions { + client: Client; + executor?: HashSigner; + sponsor?: HashSigner; +} + +export interface EvnodeSendArgs { + calls: Call[]; + executor?: HashSigner; + chainId?: bigint; + nonce?: bigint; + maxFeePerGas?: bigint; + maxPriorityFeePerGas?: bigint; + gasLimit?: bigint; + accessList?: AccessList; +} + +export interface EvnodeIntentArgs { + calls: Call[]; + executor?: HashSigner; + chainId?: bigint; + nonce?: bigint; + maxFeePerGas?: bigint; + maxPriorityFeePerGas?: bigint; + gasLimit?: bigint; + accessList?: AccessList; +} + +export interface EvnodeSponsorArgs { + intent: SponsorableIntent; + sponsor?: HashSigner; +} + +export function encodeSignedTransaction(signedTx: EvNodeSignedTransaction): Hex { + const fields = buildPayloadFields(signedTx.transaction, true); + const execSig = normalizeSignature(signedTx.executorSignature); + const envelope = toRlp([ + ...fields, + execSig.v, + hexToBigInt(execSig.r), + hexToBigInt(execSig.s), + ]); + return concat([TX_TYPE_HEX, envelope]); +} + +export function decodeEvNodeTransaction(encoded: Hex): EvNodeSignedTransaction { + const bytes = hexToBytes(encoded); + if (bytes.length === 0 || bytes[0] !== EVNODE_TX_TYPE) { + throw new Error('Invalid EvNode transaction type'); + } + + const decoded = fromRlp(bytesToHex(bytes.slice(1))) as RlpValue; + if (!Array.isArray(decoded)) { + throw new Error('Invalid EvNode transaction payload'); + } + + if (decoded.length !== 11) { + throw new Error('Invalid EvNode transaction length'); + } + + const [ + chainId, + nonce, + maxPriorityFeePerGas, + maxFeePerGas, + gasLimit, + calls, + accessList, + feePayerSignature, + v, + r, + s, + ] = decoded; + + const transaction: EvNodeTransaction = { + chainId: hexToBigIntSafe(chainId), + nonce: hexToBigIntSafe(nonce), + maxPriorityFeePerGas: hexToBigIntSafe(maxPriorityFeePerGas), + maxFeePerGas: hexToBigIntSafe(maxFeePerGas), + gasLimit: hexToBigIntSafe(gasLimit), + calls: decodeCalls(calls), + accessList: decodeAccessList(accessList), + feePayerSignature: decodeSignature(feePayerSignature), + }; + + const executorSignature = signatureFromParts(v, r, s); + return { transaction, executorSignature }; +} + +export function computeExecutorSigningHash(tx: EvNodeTransaction): Hex { + const payload = toRlp(buildPayloadFields(tx, false)); + return keccak256(concat([EXECUTOR_DOMAIN_HEX, payload])); +} + +export function computeSponsorSigningHash( + tx: EvNodeTransaction, + executorAddress: Address, +): Hex { + const payload = toRlp(buildPayloadFields(tx, false)); + return keccak256(concat([SPONSOR_DOMAIN_HEX, executorAddress, payload])); +} + +export function computeTxHash(signedTx: EvNodeSignedTransaction): Hex { + return keccak256(encodeSignedTransaction(signedTx)); +} + +export async function recoverExecutor( + signedTx: EvNodeSignedTransaction, +): Promise
{ + const hash = computeExecutorSigningHash(signedTx.transaction); + return recoverAddress({ hash, signature: normalizeSignature(signedTx.executorSignature) }); +} + +export async function recoverSponsor( + tx: EvNodeTransaction, + executorAddress: Address, +): Promise
{ + if (!tx.feePayerSignature) return null; + const hash = computeSponsorSigningHash(tx, executorAddress); + return recoverAddress({ hash, signature: normalizeSignature(tx.feePayerSignature) }); +} + +export async function signAsExecutor( + tx: EvNodeTransaction, + signer: HashSigner, +): Promise { + const hash = computeExecutorSigningHash(tx); + return signer.signHash(hash); +} + +export async function signAsSponsor( + tx: EvNodeTransaction, + executorAddress: Address, + signer: HashSigner, +): Promise { + const hash = computeSponsorSigningHash(tx, executorAddress); + return signer.signHash(hash); +} + +export function estimateIntrinsicGas(calls: Call[]): bigint { + let gas = 21000n; + + for (const call of calls) { + if (call.to === null) gas += 32000n; + + for (const byte of hexToBytes(call.data)) { + gas += byte === 0 ? 4n : 16n; + } + } + + return gas; +} + +export function validateEvNodeTx(tx: EvNodeTransaction): void { + if (tx.calls.length === 0) { + throw new Error('EvNode transaction must include at least one call'); + } + + for (let i = 1; i < tx.calls.length; i += 1) { + if (tx.calls[i].to === null) { + throw new Error('Only the first call may be CREATE'); + } + } +} + +export function evnodeActions(client: Client) { + return { + async sendEvNodeTransaction(args: { + calls: Call[]; + executor: HashSigner; + chainId?: bigint; + nonce?: bigint; + maxFeePerGas?: bigint; + maxPriorityFeePerGas?: bigint; + gasLimit?: bigint; + accessList?: AccessList; + }): Promise { + const base = await resolveBaseFields(client, args.executor.address, { + chainId: args.chainId, + nonce: args.nonce, + maxFeePerGas: args.maxFeePerGas, + maxPriorityFeePerGas: args.maxPriorityFeePerGas, + gasLimit: args.gasLimit, + accessList: args.accessList, + }, args.calls); + + const tx: EvNodeTransaction = { + ...base, + calls: args.calls, + feePayerSignature: undefined, + }; + + validateEvNodeTx(tx); + + const executorSignature = await signAsExecutor(tx, args.executor); + const signedTx: EvNodeSignedTransaction = { + transaction: tx, + executorSignature, + }; + + const serialized = encodeSignedTransaction(signedTx); + return client.request({ + method: 'eth_sendRawTransaction', + params: [serialized], + }) as Promise; + }, + + async createSponsorableIntent(args: { + calls: Call[]; + executor: HashSigner; + chainId?: bigint; + nonce?: bigint; + maxFeePerGas?: bigint; + maxPriorityFeePerGas?: bigint; + gasLimit?: bigint; + accessList?: AccessList; + }): Promise { + const base = await resolveBaseFields(client, args.executor.address, { + chainId: args.chainId, + nonce: args.nonce, + maxFeePerGas: args.maxFeePerGas, + maxPriorityFeePerGas: args.maxPriorityFeePerGas, + gasLimit: args.gasLimit, + accessList: args.accessList, + }, args.calls); + + const tx: EvNodeTransaction = { + ...base, + calls: args.calls, + feePayerSignature: undefined, + }; + + validateEvNodeTx(tx); + + const executorSignature = await signAsExecutor(tx, args.executor); + + return { + tx, + executorSignature, + executorAddress: args.executor.address, + }; + }, + + async sponsorIntent(args: { + intent: SponsorableIntent; + sponsor: HashSigner; + }): Promise { + const sponsorSignature = await signAsSponsor( + args.intent.tx, + args.intent.executorAddress, + args.sponsor, + ); + + return { + transaction: { + ...args.intent.tx, + feePayerSignature: sponsorSignature, + }, + executorSignature: args.intent.executorSignature, + }; + }, + + serializeEvNodeTransaction(signedTx: EvNodeSignedTransaction): Hex { + return encodeSignedTransaction(signedTx); + }, + + deserializeEvNodeTransaction(encoded: Hex): EvNodeSignedTransaction { + return decodeEvNodeTransaction(encoded); + }, + }; +} + +export function createEvnodeClient(options: EvnodeClientOptions) { + const actions = evnodeActions(options.client); + let defaultExecutor = options.executor; + let defaultSponsor = options.sponsor; + + const requireExecutor = (executor?: HashSigner) => { + const resolved = executor ?? defaultExecutor; + if (!resolved) throw new Error('Executor signer is required'); + return resolved; + }; + + const requireSponsor = (sponsor?: HashSigner) => { + const resolved = sponsor ?? defaultSponsor; + if (!resolved) throw new Error('Sponsor signer is required'); + return resolved; + }; + + return { + client: options.client, + actions, + setDefaultExecutor(executor: HashSigner) { + defaultExecutor = executor; + }, + setDefaultSponsor(sponsor: HashSigner) { + defaultSponsor = sponsor; + }, + send(args: EvnodeSendArgs): Promise { + return actions.sendEvNodeTransaction({ + ...args, + executor: requireExecutor(args.executor), + }); + }, + createIntent(args: EvnodeIntentArgs): Promise { + return actions.createSponsorableIntent({ + ...args, + executor: requireExecutor(args.executor), + }); + }, + sponsorIntent(args: EvnodeSponsorArgs): Promise { + return actions.sponsorIntent({ + intent: args.intent, + sponsor: requireSponsor(args.sponsor), + }); + }, + async sponsorAndSend(args: EvnodeSponsorArgs): Promise { + const signed = await actions.sponsorIntent({ + intent: args.intent, + sponsor: requireSponsor(args.sponsor), + }); + const serialized = actions.serializeEvNodeTransaction(signed); + return options.client.request({ + method: 'eth_sendRawTransaction', + params: [serialized], + }) as Promise; + }, + serialize: actions.serializeEvNodeTransaction, + deserialize: actions.deserializeEvNodeTransaction, + }; +} + +export const evnodeSerializer = defineTransaction({ + type: 'evnode', + typeId: EVNODE_TX_TYPE, + serialize: (tx) => encodeSignedTransaction(tx as EvNodeSignedTransaction), + deserialize: (bytes) => decodeEvNodeTransaction(bytes as Hex), +}); + +export function hashSignerFromRpcClient( + client: Client, + address: Address, +): HashSigner { + return { + address, + signHash: async (hash) => { + // eth_sign is expected to sign raw bytes (no EIP-191 prefix). + const signature = await client.request({ + method: 'eth_sign', + params: [address, hash], + }); + if (!isHex(signature)) { + throw new Error('eth_sign returned non-hex signature'); + } + return signature; + }, + }; +} + +function buildPayloadFields(tx: EvNodeTransaction, includeSponsorSig: boolean): RlpValue[] { + return [ + tx.chainId, + tx.nonce, + tx.maxPriorityFeePerGas, + tx.maxFeePerGas, + tx.gasLimit, + encodeCalls(tx.calls), + encodeAccessList(tx.accessList), + includeSponsorSig && tx.feePayerSignature + ? encodeSignatureList(tx.feePayerSignature) + : EMPTY_BYTES, + ]; +} + +function encodeCalls(calls: Call[]): RlpValue[] { + return calls.map((call) => [ + call.to ?? EMPTY_BYTES, + call.value, + call.data, + ]); +} + +function decodeCalls(value: RlpValue): Call[] { + if (!Array.isArray(value)) { + throw new Error('Invalid EvNode calls encoding'); + } + + return value.map((call) => { + if (!Array.isArray(call) || call.length !== 3) { + throw new Error('Invalid EvNode call encoding'); + } + + const [to, val, data] = call; + if (!isHex(to) || !isHex(val) || !isHex(data)) { + throw new Error('Invalid EvNode call values'); + } + + return { + to: to === EMPTY_BYTES ? null : (to as Address), + value: hexToBigIntSafe(val), + data, + }; + }); +} + +function encodeAccessList(accessList: AccessList): RlpValue[] { + return accessList.map((item) => [item.address, item.storageKeys]); +} + +function decodeAccessList(value: RlpValue): AccessList { + if (!Array.isArray(value)) { + throw new Error('Invalid access list encoding'); + } + + return value.map((item) => { + if (!Array.isArray(item) || item.length !== 2) { + throw new Error('Invalid access list item encoding'); + } + + const [address, storageKeys] = item; + if (!isHex(address) || !Array.isArray(storageKeys)) { + throw new Error('Invalid access list values'); + } + + return { + address: address as Address, + storageKeys: storageKeys.map((key) => { + if (!isHex(key)) throw new Error('Invalid storage key'); + return key; + }), + }; + }); +} + +function encodeSignatureList(signature: Signature): RlpValue[] { + const normalized = normalizeSignature(signature); + return [ + normalized.v, + hexToBigInt(normalized.r), + hexToBigInt(normalized.s), + ]; +} + +function decodeSignature(value: RlpValue): Signature | undefined { + if (value === EMPTY_BYTES) return undefined; + + if (!Array.isArray(value) || value.length !== 3) { + throw new Error('Invalid sponsor signature encoding'); + } + + const [v, r, s] = value; + return signatureFromParts(v, r, s); +} + +function signatureFromParts(v: RlpValue, r: RlpValue, s: RlpValue): Signature { + if (!isHex(v) || !isHex(r) || !isHex(s)) { + throw new Error('Invalid signature fields'); + } + + const vNumber = Number(hexToBigIntSafe(v)); + if (vNumber !== 0 && vNumber !== 1) { + throw new Error('Invalid signature v value'); + } + + return { + v: vNumber, + r: padTo32Bytes(r), + s: padTo32Bytes(s), + }; +} + +function normalizeSignature(signature: Signature): { v: number; r: Hex; s: Hex } { + const parsed = typeof signature === 'string' ? hexToSignature(signature) : signature; + + const v = Number(parsed.v); + const normalizedV = v === 27 || v === 28 ? v - 27 : v; + if (normalizedV !== 0 && normalizedV !== 1) { + throw new Error('Invalid signature v value'); + } + + return { + v: normalizedV, + r: padTo32Bytes(parsed.r), + s: padTo32Bytes(parsed.s), + }; +} + +function padTo32Bytes(value: Hex): Hex { + return toHex(hexToBigIntSafe(value), { size: 32 }); +} + +function hexToBigIntSafe(value: RlpValue): bigint { + if (!isHex(value)) throw new Error('Invalid hex value'); + return value === EMPTY_BYTES ? 0n : hexToBigInt(value); +} + +async function resolveBaseFields( + client: Client, + address: Address, + overrides: { + chainId?: bigint; + nonce?: bigint; + maxFeePerGas?: bigint; + maxPriorityFeePerGas?: bigint; + gasLimit?: bigint; + accessList?: AccessList; + }, + calls: Call[], +): Promise> { + const chainId = overrides.chainId ?? (await fetchChainId(client)); + const nonce = overrides.nonce ?? (await fetchNonce(client, address)); + const maxPriorityFeePerGas = + overrides.maxPriorityFeePerGas ?? (await fetchMaxPriorityFee(client)); + const maxFeePerGas = overrides.maxFeePerGas ?? (await fetchGasPrice(client)); + const gasLimit = overrides.gasLimit ?? estimateIntrinsicGas(calls); + const accessList = overrides.accessList ?? []; + + return { + chainId, + nonce, + maxPriorityFeePerGas, + maxFeePerGas, + gasLimit, + accessList, + }; +} + +async function fetchChainId(client: Client): Promise { + const result = await client.request({ method: 'eth_chainId' }); + if (!isHex(result)) throw new Error('eth_chainId returned non-hex'); + return hexToBigIntSafe(result); +} + +async function fetchNonce(client: Client, address: Address): Promise { + const result = await client.request({ + method: 'eth_getTransactionCount', + params: [address, 'pending'], + }); + if (!isHex(result)) throw new Error('eth_getTransactionCount returned non-hex'); + return hexToBigIntSafe(result); +} + +async function fetchMaxPriorityFee(client: Client): Promise { + try { + const result = await client.request({ method: 'eth_maxPriorityFeePerGas' }); + if (!isHex(result)) throw new Error('eth_maxPriorityFeePerGas returned non-hex'); + return hexToBigIntSafe(result); + } catch { + return 0n; + } +} + +async function fetchGasPrice(client: Client): Promise { + const result = await client.request({ method: 'eth_gasPrice' }); + if (!isHex(result)) throw new Error('eth_gasPrice returned non-hex'); + return hexToBigIntSafe(result); +} From 77965a06fa3a19833b3d83c5c75b71131c359bf5 Mon Sep 17 00:00:00 2001 From: Jonathan Gimeno Date: Thu, 29 Jan 2026 10:00:41 +0100 Subject: [PATCH 2/2] add tests for client --- .gitignore | 5 +- clients/evnode-viem.ts | 68 ++- clients/package-lock.json | 775 +++++++++++++++++++++++++++++++ clients/package.json | 16 + clients/test/evnode-basic.ts | 68 +++ clients/test/evnode-flows.ts | 126 +++++ clients/test/evnode-sponsored.ts | 116 +++++ clients/tsconfig.json | 11 + package-lock.json | 220 +++++++++ package.json | 19 + 10 files changed, 1406 insertions(+), 18 deletions(-) create mode 100644 clients/package-lock.json create mode 100644 clients/package.json create mode 100644 clients/test/evnode-basic.ts create mode 100644 clients/test/evnode-flows.ts create mode 100644 clients/test/evnode-sponsored.ts create mode 100644 clients/tsconfig.json create mode 100644 package-lock.json create mode 100644 package.json diff --git a/.gitignore b/.gitignore index 22a5da9..b057fee 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,9 @@ criterion/ *.tmp *.log +# Node dependencies +node_modules/ + # Environment files .env .env.local @@ -57,4 +60,4 @@ flamegraph.svg Thumbs.db # Docker build artifacts -/dist/ \ No newline at end of file +/dist/ diff --git a/clients/evnode-viem.ts b/clients/evnode-viem.ts index 2991ff2..09d880c 100644 --- a/clients/evnode-viem.ts +++ b/clients/evnode-viem.ts @@ -101,9 +101,9 @@ export function encodeSignedTransaction(signedTx: EvNodeSignedTransaction): Hex const execSig = normalizeSignature(signedTx.executorSignature); const envelope = toRlp([ ...fields, - execSig.v, - hexToBigInt(execSig.r), - hexToBigInt(execSig.s), + rlpHexFromBigInt(BigInt(execSig.v)), + rlpHexFromBigInt(hexToBigInt(execSig.r)), + rlpHexFromBigInt(hexToBigInt(execSig.s)), ]); return concat([TX_TYPE_HEX, envelope]); } @@ -161,7 +161,8 @@ export function computeSponsorSigningHash( tx: EvNodeTransaction, executorAddress: Address, ): Hex { - const payload = toRlp(buildPayloadFields(tx, false)); + const payload = encodePayloadFieldsNoList(tx, false); + // Sponsor hash preimage: 0x78 || executor_address (20 bytes) || RLP(field encodings without list header). return keccak256(concat([SPONSOR_DOMAIN_HEX, executorAddress, payload])); } @@ -424,23 +425,32 @@ export function hashSignerFromRpcClient( function buildPayloadFields(tx: EvNodeTransaction, includeSponsorSig: boolean): RlpValue[] { return [ - tx.chainId, - tx.nonce, - tx.maxPriorityFeePerGas, - tx.maxFeePerGas, - tx.gasLimit, + rlpHexFromBigInt(tx.chainId), + rlpHexFromBigInt(tx.nonce), + rlpHexFromBigInt(tx.maxPriorityFeePerGas), + rlpHexFromBigInt(tx.maxFeePerGas), + rlpHexFromBigInt(tx.gasLimit), encodeCalls(tx.calls), encodeAccessList(tx.accessList), includeSponsorSig && tx.feePayerSignature - ? encodeSignatureList(tx.feePayerSignature) + ? encodeSponsorSignature(tx.feePayerSignature) : EMPTY_BYTES, ]; } +function encodePayloadFieldsNoList( + tx: EvNodeTransaction, + includeSponsorSig: boolean, +): Hex { + const fields = buildPayloadFields(tx, includeSponsorSig); + const encodedFields = fields.map((field) => toRlp(field)); + return concat(encodedFields); +} + function encodeCalls(calls: Call[]): RlpValue[] { return calls.map((call) => [ call.to ?? EMPTY_BYTES, - call.value, + rlpHexFromBigInt(call.value), call.data, ]); } @@ -497,19 +507,24 @@ function decodeAccessList(value: RlpValue): AccessList { }); } -function encodeSignatureList(signature: Signature): RlpValue[] { +function encodeSponsorSignature(signature: Signature): RlpValue { + // Encode sponsor signature as 65-byte signature bytes (r || s || v). + // This matches the common Signature encoding used by alloy primitives. + if (typeof signature === 'string') { + return signature; + } const normalized = normalizeSignature(signature); - return [ - normalized.v, - hexToBigInt(normalized.r), - hexToBigInt(normalized.s), - ]; + const vByte = toHex(BigInt(normalized.v), { size: 1 }); + return concat([normalized.r, normalized.s, vByte]); } function decodeSignature(value: RlpValue): Signature | undefined { if (value === EMPTY_BYTES) return undefined; if (!Array.isArray(value) || value.length !== 3) { + if (isHex(value)) { + return signatureFromBytes(value); + } throw new Error('Invalid sponsor signature encoding'); } @@ -517,6 +532,21 @@ function decodeSignature(value: RlpValue): Signature | undefined { return signatureFromParts(v, r, s); } +function signatureFromBytes(value: Hex): Signature { + const bytes = hexToBytes(value); + if (bytes.length !== 65) { + throw new Error('Invalid sponsor signature length'); + } + const r = bytesToHex(bytes.slice(0, 32)); + const s = bytesToHex(bytes.slice(32, 64)); + const vRaw = bytes[64]; + const v = vRaw === 27 || vRaw === 28 ? vRaw - 27 : vRaw; + if (v !== 0 && v !== 1) { + throw new Error('Invalid signature v value'); + } + return { v, r: padTo32Bytes(r), s: padTo32Bytes(s) }; +} + function signatureFromParts(v: RlpValue, r: RlpValue, s: RlpValue): Signature { if (!isHex(v) || !isHex(r) || !isHex(s)) { throw new Error('Invalid signature fields'); @@ -554,6 +584,10 @@ function padTo32Bytes(value: Hex): Hex { return toHex(hexToBigIntSafe(value), { size: 32 }); } +function rlpHexFromBigInt(value: bigint): Hex { + return value === 0n ? EMPTY_BYTES : toHex(value); +} + function hexToBigIntSafe(value: RlpValue): bigint { if (!isHex(value)) throw new Error('Invalid hex value'); return value === EMPTY_BYTES ? 0n : hexToBigInt(value); diff --git a/clients/package-lock.json b/clients/package-lock.json new file mode 100644 index 0000000..6c9960e --- /dev/null +++ b/clients/package-lock.json @@ -0,0 +1,775 @@ +{ + "name": "@evstack/evnode-viem-client", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@evstack/evnode-viem-client", + "version": "0.0.0", + "devDependencies": { + "tsx": "^4.19.2", + "viem": "^2.45.0" + } + }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.1.tgz", + "integrity": "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@noble/ciphers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz", + "integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/base": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", + "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", + "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.9.0", + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz", + "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/abitype": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.2.3.tgz", + "integrity": "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3.22.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/esbuild": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true, + "license": "MIT" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/isows": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.7.tgz", + "integrity": "sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/ox": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.11.3.tgz", + "integrity": "sha512-1bWYGk/xZel3xro3l8WGg6eq4YEKlaqvyMtVhfMFpbJzK2F6rj4EDRtqDCWVEJMkzcmEi9uW2QxsqELokOlarw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "^1.11.0", + "@noble/ciphers": "^1.3.0", + "@noble/curves": "1.9.1", + "@noble/hashes": "^1.8.0", + "@scure/bip32": "^1.7.0", + "@scure/bip39": "^1.6.0", + "abitype": "^1.2.3", + "eventemitter3": "5.0.1" + }, + "peerDependencies": { + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/viem": { + "version": "2.45.0", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.45.0.tgz", + "integrity": "sha512-iVA9qrAgRdtpWa80lCZ6Jri6XzmLOwwA1wagX2HnKejKeliFLpON0KOdyfqvcy+gUpBVP59LBxP2aKiL3aj8fg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@noble/curves": "1.9.1", + "@noble/hashes": "1.8.0", + "@scure/bip32": "1.7.0", + "@scure/bip39": "1.6.0", + "abitype": "1.2.3", + "isows": "1.0.7", + "ox": "0.11.3", + "ws": "8.18.3" + }, + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/clients/package.json b/clients/package.json new file mode 100644 index 0000000..c31ccd6 --- /dev/null +++ b/clients/package.json @@ -0,0 +1,16 @@ +{ + "name": "@evstack/evnode-viem-client", + "version": "0.0.0", + "private": true, + "type": "module", + "description": "Local tooling and tests for the EvNode Viem client", + "scripts": { + "test:basic": "tsx test/evnode-basic.ts", + "test:flows": "tsx test/evnode-flows.ts", + "test:sponsored": "tsx test/evnode-sponsored.ts" + }, + "devDependencies": { + "tsx": "^4.19.2", + "viem": "^2.45.0" + } +} diff --git a/clients/test/evnode-basic.ts b/clients/test/evnode-basic.ts new file mode 100644 index 0000000..99ea0b0 --- /dev/null +++ b/clients/test/evnode-basic.ts @@ -0,0 +1,68 @@ +import { createClient, http } from 'viem'; +import { privateKeyToAccount, sign } from 'viem/accounts'; +import { createEvnodeClient } from '../evnode-viem.ts'; + +const RPC_URL = 'http://localhost:8545'; +const PRIVATE_KEY = + (process.env.PRIVATE_KEY?.startsWith('0x') + ? process.env.PRIVATE_KEY + : process.env.PRIVATE_KEY + ? `0x${process.env.PRIVATE_KEY}` + : undefined) ?? + '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'; +const TO_ADDRESS = process.env.TO_ADDRESS as `0x${string}` | undefined; + +const client = createClient({ + transport: http(RPC_URL), +}); + +const account = privateKeyToAccount(PRIVATE_KEY); + +const executor = { + address: account.address, + signHash: async (hash: `0x${string}`) => sign({ hash, privateKey: PRIVATE_KEY }), +}; + +const evnode = createEvnodeClient({ + client, + executor, +}); + +async function main() { + const to = TO_ADDRESS ?? account.address; + const hash = await evnode.send({ + calls: [ + { + to, + value: 0n, + data: '0x', + }, + ], + }); + + console.log('submitted tx:', hash); + + const receipt = await pollReceipt(hash); + if (receipt) { + console.log('receipt status:', receipt.status, 'block:', receipt.blockNumber); + } else { + console.log('receipt not found yet'); + } +} + +async function pollReceipt(hash: `0x${string}`) { + for (let i = 0; i < 12; i += 1) { + const receipt = await client.request({ + method: 'eth_getTransactionReceipt', + params: [hash], + }); + if (receipt) return receipt as { status: `0x${string}`; blockNumber: `0x${string}` }; + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + return null; +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/clients/test/evnode-flows.ts b/clients/test/evnode-flows.ts new file mode 100644 index 0000000..7fcb82a --- /dev/null +++ b/clients/test/evnode-flows.ts @@ -0,0 +1,126 @@ +import { createClient, hexToBigInt, http, type Hex, toHex } from 'viem'; +import { privateKeyToAccount, sign } from 'viem/accounts'; +import { randomBytes } from 'crypto'; +import { createEvnodeClient, type Call } from '../evnode-viem.ts'; + +const RPC_URL = process.env.RPC_URL ?? 'http://localhost:8545'; +const EXECUTOR_KEY = normalizeKey( + process.env.EXECUTOR_PRIVATE_KEY ?? process.env.PRIVATE_KEY, +); +const SPONSOR_KEY = normalizeKey(process.env.SPONSOR_PRIVATE_KEY ?? ''); +const TO_ADDRESS = process.env.TO_ADDRESS as `0x${string}` | undefined; +const AUTO_SPONSOR = + process.env.AUTO_SPONSOR === '1' || process.env.AUTO_SPONSOR === 'true'; +const FUND_SPONSOR = + process.env.FUND_SPONSOR === '1' || process.env.FUND_SPONSOR === 'true'; +const SPONSOR_MIN_BALANCE_WEI = BigInt(process.env.SPONSOR_MIN_BALANCE_WEI ?? '0'); +const SPONSOR_FUND_WEI = BigInt(process.env.SPONSOR_FUND_WEI ?? '10000000000000000'); + +if (!EXECUTOR_KEY) { + throw new Error('Missing EXECUTOR_PRIVATE_KEY/PRIVATE_KEY'); +} + +const client = createClient({ transport: http(RPC_URL) }); + +const executorAccount = privateKeyToAccount(EXECUTOR_KEY); +const autoSponsorKey = AUTO_SPONSOR ? toHex(randomBytes(32)) : undefined; +const sponsorKey = (SPONSOR_KEY || autoSponsorKey || EXECUTOR_KEY) as `0x${string}`; +const sponsorAccount = privateKeyToAccount(sponsorKey); + +const executor = { + address: executorAccount.address, + signHash: async (hash: Hex) => sign({ hash, privateKey: EXECUTOR_KEY }), +}; +const sponsor = { + address: sponsorAccount.address, + signHash: async (hash: Hex) => sign({ hash, privateKey: sponsorKey }), +}; + +const evnode = createEvnodeClient({ + client, + executor, + sponsor, +}); + +async function main() { + const to = TO_ADDRESS ?? executorAccount.address; + + console.log('executor', executorAccount.address); + console.log('sponsor', sponsorAccount.address); + if (autoSponsorKey) { + console.log('auto sponsor key', sponsorKey); + } + + await maybeFundSponsor(); + + await runFlow('unsponsored-single', [call(to)], false); + await runFlow('unsponsored-batch', [call(to), call(to)], false); + await runFlow('sponsored-single', [call(to)], true); + await runFlow('sponsored-batch', [call(to), call(to)], true); +} + +async function maybeFundSponsor() { + if (sponsorAccount.address === executorAccount.address) return; + if (!FUND_SPONSOR && SPONSOR_MIN_BALANCE_WEI === 0n) return; + const balanceHex = await client.request({ + method: 'eth_getBalance', + params: [sponsorAccount.address, 'latest'], + }); + const balance = hexToBigInt(balanceHex as Hex); + if (!FUND_SPONSOR && balance >= SPONSOR_MIN_BALANCE_WEI) return; + const target = SPONSOR_MIN_BALANCE_WEI > 0n ? SPONSOR_MIN_BALANCE_WEI : SPONSOR_FUND_WEI; + const amount = target > balance ? target - balance : SPONSOR_FUND_WEI; + if (amount <= 0n) return; + console.log('funding sponsor with', amount.toString(), 'wei'); + const hash = await evnode.send({ + calls: [{ to: sponsorAccount.address, value: amount, data: '0x' }], + }); + const receipt = await pollReceipt(hash); + if (!receipt) throw new Error('sponsor funding tx not mined'); +} + +async function runFlow(name: string, calls: Call[], sponsored: boolean) { + console.log(`\n== ${name} ==`); + + let hash: Hex; + if (!sponsored) { + hash = await evnode.send({ calls }); + } else { + const intent = await evnode.createIntent({ calls }); + hash = await evnode.sponsorAndSend({ intent }); + } + + console.log('submitted tx:', hash); + const receipt = await pollReceipt(hash); + if (receipt) { + console.log('receipt status:', receipt.status, 'block:', receipt.blockNumber); + } else { + console.log('receipt not found yet'); + } +} + +function call(to: `0x${string}`): Call { + return { to, value: 0n, data: '0x' }; +} + +async function pollReceipt(hash: Hex) { + for (let i = 0; i < 20; i += 1) { + const receipt = await client.request({ + method: 'eth_getTransactionReceipt', + params: [hash], + }); + if (receipt) return receipt as { status: Hex; blockNumber: Hex }; + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + return null; +} + +function normalizeKey(key?: string): `0x${string}` | '' | undefined { + if (!key) return undefined; + return key.startsWith('0x') ? (key as `0x${string}`) : (`0x${key}` as `0x${string}`); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/clients/test/evnode-sponsored.ts b/clients/test/evnode-sponsored.ts new file mode 100644 index 0000000..e0a91de --- /dev/null +++ b/clients/test/evnode-sponsored.ts @@ -0,0 +1,116 @@ +import { createClient, hexToBigInt, http, type Hex, toHex } from 'viem'; +import { privateKeyToAccount, sign } from 'viem/accounts'; +import { randomBytes } from 'crypto'; +import { createEvnodeClient } from '../evnode-viem.ts'; + +const RPC_URL = process.env.RPC_URL ?? 'http://localhost:8545'; +const EXECUTOR_KEY = normalizeKey( + process.env.EXECUTOR_PRIVATE_KEY ?? process.env.PRIVATE_KEY, +); +const SPONSOR_KEY = normalizeKey(process.env.SPONSOR_PRIVATE_KEY ?? ''); +const TO_ADDRESS = process.env.TO_ADDRESS as `0x${string}` | undefined; +const AUTO_SPONSOR = + process.env.AUTO_SPONSOR === '1' || process.env.AUTO_SPONSOR === 'true'; +const FUND_SPONSOR = + process.env.FUND_SPONSOR === '1' || process.env.FUND_SPONSOR === 'true'; +const SPONSOR_MIN_BALANCE_WEI = BigInt(process.env.SPONSOR_MIN_BALANCE_WEI ?? '0'); +const SPONSOR_FUND_WEI = BigInt(process.env.SPONSOR_FUND_WEI ?? '10000000000000000'); + +if (!EXECUTOR_KEY) { + throw new Error('Missing EXECUTOR_PRIVATE_KEY/PRIVATE_KEY'); +} + +const autoSponsorKey = AUTO_SPONSOR ? toHex(randomBytes(32)) : undefined; +const sponsorKey = (SPONSOR_KEY || autoSponsorKey || EXECUTOR_KEY) as `0x${string}`; +const client = createClient({ transport: http(RPC_URL) }); + +const executorAccount = privateKeyToAccount(EXECUTOR_KEY); +const sponsorAccount = privateKeyToAccount(sponsorKey); + +const evnode = createEvnodeClient({ + client, + executor: { + address: executorAccount.address, + signHash: async (hash: Hex) => sign({ hash, privateKey: EXECUTOR_KEY }), + }, + sponsor: { + address: sponsorAccount.address, + signHash: async (hash: Hex) => sign({ hash, privateKey: sponsorKey }), + }, +}); + +async function main() { + const to = TO_ADDRESS ?? executorAccount.address; + console.log('executor', executorAccount.address); + console.log('sponsor', sponsorAccount.address); + if (autoSponsorKey) { + console.log('auto sponsor key', sponsorKey); + } + + await maybeFundSponsor(); + const intent = await evnode.createIntent({ + calls: [{ to, value: 0n, data: '0x' }], + }); + const hash = await evnode.sponsorAndSend({ intent }); + console.log('submitted sponsored tx:', hash); + const receipt = await pollReceipt(hash); + if (receipt) { + console.log('receipt status:', receipt.status, 'block:', receipt.blockNumber); + } else { + console.log('receipt not found yet'); + } +} + +async function maybeFundSponsor() { + if (sponsorAccount.address === executorAccount.address) return; + if (!FUND_SPONSOR && SPONSOR_MIN_BALANCE_WEI === 0n) return; + const balanceHex = await client.request({ + method: 'eth_getBalance', + params: [sponsorAccount.address, 'latest'], + }); + const balance = hexToBigInt(balanceHex as Hex); + if (!FUND_SPONSOR && balance >= SPONSOR_MIN_BALANCE_WEI) return; + const target = SPONSOR_MIN_BALANCE_WEI > 0n ? SPONSOR_MIN_BALANCE_WEI : SPONSOR_FUND_WEI; + const amount = target > balance ? target - balance : SPONSOR_FUND_WEI; + if (amount <= 0n) return; + console.log('funding sponsor with', amount.toString(), 'wei'); + const hash = await evnode.send({ + calls: [{ to: sponsorAccount.address, value: amount, data: '0x' }], + }); + const receipt = await pollReceiptWithTimeout(hash, 30); + if (!receipt) throw new Error('sponsor funding tx not mined'); +} + +async function pollReceipt(hash: Hex) { + for (let i = 0; i < 15; i += 1) { + const receipt = await client.request({ + method: 'eth_getTransactionReceipt', + params: [hash], + }); + if (receipt) return receipt as { status: Hex; blockNumber: Hex }; + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + return null; +} + +async function pollReceiptWithTimeout(hash: Hex, attempts: number) { + for (let i = 0; i < attempts; i += 1) { + const receipt = await client.request({ + method: 'eth_getTransactionReceipt', + params: [hash], + }); + if (receipt) return receipt as { status: Hex; blockNumber: Hex }; + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + return null; +} + +function normalizeKey(key?: string): `0x${string}` | '' | undefined { + if (!key) return undefined; + return key.startsWith('0x') ? (key as `0x${string}`) : (`0x${key}` as `0x${string}`); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/clients/tsconfig.json b/clients/tsconfig.json new file mode 100644 index 0000000..d21bf26 --- /dev/null +++ b/clients/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "skipLibCheck": true, + "resolveJsonModule": true + }, + "include": ["evnode-viem.ts", "test/**/*.ts"] +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..1a99f36 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,220 @@ +{ + "name": "sun-valley", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "sun-valley", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "viem": "^2.45.0" + } + }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.1.tgz", + "integrity": "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==", + "license": "MIT" + }, + "node_modules/@noble/ciphers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz", + "integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/base": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", + "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", + "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.9.0", + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz", + "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/abitype": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.2.3.tgz", + "integrity": "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3.22.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, + "node_modules/isows": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.7.tgz", + "integrity": "sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/ox": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.11.3.tgz", + "integrity": "sha512-1bWYGk/xZel3xro3l8WGg6eq4YEKlaqvyMtVhfMFpbJzK2F6rj4EDRtqDCWVEJMkzcmEi9uW2QxsqELokOlarw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "^1.11.0", + "@noble/ciphers": "^1.3.0", + "@noble/curves": "1.9.1", + "@noble/hashes": "^1.8.0", + "@scure/bip32": "^1.7.0", + "@scure/bip39": "^1.6.0", + "abitype": "^1.2.3", + "eventemitter3": "5.0.1" + }, + "peerDependencies": { + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/viem": { + "version": "2.45.0", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.45.0.tgz", + "integrity": "sha512-iVA9qrAgRdtpWa80lCZ6Jri6XzmLOwwA1wagX2HnKejKeliFLpON0KOdyfqvcy+gUpBVP59LBxP2aKiL3aj8fg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@noble/curves": "1.9.1", + "@noble/hashes": "1.8.0", + "@scure/bip32": "1.7.0", + "@scure/bip39": "1.6.0", + "abitype": "1.2.3", + "isows": "1.0.7", + "ox": "0.11.3", + "ws": "8.18.3" + }, + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..c1bb9f2 --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "sun-valley", + "version": "1.0.0", + "description": "EV-reth is a specialized integration layer that enables [Reth](https://github.com/paradigmxyz/reth) to work seamlessly with Evolve, providing a custom payload builder that supports transaction submission via the Engine API.", + "main": "index.js", + "directories": { + "doc": "docs" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "commonjs", + "dependencies": { + "viem": "^2.45.0" + } +}