Created
September 5, 2017 23:12
-
-
Save HenryNguyen5/f4862c3a43de9d1ee4ff117376ae5165 to your computer and use it in GitHub Desktop.
ABI Function parser that maps an ABI function to an object that contains call and decode methods, and allows for run-time checking/parsing of given arguments
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import abi from 'ethereumjs-abi'; | |
class AbiFunction { | |
constant; | |
inputs; | |
name; | |
outputs; | |
payable; | |
type; | |
methodInputTypes; | |
inputNames; | |
methodSelector; | |
funcParams; | |
constructor(abiFunc) { | |
Object.assign(this, abiFunc); | |
this._init(); | |
} | |
_init() { | |
const { inputs } = this; | |
this.funcParams = this._makeFuncParams(); | |
//TODO: do this in O(n) | |
this.methodInputTypes = inputs.map(({ type }) => type); | |
this.inputNames = inputs.map(({ name }) => name); | |
this.methodSelector = abi | |
.methodID(this.name, this.methodInputTypes) | |
.toString('hex'); | |
} | |
call = (suppliedInputs = {}) => { | |
const { _processSuppliedArgs, _makeEncodedFuncCall } = this; | |
const args = _processSuppliedArgs(suppliedInputs); | |
const encodedCall = _makeEncodedFuncCall(args); | |
return encodedCall; | |
}; | |
decode = argString => { | |
const { methodSelector, methodInputTypes, inputNames } = this; | |
// Remove method selector from data, if present | |
argString = argString.replace(`0x${methodSelector}`, ''); | |
// Convert argdata to a hex buffer for ethereumjs-abi | |
const argBuffer = new Buffer(argString, 'hex'); | |
// Decode! | |
const argArr = abi.rawDecode(methodInputTypes, argBuffer); | |
//TODO: parse checksummed addresses | |
return argArr.reduce( | |
(argObj, currArg, index) => ({ | |
...argObj, | |
[inputNames[index]]: currArg | |
}), | |
{} | |
); | |
}; | |
_makeFuncParams = () => | |
this.inputs.reduce((inputs, currInput) => { | |
const { name, type } = currInput; | |
const inputHandler = inputToParse => { | |
//TODO: introduce typechecking and typecasting mapping for inputs | |
const value = inputToParse; | |
return { name, type, value }; | |
}; | |
return { ...inputs, [name]: { processInput: inputHandler, type } }; | |
}, {}); | |
_makeEncodedFuncCall = args => { | |
const { methodSelector, methodInputTypes } = this; | |
const encodedArgs = abi.rawEncode(methodInputTypes, args).toString('hex'); | |
return `0x${methodSelector}${encodedArgs}`; | |
}; | |
_processSuppliedArgs = suppliedArgs => { | |
const { inputNames, funcParams } = this; | |
return inputNames.map(name => { | |
const type = funcParams[name].type; | |
//TODO: parse args based on type | |
if (!suppliedArgs[name]) | |
throw Error(`Expected argument "${name}" of type "${type}" missing`); | |
const value = suppliedArgs[name]; | |
const processedArg = funcParams[name].processInput(value); | |
return processedArg.value; | |
}); | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
What this does:
Takes in a function of a contract interfaces and returns an object with
.call
and.decode
methods..call
: Accepts key-value parameters and checks them at run-time against the given ABI function to see that sufficient parameters have been given, also allows for run-time parsing of supplied parameters for things such as BN, checking for sufficient address length, making sure numbers are converted into BN, etc....decode
: accepts an encoded argument string and decodes it, returning an object with key-value pairs with a 1-1 mapping to the specific function in the ABIWhy?
Composition potential
A Contract class could use this class to make its functions, then wrap them in calls to easily generate JSON-RPC objects with the right values since each function object knows all of its own metadata such as
constant
payable
etc...TODO
constructor
fallback
ABI functions or handle them all in one classevent
ABI handler class similar to this one