UNPKG

ox

Version:

Ethereum Standard Library

422 lines (392 loc) 15.3 kB
/* eslint-disable jsdoc-js/require-jsdoc, jsdoc-js/require-description, jsdoc-js/require-example */ import type * as core_TransactionRequest from '../../tempo/TransactionRequest.js' import * as core_Hex from '../../core/Hex.js' import * as z_AccessList from '../AccessList.js' import * as z_Address from '../Address.js' import * as z_Hex from '../Hex.js' import * as z_TransactionRequest from '../TransactionRequest.js' import { encodeNumberish, uintBigintNumberish, uintNumberNumberish, } from '../internal/Integer.js' import * as z from 'zod/mini' import * as z_AuthorizationTempo from './AuthorizationTempo.js' import * as z_KeyAuthorization from './KeyAuthorization.js' import * as z_SignatureEnvelope from './SignatureEnvelope.js' const fromRpcType = { '0x76': 'tempo' } as const const toRpcType = { tempo: '0x76' } as const const KeyType = z.union([ z.literal('secp256k1'), z.literal('p256'), z.literal('webAuthn'), ]) const SignatureRpc = z.object({ r: z_Hex.Hex, s: z_Hex.Hex, v: z.optional(z_Hex.Hex), yParity: z_Hex.Hex, }) const SignatureDecoded = z.object({ r: z_Hex.Hex, s: z_Hex.Hex, v: z.optional(z.number()), yParity: z.number(), }) const CallRpc = z.object({ data: z.optional(z_Hex.Hex), to: z.optional(z_Address.Address), value: z.optional(z_Hex.Hex), }) const Call = z.object({ data: z.optional(z_Hex.Hex), to: z.optional(z_Address.Address), value: z.optional(z.bigint()), }) const CallToRpc = z.object({ data: z.optional(z_Hex.Hex), to: z.optional(z_Address.Address), value: z.optional(uintBigintNumberish()), }) const Capabilities = z.record(z.string(), z.unknown()) const MultisigInit = z.object({ salt: z_Hex.Hex, threshold: z.number(), owners: z.readonly( z.array(z.object({ owner: z_Address.Address, weight: z.number() })), ), }) /** RPC tempo transaction request schema. */ export const Rpc = z.object({ accessList: z.optional(z_AccessList.AccessList), authorizationList: z.optional(z_AuthorizationTempo.ListRpc), blobVersionedHashes: z.optional(z.readonly(z.array(z_Hex.Hex))), blobs: z.optional(z.readonly(z.array(z_Hex.Hex))), calls: z.optional(z.readonly(z.array(CallRpc))), capabilities: z.optional(Capabilities), chainId: z.optional(z_Hex.Hex), data: z.optional(z_Hex.Hex), feePayer: z.optional(z.boolean()), feePayerSignature: z.optional(z.union([SignatureRpc, z.null()])), feeToken: z.optional(z_Hex.Hex), from: z.optional(z_Address.Address), gas: z.optional(z_Hex.Hex), gasPrice: z.optional(z_Hex.Hex), input: z.optional(z_Hex.Hex), keyAuthorization: z.optional(z_KeyAuthorization.Rpc), keyData: z.optional(z_Hex.Hex), keyId: z.optional(z_Address.Address), keyType: z.optional(KeyType), maxFeePerBlobGas: z.optional(z_Hex.Hex), maxFeePerGas: z.optional(z_Hex.Hex), maxPriorityFeePerGas: z.optional(z_Hex.Hex), multisigInit: z.optional(MultisigInit), multisigSignatureCount: z.optional(z.number()), nonce: z.optional(z_Hex.Hex), nonceKey: z.optional(z_Hex.Hex), r: z.optional(z_Hex.Hex), s: z.optional(z_Hex.Hex), signature: z.optional(z_SignatureEnvelope.Rpc), to: z.optional(z.union([z_Address.Address, z.null()])), type: z.optional(z.string()), v: z.optional(z_Hex.Hex), validAfter: z.optional(z_Hex.Hex), validBefore: z.optional(z_Hex.Hex), value: z.optional(z_Hex.Hex), yParity: z.optional(z_Hex.Hex), }) const AuthorizationSigned = z.object({ address: z_Address.Address, chainId: z.number(), nonce: z.bigint(), signature: z_SignatureEnvelope.Domain, }) const AuthorizationSignedToRpc = z.object({ address: z_Address.Address, chainId: uintNumberNumberish(), nonce: uintBigintNumberish(), signature: z_SignatureEnvelope.Domain, }) /** Decoded tempo transaction request schema. */ export const Domain = z.object({ accessList: z.optional(z_AccessList.AccessList), authorizationList: z.optional(z.readonly(z.array(AuthorizationSigned))), blobVersionedHashes: z.optional(z.readonly(z.array(z_Hex.Hex))), blobs: z.optional(z.readonly(z.array(z_Hex.Hex))), calls: z.optional(z.readonly(z.array(Call))), capabilities: z.optional(Capabilities), chainId: z.optional(z.number()), data: z.optional(z_Hex.Hex), feePayer: z.optional(z.boolean()), feePayerSignature: z.optional(z.union([SignatureDecoded, z.null()])), feeToken: z.optional(z_Address.Address), from: z.optional(z_Address.Address), gas: z.optional(z.bigint()), gasPrice: z.optional(z.bigint()), input: z.optional(z_Hex.Hex), keyAuthorization: z.optional(z_KeyAuthorization.Domain), keyData: z.optional(z_Hex.Hex), keyId: z.optional(z_Address.Address), keyType: z.optional(KeyType), maxFeePerBlobGas: z.optional(z.bigint()), maxFeePerGas: z.optional(z.bigint()), maxPriorityFeePerGas: z.optional(z.bigint()), multisigInit: z.optional(MultisigInit), multisigSignatureCount: z.optional(z.number()), nonce: z.optional(z.bigint()), nonceKey: z.optional(z.union([z.bigint(), z.literal('random')])), r: z.optional(z_Hex.Hex), s: z.optional(z_Hex.Hex), signature: z.optional(z_SignatureEnvelope.Domain), to: z.optional(z.union([z_Address.Address, z.null()])), type: z.optional(z.string()), v: z.optional(z.number()), validAfter: z.optional(z.number()), validBefore: z.optional(z.number()), value: z.optional(z.bigint()), yParity: z.optional(z.number()), }) /** Encode-only decoded tempo transaction request schema accepting numberish `toRpc` inputs. */ export const DomainToRpc = z.object({ accessList: z.optional(z_AccessList.AccessList), authorizationList: z.optional(z.readonly(z.array(AuthorizationSignedToRpc))), blobVersionedHashes: z.optional(z.readonly(z.array(z_Hex.Hex))), blobs: z.optional(z.readonly(z.array(z_Hex.Hex))), calls: z.optional(z.readonly(z.array(CallToRpc))), capabilities: z.optional(Capabilities), chainId: z.optional(uintNumberNumberish()), data: z.optional(z_Hex.Hex), feePayer: z.optional(z.boolean()), feePayerSignature: z.optional(z.union([SignatureDecoded, z.null()])), feeToken: z.optional(z_Address.Address), from: z.optional(z_Address.Address), gas: z.optional(uintBigintNumberish()), gasPrice: z.optional(uintBigintNumberish()), input: z.optional(z_Hex.Hex), keyAuthorization: z.optional(z_KeyAuthorization.DomainToRpc), keyData: z.optional(z_Hex.Hex), keyId: z.optional(z_Address.Address), keyType: z.optional(KeyType), maxFeePerBlobGas: z.optional(uintBigintNumberish()), maxFeePerGas: z.optional(uintBigintNumberish()), maxPriorityFeePerGas: z.optional(uintBigintNumberish()), multisigInit: z.optional(MultisigInit), multisigSignatureCount: z.optional(z.number()), nonce: z.optional(uintBigintNumberish()), nonceKey: z.optional(z.union([uintBigintNumberish(), z.literal('random')])), r: z.optional(z_Hex.Hex), s: z.optional(z_Hex.Hex), signature: z.optional(z_SignatureEnvelope.Domain), to: z.optional(z.union([z_Address.Address, z.null()])), type: z.optional(z.string()), v: z.optional(z.number()), validAfter: z.optional(uintNumberNumberish()), validBefore: z.optional(uintNumberNumberish()), value: z.optional(uintBigintNumberish()), yParity: z.optional(z.number()), }) /** Codec decoding an RPC tempo transaction request into a transaction request. */ export const TransactionRequest = z.codec(Rpc, Domain, { decode: (value) => fromRpc(value as never) as never, encode: (value) => toRpc(value as never) as never, }) /** Encode-only tempo transaction request codec accepting numberish `toRpc` inputs. */ export const TransactionRequestToRpc = z.codec(Rpc, DomainToRpc, { decode: (value) => fromRpc(value as never) as never, encode: (value) => toRpc(value as never) as never, }) /** Decodes an RPC tempo transaction request into a transaction request. */ function fromRpc( request: core_TransactionRequest.Rpc, ): core_TransactionRequest.TransactionRequest { const { authorizationList: _, ...rest } = request const request_ = z.decode( z_TransactionRequest.TransactionRequest, rest as never, ) as core_TransactionRequest.TransactionRequest if (typeof request.type !== 'undefined') request_.type = fromRpcType[request.type as keyof typeof fromRpcType] || request_.type if (request.authorizationList) request_.authorizationList = z.decode( z_AuthorizationTempo.ListSigned, request.authorizationList as never, ) as never if (request.signature) request_.signature = z.decode( z_SignatureEnvelope.SignatureEnvelope, request.signature as never, ) as never if (request.feePayerSignature) request_.feePayerSignature = signatureFromRpc(request.feePayerSignature) if (request.calls) request_.calls = request.calls.map((call) => ({ to: call.to, data: call.data, ...(call.value && call.value !== '0x' ? { value: core_Hex.toBigInt(call.value) } : {}), })) if (typeof request.feeToken !== 'undefined') request_.feeToken = request.feeToken if (request.keyAuthorization) request_.keyAuthorization = z.decode( z_KeyAuthorization.KeyAuthorization, request.keyAuthorization as never, ) as never if (typeof request.capabilities !== 'undefined') request_.capabilities = request.capabilities if (typeof request.feePayer !== 'undefined') request_.feePayer = request.feePayer if (typeof request.keyData !== 'undefined') request_.keyData = request.keyData if (typeof request.keyId !== 'undefined') request_.keyId = request.keyId if (typeof request.keyType !== 'undefined') request_.keyType = request.keyType if (typeof request.multisigInit !== 'undefined') request_.multisigInit = request.multisigInit if (typeof request.multisigSignatureCount !== 'undefined') request_.multisigSignatureCount = request.multisigSignatureCount if (typeof request.validBefore !== 'undefined') request_.validBefore = core_Hex.toNumber(request.validBefore) if (typeof request.validAfter !== 'undefined') request_.validAfter = core_Hex.toNumber(request.validAfter) if (typeof request.nonceKey !== 'undefined') request_.nonceKey = core_Hex.toBigInt(request.nonceKey) return request_ } /** Encodes a transaction request into an RPC tempo transaction request. */ function toRpc( request: core_TransactionRequest.TransactionRequest, ): core_TransactionRequest.Rpc { const request_rpc = z.encode(z_TransactionRequest.TransactionRequestToRpc, { ...request, authorizationList: undefined, } as never) as core_TransactionRequest.Rpc const tempo = typeof request.calls !== 'undefined' || typeof request.capabilities !== 'undefined' || typeof request.feePayer !== 'undefined' || typeof request.feeToken !== 'undefined' || typeof request.keyAuthorization !== 'undefined' || typeof request.keyData !== 'undefined' || typeof request.keyId !== 'undefined' || typeof request.keyType !== 'undefined' || typeof request.multisigInit !== 'undefined' || typeof request.multisigSignatureCount !== 'undefined' || typeof request.nonceKey !== 'undefined' || typeof request.validBefore !== 'undefined' || typeof request.validAfter !== 'undefined' || request.type === 'tempo' if (request.authorizationList) request_rpc.authorizationList = z.encode( z_AuthorizationTempo.ListSignedToRpc, request.authorizationList as never, ) as never if (request.signature) request_rpc.signature = z.encode( z_SignatureEnvelope.SignatureEnvelope, request.signature as never, ) as never if (request.feePayerSignature) request_rpc.feePayerSignature = signatureToRpc(request.feePayerSignature) if (request.calls) request_rpc.calls = request.calls.map((call) => ({ to: call.to, value: call.value ? encodeNumberish(call.value) : '0x', data: call.data ?? '0x', })) else if (request.to || request.data || request.value || tempo) request_rpc.calls = [ { to: request.to || (tempo && (!request.data || request.data === '0x') ? '0x0000000000000000000000000000000000000000' : undefined), value: request.value ? encodeNumberish(request.value) : '0x', data: request.data ?? '0x', }, ] if ( typeof request.feeToken !== 'undefined' && !(request.feePayer === true && !request.feePayerSignature) ) request_rpc.feeToken = request.feeToken if (request.keyAuthorization) request_rpc.keyAuthorization = z.encode( z_KeyAuthorization.KeyAuthorizationToRpc, request.keyAuthorization as never, ) as never if (typeof request.capabilities !== 'undefined') request_rpc.capabilities = request.capabilities if (typeof request.feePayer !== 'undefined') request_rpc.feePayer = request.feePayer if (typeof request.keyData !== 'undefined') request_rpc.keyData = shimKeyData(request.keyData) if (typeof request.keyId !== 'undefined') request_rpc.keyId = request.keyId if (typeof request.keyType !== 'undefined') request_rpc.keyType = request.keyType if (typeof request.multisigInit !== 'undefined') request_rpc.multisigInit = request.multisigInit if (typeof request.multisigSignatureCount !== 'undefined') request_rpc.multisigSignatureCount = request.multisigSignatureCount if (typeof request.validBefore !== 'undefined') request_rpc.validBefore = encodeNumberish(request.validBefore) if (typeof request.validAfter !== 'undefined') request_rpc.validAfter = encodeNumberish(request.validAfter) const nonceKey = (() => { if (request.nonceKey === 'random') return core_Hex.random(24) if (typeof request.nonceKey !== 'undefined') return encodeNumberish(request.nonceKey) return undefined })() if (nonceKey) request_rpc.nonceKey = nonceKey if (tempo) { request_rpc.type = toRpcType.tempo delete request_rpc.data delete request_rpc.input delete request_rpc.to delete request_rpc.value } return request_rpc } /** * Shims key data longer than 4 bytes into a 2-byte big-endian length hint * (the node's gas estimator only accepts 1, 2, or 4-byte key data). */ function shimKeyData(data: core_Hex.Hex): core_Hex.Hex { const byteLength = core_Hex.size(data) if (byteLength <= 4) return data return core_Hex.fromNumber(byteLength, { size: 2 }) } /** Decodes an RPC signature into a recovered signature. */ function signatureFromRpc(signature: { r: core_Hex.Hex s: core_Hex.Hex v?: core_Hex.Hex | undefined yParity: core_Hex.Hex }) { const v = signature.v ? Number(signature.v) : undefined let yParity = signature.yParity ? Number(signature.yParity) : undefined if (typeof v === 'number' && typeof yParity !== 'number') { if (v === 0 || v === 27) yParity = 0 else if (v === 1 || v === 28) yParity = 1 else if (v >= 35) yParity = v % 2 === 0 ? 1 : 0 } return { r: core_Hex.padLeft(signature.r, 32), s: core_Hex.padLeft(signature.s, 32), yParity: yParity as number, } } /** Encodes a recovered signature into an RPC signature. */ function signatureToRpc(signature: { r: core_Hex.Hex s: core_Hex.Hex yParity: number }) { return { r: signature.r, s: signature.s, yParity: signature.yParity === 0 ? '0x0' : '0x1', } as const }