@reservoir0x/relay-sdk
Version:
Relay is the Fastest and Cheapest Way to Bridge and Transact Across Chains.
207 lines • 8.46 kB
JavaScript
import { LogLevel } from './logger.js';
import { getClient } from '../client.js';
import { createPublicClient, createWalletClient, custom, fallback, hexToBigInt, http } from 'viem';
export function isViemWalletClient(wallet) {
return (wallet.extend !== undefined &&
wallet.getPermissions !== undefined);
}
export const adaptViemWallet = (wallet) => {
return {
vmType: 'evm',
getChainId: async () => {
return wallet.getChainId();
},
transport: custom(wallet.transport),
address: async () => {
let address = wallet.account?.address;
if (!address) {
;
[address] = await wallet.getAddresses();
}
return address;
},
handleSignMessageStep: async (stepItem) => {
const client = getClient();
const signData = stepItem.data?.sign;
let signature;
if (signData) {
if (signData.signatureKind === 'eip191') {
client.log(['Execute Steps: Signing with eip191'], LogLevel.Verbose);
if (signData.message.match(/0x[0-9a-fA-F]{64}/)) {
// If the message represents a hash, we need to convert it to raw bytes first
signature = await wallet.signMessage({
account: wallet.account,
message: {
raw: signData.message
}
});
}
else {
signature = await wallet.signMessage({
account: wallet.account,
message: signData.message
});
}
}
else if (signData.signatureKind === 'eip712') {
const signatureData = {
account: wallet.account,
domain: signData.domain,
types: signData.types,
primaryType: signData.primaryType,
message: signData.value
};
client.log(['Execute Steps: Signing with eip712', signatureData], LogLevel.Verbose);
signature = await wallet.signTypedData(signatureData);
}
}
return signature;
},
handleSendTransactionStep: async (chainId, stepItem) => {
const stepData = stepItem.data;
const client = getClient();
const chain = getClient().chains.find((chain) => chain.id === chainId)?.viemChain;
if (!chain) {
throw 'Chain not found when sending transaction';
}
const viemClient = createWalletClient({
account: wallet.account ?? stepData.from,
chain,
transport: custom(wallet.transport, { retryCount: 10, retryDelay: 200 })
});
return await viemClient.sendTransaction({
chain,
data: stepData.data,
account: wallet.account ?? stepData.from, // use signer.account if it's defined
to: stepData.to,
value: hexToBigInt(stepData.value || 0),
...(stepData.maxFeePerGas &&
client.useGasFeeEstimations && {
maxFeePerGas: hexToBigInt(stepData.maxFeePerGas)
}),
...(stepData.maxPriorityFeePerGas &&
client.useGasFeeEstimations && {
maxPriorityFeePerGas: hexToBigInt(stepData.maxPriorityFeePerGas)
}),
...(stepData.gas &&
client.useGasFeeEstimations && {
gas: hexToBigInt(stepData.gas)
})
});
},
handleConfirmTransactionStep: async (txHash, chainId, onReplaced, onCancelled) => {
const client = getClient();
const chain = client.chains.find((chain) => chain.id === chainId);
const rpcUrl = chain?.httpRpcUrl;
const viemClient = createPublicClient({
chain: chain?.viemChain,
transport: wallet.transport
? fallback(rpcUrl
? [http(rpcUrl), custom(wallet.transport), http()]
: [custom(wallet.transport), http()])
: fallback([http(rpcUrl), http()]),
pollingInterval: client.confirmationPollingInterval
});
const receipt = await viemClient.waitForTransactionReceipt({
hash: txHash,
onReplaced: (replacement) => {
if (replacement.reason === 'cancelled') {
onCancelled();
throw Error('Transaction cancelled');
}
onReplaced(replacement.transaction.hash);
}
});
return receipt;
},
switchChain: async (chainId) => {
try {
await wallet.switchChain({
id: chainId
});
return;
}
catch (e) {
if (e && e?.message) {
if (e.message.includes('does not support the requested chain')) {
throw new Error('Wallet does not support chain');
}
else if (e.message.includes('rejected')) {
throw e;
}
else if (e.message.includes('already pending')) {
return;
}
}
const client = getClient();
const chain = client.chains.find((chain) => chain.id === chainId);
if (!chain) {
throw 'Chain missing from Relay Client';
}
try {
await wallet.addChain({
chain: chain?.viemChain
});
}
catch (e) {
if (e instanceof Error &&
e.name &&
e.name === 'InternalRpcError' &&
e.message.includes('is not a function')) {
getClient()?.log([
'Execute Steps: Detected internal RPC Error when adding a chain to the wallet',
e
], LogLevel.Verbose);
return;
}
else {
throw e;
}
}
return;
}
},
supportsAtomicBatch: async (chainId) => {
if (!wallet.account)
return false;
try {
const capabilities = await wallet.getCapabilities({
account: wallet.account,
chainId
});
return capabilities?.atomicBatch?.supported;
}
catch {
return false;
}
},
handleBatchTransactionStep: async (chainId, items) => {
const calls = items.map((item) => ({
to: item.data.to,
data: item.data.data,
value: hexToBigInt(item.data.value || 0),
...(item.data.maxFeePerGas && {
maxFeePerGas: hexToBigInt(item.data.maxFeePerGas)
}),
...(item.data.maxPriorityFeePerGas && {
maxPriorityFeePerGas: hexToBigInt(item.data.maxPriorityFeePerGas)
}),
...(item.data.gas && {
gas: hexToBigInt(item.data.gas)
})
}));
const client = getClient();
const chain = client.chains.find((chain) => chain.id === chainId)?.viemChain;
if (!chain) {
throw 'Chain not found when sending transaction';
}
const { id } = await wallet.sendCalls({
chain,
account: wallet.account,
calls
});
return id;
}
};
};
//# sourceMappingURL=viemWallet.js.map