import BigNumber from "bignumber.js" ; import { ethers } from "ethers" ; import { defaultAbiCoder } from "ethers/lib/utils" ; import { Provider } from "@ethersproject/providers" ; import { ChainTransaction , InputChainTransaction , utils , } from "@renproject/utils" ; import { findABIMethod , getEventTopic , getMintGatewayInstance , LockGatewayABI , MintGatewayABI , } from "../contracts" ; import { TypedEvent } from "../contracts/typechain/common" ; import { LogLockToChainEvent } from "../contracts/typechain/LockGatewayV3" ; import { LogBurnEvent , LogMintEvent , } from "../contracts/typechain/MintGatewayV3" ; import { LogTransferredEvent } from "../contracts/typechain/TransferWithLog" ; import { AbiItem } from "./abi" ; import { getLockGateway , getMintGateway } from "./gatewayRegistry" ; import { EvmNetworkConfig } from "./types" ; export const txHashToChainTransaction = ( chain , txHash ) : ChainTransaction => ( { chain , txidFormatted : txHash , txid : utils . toURLBase64 ( utils . fromHex ( txHash ) ) , txindex : "0" , } ) ; export const mapBurnLogToInputChainTransaction = ( chain , asset , event ) : InputChainTransaction => { const [ to , amount , burnNonce ] = event . args ; return { ... txHashToChainTransaction ( chain , event . transactionHash ) , asset , amount : amount . toString ( ) , toRecipient : to , nonce : utils . toURLBase64 ( utils . toNBytes ( burnNonce . toString ( ) , 32 ) ) , } ; } ; export const mapLockLogToInputChainTransaction = ( chain , asset , event ) : InputChainTransaction => { const [ recipientAddress , recipientChain , recipientPayload , amount , lockNonce , ] = event . args ; const nonceBuffer = utils . toNBytes ( new BigNumber ( lockNonce . toString ( ) ) , 32 ) ; if ( nonceBuffer . length !== 32 ) { throw new Error ( "Invalid nonce length" ) ; } return { ... txHashToChainTransaction ( chain , event . transactionHash ) , asset , amount : amount . toString ( ) , nonce : utils . toURLBase64 ( nonceBuffer ) , toRecipient : recipientAddress , toChain : recipientChain , toPayload : utils . toURLBase64 ( utils . fromHex ( recipientPayload ) ) , } ; } ; export const mapTransferLogToInputChainTransaction = ( chain , asset , event ) : InputChainTransaction => { const [ _from , _to , amount ] = event . args ; return { ... txHashToChainTransaction ( chain , event . transactionHash ) , asset , amount : amount . toString ( ) , } ; } ; /**
 * Convert an EVM txHash to a RenVM ChainTransaction struct.
 * The txindex for Ethereum is currently set to 0, and the nonce is used instead
 * to differentiate locks/burns in the same EVM transaction.
 */ export const findMintBySigHash = async ( network , provider , asset , nHash , sigHash ? , blockLimit ? ) : Promise < ChainTransaction | undefined > => { const gatewayAddress = await getMintGateway ( network , provider , asset ) ; const gatewayInstance = getMintGatewayInstance ( provider , gatewayAddress ) ; const logMintABI = findABIMethod ( MintGatewayABI , "LogMint" ) ; const mintEvents = ( await getPastLogs < LogMintEvent > ( provider , gatewayAddress , logMintABI , [ null , null , utils . Ox ( nHash ) ] , blockLimit ) ) . map ( ( event ) => txHashToChainTransaction ( network . selector , event . transactionHash ) ) ; if ( mintEvents . length ) { if ( mintEvents . length > 1 ) { console . warn ( `Found more than one mint log.` ) ; } return mintEvents [ 0 ] ; } if ( sigHash ) { const status = await gatewayInstance . status ( utils . Ox ( sigHash ) ) ; if ( status ) { return txHashToChainTransaction ( network . selector , "" ) ; } } return undefined ; } ; export const findReleaseBySigHash = async ( network , provider , asset , nHash , blockLimit ? ) : Promise < ChainTransaction | undefined > => { const gatewayAddress = await getLockGateway ( network , provider , asset ) ; const logLockABI = findABIMethod ( LockGatewayABI , "LogRelease" ) ; const newReleaseEvents = ( await getPastLogs < LogBurnEvent > ( provider , gatewayAddress , logLockABI , [ null , null , utils . Ox ( nHash ) ] , blockLimit ) ) . map ( ( event ) => txHashToChainTransaction ( network . selector , event . transactionHash ) ) ; if ( newReleaseEvents . length > 1 ) { console . warn ( `Found more than one release log.` ) ; } return newReleaseEvents . length ? newReleaseEvents [ 0 ] : undefined ; } ; export const filterLogs = < T extends TypedEvent > ( logs , eventABI ) : T [ ] => { if ( ! logs ) { throw Error ( "No events found in transaction" ) ; } const logTopic = utils . Ox ( getEventTopic ( eventABI ) ) ; const logDecoder = new ethers . utils . Interface ( [ eventABI ] ) ; return logs . filter ( ( event ) => event . topics [ 0 ] === logTopic ) . map ( ( event ) => ( { ... logDecoder . parseLog ( event ) , transactionHash : event . transactionHash , } as unknown as T ) ) ; } ; const getPastLogs = async < T extends TypedEvent > ( provider , contractAddress , eventABI , filter , blockLimit ? ) : Promise < T [ ] > => { let fromBlock = 1 ; let toBlock = "latest" ; if ( blockLimit ) { toBlock = new BigNumber ( ( await provider . getBlockNumber ( ) ) . toString ( ) ) . toNumber ( ) ; fromBlock = toBlock - blockLimit + 1 ; } const events = await provider . getLogs ( { address : contractAddress , fromBlock : fromBlock , toBlock : toBlock , topics : [ utils . Ox ( getEventTopic ( eventABI ) ) , ... filter ] as string [ ] , } ) ; return filterLogs < T > ( events , eventABI ) ; } ; export const validateAddress = ( address ) : boolean => { if ( /^.+\.eth$/ . exec ( address ) ) { return true ; } try { ethers . utils . getAddress ( address ) ; return true ; } catch ( _error ) { return false ; } } ; export const validateTransaction = ( transaction ) : boolean => transaction !== null && utils . isHex ( transaction . txid , { length : 32 , prefix : true } ) ; export const rawEncode = ( types , parameters ) : Buffer => utils . fromHex ( defaultAbiCoder . encode ( types , parameters ) ) ;