accounts
Version:
Tempo Accounts SDK
154 lines • 5 kB
JavaScript
import { Address, Hex } from 'ox';
import { prepareTransactionRequest } from 'viem/actions';
import * as AccessKey from '../AccessKey.js';
import * as ExecutionError from '../ExecutionError.js';
const removalErrorNames = new Set([
'InvalidSignature',
'InvalidSignatureFormat',
'InvalidSignatureType',
'KeyAlreadyRevoked',
'KeyExpired',
'KeyNotFound',
'SignatureTypeMismatch',
]);
/** Creates a transaction helper for a matching locally-signable access key. */
export async function create(options) {
const { address, calls, chainId, client, store } = options;
const account = await AccessKey.select({
account: address,
calls,
chainId,
store,
});
if (!account)
return undefined;
return createTransaction({ account, address, chainId, client, store });
}
function createTransaction(options) {
const { account, address, chainId, client, store } = options;
return {
async fill(parameters) {
try {
// Run prepareTransactionRequest to attach any pending key authorizations.
// `eth_fillTransaction` below needs to return the node's fill response.
const request = await prepareTransactionRequest(client, {
account,
...parameters,
parameters: [],
type: 'tempo',
});
return await fillTransaction(client, request);
}
catch (error) {
removeForError(error, { account, address, chainId, store });
throw error;
}
},
async prepare(parameters) {
try {
const request = await prepareTransactionRequest(client, {
account,
...parameters,
type: 'tempo',
});
return createPreparedTransaction({
account,
address,
chainId,
client,
request: request,
store,
});
}
catch (error) {
removeForError(error, { account, address, chainId, store });
throw error;
}
},
};
}
function createPreparedTransaction(options) {
const { account, address, chainId, client, request, store } = options;
async function sign() {
try {
return await account.signTransaction(request);
}
catch (error) {
removeForError(error, { account, address, chainId, store });
throw error;
}
}
return {
request,
sign,
async send() {
try {
const signed = await sign();
return (await client.request({
method: 'eth_sendRawTransaction',
params: [signed],
}));
}
catch (error) {
removeForError(error, { account, address, chainId, store });
throw error;
}
},
async sendSync() {
try {
const signed = await sign();
return (await client.request({
method: 'eth_sendRawTransactionSync',
params: [signed],
}));
}
catch (error) {
removeForError(error, { account, address, chainId, store });
throw error;
}
},
};
}
async function fillTransaction(client, parameters) {
const formatter = client.chain?.formatters?.transactionRequest;
const formatted = formatter
? formatter.format({ ...parameters }, 'fillTransaction')
: parameters;
return (await client.request({
method: 'eth_fillTransaction',
params: [formatted],
}));
}
function removeForError(error, options) {
if (!shouldRemoveForError(error))
return;
AccessKey.remove({
accessKey: options.account.accessKeyAddress,
account: options.address,
chainId: options.chainId,
store: options.store,
});
}
function shouldRemoveForError(error) {
if (!(error instanceof Error))
return false;
const parsed = ExecutionError.parse(error);
if (removalErrorNames.has(parsed.errorName))
return true;
const text = getErrorText(error);
return [...removalErrorNames].some((name) => text.includes(name));
}
function getErrorText(error) {
if (!(error instanceof Error))
return '';
const details = error.details;
const shortMessage = error.shortMessage;
const cause = error.cause;
return [
error.message,
typeof details === 'string' ? details : '',
typeof shortMessage === 'string' ? shortMessage : '',
getErrorText(cause),
].join('\n');
}
//# sourceMappingURL=AccessKeyTransaction.js.map