essential-eth 0.3.2 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,10 +10,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.Contract = exports.defineReadOnly = exports.BaseContract = void 0;
13
- const sha3_1 = require("sha3");
14
- const __1 = require("..");
13
+ const encode_decode_transaction_1 = require("./utils/encode-decode-transaction");
15
14
  const fetchers_1 = require("./utils/fetchers");
16
- const hex_to_decimal_1 = require("./utils/hex-to-decimal");
17
15
  function estimateGas(txnData) {
18
16
  // https://ethereum.stackexchange.com/questions/1570/what-does-intrinsic-gas-too-low-mean/1694
19
17
  txnData.split('').reduce((previousValue, currentValue) => {
@@ -32,78 +30,33 @@ class BaseContract {
32
30
  this._contractInterface = contractInterface;
33
31
  this._provider = signerOrProvider;
34
32
  contractInterface
35
- .filter((argument) => argument.type === 'function')
36
- .forEach((argument) => {
37
- if ('name' in argument && typeof argument.name === 'string') {
38
- defineReadOnly(this, argument.name, (..._args) => __awaiter(this, void 0, void 0, function* () {
39
- let args = _args;
33
+ .filter((jsonABIArgument) => jsonABIArgument.type === 'function')
34
+ .forEach((jsonABIArgument) => {
35
+ if ('name' in jsonABIArgument &&
36
+ typeof jsonABIArgument.name === 'string') {
37
+ defineReadOnly(this, jsonABIArgument.name, (..._args) => __awaiter(this, void 0, void 0, function* () {
38
+ let functionArguments = _args;
40
39
  let options = {};
41
40
  // remove options from encoding
42
41
  const lastArg = _args[_args.length - 1];
43
42
  if (!Array.isArray(lastArg) && typeof lastArg === 'object') {
44
43
  options = lastArg;
45
- args = _args.slice(0, _args.length - 1);
44
+ functionArguments = _args.slice(0, _args.length - 1);
46
45
  }
47
- const hash = new sha3_1.Keccak(256);
48
- const rawOutputs = argument.outputs;
49
- /* first 4 bytes will create the data parameter */
50
- const functionString = `${argument.name}(${argument.inputs.map((input) => input.type)})`;
51
- // encoding learnt from https://ethereum.stackexchange.com/questions/3514/how-to-call-a-contract-method-using-the-eth-call-json-rpc-api
52
- const functionHash = hash.update(functionString).digest('hex');
53
- if (args.length !== argument.inputs.length) {
54
- throw new Error(`args inputs of "${args.length}" does not match expected length of "${argument.inputs.length}"`);
55
- }
56
- const encodedArgs = (args || []).map((arg, i) => {
57
- let rawArg = arg;
58
- // remove leading "0x" on address types
59
- if (argument.inputs[i].type === 'address') {
60
- rawArg = arg.replace(/^0x/g, '').toLowerCase();
61
- }
62
- const argEncoded = rawArg.toString(16);
63
- const paddedEncodedArg = argEncoded.padStart(64, '0');
64
- return paddedEncodedArg;
65
- });
66
- const functionEncoded = functionHash.slice(0, 8);
67
- const myData = `0x${functionEncoded}${encodedArgs.join('')}`;
46
+ const data = (0, encode_decode_transaction_1.encodeData)(jsonABIArgument, functionArguments);
68
47
  const decimalGas = typeof options.gasLimit === 'number'
69
48
  ? options.gasLimit /* user passed in "gasLimit" directly */
70
- : typeof (argument === null || argument === void 0 ? void 0 : argument.gas) === 'number' /* ABI specified "gas". */
71
- ? estimateGas(myData)
49
+ : typeof (jsonABIArgument === null || jsonABIArgument === void 0 ? void 0 : jsonABIArgument.gas) ===
50
+ 'number' /* ABI specified "gas". */
51
+ ? estimateGas(data)
72
52
  : null;
73
53
  const nodeResponse = yield (0, fetchers_1.post)(this._provider._rpcUrl, (0, fetchers_1.buildRPCPostBody)('eth_call', [
74
- Object.assign({ to: this._address.toLowerCase(), data: myData }, (decimalGas
54
+ Object.assign({ to: this._address.toLowerCase(), data: data }, (decimalGas
75
55
  ? { gas: `0x${decimalGas.toString(16)}` }
76
56
  : {})),
77
57
  'latest',
78
58
  ]));
79
- // chunk response every 64 characters
80
- const encodedOutputs = nodeResponse.slice(2).match(/.{1,64}/g);
81
- const outputs = (encodedOutputs || []).map((output, i) => {
82
- const outputType = (rawOutputs || [])[i].type;
83
- switch (outputType) {
84
- case 'bool':
85
- switch (output) {
86
- case '0000000000000000000000000000000000000000000000000000000000000001':
87
- return true;
88
- case '0000000000000000000000000000000000000000000000000000000000000000':
89
- return false;
90
- default:
91
- throw new Error(`boolean response of ${output} not defined`);
92
- }
93
- case 'address':
94
- /* address types have 26 leading zeroes to remove */
95
- return (0, __1.toChecksumAddress)(`0x${output.slice(24)}`);
96
- case 'uint256':
97
- return (0, __1.tinyBig)((0, hex_to_decimal_1.hexToDecimal)(`0x${output}`));
98
- case 'bytes32':
99
- return `0x${output}`;
100
- case 'uint8':
101
- return Number((0, hex_to_decimal_1.hexToDecimal)(`0x${output}`));
102
- default:
103
- throw new Error(`essential-eth does not yet support "${outputType}" inputs. Make a PR today!"`);
104
- }
105
- });
106
- return outputs.length === 1 ? outputs[0] : outputs;
59
+ return (0, encode_decode_transaction_1.decodeRPCResponse)(jsonABIArgument, nodeResponse);
107
60
  }));
108
61
  }
109
62
  });
@@ -0,0 +1,2 @@
1
+ import { JSONABI } from '../../..';
2
+ export declare const fooABI: JSONABI;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ // https://docs.soliditylang.org/en/v0.6.8/abi-spec.html#examples
3
+ // contract Foo {
4
+ // function bar(bytes3[2] memory) public pure {}
5
+ // function baz(uint32 x, bool y) public pure returns (bool r) { r = x > 32 || y; }
6
+ // function sam(bytes memory, bool, uint[] memory) public pure {}
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.fooABI = void 0;
9
+ // }
10
+ exports.fooABI = [
11
+ {
12
+ inputs: [
13
+ {
14
+ internalType: 'bytes3[2]',
15
+ name: 'memory',
16
+ type: 'bytes3[2]',
17
+ },
18
+ ],
19
+ name: 'bar',
20
+ outputs: [],
21
+ type: 'function',
22
+ },
23
+ {
24
+ inputs: [
25
+ { internalType: 'uint32', name: 'x', type: 'uint32' },
26
+ { internalType: 'bool', name: 'y', type: 'bool' },
27
+ ],
28
+ name: 'baz',
29
+ outputs: [{ internalType: 'bool', name: 'r', type: 'bool' }],
30
+ type: 'function',
31
+ },
32
+ {
33
+ inputs: [
34
+ { internalType: 'bytes', name: 'memory', type: 'bytes' },
35
+ { internalType: 'bool', name: '', type: 'bool' },
36
+ { internalType: 'uint[]', name: 'memory', type: 'uint[]' },
37
+ ],
38
+ name: 'sam',
39
+ outputs: [],
40
+ type: 'function',
41
+ },
42
+ ];
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const encode_decode_transaction_1 = require("../../utils/encode-decode-transaction");
4
+ const foo_abi_1 = require("./foo-abi");
5
+ describe('foo encode', () => {
6
+ it('encodes "baz" function', () => {
7
+ const jsonABIArgument = foo_abi_1.fooABI.find((abi) => abi.name === 'baz');
8
+ const encoded = (0, encode_decode_transaction_1.encodeData)(jsonABIArgument, [69, true]);
9
+ expect(encoded).toBe('0xcdcd77c000000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001');
10
+ });
11
+ it('encodes "bar" function', () => {
12
+ const jsonABIArgument = foo_abi_1.fooABI.find((abi) => abi.name === 'bar');
13
+ const encoded = (0, encode_decode_transaction_1.encodeData)(jsonABIArgument, ['abc', 'def']);
14
+ expect(encoded).toBe('0xfce353f661626300000000000000000000000000000000000000000000000000000000006465660000000000000000000000000000000000000000000000000000000000');
15
+ });
16
+ // it('encodes "sam" function', () => {
17
+ // const jsonABIArgument = fooABI.find(
18
+ // (abi) => abi.name === 'sam',
19
+ // ) as JSONABIArgument;
20
+ // const encoded = encodeData(jsonABIArgument, ['dave', true, [1, 2, 3]]);
21
+ // expect(encoded).toBe(
22
+ // '0xa5643bf20000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000464617665000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003',
23
+ // );
24
+ // });
25
+ });
@@ -0,0 +1,3 @@
1
+ import { JSONABIArgument } from '../../types/Contract.types';
2
+ export declare function encodeData(jsonABIArgument: JSONABIArgument, args: any[]): string;
3
+ export declare function decodeRPCResponse(jsonABIArgument: JSONABIArgument, nodeResponse: string): string | number | boolean | import("../..").TinyBig | (string | number | boolean | import("../..").TinyBig)[];
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.decodeRPCResponse = exports.encodeData = void 0;
4
+ const sha3_1 = require("sha3");
5
+ const __1 = require("../..");
6
+ const hex_to_decimal_1 = require("./hex-to-decimal");
7
+ const hexTrue = '0000000000000000000000000000000000000000000000000000000000000001';
8
+ const hexFalse = '0000000000000000000000000000000000000000000000000000000000000000';
9
+ function expandType(type) {
10
+ // https://docs.soliditylang.org/en/v0.8.7/types.html#integers
11
+ if (type === 'uint[]') {
12
+ return 'uint256[]';
13
+ }
14
+ else if (type === 'int[]') {
15
+ return 'int256[]';
16
+ }
17
+ return type;
18
+ }
19
+ function encodeData(jsonABIArgument, args) {
20
+ const hash = new sha3_1.Keccak(256);
21
+ /* first 4 bytes will create the data parameter */
22
+ const functionString = `${jsonABIArgument.name}(${jsonABIArgument.inputs.map((input) => expandType(input.type))})`;
23
+ // encoding learnt from https://ethereum.stackexchange.com/questions/3514/how-to-call-a-contract-method-using-the-eth-call-json-rpc-api
24
+ const functionHash = hash.update(functionString).digest('hex');
25
+ // no arrays
26
+ const jsonABIInputsLength = jsonABIArgument.inputs.length;
27
+ let shouldValidateInputLength = true;
28
+ // inputs contains 1 or more arrays
29
+ if (jsonABIArgument.inputs.find((input) => input.type.includes('['))) {
30
+ shouldValidateInputLength = false;
31
+ }
32
+ if (shouldValidateInputLength && args.length !== jsonABIInputsLength) {
33
+ throw new Error(`args inputs of "${args.length}" does not match expected length of "${jsonABIArgument.inputs.length}"`);
34
+ }
35
+ const argsWithTypes = (jsonABIArgument.inputs || []).reduce((acc, input, i) => {
36
+ var _a;
37
+ if (input.type.includes('[')) {
38
+ // strip array and length like "[2]" from type
39
+ const basicType = (_a = /([^[]*)\[.*$/g.exec(input.type)) === null || _a === void 0 ? void 0 : _a[1];
40
+ args.forEach((arg) => {
41
+ acc = acc.concat([[arg, basicType]]);
42
+ });
43
+ return acc;
44
+ }
45
+ else {
46
+ return acc.concat([[args[i], input.type]]);
47
+ }
48
+ }, []);
49
+ const encodedArgs = argsWithTypes.map(([arg, inputType]) => {
50
+ let rawArg = arg;
51
+ if (inputType === 'bool') {
52
+ return arg ? hexTrue : hexFalse;
53
+ }
54
+ else if (inputType.startsWith('bytes')) {
55
+ // encode each character to hex
56
+ const argEncoded = rawArg
57
+ .split('')
58
+ .map((character) => character.charCodeAt(0).toString(16))
59
+ .join('');
60
+ const paddedEncodedArg = argEncoded.padEnd(64, '0');
61
+ return paddedEncodedArg;
62
+ }
63
+ // remove leading "0x" on address types
64
+ else if (inputType === 'address') {
65
+ rawArg = arg.replace(/^0x/g, '').toLowerCase();
66
+ }
67
+ const argEncoded = rawArg.toString(16);
68
+ const paddedEncodedArg = argEncoded.padStart(64, '0');
69
+ return paddedEncodedArg;
70
+ });
71
+ const functionEncoded = functionHash.slice(0, 8);
72
+ const data = `0x${functionEncoded}${encodedArgs.join('')}`;
73
+ return data;
74
+ }
75
+ exports.encodeData = encodeData;
76
+ function decodeRPCResponse(jsonABIArgument, nodeResponse) {
77
+ const rawOutputs = jsonABIArgument.outputs;
78
+ // chunk response every 64 characters
79
+ const encodedOutputs = nodeResponse.slice(2).match(/.{1,64}/g);
80
+ const outputs = (encodedOutputs || []).map((output, i) => {
81
+ const outputType = (rawOutputs || [])[i].type;
82
+ switch (outputType) {
83
+ case 'bool':
84
+ return output === hexTrue;
85
+ case 'address':
86
+ /* address types have 26 leading zeroes to remove */
87
+ return (0, __1.toChecksumAddress)(`0x${output.slice(24)}`);
88
+ case 'uint256':
89
+ return (0, __1.tinyBig)((0, hex_to_decimal_1.hexToDecimal)(`0x${output}`));
90
+ case 'bytes32':
91
+ return `0x${output}`;
92
+ case 'uint8':
93
+ return Number((0, hex_to_decimal_1.hexToDecimal)(`0x${output}`));
94
+ default:
95
+ throw new Error(`essential-eth does not yet support "${outputType}" inputs. Make a PR today!"`);
96
+ }
97
+ });
98
+ return outputs.length === 1 ? outputs[0] : outputs;
99
+ }
100
+ exports.decodeRPCResponse = decodeRPCResponse;
@@ -1,9 +1,11 @@
1
+ import { Transaction } from 'ethers';
1
2
  import { Contract } from './classes/Contract';
2
3
  import { JsonRpcProvider, jsonRpcProvider } from './providers/JsonRpcProvider';
3
4
  import { tinyBig, TinyBig } from './shared/tiny-big/tiny-big';
4
- import { JSONABI } from './types/Contract.types';
5
+ import { Block } from './types/block.types';
6
+ import { JSONABI, JSONABIArgument } from './types/Contract.types';
5
7
  import { etherToWei } from './utils/ether-to-wei';
6
8
  import { isAddress } from './utils/is-address';
7
9
  import { toChecksumAddress } from './utils/to-checksum-address';
8
10
  import { weiToEther } from './utils/wei-to-ether';
9
- export { jsonRpcProvider, JsonRpcProvider, toChecksumAddress, isAddress, etherToWei, weiToEther, tinyBig, TinyBig, Contract, JSONABI, };
11
+ export { jsonRpcProvider, JsonRpcProvider, toChecksumAddress, isAddress, etherToWei, weiToEther, tinyBig, TinyBig, Contract, JSONABI, Block, JSONABIArgument, Transaction, };
@@ -1,7 +1,7 @@
1
- export declare type ContractTypes = 'bool' | 'bytes32' | 'bytes32[]' | 'address' | 'address payable' | 'address[4]' | 'address[100]' | 'uint256' | 'uint256[100]' | 'uint8' | 'uint32' | 'string';
1
+ export declare type ContractTypes = 'bool' | 'bytes1' | 'bytes2' | 'bytes3' | 'bytes4' | 'bytes5' | 'bytes6' | 'bytes7' | 'bytes8' | 'bytes9' | 'bytes10' | 'bytes11' | 'bytes12' | 'bytes13' | 'bytes14' | 'bytes15' | 'bytes16' | 'bytes17' | 'bytes18' | 'bytes19' | 'bytes20' | 'bytes21' | 'bytes22' | 'bytes23' | 'bytes24' | 'bytes25' | 'bytes26' | 'bytes27' | 'bytes28' | 'bytes29' | 'bytes30' | 'bytes31' | 'bytes32' | 'bytes32[]' | 'address' | 'address payable' | 'address[4]' | 'address[100]' | 'uint256' | 'uint256[100]' | 'uint8' | 'uint32' | string;
2
2
  export declare type ContractInterface = JSONABI;
3
3
  export declare type ContractFunction<T = any> = (...args: Array<any>) => Promise<T>;
4
- export declare type JSONABI = {
4
+ export declare type JSONABIArgument = {
5
5
  anonymous?: false;
6
6
  inputs: {
7
7
  internalType?: ContractTypes | string;
@@ -18,4 +18,5 @@ export declare type JSONABI = {
18
18
  stateMutability?: 'view' | 'nonpayable' | string;
19
19
  type: 'function' | 'event' | 'constructor' | 'error';
20
20
  gas?: number;
21
- }[];
21
+ };
22
+ export declare type JSONABI = JSONABIArgument[];
@@ -7,10 +7,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { Keccak } from 'sha3';
11
- import { tinyBig, toChecksumAddress } from '..';
10
+ import { decodeRPCResponse, encodeData, } from './utils/encode-decode-transaction';
12
11
  import { buildRPCPostBody, post } from './utils/fetchers';
13
- import { hexToDecimal } from './utils/hex-to-decimal';
14
12
  function estimateGas(txnData) {
15
13
  txnData.split('').reduce((previousValue, currentValue) => {
16
14
  const characterCost = currentValue === '0' ? 4 : 68;
@@ -23,72 +21,32 @@ export class BaseContract {
23
21
  this._contractInterface = contractInterface;
24
22
  this._provider = signerOrProvider;
25
23
  contractInterface
26
- .filter((argument) => argument.type === 'function')
27
- .forEach((argument) => {
28
- if ('name' in argument && typeof argument.name === 'string') {
29
- defineReadOnly(this, argument.name, (..._args) => __awaiter(this, void 0, void 0, function* () {
30
- let args = _args;
24
+ .filter((jsonABIArgument) => jsonABIArgument.type === 'function')
25
+ .forEach((jsonABIArgument) => {
26
+ if ('name' in jsonABIArgument &&
27
+ typeof jsonABIArgument.name === 'string') {
28
+ defineReadOnly(this, jsonABIArgument.name, (..._args) => __awaiter(this, void 0, void 0, function* () {
29
+ let functionArguments = _args;
31
30
  let options = {};
32
31
  const lastArg = _args[_args.length - 1];
33
32
  if (!Array.isArray(lastArg) && typeof lastArg === 'object') {
34
33
  options = lastArg;
35
- args = _args.slice(0, _args.length - 1);
34
+ functionArguments = _args.slice(0, _args.length - 1);
36
35
  }
37
- const hash = new Keccak(256);
38
- const rawOutputs = argument.outputs;
39
- const functionString = `${argument.name}(${argument.inputs.map((input) => input.type)})`;
40
- const functionHash = hash.update(functionString).digest('hex');
41
- if (args.length !== argument.inputs.length) {
42
- throw new Error(`args inputs of "${args.length}" does not match expected length of "${argument.inputs.length}"`);
43
- }
44
- const encodedArgs = (args || []).map((arg, i) => {
45
- let rawArg = arg;
46
- if (argument.inputs[i].type === 'address') {
47
- rawArg = arg.replace(/^0x/g, '').toLowerCase();
48
- }
49
- const argEncoded = rawArg.toString(16);
50
- const paddedEncodedArg = argEncoded.padStart(64, '0');
51
- return paddedEncodedArg;
52
- });
53
- const functionEncoded = functionHash.slice(0, 8);
54
- const myData = `0x${functionEncoded}${encodedArgs.join('')}`;
36
+ const data = encodeData(jsonABIArgument, functionArguments);
55
37
  const decimalGas = typeof options.gasLimit === 'number'
56
38
  ? options.gasLimit
57
- : typeof (argument === null || argument === void 0 ? void 0 : argument.gas) === 'number'
58
- ? estimateGas(myData)
39
+ : typeof (jsonABIArgument === null || jsonABIArgument === void 0 ? void 0 : jsonABIArgument.gas) ===
40
+ 'number'
41
+ ? estimateGas(data)
59
42
  : null;
60
43
  const nodeResponse = yield post(this._provider._rpcUrl, buildRPCPostBody('eth_call', [
61
- Object.assign({ to: this._address.toLowerCase(), data: myData }, (decimalGas
44
+ Object.assign({ to: this._address.toLowerCase(), data: data }, (decimalGas
62
45
  ? { gas: `0x${decimalGas.toString(16)}` }
63
46
  : {})),
64
47
  'latest',
65
48
  ]));
66
- const encodedOutputs = nodeResponse.slice(2).match(/.{1,64}/g);
67
- const outputs = (encodedOutputs || []).map((output, i) => {
68
- const outputType = (rawOutputs || [])[i].type;
69
- switch (outputType) {
70
- case 'bool':
71
- switch (output) {
72
- case '0000000000000000000000000000000000000000000000000000000000000001':
73
- return true;
74
- case '0000000000000000000000000000000000000000000000000000000000000000':
75
- return false;
76
- default:
77
- throw new Error(`boolean response of ${output} not defined`);
78
- }
79
- case 'address':
80
- return toChecksumAddress(`0x${output.slice(24)}`);
81
- case 'uint256':
82
- return tinyBig(hexToDecimal(`0x${output}`));
83
- case 'bytes32':
84
- return `0x${output}`;
85
- case 'uint8':
86
- return Number(hexToDecimal(`0x${output}`));
87
- default:
88
- throw new Error(`essential-eth does not yet support "${outputType}" inputs. Make a PR today!"`);
89
- }
90
- });
91
- return outputs.length === 1 ? outputs[0] : outputs;
49
+ return decodeRPCResponse(jsonABIArgument, nodeResponse);
92
50
  }));
93
51
  }
94
52
  });
@@ -0,0 +1,2 @@
1
+ import { JSONABI } from '../../..';
2
+ export declare const fooABI: JSONABI;
@@ -0,0 +1,33 @@
1
+ export const fooABI = [
2
+ {
3
+ inputs: [
4
+ {
5
+ internalType: 'bytes3[2]',
6
+ name: 'memory',
7
+ type: 'bytes3[2]',
8
+ },
9
+ ],
10
+ name: 'bar',
11
+ outputs: [],
12
+ type: 'function',
13
+ },
14
+ {
15
+ inputs: [
16
+ { internalType: 'uint32', name: 'x', type: 'uint32' },
17
+ { internalType: 'bool', name: 'y', type: 'bool' },
18
+ ],
19
+ name: 'baz',
20
+ outputs: [{ internalType: 'bool', name: 'r', type: 'bool' }],
21
+ type: 'function',
22
+ },
23
+ {
24
+ inputs: [
25
+ { internalType: 'bytes', name: 'memory', type: 'bytes' },
26
+ { internalType: 'bool', name: '', type: 'bool' },
27
+ { internalType: 'uint[]', name: 'memory', type: 'uint[]' },
28
+ ],
29
+ name: 'sam',
30
+ outputs: [],
31
+ type: 'function',
32
+ },
33
+ ];
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ import { encodeData } from '../../utils/encode-decode-transaction';
2
+ import { fooABI } from './foo-abi';
3
+ describe('foo encode', () => {
4
+ it('encodes "baz" function', () => {
5
+ const jsonABIArgument = fooABI.find((abi) => abi.name === 'baz');
6
+ const encoded = encodeData(jsonABIArgument, [69, true]);
7
+ expect(encoded).toBe('0xcdcd77c000000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001');
8
+ });
9
+ it('encodes "bar" function', () => {
10
+ const jsonABIArgument = fooABI.find((abi) => abi.name === 'bar');
11
+ const encoded = encodeData(jsonABIArgument, ['abc', 'def']);
12
+ expect(encoded).toBe('0xfce353f661626300000000000000000000000000000000000000000000000000000000006465660000000000000000000000000000000000000000000000000000000000');
13
+ });
14
+ });
@@ -0,0 +1,3 @@
1
+ import { JSONABIArgument } from '../../types/Contract.types';
2
+ export declare function encodeData(jsonABIArgument: JSONABIArgument, args: any[]): string;
3
+ export declare function decodeRPCResponse(jsonABIArgument: JSONABIArgument, nodeResponse: string): string | number | boolean | import("../..").TinyBig | (string | number | boolean | import("../..").TinyBig)[];
@@ -0,0 +1,85 @@
1
+ import { Keccak } from 'sha3';
2
+ import { tinyBig, toChecksumAddress } from '../..';
3
+ import { hexToDecimal } from './hex-to-decimal';
4
+ const hexTrue = '0000000000000000000000000000000000000000000000000000000000000001';
5
+ const hexFalse = '0000000000000000000000000000000000000000000000000000000000000000';
6
+ function expandType(type) {
7
+ if (type === 'uint[]') {
8
+ return 'uint256[]';
9
+ }
10
+ else if (type === 'int[]') {
11
+ return 'int256[]';
12
+ }
13
+ return type;
14
+ }
15
+ export function encodeData(jsonABIArgument, args) {
16
+ const hash = new Keccak(256);
17
+ const functionString = `${jsonABIArgument.name}(${jsonABIArgument.inputs.map((input) => expandType(input.type))})`;
18
+ const functionHash = hash.update(functionString).digest('hex');
19
+ const jsonABIInputsLength = jsonABIArgument.inputs.length;
20
+ let shouldValidateInputLength = true;
21
+ if (jsonABIArgument.inputs.find((input) => input.type.includes('['))) {
22
+ shouldValidateInputLength = false;
23
+ }
24
+ if (shouldValidateInputLength && args.length !== jsonABIInputsLength) {
25
+ throw new Error(`args inputs of "${args.length}" does not match expected length of "${jsonABIArgument.inputs.length}"`);
26
+ }
27
+ const argsWithTypes = (jsonABIArgument.inputs || []).reduce((acc, input, i) => {
28
+ var _a;
29
+ if (input.type.includes('[')) {
30
+ const basicType = (_a = /([^[]*)\[.*$/g.exec(input.type)) === null || _a === void 0 ? void 0 : _a[1];
31
+ args.forEach((arg) => {
32
+ acc = acc.concat([[arg, basicType]]);
33
+ });
34
+ return acc;
35
+ }
36
+ else {
37
+ return acc.concat([[args[i], input.type]]);
38
+ }
39
+ }, []);
40
+ const encodedArgs = argsWithTypes.map(([arg, inputType]) => {
41
+ let rawArg = arg;
42
+ if (inputType === 'bool') {
43
+ return arg ? hexTrue : hexFalse;
44
+ }
45
+ else if (inputType.startsWith('bytes')) {
46
+ const argEncoded = rawArg
47
+ .split('')
48
+ .map((character) => character.charCodeAt(0).toString(16))
49
+ .join('');
50
+ const paddedEncodedArg = argEncoded.padEnd(64, '0');
51
+ return paddedEncodedArg;
52
+ }
53
+ else if (inputType === 'address') {
54
+ rawArg = arg.replace(/^0x/g, '').toLowerCase();
55
+ }
56
+ const argEncoded = rawArg.toString(16);
57
+ const paddedEncodedArg = argEncoded.padStart(64, '0');
58
+ return paddedEncodedArg;
59
+ });
60
+ const functionEncoded = functionHash.slice(0, 8);
61
+ const data = `0x${functionEncoded}${encodedArgs.join('')}`;
62
+ return data;
63
+ }
64
+ export function decodeRPCResponse(jsonABIArgument, nodeResponse) {
65
+ const rawOutputs = jsonABIArgument.outputs;
66
+ const encodedOutputs = nodeResponse.slice(2).match(/.{1,64}/g);
67
+ const outputs = (encodedOutputs || []).map((output, i) => {
68
+ const outputType = (rawOutputs || [])[i].type;
69
+ switch (outputType) {
70
+ case 'bool':
71
+ return output === hexTrue;
72
+ case 'address':
73
+ return toChecksumAddress(`0x${output.slice(24)}`);
74
+ case 'uint256':
75
+ return tinyBig(hexToDecimal(`0x${output}`));
76
+ case 'bytes32':
77
+ return `0x${output}`;
78
+ case 'uint8':
79
+ return Number(hexToDecimal(`0x${output}`));
80
+ default:
81
+ throw new Error(`essential-eth does not yet support "${outputType}" inputs. Make a PR today!"`);
82
+ }
83
+ });
84
+ return outputs.length === 1 ? outputs[0] : outputs;
85
+ }
@@ -1,9 +1,11 @@
1
+ import { Transaction } from 'ethers';
1
2
  import { Contract } from './classes/Contract';
2
3
  import { JsonRpcProvider, jsonRpcProvider } from './providers/JsonRpcProvider';
3
4
  import { tinyBig, TinyBig } from './shared/tiny-big/tiny-big';
4
- import { JSONABI } from './types/Contract.types';
5
+ import { Block } from './types/block.types';
6
+ import { JSONABI, JSONABIArgument } from './types/Contract.types';
5
7
  import { etherToWei } from './utils/ether-to-wei';
6
8
  import { isAddress } from './utils/is-address';
7
9
  import { toChecksumAddress } from './utils/to-checksum-address';
8
10
  import { weiToEther } from './utils/wei-to-ether';
9
- export { jsonRpcProvider, JsonRpcProvider, toChecksumAddress, isAddress, etherToWei, weiToEther, tinyBig, TinyBig, Contract, JSONABI, };
11
+ export { jsonRpcProvider, JsonRpcProvider, toChecksumAddress, isAddress, etherToWei, weiToEther, tinyBig, TinyBig, Contract, JSONABI, Block, JSONABIArgument, Transaction, };
@@ -1,7 +1,7 @@
1
- export declare type ContractTypes = 'bool' | 'bytes32' | 'bytes32[]' | 'address' | 'address payable' | 'address[4]' | 'address[100]' | 'uint256' | 'uint256[100]' | 'uint8' | 'uint32' | 'string';
1
+ export declare type ContractTypes = 'bool' | 'bytes1' | 'bytes2' | 'bytes3' | 'bytes4' | 'bytes5' | 'bytes6' | 'bytes7' | 'bytes8' | 'bytes9' | 'bytes10' | 'bytes11' | 'bytes12' | 'bytes13' | 'bytes14' | 'bytes15' | 'bytes16' | 'bytes17' | 'bytes18' | 'bytes19' | 'bytes20' | 'bytes21' | 'bytes22' | 'bytes23' | 'bytes24' | 'bytes25' | 'bytes26' | 'bytes27' | 'bytes28' | 'bytes29' | 'bytes30' | 'bytes31' | 'bytes32' | 'bytes32[]' | 'address' | 'address payable' | 'address[4]' | 'address[100]' | 'uint256' | 'uint256[100]' | 'uint8' | 'uint32' | string;
2
2
  export declare type ContractInterface = JSONABI;
3
3
  export declare type ContractFunction<T = any> = (...args: Array<any>) => Promise<T>;
4
- export declare type JSONABI = {
4
+ export declare type JSONABIArgument = {
5
5
  anonymous?: false;
6
6
  inputs: {
7
7
  internalType?: ContractTypes | string;
@@ -18,4 +18,5 @@ export declare type JSONABI = {
18
18
  stateMutability?: 'view' | 'nonpayable' | string;
19
19
  type: 'function' | 'event' | 'constructor' | 'error';
20
20
  gas?: number;
21
- }[];
21
+ };
22
+ export declare type JSONABI = JSONABIArgument[];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "essential-eth",
3
3
  "description": "Ultralight JS library for Ethereum utilities",
4
- "version": "0.3.2",
4
+ "version": "0.3.3",
5
5
  "license": "MIT",
6
6
  "sideEffects": false,
7
7
  "main": "./lib/cjs/index.js",