Open peersky opened 10 months ago
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…
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.
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?
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
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;
};
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 ofmyContract['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