- 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>
259 lines
8.0 KiB
TypeScript
259 lines
8.0 KiB
TypeScript
import type { Address } from 'abitype'
|
|
import { parseAccount } from '../../accounts/utils/parseAccount.js'
|
|
import type { Client } from '../../clients/createClient.js'
|
|
import type { Transport } from '../../clients/transports/createTransport.js'
|
|
import type { BaseError } from '../../errors/base.js'
|
|
import { BaseFeeScalarError } from '../../errors/fee.js'
|
|
import type { ErrorType } from '../../errors/utils.js'
|
|
import type { Account, GetAccountParameter } from '../../types/account.js'
|
|
import type { ExtractCapabilities } from '../../types/capabilities.js'
|
|
import type {
|
|
Chain,
|
|
ChainFeesFnParameters,
|
|
DeriveChain,
|
|
GetChainParameter,
|
|
} from '../../types/chain.js'
|
|
import type { Hex } from '../../types/misc.js'
|
|
import type { TransactionRequest } from '../../types/transaction.js'
|
|
import type { UnionOmit } from '../../types/utils.js'
|
|
import {
|
|
type GetTransactionErrorReturnType,
|
|
getTransactionError,
|
|
} from '../../utils/errors/getTransactionError.js'
|
|
import { extract } from '../../utils/formatters/extract.js'
|
|
import {
|
|
type FormattedTransaction,
|
|
formatTransaction,
|
|
} from '../../utils/formatters/transaction.js'
|
|
import {
|
|
type FormattedTransactionRequest,
|
|
formatTransactionRequest,
|
|
} from '../../utils/formatters/transactionRequest.js'
|
|
import { getAction } from '../../utils/getAction.js'
|
|
import type { NonceManager } from '../../utils/nonceManager.js'
|
|
import { assertRequest } from '../../utils/transaction/assertRequest.js'
|
|
import { getBlock } from './getBlock.js'
|
|
import { getChainId as getChainId_ } from './getChainId.js'
|
|
|
|
export type FillTransactionParameters<
|
|
chain extends Chain | undefined = Chain | undefined,
|
|
account extends Account | undefined = Account | undefined,
|
|
chainOverride extends Chain | undefined = Chain | undefined,
|
|
accountOverride extends Account | Address | undefined =
|
|
| Account
|
|
| Address
|
|
| undefined,
|
|
///
|
|
_derivedChain extends Chain | undefined = DeriveChain<chain, chainOverride>,
|
|
> = UnionOmit<FormattedTransactionRequest<_derivedChain>, 'from'> &
|
|
GetAccountParameter<account, accountOverride, false, true> &
|
|
GetChainParameter<chain, chainOverride> & {
|
|
/**
|
|
* Nonce manager to use for the transaction request.
|
|
*/
|
|
nonceManager?: NonceManager | undefined
|
|
}
|
|
|
|
export type FillTransactionReturnType<
|
|
chain extends Chain | undefined = Chain | undefined,
|
|
chainOverride extends Chain | undefined = Chain | undefined,
|
|
///
|
|
_derivedChain extends Chain | undefined = DeriveChain<chain, chainOverride>,
|
|
> = {
|
|
capabilities?:
|
|
| ExtractCapabilities<'fillTransaction', 'ReturnType'>
|
|
| undefined
|
|
raw: Hex
|
|
transaction: FormattedTransaction<_derivedChain>
|
|
}
|
|
|
|
export type FillTransactionErrorType =
|
|
| GetTransactionErrorReturnType<ErrorType>
|
|
| ErrorType
|
|
|
|
/**
|
|
* Fills a transaction request with the necessary fields to be signed over.
|
|
*
|
|
* - Docs: https://viem.sh/docs/actions/public/fillTransaction
|
|
*
|
|
* @param client - Client to use
|
|
* @param parameters - {@link FillTransactionParameters}
|
|
* @returns The filled transaction. {@link FillTransactionReturnType}
|
|
*
|
|
* @example
|
|
* import { createPublicClient, http } from 'viem'
|
|
* import { mainnet } from 'viem/chains'
|
|
* import { fillTransaction } from 'viem/public'
|
|
*
|
|
* const client = createPublicClient({
|
|
* chain: mainnet,
|
|
* transport: http(),
|
|
* })
|
|
* const result = await fillTransaction(client, {
|
|
* account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
|
|
* to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
|
|
* value: parseEther('1'),
|
|
* })
|
|
*/
|
|
export async function fillTransaction<
|
|
chain extends Chain | undefined,
|
|
account extends Account | undefined,
|
|
chainOverride extends Chain | undefined = undefined,
|
|
accountOverride extends Account | Address | undefined = undefined,
|
|
>(
|
|
client: Client<Transport, chain, account>,
|
|
parameters: FillTransactionParameters<
|
|
chain,
|
|
account,
|
|
chainOverride,
|
|
accountOverride
|
|
>,
|
|
): Promise<FillTransactionReturnType<chain, chainOverride>> {
|
|
const {
|
|
account = client.account,
|
|
accessList,
|
|
authorizationList,
|
|
chain = client.chain,
|
|
blobVersionedHashes,
|
|
blobs,
|
|
data,
|
|
gas,
|
|
gasPrice,
|
|
maxFeePerBlobGas,
|
|
maxFeePerGas,
|
|
maxPriorityFeePerGas,
|
|
nonce: nonce_,
|
|
nonceManager,
|
|
to,
|
|
type,
|
|
value,
|
|
...rest
|
|
} = parameters
|
|
|
|
const nonce = await (async () => {
|
|
if (!account) return nonce_
|
|
if (!nonceManager) return nonce_
|
|
if (typeof nonce_ !== 'undefined') return nonce_
|
|
const account_ = parseAccount(account)
|
|
const chainId = chain
|
|
? chain.id
|
|
: await getAction(client, getChainId_, 'getChainId')({})
|
|
return await nonceManager.consume({
|
|
address: account_.address,
|
|
chainId,
|
|
client,
|
|
})
|
|
})()
|
|
|
|
assertRequest(parameters)
|
|
|
|
const chainFormat = chain?.formatters?.transactionRequest?.format
|
|
const format = chainFormat || formatTransactionRequest
|
|
|
|
const request = format(
|
|
{
|
|
// Pick out extra data that might exist on the chain's transaction request type.
|
|
...extract(rest, { format: chainFormat }),
|
|
account: account ? parseAccount(account) : undefined,
|
|
accessList,
|
|
authorizationList,
|
|
blobs,
|
|
blobVersionedHashes,
|
|
data,
|
|
gas,
|
|
gasPrice,
|
|
maxFeePerBlobGas,
|
|
maxFeePerGas,
|
|
maxPriorityFeePerGas,
|
|
nonce,
|
|
to,
|
|
type,
|
|
value,
|
|
} as TransactionRequest,
|
|
'fillTransaction',
|
|
)
|
|
|
|
try {
|
|
const response = await client.request({
|
|
method: 'eth_fillTransaction',
|
|
params: [request],
|
|
})
|
|
const format = chain?.formatters?.transaction?.format || formatTransaction
|
|
|
|
const transaction = format(response.tx)
|
|
|
|
// Remove unnecessary fields.
|
|
delete transaction.blockHash
|
|
delete transaction.blockNumber
|
|
delete transaction.r
|
|
delete transaction.s
|
|
delete transaction.transactionIndex
|
|
delete transaction.v
|
|
delete transaction.yParity
|
|
|
|
// Rewrite fields.
|
|
transaction.data = transaction.input
|
|
|
|
// Preference supplied fees (some nodes do not take these preferences).
|
|
if (transaction.gas) transaction.gas = parameters.gas ?? transaction.gas
|
|
if (transaction.gasPrice)
|
|
transaction.gasPrice = parameters.gasPrice ?? transaction.gasPrice
|
|
if (transaction.maxFeePerBlobGas)
|
|
transaction.maxFeePerBlobGas =
|
|
parameters.maxFeePerBlobGas ?? transaction.maxFeePerBlobGas
|
|
if (transaction.maxFeePerGas)
|
|
transaction.maxFeePerGas =
|
|
parameters.maxFeePerGas ?? transaction.maxFeePerGas
|
|
if (transaction.maxPriorityFeePerGas)
|
|
transaction.maxPriorityFeePerGas =
|
|
parameters.maxPriorityFeePerGas ?? transaction.maxPriorityFeePerGas
|
|
if (typeof transaction.nonce !== 'undefined')
|
|
transaction.nonce = parameters.nonce ?? transaction.nonce
|
|
|
|
// Build fee multiplier function.
|
|
const feeMultiplier = await (async () => {
|
|
if (typeof chain?.fees?.baseFeeMultiplier === 'function') {
|
|
const block = await getAction(client, getBlock, 'getBlock')({})
|
|
return chain.fees.baseFeeMultiplier({
|
|
block,
|
|
client,
|
|
request: parameters,
|
|
} as ChainFeesFnParameters)
|
|
}
|
|
return chain?.fees?.baseFeeMultiplier ?? 1.2
|
|
})()
|
|
if (feeMultiplier < 1) throw new BaseFeeScalarError()
|
|
|
|
const decimals = feeMultiplier.toString().split('.')[1]?.length ?? 0
|
|
const denominator = 10 ** decimals
|
|
const multiplyFee = (base: bigint) =>
|
|
(base * BigInt(Math.ceil(feeMultiplier * denominator))) /
|
|
BigInt(denominator)
|
|
|
|
// Apply fee multiplier.
|
|
if (!transaction.feePayerSignature) {
|
|
if (transaction.maxFeePerGas && !parameters.maxFeePerGas)
|
|
transaction.maxFeePerGas = multiplyFee(transaction.maxFeePerGas)
|
|
if (transaction.gasPrice && !parameters.gasPrice)
|
|
transaction.gasPrice = multiplyFee(transaction.gasPrice)
|
|
}
|
|
|
|
return {
|
|
raw: response.raw,
|
|
transaction: {
|
|
from: request.from,
|
|
...transaction,
|
|
},
|
|
...(response.capabilities ? { capabilities: response.capabilities } : {}),
|
|
}
|
|
} catch (err) {
|
|
throw getTransactionError(
|
|
err as BaseError,
|
|
{
|
|
...parameters,
|
|
chain: client.chain,
|
|
} as never,
|
|
)
|
|
}
|
|
}
|