ethers-io / ethers.js

Complete Ethereum library and wallet implementation in JavaScript.
https://ethers.org/
MIT License
7.95k stars 1.85k forks source link

Contract event listener args not parsed correctly #4109

Open therealjmj opened 1 year ago

therealjmj commented 1 year ago

Ethers Version

6.4.0

Search Terms

No response

Describe the Problem

The 'args' aren't parsing correctly for polling JsonRpcProviders.

See logic here:

https://github.com/ethers-io/ethers.js/blob/c785c1e5153d5a85a78f90f177577f2c8df15529/src.ts/contract/contract.ts#L538-L544

It seems like the logic here, should change to:

if (foundFragment) {
                const _foundFragment = foundFragment;
                const args = contract.interface.decodeEventLog(foundFragment, log.data, log.topics);
                emit(contract, event, args, (listener) => {
                    return new wrappers_js_1.ContractEventPayload(contract, listener, event, _foundFragment, log);
                });
            }

This DOES fix the issue - event args pass to the listeners. However, this results in "Proxy" Result objects, that are not decoded and typed correctly.

Code Snippet

Expected:

await this.contract.on(
      this.contract.filters.Shield(),
      async (
        treeNumber: bigint,
        startPosition: bigint,
        commitments: CommitmentPreimageStructOutput[],
        shieldCiphertext: ShieldCiphertextStructOutput[],
        fees: bigint[],
        log: ShieldEvent.Log,
      ) => {
        ...
      },
    );

Actual:

await this.contract.on(
      this.contract.filters.Shield(),
      async (
        treeNumber: bigint,
        startPosition: bigint,
        commitments: Proxy,
        shieldCiphertext: Proxy,
        fees: Proxy,
        log: ShieldEvent.Log,
      ) => {
        ...
      },
    );


### Contract ABI

_No response_

### Errors

_No response_

### Environment

node.js (v12 or newer)

### Environment (Other)

_No response_
therealjmj commented 1 year ago

Besides the bug with fix above, I suppose the main question is this - What's the best way to parse from decoded Result (nested Proxy arrays) to a JavaScript object or array?

therealjmj commented 1 year ago

It's not type-safe, but definitely functional:

const recursivelyDecodeResult = (result: Result): any => {
    if (typeof result !== 'object') {
      // Raw primitive value
      return result;
    }
    try {
      const obj = result.toObject();
      if (obj._) {
        throw new Error('Decode as array, not object');
      }
      Object.keys(obj).forEach((key) => {
        obj[key] = recursivelyDecodeResult(obj[key]);
      });
      return obj;
    } catch (err) {
      // Result is array.
      return result
        .toArray()
        .map((item) => recursivelyDecodeResult(item as Result));
    }
  };
sam-goldman commented 1 year ago

Besides the bug with fix above, I suppose the main question is this - What's the best way to parse from decoded Result (nested Proxy arrays) to a JavaScript object or array?

Seconded. It'd be super useful for Result objects to have a method that recursively converts the Result to a JavaScript object or array. Basically the same as Result.toObject(), except it resolves things recursively for nested data structures. I'm currently using something similar to the recursivelyDecodeResult function that @therealjmj posted above.