Commit a4a16863 authored by aniket-engg's avatar aniket-engg Committed by Aniket

txFormat methods export improved

parent db23f70a
...@@ -5,450 +5,448 @@ import { eachOfSeries } from 'async' ...@@ -5,450 +5,448 @@ import { eachOfSeries } from 'async'
import { linkBytecode } from 'solc/linker' import { linkBytecode } from 'solc/linker'
import { isValidAddress, addHexPrefix } from 'ethereumjs-util' import { isValidAddress, addHexPrefix } from 'ethereumjs-util'
module.exports = { /**
* build the transaction data
*
* @param {Object} function abi
* @param {Object} values to encode
* @param {String} contractbyteCode
*/
export function encodeData (funABI, values, contractbyteCode) {
let encoded
let encodedHex
try {
encoded = helper.encodeParams(funABI, values)
encodedHex = encoded.toString('hex')
} catch (e) {
return { error: 'cannot encode arguments' }
}
if (contractbyteCode) {
return { data: '0x' + contractbyteCode + encodedHex.replace('0x', '') }
} else {
return { data: helper.encodeFunctionId(funABI) + encodedHex.replace('0x', '') }
}
}
/** /**
* build the transaction data * encode function / constructor parameters
* *
* @param {Object} function abi * @param {Object} params - input paramater of the function to call
* @param {Object} values to encode * @param {Object} funAbi - abi definition of the function to call. null if building data for the ctor.
* @param {String} contractbyteCode * @param {Function} callback - callback
*/ */
encodeData: function (funABI, values, contractbyteCode) { export function encodeParams (params, funAbi, callback) {
let encoded let data: any = ''
let encodedHex let dataHex = ''
let funArgs
if (params.indexOf('raw:0x') === 0) {
// in that case we consider that the input is already encoded and *does not* contain the method signature
dataHex = params.replace('raw:0x', '')
data = Buffer.from(dataHex, 'hex')
} else {
try { try {
encoded = helper.encodeParams(funABI, values) params = params.replace(/(^|,\s+|,)(\d+)(\s+,|,|$)/g, '$1"$2"$3') // replace non quoted number by quoted number
encodedHex = encoded.toString('hex') params = params.replace(/(^|,\s+|,)(0[xX][0-9a-fA-F]+)(\s+,|,|$)/g, '$1"$2"$3') // replace non quoted hex string by quoted hex string
funArgs = JSON.parse('[' + params + ']')
} catch (e) { } catch (e) {
return { error: 'cannot encode arguments' } return callback('Error encoding arguments: ' + e)
} }
if (contractbyteCode) { if (funArgs.length > 0) {
return { data: '0x' + contractbyteCode + encodedHex.replace('0x', '') }
} else {
return { data: helper.encodeFunctionId(funABI) + encodedHex.replace('0x', '') }
}
},
/**
* encode function / constructor parameters
*
* @param {Object} params - input paramater of the function to call
* @param {Object} funAbi - abi definition of the function to call. null if building data for the ctor.
* @param {Function} callback - callback
*/
encodeParams: function (params, funAbi, callback) {
let data: any = ''
let dataHex = ''
let funArgs
if (params.indexOf('raw:0x') === 0) {
// in that case we consider that the input is already encoded and *does not* contain the method signature
dataHex = params.replace('raw:0x', '')
data = Buffer.from(dataHex, 'hex')
} else {
try { try {
params = params.replace(/(^|,\s+|,)(\d+)(\s+,|,|$)/g, '$1"$2"$3') // replace non quoted number by quoted number data = helper.encodeParams(funAbi, funArgs)
params = params.replace(/(^|,\s+|,)(0[xX][0-9a-fA-F]+)(\s+,|,|$)/g, '$1"$2"$3') // replace non quoted hex string by quoted hex string dataHex = data.toString('hex')
funArgs = JSON.parse('[' + params + ']')
} catch (e) { } catch (e) {
return callback('Error encoding arguments: ' + e) return callback('Error encoding arguments: ' + e)
} }
if (funArgs.length > 0) {
try {
data = helper.encodeParams(funAbi, funArgs)
dataHex = data.toString('hex')
} catch (e) {
return callback('Error encoding arguments: ' + e)
}
}
if (data.slice(0, 9) === 'undefined') {
dataHex = data.slice(9)
}
if (data.slice(0, 2) === '0x') {
dataHex = data.slice(2)
}
} }
callback(null, { data: data, dataHex: dataHex, funArgs: funArgs }) if (data.slice(0, 9) === 'undefined') {
}, dataHex = data.slice(9)
}
if (data.slice(0, 2) === '0x') {
dataHex = data.slice(2)
}
}
callback(null, { data: data, dataHex: dataHex, funArgs: funArgs })
}
/** /**
* encode function call (function id + encoded parameters) * encode function call (function id + encoded parameters)
* *
* @param {Object} params - input paramater of the function to call * @param {Object} params - input paramater of the function to call
* @param {Object} funAbi - abi definition of the function to call. null if building data for the ctor. * @param {Object} funAbi - abi definition of the function to call. null if building data for the ctor.
* @param {Function} callback - callback * @param {Function} callback - callback
*/ */
encodeFunctionCall: function (params, funAbi, callback) { export function encodeFunctionCall (params, funAbi, callback) {
this.encodeParams(params, funAbi, (error, encodedParam) => { this.encodeParams(params, funAbi, (error, encodedParam) => {
if (error) return callback(error) if (error) return callback(error)
callback(null, { dataHex: helper.encodeFunctionId(funAbi) + encodedParam.dataHex, funAbi, funArgs: encodedParam.funArgs }) callback(null, { dataHex: helper.encodeFunctionId(funAbi) + encodedParam.dataHex, funAbi, funArgs: encodedParam.funArgs })
}) })
}, }
/** /**
* encode constructor creation and link with provided libraries if needed * encode constructor creation and link with provided libraries if needed
* *
* @param {Object} contract - input paramater of the function to call * @param {Object} contract - input paramater of the function to call
* @param {Object} params - input paramater of the function to call * @param {Object} params - input paramater of the function to call
* @param {Object} funAbi - abi definition of the function to call. null if building data for the ctor. * @param {Object} funAbi - abi definition of the function to call. null if building data for the ctor.
* @param {Object} linkLibraries - contains {linkReferences} object which list all the addresses to be linked * @param {Object} linkLibraries - contains {linkReferences} object which list all the addresses to be linked
* @param {Object} linkReferences - given by the compiler, contains the proper linkReferences * @param {Object} linkReferences - given by the compiler, contains the proper linkReferences
* @param {Function} callback - callback * @param {Function} callback - callback
*/ */
encodeConstructorCallAndLinkLibraries: function (contract, params, funAbi, linkLibraries, linkReferences, callback) { export function encodeConstructorCallAndLinkLibraries (contract, params, funAbi, linkLibraries, linkReferences, callback) {
this.encodeParams(params, funAbi, (error, encodedParam) => { this.encodeParams(params, funAbi, (error, encodedParam) => {
if (error) return callback(error) if (error) return callback(error)
let bytecodeToDeploy = contract.evm.bytecode.object let bytecodeToDeploy = contract.evm.bytecode.object
if (bytecodeToDeploy.indexOf('_') >= 0) { if (bytecodeToDeploy.indexOf('_') >= 0) {
if (linkLibraries && linkReferences) { if (linkLibraries && linkReferences) {
for (let libFile in linkLibraries) { for (let libFile in linkLibraries) {
for (let lib in linkLibraries[libFile]) { for (let lib in linkLibraries[libFile]) {
const address = linkLibraries[libFile][lib] const address = linkLibraries[libFile][lib]
if (!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) bytecodeToDeploy = this.linkLibraryStandardFromlinkReferences(lib, address.replace('0x', ''), bytecodeToDeploy, linkReferences)
}
} }
} }
} }
if (bytecodeToDeploy.indexOf('_') >= 0) { }
return callback('Failed to link some libraries') if (bytecodeToDeploy.indexOf('_') >= 0) {
} return callback('Failed to link some libraries')
return callback(null, { dataHex: bytecodeToDeploy + encodedParam.dataHex, funAbi, funArgs: encodedParam.funArgs, contractBytecode: contract.evm.bytecode.object }) }
}) return callback(null, { dataHex: bytecodeToDeploy + encodedParam.dataHex, funAbi, funArgs: encodedParam.funArgs, contractBytecode: contract.evm.bytecode.object })
}, })
}
/**
* encode constructor creation and deploy librairies if needed
*
* @param {String} contractName - current contract name
* @param {Object} contract - input paramater of the function to call
* @param {Object} contracts - map of all compiled contracts.
* @param {Object} params - input paramater of the function to call
* @param {Object} funAbi - abi definition of the function to call. null if building data for the ctor.
* @param {Function} callback - callback
* @param {Function} callbackStep - callbackStep
* @param {Function} callbackDeployLibrary - callbackDeployLibrary
* @param {Function} callback - callback
*/
encodeConstructorCallAndDeployLibraries: function (contractName, contract, contracts, params, funAbi, callback, callbackStep, callbackDeployLibrary) {
this.encodeParams(params, funAbi, (error, encodedParam) => {
if (error) return callback(error)
let dataHex = ''
const contractBytecode = contract.evm.bytecode.object
let bytecodeToDeploy = contract.evm.bytecode.object
if (bytecodeToDeploy.indexOf('_') >= 0) {
this.linkBytecode(contract, contracts, (err, bytecode) => {
if (err) {
callback('Error deploying required libraries: ' + err)
} else {
bytecodeToDeploy = bytecode + dataHex
return callback(null, {dataHex: bytecodeToDeploy, funAbi, funArgs: encodedParam.funArgs, contractBytecode, contractName: contractName})
}
}, callbackStep, callbackDeployLibrary)
return
} else {
dataHex = bytecodeToDeploy + encodedParam.dataHex
}
callback(null, {dataHex: bytecodeToDeploy, funAbi, funArgs: encodedParam.funArgs, contractBytecode, contractName: contractName})
})
},
/** /**
* (DEPRECATED) build the transaction data * encode constructor creation and deploy librairies if needed
* *
* @param {String} contractName * @param {String} contractName - current contract name
* @param {Object} contract - abi definition of the current contract. * @param {Object} contract - input paramater of the function to call
* @param {Object} contracts - map of all compiled contracts. * @param {Object} contracts - map of all compiled contracts.
* @param {Bool} isConstructor - isConstructor. * @param {Object} params - input paramater of the function to call
* @param {Object} funAbi - abi definition of the function to call. null if building data for the ctor. * @param {Object} funAbi - abi definition of the function to call. null if building data for the ctor.
* @param {Object} params - input paramater of the function to call * @param {Function} callback - callback
* @param {Function} callback - callback * @param {Function} callbackStep - callbackStep
* @param {Function} callbackStep - callbackStep * @param {Function} callbackDeployLibrary - callbackDeployLibrary
* @param {Function} callbackDeployLibrary - callbackDeployLibrary * @param {Function} callback - callback
*/ */
buildData: function (contractName, contract, contracts, isConstructor, funAbi, params, callback, callbackStep, callbackDeployLibrary) { export function encodeConstructorCallAndDeployLibraries (contractName, contract, contracts, params, funAbi, callback, callbackStep, callbackDeployLibrary) {
let funArgs = [] this.encodeParams(params, funAbi, (error, encodedParam) => {
let data: any = '' if (error) return callback(error)
let dataHex = '' let dataHex = ''
const contractBytecode = contract.evm.bytecode.object
if (params.indexOf('raw:0x') === 0) { let bytecodeToDeploy = contract.evm.bytecode.object
// in that case we consider that the input is already encoded and *does not* contain the method signature if (bytecodeToDeploy.indexOf('_') >= 0) {
dataHex = params.replace('raw:0x', '') this.linkBytecode(contract, contracts, (err, bytecode) => {
data = Buffer.from(dataHex, 'hex') if (err) {
} else { callback('Error deploying required libraries: ' + err)
try { } else {
if (params.length > 0) { bytecodeToDeploy = bytecode + dataHex
funArgs = this.parseFunctionParams(params) return callback(null, {dataHex: bytecodeToDeploy, funAbi, funArgs: encodedParam.funArgs, contractBytecode, contractName: contractName})
} }
} catch (e) { }, callbackStep, callbackDeployLibrary)
return callback('Error encoding arguments: ' + e) return
}
try {
data = helper.encodeParams(funAbi, funArgs)
dataHex = data.toString('hex')
} catch (e) {
return callback('Error encoding arguments: ' + e)
}
if (data.slice(0, 9) === 'undefined') {
dataHex = data.slice(9)
}
if (data.slice(0, 2) === '0x') {
dataHex = data.slice(2)
}
}
let contractBytecode
if (isConstructor) {
contractBytecode = contract.evm.bytecode.object
let bytecodeToDeploy = contract.evm.bytecode.object
if (bytecodeToDeploy.indexOf('_') >= 0) {
this.linkBytecode(contract, contracts, (err, bytecode) => {
if (err) {
callback('Error deploying required libraries: ' + err)
} else {
bytecodeToDeploy = bytecode + dataHex
return callback(null, {dataHex: bytecodeToDeploy, funAbi, funArgs, contractBytecode, contractName: contractName})
}
}, callbackStep, callbackDeployLibrary)
return
} else {
dataHex = bytecodeToDeploy + dataHex
}
} else { } else {
dataHex = helper.encodeFunctionId(funAbi) + dataHex dataHex = bytecodeToDeploy + encodedParam.dataHex
} }
callback(null, { dataHex, funAbi, funArgs, contractBytecode, contractName: contractName }) callback(null, {dataHex: bytecodeToDeploy, funAbi, funArgs: encodedParam.funArgs, contractBytecode, contractName: contractName})
}, })
}
atAddress: function () {}, /**
* (DEPRECATED) build the transaction data
*
* @param {String} contractName
* @param {Object} contract - abi definition of the current contract.
* @param {Object} contracts - map of all compiled contracts.
* @param {Bool} isConstructor - isConstructor.
* @param {Object} funAbi - abi definition of the function to call. null if building data for the ctor.
* @param {Object} params - input paramater of the function to call
* @param {Function} callback - callback
* @param {Function} callbackStep - callbackStep
* @param {Function} callbackDeployLibrary - callbackDeployLibrary
*/
export function buildData (contractName, contract, contracts, isConstructor, funAbi, params, callback, callbackStep, callbackDeployLibrary) {
let funArgs = []
let data: any = ''
let dataHex = ''
linkBytecodeStandard: function (contract, contracts, callback, callbackStep, callbackDeployLibrary) { if (params.indexOf('raw:0x') === 0) {
let contractBytecode = contract.evm.bytecode.object // in that case we consider that the input is already encoded and *does not* contain the method signature
eachOfSeries(contract.evm.bytecode.linkReferences, (libs, file, cbFile) => { dataHex = params.replace('raw:0x', '')
eachOfSeries(contract.evm.bytecode.linkReferences[file], (libRef, libName, cbLibDeployed) => { data = Buffer.from(dataHex, 'hex')
const library = contracts[file][libName] } else {
if (library) { try {
this.deployLibrary(file + ':' + libName, libName, library, contracts, (error, address) => { if (params.length > 0) {
if (error) { funArgs = this.parseFunctionParams(params)
return cbLibDeployed(error)
}
let hexAddress = address.toString('hex')
if (hexAddress.slice(0, 2) === '0x') {
hexAddress = hexAddress.slice(2)
}
contractBytecode = this.linkLibraryStandard(libName, hexAddress, contractBytecode, contract)
cbLibDeployed()
}, callbackStep, callbackDeployLibrary)
} else {
cbLibDeployed('Cannot find compilation data of library ' + libName)
}
}, (error) => {
cbFile(error)
})
}, (error) => {
if (error) {
callbackStep(error)
} }
callback(error, contractBytecode) } catch (e) {
}) return callback('Error encoding arguments: ' + e)
},
linkBytecodeLegacy: function (contract, contracts, callback, callbackStep, callbackDeployLibrary) {
const libraryRefMatch = contract.evm.bytecode.object.match(/__([^_]{1,36})__/)
if (!libraryRefMatch) {
return callback('Invalid bytecode format.')
}
const libraryName = libraryRefMatch[1]
// file_name:library_name
const libRef = libraryName.match(/(.*):(.*)/)
if (!libRef) {
return callback('Cannot extract library reference ' + libraryName)
}
if (!contracts[libRef[1]] || !contracts[libRef[1]][libRef[2]]) {
return callback('Cannot find library reference ' + libraryName)
}
const libraryShortName = libRef[2]
const library = contracts[libRef[1]][libraryShortName]
if (!library) {
return callback('Library ' + libraryName + ' not found.')
} }
this.deployLibrary(libraryName, libraryShortName, library, contracts, (err, address) => { try {
if (err) { data = helper.encodeParams(funAbi, funArgs)
return callback(err) dataHex = data.toString('hex')
} } catch (e) {
let hexAddress = address.toString('hex') return callback('Error encoding arguments: ' + e)
if (hexAddress.slice(0, 2) === '0x') {
hexAddress = hexAddress.slice(2)
}
contract.evm.bytecode.object = this.linkLibrary(libraryName, hexAddress, contract.evm.bytecode.object)
this.linkBytecode(contract, contracts, callback, callbackStep, callbackDeployLibrary)
}, callbackStep, callbackDeployLibrary)
},
linkBytecode: function (contract, contracts, callback, callbackStep, callbackDeployLibrary) {
if (contract.evm.bytecode.object.indexOf('_') < 0) {
return callback(null, contract.evm.bytecode.object)
} }
if (contract.evm.bytecode.linkReferences && Object.keys(contract.evm.bytecode.linkReferences).length) { if (data.slice(0, 9) === 'undefined') {
this.linkBytecodeStandard(contract, contracts, callback, callbackStep, callbackDeployLibrary) dataHex = data.slice(9)
} else {
this.linkBytecodeLegacy(contract, contracts, callback, callbackStep, callbackDeployLibrary)
} }
}, if (data.slice(0, 2) === '0x') {
dataHex = data.slice(2)
deployLibrary: function (libraryName, libraryShortName, library, contracts, callback, callbackStep, callbackDeployLibrary) {
const address = library.address
if (address) {
return callback(null, address)
} }
const bytecode = library.evm.bytecode.object }
if (bytecode.indexOf('_') >= 0) { let contractBytecode
this.linkBytecode(library, contracts, (err, bytecode) => { if (isConstructor) {
if (err) callback(err) contractBytecode = contract.evm.bytecode.object
else { let bytecodeToDeploy = contract.evm.bytecode.object
library.evm.bytecode.object = bytecode if (bytecodeToDeploy.indexOf('_') >= 0) {
this.deployLibrary(libraryName, libraryShortName, library, contracts, callback, callbackStep, callbackDeployLibrary) this.linkBytecode(contract, contracts, (err, bytecode) => {
if (err) {
callback('Error deploying required libraries: ' + err)
} else {
bytecodeToDeploy = bytecode + dataHex
return callback(null, {dataHex: bytecodeToDeploy, funAbi, funArgs, contractBytecode, contractName: contractName})
} }
}, callbackStep, callbackDeployLibrary) }, callbackStep, callbackDeployLibrary)
return
} else { } else {
callbackStep(`creation of library ${libraryName} pending...`) dataHex = bytecodeToDeploy + dataHex
const data = {dataHex: bytecode, funAbi: {type: 'constructor'}, funArgs: [], contractBytecode: bytecode, contractName: libraryShortName}
callbackDeployLibrary({ data: data, useCall: false }, (err, txResult) => {
if (err) {
return callback(err)
}
const address = txResult.result.createdAddress || txResult.result.contractAddress
library.address = address
callback(err, address)
})
} }
}, } else {
dataHex = helper.encodeFunctionId(funAbi) + dataHex
}
callback(null, { dataHex, funAbi, funArgs, contractBytecode, contractName: contractName })
}
linkLibraryStandardFromlinkReferences: function (libraryName, address, bytecode, linkReferences) { export function atAddress () {}
for (let file in linkReferences) {
for (let libName in linkReferences[file]) { export function linkBytecodeStandard (contract, contracts, callback, callbackStep, callbackDeployLibrary) {
if (libraryName === libName) { let contractBytecode = contract.evm.bytecode.object
bytecode = this.setLibraryAddress(address, bytecode, linkReferences[file][libName]) 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) => {
if (error) {
return cbLibDeployed(error)
}
let hexAddress = address.toString('hex')
if (hexAddress.slice(0, 2) === '0x') {
hexAddress = hexAddress.slice(2)
}
contractBytecode = this.linkLibraryStandard(libName, hexAddress, contractBytecode, contract)
cbLibDeployed()
}, callbackStep, callbackDeployLibrary)
} else {
cbLibDeployed('Cannot find compilation data of library ' + libName)
} }
}, (error) => {
cbFile(error)
})
}, (error) => {
if (error) {
callbackStep(error)
} }
return bytecode callback(error, contractBytecode)
}, })
}
linkLibraryStandard: function (libraryName, address, bytecode, contract) { export function linkBytecodeLegacy (contract, contracts, callback, callbackStep, callbackDeployLibrary) {
return this.linkLibraryStandardFromlinkReferences(libraryName, address, bytecode, contract.evm.bytecode.linkReferences) const libraryRefMatch = contract.evm.bytecode.object.match(/__([^_]{1,36})__/)
}, if (!libraryRefMatch) {
return callback('Invalid bytecode format.')
}
const libraryName = libraryRefMatch[1]
// file_name:library_name
const libRef = libraryName.match(/(.*):(.*)/)
if (!libRef) {
return callback('Cannot extract library reference ' + libraryName)
}
if (!contracts[libRef[1]] || !contracts[libRef[1]][libRef[2]]) {
return callback('Cannot find library reference ' + libraryName)
}
const libraryShortName = libRef[2]
const library = contracts[libRef[1]][libraryShortName]
if (!library) {
return callback('Library ' + libraryName + ' not found.')
}
this.deployLibrary(libraryName, libraryShortName, library, contracts, (err, address) => {
if (err) {
return callback(err)
}
let hexAddress = address.toString('hex')
if (hexAddress.slice(0, 2) === '0x') {
hexAddress = hexAddress.slice(2)
}
contract.evm.bytecode.object = this.linkLibrary(libraryName, hexAddress, contract.evm.bytecode.object)
this.linkBytecode(contract, contracts, callback, callbackStep, callbackDeployLibrary)
}, callbackStep, callbackDeployLibrary)
}
setLibraryAddress: function (address, bytecodeToLink, positions) { export function linkBytecode (contract, contracts, callback?, callbackStep?, callbackDeployLibrary?) {
if (positions) { if (contract.evm.bytecode.object.indexOf('_') < 0) {
for (let pos of positions) { return callback(null, contract.evm.bytecode.object)
const regpos = bytecodeToLink.match(new RegExp(`(.{${2 * pos.start}})(.{${2 * pos.length}})(.*)`)) }
if (regpos) { if (contract.evm.bytecode.linkReferences && Object.keys(contract.evm.bytecode.linkReferences).length) {
bytecodeToLink = regpos[1] + address + regpos[3] this.linkBytecodeStandard(contract, contracts, callback, callbackStep, callbackDeployLibrary)
} } else {
this.linkBytecodeLegacy(contract, contracts, callback, callbackStep, callbackDeployLibrary)
}
}
export function deployLibrary (libraryName, libraryShortName, library, contracts, callback, callbackStep, callbackDeployLibrary) {
const address = library.address
if (address) {
return callback(null, address)
}
const bytecode = library.evm.bytecode.object
if (bytecode.indexOf('_') >= 0) {
this.linkBytecode(library, contracts, (err, bytecode) => {
if (err) callback(err)
else {
library.evm.bytecode.object = bytecode
this.deployLibrary(libraryName, libraryShortName, library, contracts, callback, callbackStep, callbackDeployLibrary)
}
}, callbackStep, callbackDeployLibrary)
} else {
callbackStep(`creation of library ${libraryName} pending...`)
const data = {dataHex: bytecode, funAbi: {type: 'constructor'}, funArgs: [], contractBytecode: bytecode, contractName: libraryShortName}
callbackDeployLibrary({ data: data, useCall: false }, (err, txResult) => {
if (err) {
return callback(err)
}
const address = txResult.result.createdAddress || txResult.result.contractAddress
library.address = address
callback(err, address)
})
}
}
export function linkLibraryStandardFromlinkReferences (libraryName, address, bytecode, linkReferences) {
for (let file in linkReferences) {
for (let libName in linkReferences[file]) {
if (libraryName === libName) {
bytecode = this.setLibraryAddress(address, bytecode, linkReferences[file][libName])
} }
} }
return bytecodeToLink }
}, return bytecode
}
linkLibrary: function (libraryName, address, bytecodeToLink) { export function linkLibraryStandard (libraryName, address, bytecode, contract) {
return linkBytecode(bytecodeToLink, { [libraryName]: addHexPrefix(address) }) return this.linkLibraryStandardFromlinkReferences(libraryName, address, bytecode, contract.evm.bytecode.linkReferences)
}, }
decodeResponse: function (response, fnabi) { export function setLibraryAddress (address, bytecodeToLink, positions) {
// Only decode if there supposed to be fields if (positions) {
if (fnabi.outputs && fnabi.outputs.length > 0) { for (let pos of positions) {
try { const regpos = bytecodeToLink.match(new RegExp(`(.{${2 * pos.start}})(.{${2 * pos.length}})(.*)`))
let i if (regpos) {
bytecodeToLink = regpos[1] + address + regpos[3]
}
}
}
return bytecodeToLink
}
const outputTypes = [] export function linkLibrary (libraryName, address, bytecodeToLink) {
for (i = 0; i < fnabi.outputs.length; i++) { return linkBytecode(bytecodeToLink, { [libraryName]: addHexPrefix(address) })
const type = fnabi.outputs[i].type }
outputTypes.push(type.indexOf('tuple') === 0 ? helper.makeFullTypeDefinition(fnabi.outputs[i]) : type)
} export function decodeResponse (response, fnabi) {
// Only decode if there supposed to be fields
if (fnabi.outputs && fnabi.outputs.length > 0) {
try {
let i
if (!response.length) response = new Uint8Array(32 * fnabi.outputs.length) // ensuring the data is at least filled by 0 cause `AbiCoder` throws if there's not engouh data const outputTypes = []
// decode data for (i = 0; i < fnabi.outputs.length; i++) {
const abiCoder = new ethers.utils.AbiCoder() const type = fnabi.outputs[i].type
const decodedObj = abiCoder.decode(outputTypes, response) outputTypes.push(type.indexOf('tuple') === 0 ? helper.makeFullTypeDefinition(fnabi.outputs[i]) : type)
}
const json = {} if (!response.length) response = new Uint8Array(32 * fnabi.outputs.length) // ensuring the data is at least filled by 0 cause `AbiCoder` throws if there's not engouh data
for (i = 0; i < outputTypes.length; i++) { // decode data
const name = fnabi.outputs[i].name const abiCoder = new ethers.utils.AbiCoder()
json[i] = outputTypes[i] + ': ' + (name ? name + ' ' + decodedObj[i] : decodedObj[i]) const decodedObj = abiCoder.decode(outputTypes, response)
}
return json const json = {}
} catch (e) { for (i = 0; i < outputTypes.length; i++) {
return { error: 'Failed to decode output: ' + e } const name = fnabi.outputs[i].name
json[i] = outputTypes[i] + ': ' + (name ? name + ' ' + decodedObj[i] : decodedObj[i])
} }
return json
} catch (e) {
return { error: 'Failed to decode output: ' + e }
} }
return {} }
}, return {}
}
parseFunctionParams: function (params) { export function parseFunctionParams (params) {
let args = [] let args = []
// Check if parameter string starts with array or string // Check if parameter string starts with array or string
let startIndex = this.isArrayOrStringStart(params, 0) ? -1 : 0 let startIndex = this.isArrayOrStringStart(params, 0) ? -1 : 0
for (let i = 0; i < params.length; i++) { for (let i = 0; i < params.length; i++) {
// If a quote is received // If a quote is received
if (params.charAt(i) === '"') { if (params.charAt(i) === '"') {
startIndex = -1 startIndex = -1
let endQuoteIndex = false let endQuoteIndex = false
// look for closing quote. On success, push the complete string in arguments list // look for closing quote. On success, push the complete string in arguments list
for (let j = i + 1; !endQuoteIndex; j++) { for (let j = i + 1; !endQuoteIndex; j++) {
if (params.charAt(j) === '"') { if (params.charAt(j) === '"') {
args.push(params.substring(i + 1, j)) args.push(params.substring(i + 1, j))
endQuoteIndex = true endQuoteIndex = true
i = j i = j
}
// Throw error if end of params string is arrived but couldn't get end quote
if (!endQuoteIndex && j === params.length - 1) {
throw new Error('invalid params')
}
} }
} else if (params.charAt(i) === '[') { // If an array/struct opening bracket is received // Throw error if end of params string is arrived but couldn't get end quote
startIndex = -1 if (!endQuoteIndex && j === params.length - 1) {
let bracketCount = 1 throw new Error('invalid params')
let j
for (j = i + 1; bracketCount !== 0; j++) {
// Increase count if another array opening bracket is received (To handle nested array)
if (params.charAt(j) === '[') {
bracketCount++
} else if (params.charAt(j) === ']') { // // Decrease count if an array closing bracket is received (To handle nested array)
bracketCount--
}
// Throw error if end of params string is arrived but couldn't get end of tuple
if (bracketCount !== 0 && j === params.length - 1) {
throw new Error('invalid tuple params')
}
} }
// If bracketCount = 0, it means complete array/nested array parsed, push it to the arguments list }
args.push(JSON.parse(params.substring(i, j))) } else if (params.charAt(i) === '[') { // If an array/struct opening bracket is received
i = j - 1 startIndex = -1
} else if (params.charAt(i) === ',') { let bracketCount = 1
// if startIndex >= 0, it means a parameter was being parsed, it can be first or other parameter let j
if (startIndex >= 0) { for (j = i + 1; bracketCount !== 0; j++) {
args.push(params.substring(startIndex, i)) // Increase count if another array opening bracket is received (To handle nested array)
if (params.charAt(j) === '[') {
bracketCount++
} else if (params.charAt(j) === ']') { // // Decrease count if an array closing bracket is received (To handle nested array)
bracketCount--
}
// Throw error if end of params string is arrived but couldn't get end of tuple
if (bracketCount !== 0 && j === params.length - 1) {
throw new Error('invalid tuple params')
} }
// Register start index of a parameter to parse
startIndex = this.isArrayOrStringStart(params, i + 1) ? -1 : i + 1
} else if (startIndex >= 0 && i === params.length - 1) {
// If start index is registered and string is completed (To handle last parameter)
args.push(params.substring(startIndex, params.length))
} }
} // If bracketCount = 0, it means complete array/nested array parsed, push it to the arguments list
args = args.map(e => { args.push(JSON.parse(params.substring(i, j)))
if (!Array.isArray(e)) { i = j - 1
return e.trim() } else if (params.charAt(i) === ',') {
} else { // if startIndex >= 0, it means a parameter was being parsed, it can be first or other parameter
return e if (startIndex >= 0) {
args.push(params.substring(startIndex, i))
} }
}) // Register start index of a parameter to parse
return args startIndex = this.isArrayOrStringStart(params, i + 1) ? -1 : i + 1
}, } else if (startIndex >= 0 && i === params.length - 1) {
// If start index is registered and string is completed (To handle last parameter)
isArrayOrStringStart: function (str, index) { args.push(params.substring(startIndex, params.length))
return str.charAt(index) === '"' || str.charAt(index) === '[' }
} }
args = args.map(e => {
if (!Array.isArray(e)) {
return e.trim()
} else {
return e
}
})
return args
}
export function isArrayOrStringStart (str, index) {
return str.charAt(index) === '"' || str.charAt(index) === '['
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment