autotask for checking L1 ownership before L2 tx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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