- 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>
392 lines
12 KiB
TypeScript
392 lines
12 KiB
TypeScript
// TODO: Find opportunities to make this file less duplicated + more simplified with Viem v3.
|
|
|
|
import type { Address } from 'abitype'
|
|
import * as Hex from 'ox/Hex'
|
|
import * as Secp256k1 from 'ox/Secp256k1'
|
|
import * as Signature from 'ox/Signature'
|
|
import {
|
|
type AuthorizationTempo,
|
|
type KeyAuthorization,
|
|
type TransactionReceipt as ox_TransactionReceipt,
|
|
SignatureEnvelope,
|
|
type TempoAddress,
|
|
TxEnvelopeTempo as TxTempo,
|
|
} from 'ox/tempo'
|
|
import type { Account } from '../accounts/types.js'
|
|
import type { FeeValuesEIP1559 } from '../types/fee.js'
|
|
import type { Signature as viem_Signature } from '../types/misc.js'
|
|
import type {
|
|
RpcTransaction as viem_RpcTransaction,
|
|
RpcTransactionRequest as viem_RpcTransactionRequest,
|
|
} from '../types/rpc.js'
|
|
import type {
|
|
AccessList,
|
|
TransactionBase,
|
|
TransactionRequestBase,
|
|
TransactionSerializableBase,
|
|
TransactionSerializedGeneric,
|
|
Transaction as viem_Transaction,
|
|
TransactionReceipt as viem_TransactionReceipt,
|
|
TransactionRequest as viem_TransactionRequest,
|
|
TransactionSerializable as viem_TransactionSerializable,
|
|
TransactionSerialized as viem_TransactionSerialized,
|
|
TransactionType as viem_TransactionType,
|
|
} from '../types/transaction.js'
|
|
import type { ExactPartial, OneOf, PartialBy } from '../types/utils.js'
|
|
import { getTransactionType as viem_getTransactionType } from '../utils/transaction/getTransactionType.js'
|
|
import {
|
|
type ParseTransactionReturnType,
|
|
parseTransaction as viem_parseTransaction,
|
|
} from '../utils/transaction/parseTransaction.js'
|
|
import { serializeTransaction as viem_serializeTransaction } from '../utils/transaction/serializeTransaction.js'
|
|
|
|
export type Transaction<
|
|
bigintType = bigint,
|
|
numberType = number,
|
|
pending extends boolean = false,
|
|
> = OneOf<
|
|
| viem_Transaction<bigintType, numberType, pending>
|
|
| TransactionTempo<bigintType, numberType, pending>
|
|
>
|
|
export type TransactionRpc<pending extends boolean = false> = OneOf<
|
|
| viem_RpcTransaction<pending>
|
|
| (Omit<
|
|
TransactionTempo<Hex.Hex, Hex.Hex, pending, '0x76'>,
|
|
'authorizationList' | 'keyAuthorization' | 'signature'
|
|
> & {
|
|
authorizationList?: AuthorizationTempo.ListRpc | undefined
|
|
keyAuthorization?: KeyAuthorization.Rpc | null | undefined
|
|
signature: SignatureEnvelope.SignatureEnvelopeRpc
|
|
})
|
|
>
|
|
|
|
export type TransactionTempo<
|
|
quantity = bigint,
|
|
index = number,
|
|
isPending extends boolean = boolean,
|
|
type = 'tempo',
|
|
> = PartialBy<
|
|
Omit<TransactionBase<quantity, index, isPending>, 'input' | 'value' | 'to'>,
|
|
'r' | 's' | 'v' | 'yParity'
|
|
> & {
|
|
accessList: AccessList
|
|
authorizationList?: AuthorizationTempo.ListSigned<quantity, index> | undefined
|
|
calls: readonly TxTempo.Call<quantity>[]
|
|
chainId: index
|
|
feeToken?: Address | undefined
|
|
feePayerSignature?: viem_Signature | undefined
|
|
keyAuthorization?: KeyAuthorization.Signed<quantity, index> | null | undefined
|
|
nonceKey?: quantity | undefined
|
|
signature: SignatureEnvelope.SignatureEnvelope
|
|
type: type
|
|
validBefore?: index | undefined
|
|
validAfter?: index | undefined
|
|
} & FeeValuesEIP1559<quantity>
|
|
|
|
export type TransactionRequest<
|
|
bigintType = bigint,
|
|
numberType = number,
|
|
> = OneOf<
|
|
| viem_TransactionRequest<bigintType, numberType>
|
|
| TransactionRequestTempo<bigintType, numberType>
|
|
>
|
|
export type TransactionRequestRpc = OneOf<
|
|
viem_RpcTransactionRequest | TransactionRequestTempo<Hex.Hex, Hex.Hex, '0x76'>
|
|
>
|
|
|
|
export type TransactionReceipt<
|
|
quantity = bigint,
|
|
index = number,
|
|
status = 'success' | 'reverted',
|
|
type = TransactionType,
|
|
> = viem_TransactionReceipt<quantity, index, status, type> & {
|
|
feePayer?: Address | undefined
|
|
feeToken?: Address | undefined
|
|
}
|
|
|
|
export type TransactionReceiptRpc = TransactionReceipt<
|
|
Hex.Hex,
|
|
Hex.Hex,
|
|
ox_TransactionReceipt.RpcStatus,
|
|
ox_TransactionReceipt.RpcType
|
|
>
|
|
|
|
export type TransactionRequestTempo<
|
|
quantity = bigint,
|
|
index = number,
|
|
type = 'tempo',
|
|
> = TransactionRequestBase<quantity, index, type> &
|
|
ExactPartial<FeeValuesEIP1559<quantity>> & {
|
|
accessList?: AccessList | undefined
|
|
keyAuthorization?: KeyAuthorization.Signed<quantity, index> | undefined
|
|
calls?: readonly TxTempo.Call<quantity, TempoAddress.Address>[] | undefined
|
|
feePayer?: Account | true | undefined
|
|
feeToken?: TempoAddress.Address | bigint | undefined
|
|
nonceKey?: 'expiring' | quantity | undefined
|
|
validBefore?: index | undefined
|
|
validAfter?: index | undefined
|
|
}
|
|
|
|
export type TransactionSerializable = OneOf<
|
|
viem_TransactionSerializable | TransactionSerializableTempo
|
|
>
|
|
|
|
export type TransactionSerializableTempo<
|
|
quantity = bigint,
|
|
index = number,
|
|
> = TransactionSerializableBase<quantity, index> &
|
|
ExactPartial<FeeValuesEIP1559<quantity>> & {
|
|
accessList?: AccessList | undefined
|
|
calls: readonly TxTempo.Call<quantity>[]
|
|
chainId: number
|
|
feeToken?: Address | bigint | undefined
|
|
feePayerSignature?: viem_Signature | null | undefined
|
|
from?: Address | undefined
|
|
keyAuthorization?: KeyAuthorization.Signed<quantity, index> | undefined
|
|
nonceKey?: quantity | undefined
|
|
signature?: SignatureEnvelope.SignatureEnvelope<quantity, index> | undefined
|
|
validBefore?: index | undefined
|
|
validAfter?: index | undefined
|
|
type?: 'tempo' | undefined
|
|
}
|
|
|
|
export type TransactionSerialized<
|
|
type extends TransactionType = TransactionType,
|
|
> = viem_TransactionSerialized<type> | TransactionSerializedTempo
|
|
|
|
export type TransactionSerializedTempo = `0x76${string}`
|
|
export type TransactionSerializedFeePayer = `0x78${string}`
|
|
|
|
export type TransactionType = viem_TransactionType | 'tempo'
|
|
|
|
export function getType(
|
|
transaction: Record<string, unknown>,
|
|
): Transaction['type'] {
|
|
const account = transaction.account as
|
|
| { keyType?: string | undefined; source?: string | undefined }
|
|
| undefined
|
|
if (
|
|
(account?.keyType && account.keyType !== 'secp256k1') ||
|
|
account?.source === 'accessKey' ||
|
|
typeof transaction.calls !== 'undefined' ||
|
|
typeof transaction.feePayer !== 'undefined' ||
|
|
typeof transaction.feeToken !== 'undefined' ||
|
|
typeof transaction.keyAuthorization !== 'undefined' ||
|
|
typeof transaction.nonceKey !== 'undefined' ||
|
|
typeof transaction.signature !== 'undefined' ||
|
|
typeof transaction.validBefore !== 'undefined' ||
|
|
typeof transaction.validAfter !== 'undefined'
|
|
)
|
|
return 'tempo' as never
|
|
if (transaction.type) return transaction.type as never
|
|
return viem_getTransactionType(transaction) as never
|
|
}
|
|
|
|
export function isTempo(transaction: Record<string, unknown>) {
|
|
try {
|
|
const type = getType(transaction)
|
|
return type === 'tempo'
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|
|
|
|
export function deserialize<
|
|
const serialized extends TransactionSerializedGeneric,
|
|
>(serializedTransaction: serialized): deserialize.ReturnValue<serialized> {
|
|
const type = Hex.slice(serializedTransaction, 0, 1)
|
|
if (type === '0x76' || type === '0x78')
|
|
return deserializeTempo(serializedTransaction as `0x76${string}`) as never
|
|
return viem_parseTransaction(serializedTransaction) as never
|
|
}
|
|
|
|
export declare namespace deserialize {
|
|
export type ReturnValue<
|
|
serialized extends
|
|
TransactionSerializedGeneric = TransactionSerializedGeneric,
|
|
> = serialized extends TransactionSerializedTempo
|
|
? TransactionSerializableTempo
|
|
: serialized extends TransactionSerializedFeePayer
|
|
? TransactionSerializableTempo
|
|
: ParseTransactionReturnType<serialized>
|
|
}
|
|
|
|
export async function serialize(
|
|
transaction: TransactionSerializable & {
|
|
feePayer?: Account | true | undefined
|
|
from?: TempoAddress.Address | undefined
|
|
},
|
|
signature?:
|
|
| OneOf<SignatureEnvelope.SignatureEnvelope | viem_Signature>
|
|
| undefined,
|
|
) {
|
|
// If the transaction is not a Tempo transaction, route to Viem serializer.
|
|
if (!isTempo(transaction)) {
|
|
if (signature && 'type' in signature && signature.type !== 'secp256k1')
|
|
throw new Error(
|
|
'Unsupported signature type. Expected `secp256k1` but got `' +
|
|
signature.type +
|
|
'`.',
|
|
)
|
|
if (signature && 'type' in signature) {
|
|
const { r, s, yParity } = signature?.signature!
|
|
return viem_serializeTransaction(transaction as never, {
|
|
r: Hex.fromNumber(r, { size: 32 }),
|
|
s: Hex.fromNumber(s, { size: 32 }),
|
|
yParity,
|
|
})
|
|
}
|
|
return viem_serializeTransaction(transaction as never, signature)
|
|
}
|
|
|
|
const type = getType(transaction)
|
|
if (type === 'tempo') return serializeTempo(transaction as never, signature)
|
|
|
|
throw new Error('Unsupported transaction type')
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
// Internal
|
|
|
|
/** @internal */
|
|
function deserializeTempo(
|
|
serializedTransaction: TransactionSerializedTempo,
|
|
): TransactionSerializableTempo {
|
|
const { feePayerSignature, nonce, ...tx } = TxTempo.deserialize(
|
|
serializedTransaction,
|
|
)
|
|
return {
|
|
...tx,
|
|
nonce: Number(nonce ?? 0n),
|
|
feePayerSignature: feePayerSignature
|
|
? {
|
|
r: Hex.fromNumber(feePayerSignature.r, { size: 32 }),
|
|
s: Hex.fromNumber(feePayerSignature.s, { size: 32 }),
|
|
yParity: feePayerSignature.yParity,
|
|
}
|
|
: feePayerSignature,
|
|
} satisfies TransactionSerializableTempo
|
|
}
|
|
|
|
/** @internal */
|
|
async function serializeTempo(
|
|
transaction: TransactionSerializableTempo & {
|
|
feePayer?: Account | true | undefined
|
|
from?: Address | undefined
|
|
},
|
|
sig?: OneOf<SignatureEnvelope.SignatureEnvelope | viem_Signature> | undefined,
|
|
) {
|
|
const signature = (() => {
|
|
if (transaction.signature) return transaction.signature
|
|
if (sig && 'type' in sig) return sig as SignatureEnvelope.SignatureEnvelope
|
|
if (sig)
|
|
return SignatureEnvelope.from({
|
|
r: BigInt(sig.r!),
|
|
s: BigInt(sig.s!),
|
|
yParity: Number(sig.yParity!),
|
|
})
|
|
return undefined
|
|
})()
|
|
|
|
const { chainId, feePayer, nonce, ...rest } = transaction
|
|
|
|
const feePayerSignature = (() => {
|
|
const feePayerSignature = transaction.feePayerSignature
|
|
if (feePayerSignature)
|
|
return {
|
|
r: BigInt(feePayerSignature.r!),
|
|
s: BigInt(feePayerSignature.s!),
|
|
yParity: Number(feePayerSignature.yParity),
|
|
}
|
|
if (feePayerSignature === null || feePayer) return null
|
|
return undefined
|
|
})()
|
|
|
|
const transaction_ox = {
|
|
...rest,
|
|
calls: rest.calls?.length
|
|
? rest.calls
|
|
: [
|
|
{
|
|
to:
|
|
rest.to ||
|
|
(!rest.data || rest.data === '0x'
|
|
? '0x0000000000000000000000000000000000000000'
|
|
: undefined),
|
|
value: rest.value,
|
|
data: rest.data,
|
|
},
|
|
],
|
|
chainId: Number(chainId),
|
|
feePayerSignature,
|
|
type: 'tempo',
|
|
...(nonce ? { nonce: BigInt(nonce) } : {}),
|
|
} satisfies TxTempo.TxEnvelopeTempo
|
|
|
|
// If we have marked the transaction as intended to be paid
|
|
// by a fee payer (feePayer: true), we will not use the fee token
|
|
// as the fee payer will choose their fee token.
|
|
if (feePayer === true) delete transaction_ox.feeToken
|
|
|
|
if (signature && typeof transaction.feePayer === 'object') {
|
|
const tx = TxTempo.from(transaction_ox, {
|
|
signature,
|
|
})
|
|
|
|
const sender = (() => {
|
|
if (transaction.from) return transaction.from
|
|
if (signature.type === 'secp256k1')
|
|
return Secp256k1.recoverAddress({
|
|
payload: TxTempo.getSignPayload(tx),
|
|
signature: signature.signature,
|
|
})
|
|
throw new Error('Unable to extract sender from transaction or signature.')
|
|
})()
|
|
|
|
const hash = TxTempo.getFeePayerSignPayload(tx, {
|
|
sender,
|
|
})
|
|
|
|
const feePayerSignature = await transaction.feePayer.sign!({
|
|
hash,
|
|
})
|
|
|
|
return TxTempo.serialize(tx, {
|
|
feePayerSignature: Signature.from(feePayerSignature),
|
|
})
|
|
}
|
|
|
|
if (feePayer === true) {
|
|
if (signature)
|
|
return TxTempo.serialize(transaction_ox, {
|
|
format: 'feePayer',
|
|
sender: transaction.from,
|
|
signature,
|
|
})
|
|
return TxTempo.serialize(transaction_ox, {
|
|
feePayerSignature: null,
|
|
})
|
|
}
|
|
|
|
return TxTempo.serialize(
|
|
// If we have specified a fee payer, the user will not be signing over the fee token.
|
|
// Defer the fee token signing to the fee payer.
|
|
{ ...transaction_ox, ...(feePayer ? { feeToken: undefined } : {}) },
|
|
{
|
|
feePayerSignature: undefined,
|
|
signature,
|
|
},
|
|
)
|
|
}
|
|
|
|
// Export types required for inference.
|
|
// biome-ignore lint/performance/noBarrelFile: _
|
|
export {
|
|
/** @deprecated */
|
|
KeyAuthorization as z_KeyAuthorization,
|
|
/** @deprecated */
|
|
SignatureEnvelope as z_SignatureEnvelope,
|
|
/** @deprecated */
|
|
TxEnvelopeTempo as z_TxEnvelopeTempo,
|
|
} from 'ox/tempo'
|