accounts
Version:
Tempo Accounts SDK
122 lines • 5.17 kB
JavaScript
import {} from 'ox';
import { createClient, http, } from 'viem';
import { Transaction } from 'viem/tempo';
const defaultClients = new Map();
const clients = new WeakMap();
/** Resolves a viem Client for a given chain ID (cached). */
export function fromChainId(chainId, options) {
const { chains, feePayer: feePayerOption, provider, store, transports } = options;
const feePayerUrl = (() => {
if (feePayerOption === false)
return undefined;
if (typeof feePayerOption === 'string')
return normalizeFeePayerUrl(feePayerOption);
if (feePayerOption?.url)
return normalizeFeePayerUrl(feePayerOption.url);
return undefined;
})();
const precedence = (() => {
if (typeof feePayerOption === 'object' && feePayerOption !== null)
return feePayerOption.precedence ?? 'fee-payer-first';
return 'fee-payer-first';
})();
const id = chainId ?? store.getState().chainId;
const key = `${id}:${provider ? 'p' : ''}:${feePayerOption === false ? 'no-fp' : (feePayerUrl ?? '')}:${precedence}`;
// Scope the cache by `provider` (preferred) or `transports`, falling back
// to a shared module-level map. The cache key only encodes a boolean for
// `provider`, so without scoping, two providers sharing the same chainId
// would hit each other's cached clients and route requests to the wrong
// adapter via `providerTransport`.
const scope = provider ?? transports;
const cache = (() => {
if (!scope)
return defaultClients;
let map = clients.get(scope);
if (!map) {
map = new Map();
clients.set(scope, map);
}
return map;
})();
let client = cache.get(key);
if (!client) {
const chain = chains.find((c) => c.id === id) ?? chains[0];
const base = transports?.[id] ?? http();
const transport_base = provider ? providerTransport(provider, base) : base;
const transport = feePayerUrl
? feePayerTransport(transport_base, feePayerUrl, precedence)
: transport_base;
client = createClient({ chain, transport, pollingInterval: 1000 });
cache.set(key, client);
}
return client;
}
/**
* Creates a transport that routes requests through the provider, falling
* back to the given base transport for methods the provider proxies to RPC.
*/
function providerTransport(provider, base) {
return (params) => {
const baseTransport = base(params);
return {
...baseTransport,
async request({ method, params: reqParams }) {
return provider.request({
method,
params: reqParams,
});
},
};
};
}
/**
* Resolves a fee payer URL to an absolute URL string. Relative paths (e.g.
* `/relay`) are resolved against `window.location.origin` when running in a
* browser; on the server, relative paths are returned as-is.
*/
function normalizeFeePayerUrl(url) {
if (url.startsWith('http://') || url.startsWith('https://'))
return url;
if (typeof window !== 'undefined')
return new URL(url, window.location.origin).href;
return url;
}
function feePayerTransport(base, url, precedence) {
return (params) => {
const baseTransport = base(params);
const sponsor = http(url)(params);
return {
...baseTransport,
async request({ method, params: rpcParams }) {
const args = rpcParams;
if (precedence === 'fee-payer-first' && method === 'eth_fillTransaction') {
const request = args?.[0];
if (request &&
typeof request === 'object' &&
'feePayer' in request &&
(request.feePayer === true || typeof request.feePayer === 'string'))
return sponsor.request({
method,
params: [{ ...request, feePayer: true }],
});
}
if (method === 'eth_sendRawTransaction' || method === 'eth_sendRawTransactionSync') {
const serialized = args?.[0];
if (typeof serialized === 'string' &&
(serialized.startsWith('0x76') || serialized.startsWith('0x78'))) {
const deserialized = Transaction.deserialize(serialized);
if ('feePayerSignature' in deserialized && deserialized.feePayerSignature === null) {
const signed = await sponsor.request({
method: 'eth_signRawTransaction',
params: [serialized],
});
return await baseTransport.request({ method, params: [signed] });
}
}
}
return await baseTransport.request({ method, params: rpcParams });
},
};
};
}
//# sourceMappingURL=Client.js.map