-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
20401db
commit 7760d03
Showing
9 changed files
with
2,218 additions
and
135 deletions.
There are no files selected for viewing
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
/** @type {import('ts-jest').JestConfigWithTsJest} */ | ||
module.exports = { | ||
preset: 'ts-jest', | ||
testEnvironment: 'node', | ||
testPathIgnorePatterns: ['/node_modules/', '/lib/'], | ||
}; |
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
[ | ||
{ | ||
"payable": true, | ||
"stateMutability": "payable", | ||
"type": "fallback" | ||
}, | ||
{ | ||
"constant": true, | ||
"inputs": [ | ||
{ | ||
"name": "user", | ||
"type": "address" | ||
}, | ||
{ | ||
"name": "token", | ||
"type": "address" | ||
} | ||
], | ||
"name": "tokenBalance", | ||
"outputs": [ | ||
{ | ||
"name": "", | ||
"type": "uint256" | ||
} | ||
], | ||
"payable": false, | ||
"stateMutability": "view", | ||
"type": "function" | ||
} | ||
] | ||
|
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import * as ethers from 'ethers'; | ||
|
||
import BalanceCheckerAbi from './BalanceChecker.abi.json'; | ||
import { BigNumber } from 'bignumber.js'; | ||
import { FastABI } from '..'; | ||
import { MethodAbi } from 'ethereum-types'; | ||
import util from 'util'; | ||
|
||
/** | ||
* Tests FastABI by comparing the results of encoding/decoding with ethers.js. | ||
* | ||
* Test ABI is from https://github.com/wbobeirne/eth-balance-checker/blob/master/abis/BalanceChecker.abi.json | ||
*/ | ||
describe('fastAbi', () => { | ||
it('encodes inputs', () => { | ||
const balanceCheckerEthersInterface = new ethers.Interface(BalanceCheckerAbi as MethodAbi[]); | ||
const encodedFunctionData = balanceCheckerEthersInterface.encodeFunctionData('tokenBalance', [ | ||
'0x4Ea754349AcE5303c82f0d1D491041e042f2ad22', | ||
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', | ||
]); | ||
|
||
const balanceCheckerFastAbi = new FastABI(BalanceCheckerAbi as MethodAbi[]); | ||
const encodedFunctionDataFast = balanceCheckerFastAbi.encodeInput('tokenBalance', [ | ||
'0x4Ea754349AcE5303c82f0d1D491041e042f2ad22', | ||
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', | ||
]); | ||
|
||
expect(encodedFunctionDataFast).toEqual(encodedFunctionData); | ||
}); | ||
|
||
it('decodes inputs', () => { | ||
// Caldata from 'encodes inputs' test | ||
const calldata = | ||
'0x1049334f0000000000000000000000008ba1f109551bd432803012645ac136ddd64dba72000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; | ||
const balanceCheckerEthersInterface = new ethers.Interface(BalanceCheckerAbi as MethodAbi[]); | ||
const decodedFunctionDataEthers = balanceCheckerEthersInterface.decodeFunctionData('tokenBalance', calldata); | ||
|
||
const balanceCheckerFastAbi = new FastABI(BalanceCheckerAbi as MethodAbi[]); | ||
const decodedFunctionDataFast: string[] = balanceCheckerFastAbi.decodeInput('tokenBalance', calldata); | ||
|
||
expect(decodedFunctionDataFast).toEqual(decodedFunctionDataEthers.map((a) => a.toLowerCase())); | ||
}); | ||
|
||
it('decodes outputs', () => { | ||
const result = '0x00000000000000000000000000000000000000000000000000eb01cd45901fac'; | ||
const balanceCheckerEthersInterface = new ethers.Interface(BalanceCheckerAbi as MethodAbi[]); | ||
const decodedOutputEthers: bigint[] = balanceCheckerEthersInterface.decodeFunctionResult( | ||
'tokenBalance', | ||
result, | ||
); | ||
|
||
const balanceCheckerFastAbi = new FastABI(BalanceCheckerAbi as MethodAbi[]); | ||
const decodedOutputFast: BigNumber = balanceCheckerFastAbi.decodeOutput('tokenBalance', result); | ||
|
||
expect(decodedOutputFast.toNumber()).toEqual(Number(decodedOutputEthers[0])); | ||
}); | ||
}); |
This file was deleted.
Oops, something went wrong.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,112 @@ | ||
export { FastABI } from './fast_abi'; | ||
import { DataItem, MethodAbi } from 'ethereum-types'; | ||
import { BigNumber } from 'bignumber.js'; | ||
|
||
const { coderNew, coderEncodeInput, coderDecodeInput, coderDecodeOutput } = require('../bin'); | ||
|
||
interface Opts { | ||
BigNumber: any; | ||
} | ||
|
||
export class FastABI { | ||
private readonly _coder: any; | ||
private readonly _abi: MethodAbi[]; | ||
private readonly _opts: Opts; | ||
|
||
constructor(abi: MethodAbi[], opts?: Opts) { | ||
this._opts = { BigNumber: BigNumber, ...opts } || { BigNumber: BigNumber }; | ||
this._abi = abi; | ||
this._coder = coderNew(JSON.stringify(abi)); | ||
} | ||
|
||
public encodeInput(fnName: string, values: any[]): string { | ||
const found = this._abi.filter((a) => a.name === fnName)[0]; | ||
const args = this._serializeArgsOut(values, found.inputs); | ||
try { | ||
const encoded = coderEncodeInput.call(this._coder, fnName, args); | ||
return `0x${encoded}`; | ||
} catch (e) { | ||
throw new Error(`${(e as Error).message}.\nvalues=${JSON.stringify(values)}\nargs=${JSON.stringify(args)}`); | ||
} | ||
} | ||
|
||
/** | ||
* NOTE: does not produce checksummed values | ||
*/ | ||
public decodeInput(fnName: string, output: string): any { | ||
const found = this._abi.filter((a) => a.name === fnName)[0]; | ||
const decoded = coderDecodeInput.call(this._coder, fnName, output); | ||
return this._deserializeResultsIn(found.inputs, decoded); | ||
} | ||
|
||
public decodeOutput(fnName: string, output: string): any { | ||
const found = this._abi.filter((a) => a.name === fnName)[0]; | ||
const decoded = coderDecodeOutput.call(this._coder, fnName, output); | ||
return this._deserializeResultsIn(found.outputs, decoded); | ||
} | ||
|
||
private _deserializeResultIn(abi: DataItem, value: any): any { | ||
if (abi.type.indexOf('[]') !== -1) { | ||
// Pop off the last [] and serialize each sub value | ||
let type = abi.type.split('[]'); | ||
type.pop(); | ||
let newType = type.join('[]'); // e.g address[][] -> address[] | ||
return (value as any[]).map((v) => this._deserializeResultIn({ ...abi, type: newType }, v)); | ||
} | ||
if (abi.type.indexOf('int') !== -1) { | ||
return new this._opts.BigNumber(value); | ||
} | ||
if (abi.type === 'tuple' && abi.components) { | ||
const output: any = {}; | ||
for (const [i, c] of Object.entries(abi.components)) { | ||
output[c.name] = this._deserializeResultIn(c, value[parseInt(i)]); | ||
} | ||
return output; | ||
} | ||
return value; | ||
} | ||
|
||
private _deserializeResultsIn(abis: DataItem[], values: any[]): any { | ||
const output: any = []; | ||
for (const [i, v] of Object.entries(abis)) { | ||
output.push(this._deserializeResultIn(v, values[parseInt(i)])); | ||
} | ||
if (abis.length === 1) { | ||
return output[0]; | ||
} | ||
return output; | ||
} | ||
|
||
// Convert the javascript arguments into the FastAbi preferred arguments | ||
private _serializeArgsOut(abis: DataItem[], args: any[]): any[] { | ||
return abis.map((abi, i) => this._serializeArgOut(args[i], abi)); | ||
} | ||
|
||
private _serializeArgOut(abi: DataItem, arg: any): any { | ||
if (arg === undefined) { | ||
throw new Error(`Encountered undefined argument`); | ||
} | ||
|
||
if (abi.type.indexOf('[]') !== -1) { | ||
// Pop off the last [] and serialize each sub value | ||
let type = abi.type.split('[]'); | ||
type.pop(); | ||
let newType = type.join('[]'); // e.g address[][] -> address[] | ||
return (arg as any[]).map((v) => this._serializeArgOut({ ...abi, type: newType }, v)); | ||
} | ||
|
||
// Convert from { b: 2, a: 1 } into a component ordered value array, [1,2] | ||
if (abi.type === 'tuple' && abi.components) { | ||
const output: any[] = []; | ||
for (const [_i, c] of Object.entries(abi.components)) { | ||
output.push(this._serializeArgOut(c, arg[c.name])); | ||
} | ||
return output; | ||
} | ||
|
||
if (this._opts.BigNumber.isBigNumber(arg)) { | ||
return arg.toString(10); | ||
} | ||
|
||
return arg.toString(); | ||
} | ||
} |
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
Oops, something went wrong.