'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(', ') }