import { ethers , providers , Signer } from 'ethers' ; import { ContractDeployer } from './gnosis/contractDeployer' ; import { MultisigConfig } from './multisigConfig' ; import { SafeTransactionDataPartial } from '@gnosis.pm/safe-core-sdk-types' ; import Safe , { EthersAdapter } from '@gnosis.pm/safe-core-sdk' ; import SafeServiceClient from '@gnosis.pm/safe-service-client' ; import { Executed , MultisigState , Proposed } from './multisigState' ; import { log , logTx } from '../logging' ; /**
 * Builds multisig parts and provides construction of multisig executable.
 * This split responsibility of multisig definition building and its execution at later state.
 *
 * Encapsulates the multisig implementation details (multisig vendor/service) from all the other logic.
 */ export class MultisigBuilder { private _contractDeployer : ContractDeployer ; public name : string ; public txBatch : providers . TransactionRequest [ ] ; param name param networkChainId constructor ( name , networkChainId ) { this . name = name ; this . txBatch = [ ] ; this . _contractDeployer = new ContractDeployer ( networkChainId ) ; } param tx returns public async addContractDeployment ( tx ) { const { transaction : wrappedTx , address } = await this . _contractDeployer . createDeploymentTx ( tx ) ; this . txBatch . push ( wrappedTx ) ; return address ; } param tx public async addContractInteraction ( tx ) { this . txBatch . push ( tx ) ; } param signer param config public buildExecutable ( signer , config ) { return new MultisigExecutable ( this . name , signer , config ) ; } } /**
 * Multisig state-changing operations.
 *
 * Encapsulates the multisig implementation details (multisig vendor/service) from all the other logic.
 */ export class MultisigExecutable { private _signer : Signer ; private _safe ? : Safe ; private _config : MultisigConfig ; private _safeServiceClient : SafeServiceClient ; public name : string ; constructor ( name , signer , config ) { this . name = name ; this . _config = config ; this . _signer = signer ; this . _safeServiceClient = new SafeServiceClient ( config . gnosisServiceUri ) ; } param tx returns public async propose ( tx ) { const safe = await this . ensureSafe ( ) ; const txs = Array . isArray ( tx ) ? tx : [ tx ] ; const safeMultisigParts = txs . map ( ( tx ) => { const part = { to : tx . to , data : tx . data , value : tx . value ?. toString ( ) ?? '0' , } as SafeTransactionDataPartial ; logTx ( `[MULTISIG-PART] ${ this . name }` , { from : '' , to : tx . to , data : tx . data , } ) ; return part ; } ) ; const safeMultisigTx = await safe . createTransaction ( safeMultisigParts ) ; const safeMultisigTxHash = await safe . getTransactionHash ( safeMultisigTx ) ; const senderAddress = await this . _signer . getAddress ( ) ; await this . _safeServiceClient . proposeTransaction ( { safeAddress : safe . getAddress ( ) , safeTxHash : safeMultisigTxHash , safeTransaction : safeMultisigTx , senderAddress , } ) ; logTx ( `[MULTISIG] ${ this . name }` , { hash : safeMultisigTxHash , from : senderAddress , to : safe . getAddress ( ) } ) ; return safeMultisigTxHash ; } param id public async approve ( id ) { const safe = await this . ensureSafe ( ) ; const confirmationSignature = await safe . signTransactionHash ( id ) ; await this . _safeServiceClient . confirmTransaction ( id , confirmationSignature . data ) ; log ( `[MULTISIG] Approved ${ id } by ${ await this . _signer . getAddress ( ) }` ) ; } param id public async checkState ( id ) { const response = await this . _safeServiceClient . getTransaction ( id ) ; const state = response . isExecuted ? ( { kind : 'EXECUTED' , txHash : response . transactionHash } as Executed ) : ( { kind : 'PROPOSED' } as Proposed ) ; log ( `🔍 Checking multisig (ID=${ id }). State: ${ state . kind }.` ) ; return state ; } private async ensureSafe ( ) { if ( this . _safe ) return < Safe > this . _safe ; this . _safe = await Safe . create ( { ethAdapter : new EthersAdapter ( { ethers , signer : this . _signer } ) , safeAddress : this . _config . gnosisSafeAddress , } ) ; return this . _safe ; } }