accounts
Version:
Tempo Accounts SDK
103 lines • 4.66 kB
JavaScript
import { RpcResponse, Signature } from 'ox';
import { TxEnvelopeTempo } from 'ox/tempo';
import { Transaction } from 'viem/tempo';
import * as Utils from './utils.js';
/** Returns sponsor metadata for `eth_fillTransaction` responses. */
export function getSponsor(options) {
const { account, name, url } = options;
return {
address: account.address,
...(name ? { name } : {}),
...(url ? { url } : {}),
};
}
/** Returns whether the fee payer approves a filled transaction. */
export async function shouldSponsor(options) {
const { sender, transaction, validate } = options;
if (!validate)
return true;
return await validate({
...transaction,
from: sender,
});
}
/** Returns whether a raw Tempo transaction is explicitly requesting sponsorship. */
export function requestsRawSponsorship(serialized) {
if (!Utils.isSerializedTempoTransaction(serialized))
return false;
const transaction = Transaction.deserialize(serialized);
return 'feePayerSignature' in transaction && transaction.feePayerSignature === null;
}
/** Returns `true` when a fill request already has the fields needed for sponsorship signing. */
export function isPreparedTransaction(value) {
return (typeof value.from === 'string' &&
typeof Utils.resolveChainId(value.chainId) === 'number' &&
typeof value.gas !== 'undefined' &&
typeof value.nonce !== 'undefined' &&
(typeof value.maxFeePerGas !== 'undefined' || typeof value.gasPrice !== 'undefined'));
}
/** Signs a filled transaction as the fee payer. */
export async function sign(options) {
const { account, transaction, sender } = options;
const from = transaction.from ?? sender;
const { signature: _, ...withoutSenderSig } = transaction;
const prepared = { ...withoutSenderSig, from };
if (!prepared.from)
throw new RpcResponse.InvalidParamsError({
message: 'Transaction sender must be provided before fee payer signing.',
});
if (!account.sign)
throw new Error('Fee payer account cannot sign transactions.');
const feePayerSignature = Signature.from(await account.sign({
hash: TxEnvelopeTempo.getFeePayerSignPayload(TxEnvelopeTempo.from(prepared), {
sender: prepared.from,
}),
}));
return { ...prepared, feePayerSignature };
}
/** Handles `eth_signRawTransaction` and broadcast methods for sponsored Tempo transactions. */
export async function handleRawTransaction(options) {
const { account, feeToken: sponsorFeeToken, getClient, method, request, validate } = options;
const serialized = request.params?.[0];
if (!Utils.isSerializedTempoTransaction(serialized))
throw new RpcResponse.InvalidParamsError({
message: 'Only Tempo (0x76/0x78) transactions are supported.',
});
const transaction = Transaction.deserialize(serialized);
// Prefer sender recovered from raw envelope; multisig finalize path supplies fallback sender.
const sender = transaction.from ?? options.sender;
// Sponsorship only applies after sender has signed original transaction.
if (!transaction.signature || !sender)
throw new RpcResponse.InvalidParamsError({
message: 'Transaction must be signed by the sender before fee payer signing.',
});
if (!account.sign)
throw new Error('Fee payer account cannot sign transactions.');
const client = getClient(transaction.chainId);
const feeToken_chain = client.chain?.feeToken;
const feeToken = transaction.feeToken ??
sponsorFeeToken ??
(await options.getFeeToken?.(transaction.chainId)) ??
feeToken_chain;
const transaction_sponsored = feeToken ? { ...transaction, feeToken } : transaction;
if (validate && !(await validate(transaction_sponsored)))
throw new RpcResponse.InvalidParamsError({
message: 'Sponsorship rejected.',
});
const envelope = TxEnvelopeTempo.from(transaction_sponsored);
const feePayerSignature = Signature.from(await account.sign({
hash: TxEnvelopeTempo.getFeePayerSignPayload(envelope, { sender }),
}));
const serializedTransaction = TxEnvelopeTempo.serialize(envelope, {
feePayerSignature,
signature: transaction.signature,
});
// Raw-sign requests stop after fee-payer signature is added; send methods broadcast it.
if (method === 'eth_signRawTransaction')
return serializedTransaction;
return await client.request({
method: method,
params: [serializedTransaction],
});
}
//# sourceMappingURL=sponsorship.js.map