Skip to content

Commit

Permalink
Implement static type interface for Contract (#4604)
Browse files Browse the repository at this point in the history
* Add contract static type support

* Update contract typing and add tests for erc20

* Add ERC721 type tests

* 🎨 Update docs and readme

* 📝 Updated changelog

* 🎨 Use PromiEvent implementation

* 🎨 Fix some reference typing

* 📝 Add compatibility docs

* 📝 Update the readme to list a reference to a limitation

* Add unit tests for some types

* 🎨 Fix the conflicts

* 🎨 Fix lint errors

* 🚨 Fix linting warnings

* Apply suggestions from code review

Co-authored-by: Wyatt Barnes <me@wyatt.email>

* Update code per feedback

Co-authored-by: Wyatt Barnes <me@wyatt.email>
  • Loading branch information
nazarhussain and spacesailor24 authored Dec 17, 2021
1 parent f3daf0e commit 3f72f52
Show file tree
Hide file tree
Showing 21 changed files with 1,389 additions and 117 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -343,3 +343,8 @@ Released with 1.0.0-beta.37 code base.
2. `ignoreLength` will be removed as an optional parameter for `privateKeyToAccount`
3. The `Wallet` no more supports address/number indexing. Have to use `wallet.get` instead.
4. `Wallet.create` function doesn't accepts `entropy` param

#### web3-eth-contract

1. Event logs does not support types for indexed properties but named properties supported.
2. Types for overloaded ABI functions are not yet supported.
4 changes: 2 additions & 2 deletions packages/web3-common/src/promi_event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ export type PromiseExecutor<T> = (
reject: (reason: unknown) => void,
) => void;

export class PromiEvent<ResolveType, T extends Web3EventMap>
extends Web3EventEmitter<T>
export class PromiEvent<ResolveType, EventMap extends Web3EventMap>
extends Web3EventEmitter<EventMap>
implements Promise<ResolveType>
{
private readonly _promise: Promise<ResolveType>;
Expand Down
5 changes: 3 additions & 2 deletions packages/web3-eth-abi/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@
"test:integration": "jest --config=./test/integration/jest.config.js"
},
"dependencies": {
"web3-utils": "^4.0.0-alpha.0",
"@ethersproject/abi": "^5.0.7",
"web3-common": "^1.0.0-alpha.0",
"@ethersproject/abi": "^5.0.7"
"web3-utils": "^4.0.0-alpha.0"
},
"devDependencies": {
"@types/jest": "^27.0.3",
Expand All @@ -41,6 +41,7 @@
"jest": "^27.3.1",
"jest-extended": "^1.1.0",
"jest-when": "^3.4.1",
"@humeris/espresso-shot": "^4.0.0",
"prettier": "^2.4.1",
"ts-jest": "^27.0.7",
"typescript": "^4.5.2"
Expand Down
4 changes: 2 additions & 2 deletions packages/web3-eth-abi/src/api/events_api.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { AbiError } from 'web3-common';
import { sha3Raw } from 'web3-utils';
import { JsonAbiEventFragment } from '../types';
import { AbiEventFragment } from '../types';
import { jsonInterfaceMethodToString, isAbiEventFragment } from '../utils';

/**
* Encodes the event name to its ABI signature, which are the sha3 hash of the event name including input types..
*/
export const encodeEventSignature = (functionName: string | JsonAbiEventFragment): string => {
export const encodeEventSignature = (functionName: string | AbiEventFragment): string => {
if (typeof functionName !== 'string' && !isAbiEventFragment(functionName)) {
throw new AbiError('Invalid parameter value in encodeEventSignature');
}
Expand Down
6 changes: 3 additions & 3 deletions packages/web3-eth-abi/src/api/functions_api.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { AbiError } from 'web3-common';
import { sha3Raw } from 'web3-utils';
import { isAbiFunctionFragment, jsonInterfaceMethodToString } from '../utils';
import { JsonAbiFunctionFragment } from '../types';
import { AbiFunctionFragment } from '../types';
import { encodeParameters } from './parameters_api';

/**
* Encodes the function name to its ABI representation, which are the first 4 bytes of the sha3 of the function name including types.
*/
export const encodeFunctionSignature = (functionName: string | JsonAbiFunctionFragment): string => {
export const encodeFunctionSignature = (functionName: string | AbiFunctionFragment): string => {
if (typeof functionName !== 'string' && !isAbiFunctionFragment(functionName)) {
throw new AbiError('Invalid parameter value in encodeFunctionSignature');
}
Expand All @@ -27,7 +27,7 @@ export const encodeFunctionSignature = (functionName: string | JsonAbiFunctionFr
* Encodes a function call from its json interface and parameters.
*/
export const encodeFunctionCall = (
jsonInterface: JsonAbiFunctionFragment,
jsonInterface: AbiFunctionFragment,
params: unknown[],
): string => {
if (!isAbiFunctionFragment(jsonInterface)) {
Expand Down
12 changes: 6 additions & 6 deletions packages/web3-eth-abi/src/api/logs_api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { HexString } from 'web3-utils';
import { JsonAbiParameter } from '../types';
import { AbiParameter } from '../types';
import { decodeParameter, decodeParametersWith } from './parameters_api';

const STATIC_TYPES = ['bool', 'string', 'int', 'uint', 'address', 'fixed', 'ufixed'];
Expand All @@ -8,28 +8,28 @@ const STATIC_TYPES = ['bool', 'string', 'int', 'uint', 'address', 'fixed', 'ufix
* Decodes ABI-encoded log data and indexed topic data
*/
export const decodeLog = <ReturnType extends Record<string, unknown>>(
inputs: Array<JsonAbiParameter>,
inputs: Array<AbiParameter>,
data: HexString,
topics: string | string[],
) => {
const clonedTopics = Array.isArray(topics) ? topics : [topics];

const clonedData = data ?? '';

const notIndexedInputs: Array<string | JsonAbiParameter> = [];
const indexedParams: Array<string | JsonAbiParameter> = [];
const notIndexedInputs: Array<string | AbiParameter> = [];
const indexedParams: Array<string | AbiParameter> = [];
let topicCount = 0;

// TODO check for anonymous logs?
for (const [i, input] of inputs.entries()) {
if (input.indexed) {
indexedParams[i] = STATIC_TYPES.some(s => input.type.startsWith(s))
? (decodeParameter(input.type, clonedTopics[topicCount]) as JsonAbiParameter)
? (decodeParameter(input.type, clonedTopics[topicCount]) as AbiParameter)
: clonedTopics[topicCount];

topicCount += 1;
} else {
notIndexedInputs[i] = input as unknown as JsonAbiParameter;
notIndexedInputs[i] = input as unknown as AbiParameter;
}
}

Expand Down
6 changes: 4 additions & 2 deletions packages/web3-eth-abi/src/api/parameters_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import { formatParam, isAbiFragment, mapTypes, modifyParams } from '../utils';
/**
* Should be used to encode list of params
*/
export const encodeParameters = (abi: AbiInput[], params: unknown[]): string => {
export const encodeParameters = (abi: ReadonlyArray<AbiInput>, params: unknown[]): string => {
try {
const modifiedTypes = mapTypes(Array.isArray(abi) ? abi : [abi]);
const modifiedTypes = mapTypes(
Array.isArray(abi) ? (abi as AbiInput[]) : ([abi] as unknown as AbiInput[]),
);
const modifiedParams: Array<unknown> = [];

for (const [index, param] of params.entries()) {
Expand Down
34 changes: 34 additions & 0 deletions packages/web3-eth-abi/src/number_map_type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
type _SolidityIndexRange =
| 1
| 2
| 3
| 4
| 5
| 6
| 7
| 8
| 9
| 10
| 11
| 12
| 13
| 14
| 15
| 16
| 17
| 18
| 19
| 20
| 21
| 22
| 25
| 26
| 27
| 28
| 29
| 30;

export type ConvertToNumber<
T extends string,
Range extends number = _SolidityIndexRange,
> = Range extends unknown ? (`${Range}` extends T ? Range : never) : never;
Loading

0 comments on commit 3f72f52

Please sign in to comment.