Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

decoded event/calldata es6 proxies dissapear / hard to use #4523

Open
peersky opened this issue Dec 26, 2023 · 5 comments
Open

decoded event/calldata es6 proxies dissapear / hard to use #4523

peersky opened this issue Dec 26, 2023 · 5 comments
Assignees
Labels
investigate Under investigation and may be a bug. v5 Issues regarding legacy-v5

Comments

@peersky
Copy link

peersky commented Dec 26, 2023

Ethers Version

5.7.2

Search Terms

ES6 proxy,event,calldata,parsed data dissapears

Describe the Problem

Whenever you call or parse event from chain with ethers, if returned is object, and ethers had ABI provided - it will return object of type array with object keys per ABI description added as ES6 Proxies.
This allows us to do something like myContract['myStateMethod'].stateVariable instead of myContract['myStateMethod'][0]

However this becomes a hell if you want to use ethersjs in say frontend code (And I do have reasons to choose it over wagmi).

In such case caching libraries such as react-query will simply lose all of these proxies during refetch.

Converting ES6 proxies in to objects is a hell of a job and cannot be addressed properly outside of the ethersjs itself.

Proposed solution:
instead of proxies return proper objects.

Code Snippet

No response

Contract ABI

No response

Errors

No response

Environment

Browser (Chrome, Safari, etc), React Native/Expo/JavaScriptCore

Environment (Other)

No response

@peersky peersky added investigate Under investigation and may be a bug. v5 Issues regarding legacy-v5 labels Dec 26, 2023
@ricmoo
Copy link
Member

ricmoo commented Dec 26, 2023

There aren’t any Proxy objects in v5. It is designed for ES3, for which Proxy isn’t present.

In v6 though, contract uses Procy, which is required to be able to trap arbitrary names and signatures for lookup (since it may need to further refine on input parameters).

There should be a way to wrap a proxy; I believe Vue uses something called markRaw which will protect it.

But in v5, there definitely isn’t any semblance of Proxy…

@peersky
Copy link
Author

peersky commented Dec 27, 2023

Hmm maybe Im missing something but In any way I had to write code that converts arrays with non-number keys in to objects to counter issue I was facing, because react-query refetch would wipe out all proxies.
Im using tanstack react query with React, so no Vue methods availible.

@ricmoo
Copy link
Member

ricmoo commented Dec 27, 2023

Hmmm.. In v5, they aren't a Proxy, but they are an Array with properties set. So, something like:

a = [ 1, 2, 3 ];
a.first = 1;
a.second = 2;
a.third = 3;

// In the event of a deferred error, like a bad string
a = [ "a", "b"]
Object.defineProperty(a, 2, { get: () => { throw new Error("invalid string"); } } );
a.first = "a";
b.second = "b";
Object.defineProperty(a, "third", { get: () => { throw new Error("invalid string"); } } );

Maybe some aspect of that was confusing React?

This is also the reason Result objects moved to Proxy in v6. Is there a standard way for React components to make themselves serializable, btw?

@jxom
Copy link

jxom commented Dec 30, 2023

Wagmi had encountered this issue in the past when we integrated with Ethers. It is because arrays with assigned named properties (or vice-versa) aren’t serializable in JS itself (it serializes to a “plain” array and omits the named properties). To overcome this issue, we also had to store a reference to the ABI signature and parse + decode the serialized plain array: https://github.com/wevm/wagmi/blob/fc10ebe659dd5f3b7a8e00581f094652280a779b/packages/core/src/utils/parseContractResult.ts

Usage with React Query here: https://github.com/wevm/wagmi/blob/fc10ebe659dd5f3b7a8e00581f094652280a779b/packages/react/src/hooks/contracts/useContractRead.ts#L161

@peersky
Copy link
Author

peersky commented Jan 12, 2024

I solved this in my application level by wrapping any ethers js decoding in to recursive function that returns objects instead of mixed array/object. Im also doing hard copies from all values to be double safe there are no proxy objects left.

It might be not the most elegant solution, I haven't solved typescript typings completely, and technically breaks the rpc specifications (must return arrays), but it works for me and I can use it further in my code with great comfort:

export const deproxify = <T extends any>(object: T) => {
    if (typeof object == "string") return object;
    if (
        !!object &&
        Object.prototype.hasOwnProperty.call(object, "_isBigNumber")
    ) {
        return ethers.BigNumber.from((" " + object.toString()).slice(1)) as T;
    }
    if (!object) return object;
    let isMixedArrayObject =
        Array.isArray(object) &&
        Object.keys(object).some((key) => isNaN(Number(key)));
    let result = Array.isArray(object) && !isMixedArrayObject ? [] : {};
    Object.keys(object).forEach((key) => {
        if (isMixedArrayObject && !isNaN(Number(key))) {
            // do nothing -> remove array elements and create object
        }
        // else - make sure we do hard copy of values
        else if (typeof object[key] === "string")
            result[key] = (" " + object[key]).slice(1);
        else if (typeof object[key] === "number")
            result[key] = Number(object[key]);
        else if (typeof object[key] === "boolean")
            result[key] = Boolean(object[key]);
        else if (object[key] === null) result[key] = null;
        else if (object[key] === undefined) result[key] = undefined;
        else if (object[key]?._isBigNumber) {
            result[key] = ethers.BigNumber.from(
                (" " + object[key].toString()).slice(1)
            );
        } else result[key] = deproxify(object[key]);
    });
    return result as T;
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
investigate Under investigation and may be a bug. v5 Issues regarding legacy-v5
Projects
None yet
Development

No branches or pull requests

3 participants