import { isDefined , isDeployedBytecodeEqual } from "@joincivil/utils" ; import { TwoStepEthTransaction } from "../../types" ; import { BigNumber , EthAddress , TxHash } from "@joincivil/typescript-types" ; import { EthApi } from "@joincivil/ethapi" ; import { artifacts } from "../generated/artifacts" ; import { MultiSigWallet } from "../generated/wrappers/multi_sig_wallet" ; import { Contract } from "../interfaces/contract" ; import { createTwoStepSimple , isDecodedLog , isOwnableContract } from "../utils/contracts" ; import { Multisig } from "./multisig" ; /**
 * Proxies functionionality to a contract instance via multisig wallet. Also supports instantiation *without* multisig proxy, in which case calls are passed directly to the contract. If the wrapped contract has an `owner` property, it will be checked to see if the owner is a multisig wallet.
 */ export class BaseMultisigProxy { protected ethApi : EthApi ; protected multisig ? : Multisig ; protected instance : Contract ; protected constructor ( ethApi , instance ) { this . ethApi = ethApi ; this . instance = instance ; } public get multisigEnabled ( ) : boolean { return isDefined ( this . multisig ) ; } public requireMultisig ( ) { if ( ! this . multisig ) { throw new Error ( "Unexpected state happened, multisig not found but required" ) ; } return this . multisig ; } public async owners ( ) { if ( isDefined ( this . multisig ) ) { return this . multisig . owners ( ) ; } else if ( isOwnableContract ( this . instance ) && this . instance . owner ) { return [ await this . instance . owner . callAsync ( ) ] ; } else { return [ ] ; } } public async isOwner ( address ) { if ( isDefined ( this . multisig ) ) { return this . multisig . isOwner ( address ) ; } else if ( isOwnableContract ( this . instance ) && this . instance . owner ) { return address === ( await this . instance . owner . callAsync ( ) ) ; } else { return false ; } } public async getMultisigAddress ( ) { if ( isDefined ( this . multisig ) ) { return this . multisig . address ; } return undefined ; } public async isAddressMultisigWallet ( address ) { const code = await this . ethApi . getCode ( address ) ; return isDeployedBytecodeEqual ( artifacts . MultiSigWallet . deployedBytecode , code ) ; } param multisigAddress protected async resolveMultisig ( multisigAddress ? ) { if ( multisigAddress ) { if ( ! ( await this . isAddressMultisigWallet ( multisigAddress ) ) ) { throw new Error ( "Expected multisig at address " + multisigAddress + " but none found" ) ; } this . multisig = Multisig . atUntrusted ( this . ethApi , multisigAddress ) ; } else if ( isOwnableContract ( this . instance ) && this . instance . owner ) { const ownerAddress = await this . instance . owner . callAsync ( ) ; if ( await this . isAddressMultisigWallet ( ownerAddress ) ) { this . multisig = Multisig . atUntrusted ( this . ethApi , ownerAddress ) ; } } } protected createProxyTransaction ( txHash ) { const twoStep = createTwoStepSimple ( this . ethApi , txHash ) ; return { ... twoStep , isProxied : true , proxiedId : async ( ) => { const receipt = await twoStep . awaitReceipt ( ) ; const submissionLogs = receipt . logs . filter ( log => isDecodedLog ( log ) && log . event === MultiSigWallet . Events . Submission ) . map ( log => log as MultiSigWallet . Logs . Submission ) ; if ( submissionLogs . length !== 1 ) { throw new Error ( "Too many Submission events than expected in multisig" ) ; } const idFromReceipt = ( submissionLogs [ 0 ] . returnValues || submissionLogs [ 0 ] . args ) . 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:" , submissionLogs [ 0 ] ) ; id = idFromReceipt as any ; } return id ; } , } ; } } export interface MultisigProxyTransaction extends TwoStepEthTransaction { readonly isProxied ? : true ; proxiedId ? ( ) : Promise < number > ; }