Files
FrenoCorp/node_modules/viem/tempo/chainConfig.ts
Michael Freno 7c684a42cc FRE-600: Fix code review blockers
- Consolidated duplicate UndoManagers to single instance
- Fixed connection promise to only resolve on 'connected' status
- Fixed WebSocketProvider import (WebsocketProvider)
- Added proper doc.destroy() cleanup
- Renamed isPresenceInitialized property to avoid conflict

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-25 00:08:01 -04:00

176 lines
6.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import * as Address from 'ox/Address'
import * as Hex from 'ox/Hex'
import * as PublicKey from 'ox/PublicKey'
import { SignatureEnvelope, type TokenId } from 'ox/tempo'
import { getCode } from '../actions/public/getCode.js'
import { verifyHash } from '../actions/public/verifyHash.js'
import { maxUint256 } from '../constants/number.js'
import type { Chain, ChainConfig as viem_ChainConfig } from '../types/chain.js'
import { extendSchema } from '../utils/chain/defineChain.js'
import { defineTransaction } from '../utils/formatters/transaction.js'
import { defineTransactionReceipt } from '../utils/formatters/transactionReceipt.js'
import { defineTransactionRequest } from '../utils/formatters/transactionRequest.js'
import { getAction } from '../utils/getAction.js'
import { keccak256 } from '../utils/hash/keccak256.js'
import type { SerializeTransactionFn } from '../utils/transaction/serializeTransaction.js'
import type { Account } from './Account.js'
import { getMetadata } from './actions/accessKey.js'
import * as Formatters from './Formatters.js'
import type { Hardfork } from './Hardfork.js'
import * as Concurrent from './internal/concurrent.js'
import * as Transaction from './Transaction.js'
const maxExpirySecs = 25
export const chainConfig = {
blockTime: 1_000,
extendSchema: extendSchema<{
feeToken?: TokenId.TokenIdOrAddress | undefined
hardfork?: Hardfork | undefined
}>(),
formatters: {
transaction: defineTransaction({
exclude: ['aaAuthorizationList' as never],
format: Formatters.formatTransaction,
}),
transactionReceipt: defineTransactionReceipt({
format: Formatters.formatTransactionReceipt,
}),
transactionRequest: defineTransactionRequest({
format: Formatters.formatTransactionRequest,
}),
},
prepareTransactionRequest: [
async (r, { phase }) => {
const request = r as Transaction.TransactionRequest & {
account?: Account | undefined
chain?:
| (Chain & { feeToken?: TokenId.TokenIdOrAddress | undefined })
| undefined
}
// FIXME: node estimates gas with secp256k1 dummy sig + null feePayerSignature.
// Actual tx has larger keychain/webAuthn sigs + real fee payer sig, costing more intrinsic gas.
if (phase === 'afterFillParameters') {
if (request.feePayer) {
if (request.keyAuthorization?.signature.type === 'webAuthn')
request.gas = (request.gas ?? 0n) + 20_000n
else if (request.account?.source === 'accessKey')
request.gas = (request.gas ?? 0n) + 10_000n
}
return request as unknown as typeof r
}
// Use expiring nonces for concurrent transactions (TIP-1009).
// When nonceKey is 'expiring', feePayer is specified, or concurrent requests
// are detected, we use expiring nonces (nonceKey = uint256.max) with a
// validBefore timestamp.
const useExpiringNonce = await (async () => {
if (request.nonceKey === 'expiring') return true
if (request.feePayer && typeof request.nonceKey === 'undefined')
return true
const address = request.account?.address
if (address && typeof request.nonceKey === 'undefined')
return await Concurrent.detect(address)
return false
})()
if (useExpiringNonce) {
request.nonceKey = maxUint256
request.nonce = 0
if (typeof request.validBefore === 'undefined')
request.validBefore = Math.floor(Date.now() / 1000) + maxExpirySecs
} else if (typeof request.nonceKey !== 'undefined') {
// Explicit nonceKey provided (2D nonce mode)
request.nonce = typeof request.nonce === 'number' ? request.nonce : 0
}
if (!request.feeToken && request.chain?.feeToken)
request.feeToken = request.chain.feeToken
return request as unknown as typeof r
},
{ runAt: ['beforeFillTransaction', 'afterFillParameters'] },
],
serializers: {
// TODO: casting to satisfy viem viem v3 to have more flexible serializer type.
transaction: ((transaction, signature) =>
Transaction.serialize(transaction, signature)) as SerializeTransactionFn,
},
async verifyHash(client, parameters) {
const { address, hash, signature, mode } = parameters
const envelope = (() => {
if (typeof signature !== 'string') return
try {
return SignatureEnvelope.deserialize(signature)
} catch {
return undefined
}
})()
// `verifyHash` supports "signature envelopes" (a Tempo proposal) to natively verify arbitrary
// envelope-compatible (WebAuthn, P256, etc.) signatures.
if (envelope) {
// Access key (keychain) signature verification: check the key is
// authorized, not expired, and not revoked on the AccountKeychain.
if (envelope?.type === 'keychain' && mode === 'allowAccessKey') {
const accessKeyAddress = Address.fromPublicKey(
PublicKey.from(envelope.inner.publicKey as PublicKey.PublicKey),
)
const keyInfo = await getMetadata(client, {
account: address,
accessKey: accessKeyAddress,
blockNumber: parameters.blockNumber,
blockTag: parameters.blockTag,
} as never)
if (keyInfo.isRevoked) return false
if (keyInfo.expiry <= BigInt(Math.floor(Date.now() / 1000)))
return false
// For v2 keychain envelopes, the inner signature signs
// keccak256(0x04 || hash || userAddress).
const innerPayload =
envelope.version === 'v2'
? keccak256(Hex.concat('0x04', hash, address))
: hash
return SignatureEnvelope.verify(envelope.inner, {
address: accessKeyAddress,
payload: innerPayload,
})
}
// Stateless, non-keychain signature envelopes (P256, WebAuthn) can be
// verified directly without a network request.
if (envelope.type === 'p256' || envelope.type === 'webAuthn') {
const code = await getCode(client, {
address,
blockNumber: parameters.blockNumber,
blockTag: parameters.blockTag,
} as never)
// Check if EOA, if not, we want to go down the ERC-1271 flow.
if (
// not a contract (EOA)
!code ||
// default delegation (tempo EOA)
code === '0xef01007702c00000000000000000000000000000000000'
)
return SignatureEnvelope.verify(envelope, {
address,
payload: hash,
})
}
}
return await getAction(
client,
verifyHash,
'verifyHash',
)({ ...parameters, chain: null })
},
} as const satisfies viem_ChainConfig & { blockTime: number }
export type ChainConfig = typeof chainConfig