accounts
Version:
Tempo Accounts SDK
96 lines • 4.09 kB
JavaScript
import { RpcResponse, Signature } from 'ox';
import { TxEnvelopeTempo } from 'ox/tempo';
import { signTransaction } from 'viem/actions';
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 (!serialized.startsWith('0x76') && !serialized.startsWith('0x78'))
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, getClient, method, request, validate } = options;
const serialized = request.params?.[0];
if (!serialized?.startsWith('0x76') && !serialized?.startsWith('0x78'))
throw new RpcResponse.InvalidParamsError({
message: 'Only Tempo (0x76/0x78) transactions are supported.',
});
const transaction = Transaction.deserialize(serialized);
if (!transaction.signature || !transaction.from)
throw new RpcResponse.InvalidParamsError({
message: 'Transaction must be signed by the sender before fee payer signing.',
});
if (validate && !(await validate(transaction)))
throw new RpcResponse.InvalidParamsError({
message: 'Sponsorship rejected.',
});
const client = getClient(transaction.chainId);
const serializedTransaction = toSerializedTransaction(await signTransaction(client, {
...transaction,
account,
feePayer: account,
}));
if (method === 'eth_signRawTransaction')
return serializedTransaction;
return await client.request({
method: method,
params: [serializedTransaction],
});
}
function toSerializedTransaction(value) {
if (typeof value === 'string')
return value;
if (value && typeof value === 'object' && 'raw' in value && typeof value.raw === 'string')
return value.raw;
throw new Error('Expected a serialized transaction result.');
}
//# sourceMappingURL=sponsorship.js.map