@fuel-ts/abi-coder
Advanced tools
Comparing version 0.0.0-master-46f8ae5 to 0.0.0-master-4fae213
@@ -16,2 +16,3 @@ import type { ParamType } from '@ethersproject/abi'; | ||
readonly name: string; | ||
readonly strictInputs: boolean; | ||
readonly inputs: Array<ParamType>; | ||
@@ -23,2 +24,3 @@ readonly outputs: Array<ParamType>; | ||
readonly name: string; | ||
readonly strictInputs: boolean; | ||
readonly inputs: Array<ParamType>; | ||
@@ -25,0 +27,0 @@ readonly outputs: Array<ParamType>; |
@@ -12,4 +12,5 @@ "use strict"; | ||
this.outputs = params.outputs; | ||
this.strictInputs = params.strictInputs; | ||
} | ||
} | ||
exports.Fragment = Fragment; |
@@ -0,1 +1,2 @@ | ||
import type { JsonFragmentType } from '@ethersproject/abi'; | ||
import type { JsonFragment } from './fragment'; | ||
@@ -5,4 +6,6 @@ import { Fragment } from './fragment'; | ||
static fromObject(value: JsonFragment): FunctionFragment; | ||
static strictArguments(fragment: readonly JsonFragmentType[]): boolean; | ||
static allowOnlyArguments(inputs: readonly JsonFragmentType[]): readonly JsonFragmentType[]; | ||
format(): string; | ||
} | ||
//# sourceMappingURL=function-fragment.d.ts.map |
@@ -5,2 +5,11 @@ "use strict"; | ||
const fragment_1 = require("./fragment"); | ||
/** | ||
* An override for the `format` method of Ethers' ParamType to handle Fuel/Ethereum ABI incompatibilities | ||
*/ | ||
function formatOverride(format) { | ||
if ((!format || format === abi_1.FormatTypes.sighash) && this.type.startsWith('struct ')) { | ||
return `s${this.format(format)}`; | ||
} | ||
return this.format(format); | ||
} | ||
class FunctionFragment extends fragment_1.Fragment { | ||
@@ -12,3 +21,4 @@ static fromObject(value) { | ||
name: value.name, | ||
inputs: inputs.map(abi_1.ParamType.fromObject), | ||
inputs: FunctionFragment.allowOnlyArguments(inputs).map(abi_1.ParamType.fromObject), | ||
strictInputs: FunctionFragment.strictArguments(inputs), | ||
outputs: outputs.map(abi_1.ParamType.fromObject), | ||
@@ -18,4 +28,13 @@ }; | ||
} | ||
static strictArguments(fragment) { | ||
return !(fragment.length === 4 && | ||
fragment[0].type === 'u64' && | ||
fragment[1].type === 'u64' && | ||
fragment[2].type === 'b256'); | ||
} | ||
static allowOnlyArguments(inputs) { | ||
return FunctionFragment.strictArguments(inputs) ? inputs : inputs.slice(3); | ||
} | ||
format() { | ||
const inputFormat = this.inputs.map((input) => input.format()); | ||
const inputFormat = this.inputs.map((input) => formatOverride.call(input)); | ||
return `${this.name}(${['u64', 'u64', 'b256', ...inputFormat].join(',')})`; | ||
@@ -22,0 +41,0 @@ } |
import type { BytesLike } from '@ethersproject/bytes'; | ||
import AbiCoder from './abi-coder'; | ||
import type { Values } from './coders/abstract-coder'; | ||
import type { Fragment, JsonFragment } from './fragments/fragment'; | ||
@@ -15,3 +16,3 @@ import FunctionFragment from './fragments/function-fragment'; | ||
decodeFunctionData(functionFragment: FunctionFragment | string, data: BytesLike): any; | ||
encodeFunctionData(functionFragment: FunctionFragment | string, values?: ReadonlyArray<any>): string; | ||
encodeFunctionData(functionFragment: FunctionFragment | string, values?: ReadonlyArray<Values>): string; | ||
decodeFunctionResult(functionFragment: FunctionFragment | string, data: BytesLike): any; | ||
@@ -18,0 +19,0 @@ encodeFunctionResult(functionFragment: FunctionFragment | string, values?: ReadonlyArray<any>): string; |
@@ -11,2 +11,3 @@ "use strict"; | ||
const abi_coder_1 = __importDefault(require("./abi-coder")); | ||
const boolean_1 = __importDefault(require("./coders/boolean")); | ||
const function_fragment_1 = __importDefault(require("./fragments/function-fragment")); | ||
@@ -74,3 +75,12 @@ const logger = new logger_1.Logger('0.0.1'); | ||
} | ||
return (0, bytes_1.hexlify)((0, bytes_1.concat)([Interface.getSighash(fragment), this.abiCoder.encode(fragment.inputs, values)])); | ||
const selector = Interface.getSighash(fragment); | ||
const args = this.abiCoder.encode(fragment.inputs, fragment.strictInputs || values.length === fragment.inputs.length ? values : values.slice(3)); | ||
if (fragment.inputs.length !== 1) { | ||
throw new Error('For now, ABI functions must take exactly one parameter'); | ||
} | ||
return (0, bytes_1.hexlify)((0, bytes_1.concat)([ | ||
selector, | ||
new boolean_1.default('isStruct').encode(fragment.inputs[0].type.startsWith('struct')), | ||
args, | ||
])); | ||
} | ||
@@ -77,0 +87,0 @@ // Decode the result of a function call |
@@ -37,3 +37,3 @@ "use strict"; | ||
functionInterface = new interface_1.default([jsonFragment]); | ||
expect(functionInterface.encodeFunctionData('entry_one', [42])).toEqual('0x0000000044aa0fa9000000000000002a'); | ||
expect(functionInterface.encodeFunctionData('entry_one', [42])).toEqual('0x0000000044aa0fa90000000000000000000000000000002a'); | ||
const decoded = functionInterface.decodeFunctionData('entry_one', '0x0000000044aa0fa9000000000000002a'); | ||
@@ -62,3 +62,3 @@ // toEqual can't handle BigNumbers so JSON.stringify is used | ||
]); | ||
expect(functionInterface.encodeFunctionData('takes_array', [[1, 2, 3]])).toEqual('0x0000000053030075000000000000000100000000000000020000000000000003'); | ||
expect(functionInterface.encodeFunctionData('takes_array', [[1, 2, 3]])).toEqual('0x00000000530300750000000000000000000000000000000100000000000000020000000000000003'); | ||
}); | ||
@@ -94,8 +94,9 @@ it('can encode and decodes function data with tuple values', () => { | ||
}, | ||
])).toEqual('0x00000000ba463b0d666f6f00000000d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b'); | ||
])).toEqual('0x00000000ba463b0d0000000000000000666f6f00000000d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b'); | ||
expect(functionInterface.encodeFunctionData('tuple_function', [ | ||
['foo', '0xd5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b'], | ||
])).toEqual('0x00000000ba463b0d666f6f00000000d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b'); | ||
])).toEqual('0x00000000ba463b0d0000000000000000666f6f00000000d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b'); | ||
}); | ||
it('can encode and decodes function data with empty values', () => { | ||
// TODO: Enable this test when zero arg functions are supported | ||
it.skip('can encode and decodes function data with empty values', () => { | ||
functionInterface = new interface_1.default([ | ||
@@ -117,2 +118,147 @@ { type: 'function', inputs: [], name: 'entry_one', outputs: [] }, | ||
}); | ||
it('can encode struct', () => { | ||
functionInterface = new interface_1.default([ | ||
{ | ||
type: 'function', | ||
name: 'entry_one', | ||
inputs: [ | ||
{ | ||
name: 'my_u64', | ||
type: 'u64', | ||
}, | ||
{ | ||
name: 'my_struct', | ||
type: 'struct MyStruct', | ||
components: [ | ||
{ | ||
name: 'dummy_a', | ||
type: 'bool', | ||
}, | ||
{ | ||
name: 'dummy_b', | ||
type: 'u64', | ||
}, | ||
], | ||
}, | ||
], | ||
outputs: [{ name: 'ret', type: 'u64' }], | ||
}, | ||
]); | ||
expect(functionInterface.getFunction('entry_one').format()).toEqual('entry_one(u64,u64,b256,u64,s(bool,u64))'); | ||
}); | ||
it('can remove the first 3 arguments from the abi if they have gas, coin and color arguments', () => { | ||
const json = { | ||
type: 'function', | ||
inputs: [ | ||
{ | ||
name: 'gas_', | ||
type: 'u64', | ||
}, | ||
{ | ||
name: 'amount_', | ||
type: 'u64', | ||
}, | ||
{ | ||
name: 'color_', | ||
type: 'b256', | ||
}, | ||
{ name: 'arg', type: 'u64' }, | ||
], | ||
name: 'entry_one', | ||
outputs: [], | ||
}; | ||
functionInterface = new interface_1.default([json]); | ||
const newFragment = function_fragment_1.default.fromObject(json); | ||
expect(Object.values(functionInterface.functions)).toHaveLength(1); | ||
expect(functionInterface.getFunction('entry_one(u64,u64,b256,u64)')).toEqual(newFragment); | ||
expect(functionInterface.getFunction('entry_one')).toEqual(newFragment); | ||
expect(functionInterface.getFunction('0x0000000044aa0fa9')).toEqual(newFragment); | ||
expect(functionInterface.encodeFunctionData('entry_one', [42])).toEqual('0x0000000044aa0fa90000000000000000000000000000002a'); | ||
expect(functionInterface.encodeFunctionData('entry_one', [ | ||
42, | ||
42, | ||
'0xd5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930bd5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b', | ||
42, | ||
])).toEqual('0x0000000044aa0fa90000000000000000000000000000002a'); | ||
const decoded = functionInterface.decodeFunctionData('entry_one', '0x0000000044aa0fa9000000000000002a'); | ||
// toEqual can't handle BigNumbers so JSON.stringify is used | ||
expect(JSON.stringify(decoded)).toEqual(JSON.stringify([bignumber_1.BigNumber.from(42)])); | ||
functionInterface = new interface_1.default([ | ||
{ | ||
inputs: [ | ||
{ | ||
name: 'gas_', | ||
type: 'u64', | ||
}, | ||
{ | ||
name: 'amount_', | ||
type: 'u64', | ||
}, | ||
{ | ||
name: 'color_', | ||
type: 'b256', | ||
}, | ||
{ | ||
name: 'person', | ||
type: 'tuple', | ||
components: [ | ||
{ | ||
name: 'name', | ||
type: 'str[20]', | ||
}, | ||
{ | ||
name: 'address', | ||
type: 'address', | ||
}, | ||
], | ||
}, | ||
], | ||
name: 'tuple_function', | ||
outputs: [], | ||
type: 'function', | ||
}, | ||
]); | ||
expect(functionInterface.encodeFunctionData('tuple_function', [ | ||
{ | ||
address: '0xd5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b', | ||
name: 'foo', | ||
}, | ||
])).toEqual('0x00000000ba463b0d0000000000000000666f6f00000000d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b'); | ||
functionInterface = new interface_1.default([ | ||
{ | ||
type: 'function', | ||
name: 'entry_one', | ||
inputs: [ | ||
{ | ||
name: 'gas_', | ||
type: 'u64', | ||
}, | ||
{ | ||
name: 'amount_', | ||
type: 'u64', | ||
}, | ||
{ | ||
name: 'color_', | ||
type: 'b256', | ||
}, | ||
{ | ||
name: 'my_struct', | ||
type: 'struct MyStruct', | ||
components: [ | ||
{ | ||
name: 'dummy_a', | ||
type: 'bool', | ||
}, | ||
{ | ||
name: 'dummy_b', | ||
type: 'u64', | ||
}, | ||
], | ||
}, | ||
], | ||
outputs: [{ name: 'ret', type: 'u64' }], | ||
}, | ||
]); | ||
expect(functionInterface.getFunction('entry_one').format()).toEqual('entry_one(u64,u64,b256,s(bool,u64))'); | ||
}); | ||
}); |
{ | ||
"name": "@fuel-ts/abi-coder", | ||
"version": "0.0.0-master-46f8ae5", | ||
"version": "0.0.0-master-4fae213", | ||
"description": "", | ||
"author": "Fuel Labs <contact@fuel.sh> (https://fuel.sh/)", | ||
"author": "Fuel Labs <contact@fuel.sh> (https://fuel.network/)", | ||
"typedocMain": "src/index.ts", | ||
"main": "dist/index.js", | ||
@@ -19,3 +20,3 @@ "files": [ | ||
}, | ||
"gitHead": "e7e4b5ede9730ff47c2bd3ad9defebb2169c0bb4" | ||
"gitHead": "bf655ca3eacb7efc754db9787e0fce4cacfba2ad" | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
76536
1227