'use strict'
import { ethers } from 'ethers'
export function makeFullTypeDefinition ( typeDef ) {
if ( typeDef && typeDef . type . indexOf ( 'tuple' ) === 0 && typeDef . components ) {
const innerTypes = typeDef . components . map ( ( innerType ) = > { return makeFullTypeDefinition ( innerType ) } )
return ` tuple( ${ innerTypes . join ( ',' ) } ) ${ extractSize ( typeDef . type ) } `
}
return typeDef . type
}
export function encodeParams ( funABI , args ) {
const types = [ ]
if ( funABI . inputs && funABI . inputs . length ) {
for ( let i = 0 ; i < funABI . inputs . length ; i ++ ) {
const type = funABI . inputs [ i ] . type
// "false" will be converting to `false` and "true" will be working
// fine as abiCoder assume anything in quotes as `true`
if ( type === 'bool' && args [ i ] === 'false' ) {
args [ i ] = false
}
types . push ( type . indexOf ( 'tuple' ) === 0 ? makeFullTypeDefinition ( funABI . inputs [ i ] ) : type )
if ( args . length < types . length ) {
args . push ( '' )
}
}
}
// NOTE: the caller will concatenate the bytecode and this
// it could be done here too for consistency
const abiCoder = new ethers . utils . AbiCoder ( )
return abiCoder . encode ( types , args )
}
export function encodeFunctionId ( funABI ) {
if ( funABI . type === 'fallback' || funABI . type === 'receive' ) return '0x'
const abi = new ethers . utils . Interface ( [ funABI ] )
return abi . getSighash ( funABI . name )
}
export function getFunctionFragment ( funABI ) : ethers . utils . Interface {
if ( funABI . type === 'fallback' || funABI . type === 'receive' ) return null
return new ethers . utils . Interface ( [ funABI ] )
}
export function sortAbiFunction ( contractabi ) {
// Check if function is constant (introduced with Solidity 0.6.0)
const isConstant = ( { stateMutability } ) = > stateMutability === 'view' || stateMutability === 'pure'
// Sorts the list of ABI entries. Constant functions will appear first,
// followed by non-constant functions. Within those t wo groupings, functions
// will be sorted by their names.
return contractabi . sort ( function ( a , b ) {
if ( isConstant ( a ) && ! isConstant ( b ) ) {
return 1
} else if ( isConstant ( b ) && ! isConstant ( a ) ) {
return - 1
}
// If we reach here, either a and b are both constant or both not; sort by name then
// special case for fallback, receive and constructor function
if ( a . type === 'function' && typeof a . name !== 'undefined' ) {
return a . name . localeCompare ( b . name )
} else if ( a . type === 'constructor' || a . type === 'fallback' || a . type === 'receive' ) {
return 1
}
} )
}
export function getConstructorInterface ( abi ) {
const funABI = { name : '' , inputs : [ ] , type : 'constructor' , payable : false , outputs : [ ] }
if ( typeof abi === 'string' ) {
try {
abi = JSON . parse ( abi )
} catch ( e ) {
console . log ( 'exception retrieving ctor abi ' + abi )
return funABI
}
}
for ( let i = 0 ; i < abi . length ; i ++ ) {
if ( abi [ i ] . type === 'constructor' ) {
funABI . inputs = abi [ i ] . inputs || [ ]
funABI . payable = abi [ i ] . payable
funABI [ 'stateMutability' ] = abi [ i ] . stateMutability
break
}
}
return funABI
}
export function serializeInputs ( fnAbi ) {
let serialized = '('
if ( fnAbi . inputs && fnAbi . inputs . length ) {
serialized += fnAbi . inputs . map ( ( input ) = > { return input . type } ) . join ( ',' )
}
serialized += ')'
return serialized
}
export function extractSize ( type ) {
const size = type . match ( /([a-zA-Z0-9])(\[.*\])/ )
return size ? size [ 2 ] : ''
}
export function getFunction ( abi , fnName ) {
for ( let i = 0 ; i < abi . length ; i ++ ) {
const fn = abi [ i ]
if ( fn . type === 'function' && fnName === fn . name + '(' + fn . inputs . map ( ( value ) = > {
if ( value . components ) {
const fullType = makeFullTypeDefinition ( value )
return fullType . replace ( /tuple/g , '' ) // return of makeFullTypeDefinition might contain `tuple`, need to remove it cause `methodIdentifier` (fnName) does not include `tuple` keyword
} else {
return value . type
}
} ) . join ( ',' ) + ')' ) {
return fn
}
}
return null
}
export function getFallbackInterface ( abi ) {
for ( let i = 0 ; i < abi . length ; i ++ ) {
if ( abi [ i ] . type === 'fallback' ) {
return abi [ i ]
}
}
}
export function getReceiveInterface ( abi ) {
for ( let i = 0 ; i < abi . length ; i ++ ) {
if ( abi [ i ] . type === 'receive' ) {
return abi [ i ]
}
}
}
/ * *
* return the contract obj of the given @arg name . Uses last compilation result .
* return null if not found
* @param { String } name - contract name
* @returns contract obj and associated file : { contract , file } or null
* /
export function getContract ( contractName , contracts ) {
for ( const file in contracts ) {
if ( contracts [ file ] [ contractName ] ) {
return { object : contracts [ file ] [ contractName ] , file : file }
}
}
return null
}
/ * *
* call the given @arg cb ( function ) for all the contracts . Uses last compilation result
* stop visiting when cb return true
* @param { Function } cb - callback
* /
export function visitContracts ( contracts , cb ) {
for ( const file in contracts ) {
for ( const name in contracts [ file ] ) {
if ( cb ( { name : name , object : contracts [ file ] [ name ] , file : file } ) ) return
}
}
}
export function inputParametersDeclarationToString ( abiinputs ) {
const inputs = ( abiinputs || [ ] ) . map ( ( inp ) = > inp . type + ' ' + inp . name )
return inputs . join ( ', ' )
}