import { ethers } from "ethers" ; const { utils : ethersUtils } = ethers ; const { bigNumberify } = ethersUtils ; const { Interface } = ethersUtils ; const GAS_PRICE = bigNumberify ( `${ 1e11 }` ) ; export default class GnosisSafeUtils { private contract : { _getProvider : ( ) => void ; getAddress : ( ) => void ; getABI : { ( ) : void ; ( ) : void ; } ; } ; constructor ( contract ) { this . contract = contract ; } multiSign = ( wallets , hash ) => { let signatures = "0x" ; wallets . forEach ( ( wallet ) => { const signingKey = new ethers . utils . SigningKey ( wallet . privateKey ) ; const sig = signingKey . signDigest ( hash ) ; signatures += ethers . utils . joinSignature ( sig ) . slice ( 2 ) ; } ) ; return signatures ; } ; estimateFromSafeTxGas = async ( to , value , data , operation ) => { const provider = this . contract . _getProvider ( ) ; const safeContractAddress = this . contract . getAddress ( ) ; const callToEstimate = this . encodeFunctionCall ( this . contract . getABI ( ) , "requiredTxGas" , [ to , value , data , operation ] ) ; const estimateResponse = await provider . call ( { to : safeContractAddress , from : safeContractAddress , data : callToEstimate , } ) ; const estimatedGasHex = "0x" + estimateResponse . substring ( 138 ) ; let estimatedGas = bigNumberify ( estimatedGasHex ) ; estimatedGas = estimatedGas . add ( 10000 ) ; return estimatedGas ; } ; estimateDataGas = ( safe , to , value , data , operation , txGasEstimate , gasToken , refundReceiver , signatureCount ) => { const sigRcost = 2176 ; const sigScost = 2176 ; const sigVcost = 68 ; const eachSigCost = sigRcost + sigScost + sigVcost ; const signatureCost = signatureCount * eachSigCost ; const dataGas = 0 ; const gasPrice = GAS_PRICE ; const signatures = "0x" ; const callToEstimate = this . encodeFunctionCall ( this . contract . getABI ( ) , "execTransaction" , [ to , value , data , operation , txGasEstimate , dataGas , gasPrice , gasToken , refundReceiver , signatures , ] ) ; let estimatedGas = this . estimateDataGasCosts ( callToEstimate ) + signatureCost ; if ( estimatedGas > 65536 ) { estimatedGas += 64 ; } else { estimatedGas += 128 ; } estimatedGas += 32000 ; return estimatedGas ; } ; private estimateDataGasCosts = ( dataString ) => { const reducer = ( accumulator , currentValue ) => ( accumulator += this . dataGasValue ( currentValue ) ) ; return dataString . match ( /.{2}/g ) . reduce ( reducer , 0 ) ; } ; private dataGasValue = ( hexValue ) => { switch ( hexValue ) { case "0x" : return 0 ; case "00" : return 4 ; default : return 68 ; } } ; encodeFunctionCall = ( abi , functionName , args ) => { const contractInterface = new Interface ( abi ) ; return contractInterface . functions [ functionName ] . encode ( args ) ; } ; }