import * as ethers from 'ethers' ; import { BytesLike , BigNumberish } from 'ethers' ; import { MainModule } from 'src/gen/typechain' ; export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' ; export const createTestWallet = ( web3 , addressIndex = 0 ) => { const provider = new Web3DebugProvider ( web3 . currentProvider ) ; const wallet = ethers . Wallet . fromMnemonic ( process . env . npm_package_config_mnemonic ! , `m/44'/60'/0'/0/${ addressIndex }` ) . connect ( provider ) ; const signer = provider . getSigner ( addressIndex ) ; return { wallet , provider , signer } ; } ; export function RevertError ( errorMessage ? ) { let prefix = 'VM Exception while processing transaction: revert' ; return errorMessage ? `${ prefix + ' ' + errorMessage }` : prefix ; } export interface JSONRPCRequest { jsonrpc : string ; id : number ; method : any ; params : any ; } export class Web3DebugProvider extends ethers . providers . JsonRpcProvider { public reqCounter = 0 ; public reqLog : JSONRPCRequest [ ] = [ ] ; readonly _web3Provider : ethers . providers . ExternalProvider ; private _sendAsync : ( request : any , callback : ( error : any , response : any ) => void ) => void ; constructor ( web3Provider , network ? ) { super ( web3Provider . host || web3Provider . path || '' , network ) ; if ( web3Provider ) { if ( web3Provider . sendAsync ) { this . _sendAsync = web3Provider . sendAsync . bind ( web3Provider ) ; } else if ( web3Provider . send ) { this . _sendAsync = web3Provider . send . bind ( web3Provider ) ; } } if ( ! web3Provider || ! this . _sendAsync ) { ethers . logger . throwError ( 'invalid web3Provider' , ethers . errors . INVALID_ARGUMENT , { arg : 'web3Provider' , value : web3Provider } ) ; } ethers . utils . defineReadOnly ( this , '_web3Provider' , web3Provider ) ; } send ( method , params ) { this . reqCounter ++ ; return new Promise ( ( resolve , reject ) => { let request = { method : method , params : params , id : this . reqCounter , jsonrpc : '2.0' } as JSONRPCRequest ; this . reqLog . push ( request ) ; this . _sendAsync ( request , function ( error , result ) { if ( error ) { reject ( error ) ; return ; } if ( result . error ) { let error = new Error ( result . error . message ) ; error . code = result . error . code ; error . data = result . error . data ; reject ( error ) ; return ; } resolve ( result . result ) ; } ) ; } ) ; } getPastRequest ( reverseIndex = 0 ) { if ( this . reqLog . length === 0 ) { return { jsonrpc : '2.0' , id : 0 , method : null , params : null } ; } return this . reqLog [ this . reqLog . length - reverseIndex - 1 ] ; } } export async function ethSign ( wallet , message , hashed = false ) { let hash = hashed ? message : ethers . utils . keccak256 ( message ) ; let hashArray = ethers . utils . arrayify ( hash ) ; let ethsigNoType = await wallet . signMessage ( hashArray ) ; return ethsigNoType . endsWith ( '03' ) || ethsigNoType . endsWith ( '02' ) ? ethsigNoType : ethsigNoType + '02' ; } export const MetaTransactionsType = `tuple(
  bool delegateCall,
  bool revertOnError,
  uint256 gasLimit,
  address target,
  uint256 value,
  bytes data
)[]` ; export function encodeMessageData ( owner , message , networkId ) { return encodeMessageSubDigest ( owner , ethers . utils . keccak256 ( message ) , networkId ) ; } export function encodeMessageSubDigest ( owner , digest , networkId ) { return ethers . utils . solidityPack ( [ 'string' , 'uint256' , 'address' , 'bytes32' ] , [ '\x19\x01' , networkId , owner , digest ] ) ; } export function encodeMetaTransactionsData ( owner , txs , networkId , nonce ) { const transactions = ethers . utils . defaultAbiCoder . encode ( [ 'uint256' , MetaTransactionsType ] , [ nonce , txs ] ) ; return encodeMessageData ( owner , transactions , networkId ) ; } export async function walletSign ( owner , message , forceDynamicSize = false ) { return walletMultiSign ( [ { weight : 1 , owner : owner } ] , 1 , message , forceDynamicSize ) ; } export function compareAddr ( a , b ) { const addrA = typeof a === 'string' ? a : a . address ; const addrB = typeof b === 'string' ? b : b . address ; const bigA = ethers . BigNumber . from ( addrA ) ; const bigB = ethers . BigNumber . from ( addrB ) ; if ( bigA . lt ( bigB ) ) { return - 1 ; } else if ( bigA . eq ( bigB ) ) { return 0 ; } else { return 1 ; } } export async function walletMultiSign ( accounts , threshold , message , forceDynamicSize = false , hashed = false ) { const sorted = accounts . sort ( ( a , b ) => compareAddr ( a . owner , b . owner ) ) ; const accountBytes = await Promise . all ( sorted . map ( async ( a ) => { if ( typeof a . owner === 'string' && ! a . signature ) { return ethers . utils . solidityPack ( [ 'uint8' , 'uint8' , 'address' ] , [ 1 , a . weight , a . owner ] ) ; } else { const signature = ethers . utils . arrayify ( a . signature ? a . signature as string : await ethSign ( a . owner as ethers . Wallet , message , hashed ) ) ; if ( forceDynamicSize || signature . length !== 66 ) { const address = typeof a . owner === 'string' ? a . owner : a . owner . address ; return ethers . utils . solidityPack ( [ 'uint8' , 'uint8' , 'address' , 'uint16' , 'bytes' ] , [ 2 , a . weight , address , signature . length , signature ] ) ; } else { return ethers . utils . solidityPack ( [ 'uint8' , 'uint8' , 'bytes' ] , [ 0 , a . weight , signature ] ) ; } } } ) ) ; return ethers . utils . solidityPack ( [ 'uint16' , ... Array ( accounts . length ) . fill ( 'bytes' ) ] , [ threshold , ... accountBytes ] ) ; } export async function signAndEncodeMetaTxn ( wallet , owner , txs , networkId , nonce = undefined , forceDynamicSize = false ) { return multiSignAndEncodeMetaTxn ( wallet , [ { weight : 1 , owner } ] , 1 , txs , networkId , nonce , forceDynamicSize ) ; } export async function multiSignAndEncodeMetaTxn ( wallet , accounts , threshold , txs , networkId , nonce = undefined , forceDynamicSize = false ) { if ( ! nonce ) nonce = await nextNonce ( wallet ) ; const signature = await multiSignMetaTransactions ( wallet , accounts , threshold , txs , networkId , nonce , forceDynamicSize ) ; return wallet . contract . methods . execute ( txs , nonce , signature ) . encodeABI ( ) ; } export async function multiSignMetaTransactions ( wallet , accounts , threshold , txs , networkId , nonce , forceDynamicSize = false ) { const data = encodeMetaTransactionsData ( wallet . address , txs , networkId , nonce ) ; return walletMultiSign ( accounts , threshold , data , forceDynamicSize ) ; } export async function nextNonce ( wallet ) { return ( await wallet . nonce ( ) ) . toNumber ( ) ; } export async function signAndExecuteMetaTx ( wallet , owner , txs , networkId , nonce = undefined , forceDynamicSize = false ) { return multiSignAndExecuteMetaTx ( wallet , [ { weight : 1 , owner : owner } ] , 1 , txs , networkId , nonce , forceDynamicSize ) ; } export async function multiSignAndExecuteMetaTx ( wallet , accounts , threshold , txs , networkId , nonce = undefined , forceDynamicSize = false ) { if ( ! nonce ) nonce = await nextNonce ( wallet ) ; const signature = await multiSignMetaTransactions ( wallet , accounts , threshold , txs , networkId , nonce , forceDynamicSize ) ; return wallet . execute ( txs , nonce , signature ) ; } export function encodeImageHash ( threshold , accounts ) { const sorted = accounts . sort ( ( a , b ) => compareAddr ( a . address , b . address ) ) ; let imageHash = ethers . utils . solidityPack ( [ 'uint256' ] , [ threshold ] ) ; sorted . forEach ( ( a ) => imageHash = ethers . utils . keccak256 ( ethers . utils . defaultAbiCoder . encode ( [ 'bytes32' , 'uint8' , 'address' ] , [ imageHash , a . weight , a . address ] ) ) ) ; return imageHash ; } export function encodeNonce ( space , nonce ) { const shiftedSpace = ethers . BigNumber . from ( space ) . mul ( ethers . constants . Two . pow ( 96 ) ) ; return ethers . BigNumber . from ( nonce ) . add ( shiftedSpace ) ; } export function moduleStorageKey ( key , subkey ? ) { if ( ! subkey ) { return ethers . utils . id ( key ) ; } return ethers . utils . keccak256 ( ethers . utils . defaultAbiCoder . encode ( [ 'bytes32' , 'bytes32' ] , [ moduleStorageKey ( key ) , subkey ] ) ) ; } function xor ( a , b ) { if ( ! Buffer . isBuffer ( a ) ) a = Buffer . from ( ethers . utils . arrayify ( a ) ) ; if ( ! Buffer . isBuffer ( b ) ) b = Buffer . from ( ethers . utils . arrayify ( b ) ) ; return ethers . utils . hexlify ( a . map ( ( v , i ) => v ^ b [ i ] ) ) ; } export function interfaceIdOf ( int ) { const signatures = Object . keys ( int . functions ) . filter ( ( k ) => k . indexOf ( '(' ) !== - 1 ) . map ( ( k ) => int . getSighash ( int . functions [ k ] ) ) ; return signatures . reduce ( ( p , c ) => xor ( p , c ) ) ; } export const WALLET_CODE = '0x603a600e3d39601a805130553df3363d3d373d3d3d363d30545af43d82803e903d91601857fd5bf3' ; export function addressOf ( factory , mainModule , imageHash ) { const codeHash = ethers . utils . keccak256 ( ethers . utils . solidityPack ( [ 'bytes' , 'bytes32' ] , [ WALLET_CODE , ethers . utils . hexZeroPad ( mainModule , 32 ) ] ) ) ; const hash = ethers . utils . keccak256 ( ethers . utils . solidityPack ( [ 'bytes1' , 'address' , 'bytes32' , 'bytes32' ] , [ '0xff' , factory , imageHash , codeHash ] ) ) ; return ethers . utils . getAddress ( ethers . utils . hexDataSlice ( hash , 12 ) ) ; }