Skip to content

Instantly share code, notes, and snippets.

@ankushswar1
Last active August 15, 2023 16:45
Show Gist options
  • Save ankushswar1/4dea5adc1cd253e38c2efbe7ad32b8af to your computer and use it in GitHub Desktop.
Save ankushswar1/4dea5adc1cd253e38c2efbe7ad32b8af to your computer and use it in GitHub Desktop.
ReservoirWallet adapter using Gelato relay for gasless transactions
import type { ReservoirWallet } from '@reservoir0x/reservoir-sdk';
import { GelatoRelay } from '@gelatonetwork/relay-sdk';
import type { Signer } from 'ethers/lib/ethers';
import { arrayify } from 'ethers/lib/utils';
import type { TypedDataSigner } from '@ethersproject/abstract-signer/lib/index';
import { getTaskStatus } from './poll';
const sleep = (time: number) => new Promise((resolve) => setTimeout(resolve, time));
// Helper function to poll the status of a Gelato relay task given the taskId
const getTaskStatus = async (taskId: string) => {
let taskState;
const terminalStates = ['ExecReverted', 'Cancelled', 'ExecSuccess'];
do {
const response = await fetch(`https://api.gelato.digital/tasks/status/${taskId}`);
const status = await response.json();
taskState = status.task.taskState;
if (taskState === 'ExecSuccess') {
// Success case - return transaction hash
return status.task.transactionHash;
} else if (taskState === 'ExecReverted' || taskState === 'Cancelled') {
// Error case - return undefined
throw new Error(`Transaction failed in taskId ${taskId} with error: ${status.task.lastCheckMessage}`);
} else {
await sleep(3000);
}
} while (!terminalStates.includes(taskState));
};
// Returns a ReservoirWallet adapter that sends gasless transactions using Gelato
// Make sure the wallet has been switched to your desired network, before calling
// this function.
export const adaptGelatoRelayer = (signer: Signer, gelatoApiKey: string): ReservoirWallet => {
return {
// https://github.com/reservoirprotocol/reservoir-kit/blob/main/packages/ethers-wallet-adapter/src/adapter.ts
address: async () => {
return signer.getAddress();
},
// https://github.com/reservoirprotocol/reservoir-kit/blob/main/packages/ethers-wallet-adapter/src/adapter.ts
handleSignMessageStep: async (stepItem) => {
const signData = stepItem.data?.sign;
let signature: string | undefined;
if (signData) {
// Request user signature
if (signData.signatureKind === 'eip191') {
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 signer.signMessage(arrayify(signData.message));
} else {
signature = await signer.signMessage(signData.message);
}
} else if (signData.signatureKind === 'eip712') {
signature = await (signer as unknown as TypedDataSigner)._signTypedData(
signData.domain,
signData.types,
signData.value,
);
}
}
return signature;
},
handleSendTransactionStep: async (chainId, stepItem) => {
const { ...stepData } = stepItem.data;
const address = await signer.getAddress();
const relay = new GelatoRelay();
const request = {
chainId: chainId,
target: stepData.to,
data: stepData.data,
user: address,
};
try {
const relayResponse = await relay.sponsoredCallERC2771(request, signer, gelatoApiKey);
const { taskId } = relayResponse;
const txHash = await getTaskStatus(taskId);
return txHash;
} catch (error) {
throw new Error(`Gelato sponsored call failed with error: ${error}`);
}
},
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment