From db23f70adacb7fddb6b786ae02f2d47758aad59e Mon Sep 17 00:00:00 2001 From: aniket-engg Date: Mon, 30 Nov 2020 23:32:06 +0530 Subject: [PATCH] txFormat and txHelper --- libs/remix-lib/src/execution/txFormat.ts | 20 +- libs/remix-lib/src/execution/txHelper.ts | 285 +++++++++++------------ 2 files changed, 151 insertions(+), 154 deletions(-) diff --git a/libs/remix-lib/src/execution/txFormat.ts b/libs/remix-lib/src/execution/txFormat.ts index c335e225ca..e013069b9a 100644 --- a/libs/remix-lib/src/execution/txFormat.ts +++ b/libs/remix-lib/src/execution/txFormat.ts @@ -1,9 +1,9 @@ 'use strict' -const ethers = require('ethers') +import { ethers } from 'ethers' const helper = require('./txHelper') -const asyncJS = require('async') -const solcLinker = require('solc/linker') -const ethJSUtil = require('ethereumjs-util') +import { eachOfSeries } from 'async' +import { linkBytecode } from 'solc/linker' +import { isValidAddress, addHexPrefix } from 'ethereumjs-util' module.exports = { @@ -38,7 +38,7 @@ module.exports = { * @param {Function} callback - callback */ encodeParams: function (params, funAbi, callback) { - let data = '' + let data: any = '' let dataHex = '' let funArgs if (params.indexOf('raw:0x') === 0) { @@ -104,7 +104,7 @@ module.exports = { for (let libFile in linkLibraries) { for (let lib in linkLibraries[libFile]) { const address = linkLibraries[libFile][lib] - if (!ethJSUtil.isValidAddress(address)) return callback(address + ' is not a valid address. Please check the provided address is valid.') + if (!isValidAddress(address)) return callback(address + ' is not a valid address. Please check the provided address is valid.') bytecodeToDeploy = this.linkLibraryStandardFromlinkReferences(lib, address.replace('0x', ''), bytecodeToDeploy, linkReferences) } } @@ -168,7 +168,7 @@ module.exports = { */ buildData: function (contractName, contract, contracts, isConstructor, funAbi, params, callback, callbackStep, callbackDeployLibrary) { let funArgs = [] - let data = '' + let data: any = '' let dataHex = '' if (params.indexOf('raw:0x') === 0) { @@ -223,8 +223,8 @@ module.exports = { linkBytecodeStandard: function (contract, contracts, callback, callbackStep, callbackDeployLibrary) { let contractBytecode = contract.evm.bytecode.object - asyncJS.eachOfSeries(contract.evm.bytecode.linkReferences, (libs, file, cbFile) => { - asyncJS.eachOfSeries(contract.evm.bytecode.linkReferences[file], (libRef, libName, cbLibDeployed) => { + eachOfSeries(contract.evm.bytecode.linkReferences, (libs, file, cbFile) => { + eachOfSeries(contract.evm.bytecode.linkReferences[file], (libRef, libName, cbLibDeployed) => { const library = contracts[file][libName] if (library) { this.deployLibrary(file + ':' + libName, libName, library, contracts, (error, address) => { @@ -351,7 +351,7 @@ module.exports = { }, linkLibrary: function (libraryName, address, bytecodeToLink) { - return solcLinker.linkBytecode(bytecodeToLink, { [libraryName]: ethJSUtil.addHexPrefix(address) }) + return linkBytecode(bytecodeToLink, { [libraryName]: addHexPrefix(address) }) }, decodeResponse: function (response, fnabi) { diff --git a/libs/remix-lib/src/execution/txHelper.ts b/libs/remix-lib/src/execution/txHelper.ts index 879377de95..20b573880b 100644 --- a/libs/remix-lib/src/execution/txHelper.ts +++ b/libs/remix-lib/src/execution/txHelper.ts @@ -1,167 +1,164 @@ 'use strict' import { ethers } from 'ethers' -module.exports = { - makeFullTypeDefinition: function (typeDef) { - if (typeDef && typeDef.type.indexOf('tuple') === 0 && typeDef.components) { - const innerTypes = typeDef.components.map((innerType) => { return this.makeFullTypeDefinition(innerType) }) - return `tuple(${innerTypes.join(',')})${this.extractSize(typeDef.type)}` - } - return typeDef.type - }, - - encodeParams: function (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 ? this.makeFullTypeDefinition(funABI.inputs[i]) : type) - if (args.length < types.length) { - args.push('') - } - } - } +export function makeFullTypeDefinition (typeDef) { + if (typeDef && typeDef.type.indexOf('tuple') === 0 && typeDef.components) { + const innerTypes = typeDef.components.map((innerType) => { return this.makeFullTypeDefinition(innerType) }) + return `tuple(${innerTypes.join(',')})${this.extractSize(typeDef.type)}` + } + return typeDef.type +} - // 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) - }, - - encodeFunctionId: function (funABI) { - if (funABI.type === 'fallback' || funABI.type === 'receive') return '0x' - let abi = new ethers.utils.Interface([funABI]) - return abi.getSighash(funABI.name) - }, - - sortAbiFunction: function (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 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 } - }) - }, - - getConstructorInterface: function (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 + types.push(type.indexOf('tuple') === 0 ? this.makeFullTypeDefinition(funABI.inputs[i]) : type) + if (args.length < types.length) { + args.push('') } } + } - 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 - } - } + // 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) +} - return funABI - }, +export function encodeFunctionId (funABI) { + if (funABI.type === 'fallback' || funABI.type === 'receive') return '0x' + let abi = new ethers.utils.Interface([funABI]) + return abi.getSighash(funABI.name) +} - serializeInputs: function (fnAbi) { - let serialized = '(' - if (fnAbi.inputs && fnAbi.inputs.length) { - serialized += fnAbi.inputs.map((input) => { return input.type }).join(',') +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 } - serialized += ')' - return serialized - }, - - extractSize: function (type) { - const size = type.match(/([a-zA-Z0-9])(\[.*\])/) - return size ? size[2] : '' - }, - - getFunction: function (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) { - let fullType = this.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 - } + // 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 } - return null - }, + }) +} - getFallbackInterface: function (abi) { - for (let i = 0; i < abi.length; i++) { - if (abi[i].type === 'fallback') { - return abi[i] - } +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 } - }, + } - getReceiveInterface: function (abi) { - for (let i = 0; i < abi.length; i++) { - if (abi[i].type === 'receive') { - return abi[i] - } + 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 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 - */ - getContract: (contractName, contracts) => { - for (let file in contracts) { - if (contracts[file][contractName]) { - return { object: contracts[file][contractName], file: file } + } + + 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) { + let fullType = this.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 - }, - - /** - * call the given @arg cb (function) for all the contracts. Uses last compilation result - * stop visiting when cb return true - * @param {Function} cb - callback - */ - visitContracts: (contracts, cb) => { - for (let file in contracts) { - for (let name in contracts[file]) { - if (cb({ name: name, object: contracts[file][name], file: file })) return - } + } + 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 (let file in contracts) { + if (contracts[file][contractName]) { + return { object: contracts[file][contractName], file: file } } - }, + } + return null +} - inputParametersDeclarationToString: function (abiinputs) { - const inputs = (abiinputs || []).map((inp) => inp.type + ' ' + inp.name) - return inputs.join(', ') +/** + * 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 (let file in contracts) { + for (let 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(', ') }