import "ethers/dist/shims.js" ; import { ethers } from "ethers" ; import Utils from "./Utils" ; import TasitProviderHelpers from "@tasit/provider-helpers" ; import Subscription from "../subscription" ; import Action from "../action" ; const { ProviderFactory } = TasitProviderHelpers ; ethers . errors . setLogLevel ( "error" ) ; import { Wallet , ContractFunction } from "./types" ; export class Contract extends Subscription { private provider : any ; private ethersContract : { [ x ] : ( arg0 : any ) => void ; signer : any ; address ? : any ; interface ? : any ; } ; constructor ( address , abi , wallet ? ) { if ( ! Utils . isAddress ( address ) || ! Utils . isABI ( abi ) ) throw new Error ( `Cannot create a Contract without a address and ABI` ) ; if ( wallet && ! Utils . isEthersJsSigner ( wallet ) ) throw new Error ( `Cannot set an invalid wallet for a Contract` ) ; const provider = ProviderFactory . getProvider ( ) ; const signerOrProvider = wallet ? wallet . connect ( provider ) : provider ; const ethersContract = new ethers . Contract ( address , abi , signerOrProvider ) ; super ( ethersContract ) ; this . provider = provider ; this . ethersContract = ethersContract ; this . addFunctionsToContract ( ) ; } setAccount = ( wallet ) => { if ( ! Utils . isEthersJsSigner ( wallet ) ) throw new Error ( `Cannot set an invalid account for a Contract` ) ; this . ethersContract = new ethers . Contract ( this . ethersContract . address , this . ethersContract . interface . abi , wallet . connect ( this . provider ) ) ; this . addFunctionsToContract ( ) ; } ; getAccount = ( ) => { const { signer : wallet } = this . ethersContract ; return wallet ; } ; removeAccount = ( ) => { this . ethersContract = new ethers . Contract ( this . ethersContract . address , this . ethersContract . interface . abi , this . provider ) ; this . addFunctionsToContract ( ) ; } ; getAddress = ( ) => { return this . ethersContract . address ; } ; getABI = ( ) => { return this . ethersContract . interface . abi ; } ; _getProvider = ( ) => { return this . provider ; } ; on = ( eventName , listener ) => { this . addListener ( eventName , listener , false ) ; } ; once = ( eventName , listener ) => { this . addListener ( eventName , listener , true ) ; } ; private addListener = ( eventName , listener , once ) => { if ( ! this . isEventValid ( eventName ) ) throw new Error ( `Event '${ eventName }' not found.` ) ; if ( eventName === "error" && once ) throw new Error ( `Use on() function to subscribe to an error event.` ) ; if ( eventName === "error" ) { this . _addErrorListener ( listener ) ; } else { this . addContractEventListener ( eventName , listener , once ) ; } } ; private addContractEventListener = ( eventName , listener , once ) => { const ethersListener = async ( ... args ) => { try { const event = args . pop ( ) ; const message = { data : { args : event . args , } , } ; if ( once ) this . off ( eventName ) ; await listener ( message ) ; } catch ( error ) { this . _emitErrorEventFromEventListener ( new Error ( `Listener function with error: ${ error . message }` ) , eventName ) ; } } ; this . _addEventListener ( eventName , ethersListener ) ; } ; private isEventValid = ( eventName ) => { return ( eventName === "error" || this . ethersContract . interface . events [ eventName ] !== undefined ) ; } ; private addFunctionsToContract = ( ) => { this . ethersContract . interface . abi . filter ( ( json ) => { return json . type === "function" ; } ) . forEach ( ( f ) => { const isWrite = ( f . constant === undefined && f . stateMutability !== "view" && f . stateMutability !== "pure" ) || ( f . constant !== undefined && f . constant === false ) ; if ( isWrite ) this . attachWriteFunction ( f ) ; else { this . attachReadFunction ( f ) ; } } ) ; } ; private attachReadFunction = ( f ) => { this [ f . name ] = async ( arg0 ) => { const value = await this . ethersContract [ f . name ] ( arg0 ) ; return value ; } ; } ; private attachWriteFunction = ( f ) => { this [ f . name ] = ( ... args ) => { if ( ! Utils . isEthersJsSigner ( this . ethersContract . signer ) ) throw new Error ( `Cannot write data to a Contract without a wallet` ) ; const { signer , address } = this . ethersContract ; const data = this . ethersContract . interface . functions [ f . name ] . encode ( args ) ; const rawTx = { to : address , data } ; const action = new Action ( rawTx , this . provider , signer ) ; const errorListener = ( error ) => { this . _emitErrorEvent ( error ) ; } ; action . on ( "error" , errorListener ) ; return action ; } ; } ; } export default Contract ;