Skip to content

Instantly share code, notes, and snippets.

@sbauch
Created Oct 1, 2021
Embed
What would you like to do?
autotask for checking L1 ownership before L2 tx
import {
DefenderRelayProvider,
DefenderRelaySigner,
} from 'defender-relay-client/lib/ethers';
import {BigNumber, Contract, utils, providers} from 'ethers';
import IERC721 from '@openzeppelin/contracts/build/contracts/IERC721.json';
import Forwarder from '../../deployments/mumbai/Forwarder.json';
const OS_SHARED_CONTRACT = '0x495f947276749ce646f68ac8c248420045cb7b5e';
const osABI = [
'function balanceOf(address _owner, uint256 _id) external view returns (uint256)',
];
async function isOwner(
contractAddress: string,
tokenId: string,
address: string,
mainnetRpcUrl: string
) {
const mainnetProvider = new providers.JsonRpcProvider(mainnetRpcUrl);
if (
utils.getAddress(contractAddress) === utils.getAddress(OS_SHARED_CONTRACT)
) {
const nftContract = new Contract(contractAddress, osABI, mainnetProvider);
const balance = await nftContract.balanceOf(
address,
BigNumber.from(tokenId)
);
return !!balance;
}
const nftContract = new Contract(
contractAddress,
IERC721.abi,
mainnetProvider
);
const owner = await nftContract.ownerOf(BigNumber.from(tokenId));
return utils.getAddress(owner) === utils.getAddress(address);
}
async function relay(
forwarder: Contract,
request: any,
signature: string,
mainnetRpcUrl: string
) {
const {
extra: {contractAddress, tokenId},
} = request;
const accepts = await isOwner(
contractAddress,
tokenId,
request.from,
mainnetRpcUrl
);
if (!accepts) throw new Error(`Rejected request to ${request.to}`);
// Validate request on the forwarder contract
const valid = await forwarder.verify(request, signature);
if (!valid) throw new Error(`Invalid request`);
// Send meta-tx through relayer to the forwarder contract
const gasLimit = (parseInt(request.gas) + 50000).toString();
return await forwarder.execute(request, signature, {gasLimit});
}
// Entrypoint for the Autotask
export async function handler(event: any) {
// Parse webhook payload
if (!event.request || !event.request.body) throw new Error(`Missing payload`);
const {request, signature} = event.request.body;
console.log(`Relaying`, request);
const {mainnetRpcUrl} = event.secrets;
// Initialize Relayer provider and signer, and forwarder contract
const credentials = {...event};
const provider = new DefenderRelayProvider(credentials);
const signer = new DefenderRelaySigner(credentials, provider, {
speed: 'fast',
});
const forwarder = new Contract(Forwarder.address, Forwarder.abi, signer);
// Relay transaction!
const tx = await relay(forwarder, request, signature, mainnetRpcUrl);
console.log(`Sent meta-tx: ${tx.hash}`);
return {txHash: tx.hash};
}
// Sample typescript type definitions
type EnvInfo = {
API_KEY: string;
API_SECRET: string;
};
// To run locally (this code will not be executed in Autotasks)
if (require.main === module) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('dotenv').config();
const {API_KEY: apiKey, API_SECRET: apiSecret} = process.env as EnvInfo;
handler({apiKey, apiSecret})
.then(() => process.exit(0))
.catch((error: Error) => {
console.error(error);
process.exit(1);
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment