import { EthApi , requireAccount } from "@joincivil/ethapi" ; import { CivilErrors } from "@joincivil/utils" ; import { BigNumber } from "@joincivil/typescript-types" ; import { Observable } from "rxjs" ; import { EthAddress , TwoStepEthTransaction } from "../../types" ; import { BaseWrapper } from "../basewrapper" ; import { MultiSigWallet , MultiSigWalletContract } from "../generated/wrappers/multi_sig_wallet" ; import { createTwoStepSimple , createTwoStepTransaction , isDecodedLog } from "../utils/contracts" ; import { MultisigTransaction } from "./multisigtransaction" ; import { TransactionConfig } from "web3-core" ; import { ethers } from "ethers" ; export class Multisig extends BaseWrapper < MultiSigWalletContract > { public static atUntrusted ( ethApi , address ) { const instance = MultiSigWalletContract . atUntrusted ( ethApi , address ) ; return new Multisig ( ethApi , instance , 0 ) ; } public static async deployTrusted ( ethApi , owners , required ) { return createTwoStepTransaction ( ethApi , await MultiSigWalletContract . deployTrusted . sendTransactionAsync ( ethApi , owners , ethApi . toBigNumber ( required ) , { from : "" , } ) , receipt => new Multisig ( ethApi , MultiSigWalletContract . atUntrusted ( ethApi , receipt . contractAddress ! ) , 0 ) ) ; } private constructor ( ethApi , instance , defaultBlock ) { super ( ethApi , instance , defaultBlock ) ; } /**
     * A list of current owners that can submit, confirm and execute transactions
     * in this wallet
     */ public async owners ( ) { return this . instance . getOwners . callAsync ( ) ; } param address public async isOwner ( address ? ) { const who = address || ( await requireAccount ( this . ethApi ) . toPromise ( ) ) ; return this . instance . isOwner . callAsync ( who ) ; } /**
     * How many owners need to confirm a submited transaction before it's allowed to be executed
     */ public async required ( ) { return new BigNumber ( await this . instance . required . callAsync ( ) ) . toNumber ( ) ; } param owner public async addOwner ( owner ) { await this . requireOwner ( ) ; const options = await this . instance . addOwner . getRaw ( owner , { gas : 0 } ) ; return this . submitTransaction ( this . address , this . ethApi . toBigNumber ( 0 ) , options . data ! ) ; } public async estimateAddOwner ( owner ) { await this . requireOwner ( ) ; const options = await this . instance . addOwner . getRaw ( owner , { gas : 0 } ) ; return this . estimateTransaction ( this . address , this . ethApi . toBigNumber ( 0 ) , options . data ! ) ; } param owner public async removeOwner ( owner ) { await this . requireOwner ( ) ; const options = await this . instance . removeOwner . getRaw ( owner , { gas : 0 } ) ; return this . submitTransaction ( this . address , this . ethApi . toBigNumber ( 0 ) , options . data ! ) ; } param owner public async replaceOwner ( oldOwner , newOwner ) { await this . requireOwner ( ) ; const options = await this . instance . replaceOwner . getRaw ( oldOwner , newOwner , { gas : 0 } ) ; return this . submitTransaction ( this . address , this . ethApi . toBigNumber ( 0 ) , options . data ! ) ; } param ethers public async transferEther ( ether ) { const wei = ethers . utils . parseEther ( ether . toString ( ) ) ; return createTwoStepSimple ( this . ethApi , await this . ethApi . sendTransaction ( { to : this . address , value : wei . toString ( ) } ) ) ; } param newRequired public async changeRequirement ( newRequired ) { await this . requireOwner ( ) ; const options = await this . instance . changeRequirement . getRaw ( this . ethApi . toBigNumber ( newRequired ) , { gas : 0 } ) ; return this . submitTransaction ( this . address , this . ethApi . toBigNumber ( 0 ) , options . data ! ) ; } param address param weiToSend param payload public async submitTransaction ( address , weiToSend , payload ) { return createTwoStepTransaction ( this . ethApi , await this . instance . submitTransaction . sendTransactionAsync ( address , weiToSend . toString ( ) , payload ) , async ( receipt ) => { const event = receipt . logs . filter ( isDecodedLog ) . find ( log => log . event === MultiSigWallet . Events . Submission ) ; if ( ! event ) { throw new Error ( "No Submisison event found when adding transaction to Multisig" ) ; } const idFromReceipt = ( ( event . returnValues || event . args ) as MultiSigWallet . Args . Submission ) . transactionId ; let id ; try { id = new BigNumber ( idFromReceipt ) . toNumber ( ) ; } catch ( err ) { console . warn ( "Failed to convert receipt log event transaction ID into number to create MultisigTransaction to return in receipt. Error:" , err , "Log event:" , event ) ; id = idFromReceipt as any ; } return this . transaction ( id ) ; } ) ; } public async confirmTransaction ( txId ) { return createTwoStepTransaction ( this . ethApi , await this . instance . confirmTransaction . sendTransactionAsync ( this . ethApi . toBigNumber ( txId ) ) , async ( receipt ) => { const event = receipt . logs . filter ( isDecodedLog ) . find ( log => log . event === MultiSigWallet . Events . Confirmation ) ; if ( ! event ) { throw new Error ( "No Confirmation event found when confirming transaction to Multisig" ) ; } return this . transaction ( new BigNumber ( ( event . returnValues as MultiSigWallet . Args . Confirmation ) . transactionId ) . toNumber ( ) ) ; } ) ; } public async estimateTransaction ( address , weiToSend , payload ) { return this . instance . submitTransaction . estimateGasAsync ( address , weiToSend . toString ( ) , payload , { } ) ; } public async getRawTransaction ( address , weiToSend , payload ) { return this . instance . submitTransaction . getRaw ( address , weiToSend . toString ( ) , payload , { gas : 0 } ) ; } param filters public async transactionCount ( filters = { pending : true } ) { return parseInt ( await this . instance . getTransactionCount . callAsync ( filters . pending || false , filters . executed || false ) , 10 ) ; } param filters public transactions ( filters = { pending : true } ) { return Observable . fromPromise ( this . instance . getTransactionCount . callAsync ( filters . pending || false , filters . executed || false ) ) . concatMap ( async ( numTransactions ) => this . instance . getTransactionIds . callAsync ( this . ethApi . toBigNumber ( 0 ) . toString ( ) , this . ethApi . toBigNumber ( numTransactions ) . toString ( ) , filters . pending || false , filters . executed || false ) ) . concatMap ( ids => Observable . from ( ids ) ) . concatMap ( async ( id ) => { const bn = new BigNumber ( id ) ; return this . transaction ( bn . toNumber ( ) ) ; } ) ; } param id public async transaction ( id ) { return MultisigTransaction . fromId ( this . ethApi , this . instance , id ) ; } private async requireOwner ( who ? ) { const owner = who || ( await requireAccount ( this . ethApi ) . toPromise ( ) ) ; if ( ! ( await this . isOwner ( owner ) ) ) { throw new Error ( CivilErrors . NoPrivileges ) ; } } } export interface TransactionFilters { pending ? : boolean ; executed ? : boolean ; }