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

Add API to ABI coder for error recovery #800

Closed
ricmoo opened this issue Apr 25, 2020 · 5 comments
Closed

Add API to ABI coder for error recovery #800

ricmoo opened this issue Apr 25, 2020 · 5 comments
Labels
enhancement New feature or improvement.

Comments

@ricmoo
Copy link
Member

ricmoo commented Apr 25, 2020

When decoding events and functions for an ABI, various errors can occur, which are currently swallowed.

It would be nice if there was a way to gracefully recover from errors.

Errors to consider:

  • Invalid UTF-8 string data (this is the most common); if a
  • Insufficient data; this can occur when there is a mismatch in which event arguments are indexed or a function return type is incorrect

There is still some effort to figure out the best way to bubble these up to higher level libraries, but adding some of the functionality to the low-level coder should be fairly straight forward.

Possible Solutions:

  1. Allow an errorFunc function which will be called on errors to provide an alternate value
  2. Place a getter on the Result object that will throw when accessed, but with an error that contains the problematic data
  3. Throw an error, with a recover function which will allow continuing decoding after the state has been corrected

I'll be experimenting, and please feel free to make additional suggestions. :)

@ricmoo ricmoo added enhancement New feature or improvement. next version (v5) on-deck This Enhancement or Bug is currently being worked on. labels Apr 25, 2020
@ricmoo
Copy link
Member Author

ricmoo commented Apr 25, 2020

I've added initial support for option 2 above.

Basically, if you have a signature like "event something(string bar, uint bar)", you willet back a Result object.

Imagine that bar has overlong code units in it and decode the Result object:

const utils = ethers.utils;

// This works fine:
console.log(result.bar);

// This would throw:
console.log(result.foo);


// However, there error contains sufficient data to recover:
try {
    console.log(result.foo);
} catch (error) {
    const de = error.decoderError;
    // console.log(de);
    // Error: "phrase: invalid codepoint at offset 0; bad codepoint prefix (argument="bytes", value={"0":255,"1":101,"2":108,"3":108,"4":111,"5":32,"6":87,"7":111,"8":114,"9":108,"10":100}, code=INVALID_ARGUMENT, version=strings/5.0.0-beta.136)" {
    //    reason: 'invalid codepoint at offset 0; bad codepoint prefix',
    //    code: 'INVALID_ARGUMENT',
    //    argument: 'bytes',
    //    value: Uint8Array [ 255, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100 ],
    //    baseType: 'string',
    //    name: 'phrase',
    //    type: 'string'
    // }
    const replaced = utils.toUtf8String(de.value, utils.Utf8ErrorFuncs.ignore);
    console.log(replaced);
}

So, non-exceptional cases can work like normal, and if someone cares about badly formatted data, they can detect it and correct it.

Thoughts?

@ricmoo
Copy link
Member Author

ricmoo commented Apr 25, 2020

(oh also, if there is bad data, but in a part that the person doesn't care about, they never even need to know)

@ricmoo
Copy link
Member Author

ricmoo commented Apr 26, 2020

Some quick notes on trying to adopt this. Framework developers that wish to process errors in a specific way may need some introspection, especially if apply is used.

Consider the following:

// Simulate a Result object with a bad item at index 2
a = [ 1, 2, 3 ]
Object.defineProperty(a, 2, { get: () => { throw new Error("foo"); } })
// [ 1, 2, [Getter] ]

//////////
// Safe

Object.keys(a)
// [ '0', '1', '2' ]

function foo() { }
foo.call(null, a)

a[1]

for (let i = 0; i < a.length; i++) { console.log(i); }

//////////
// Unsafe (anything that requires dereferencing the bad value)

Object.values(a)

// Including apply
function foo() { }
foo.apply(null, a)

a[2]

for (let i = 0; i < a.length; i++) { console.log(a[i]); }

@ricmoo
Copy link
Member Author

ricmoo commented May 7, 2020

This has been added. :)

@ricmoo ricmoo closed this as completed May 7, 2020
@ricmoo ricmoo removed the on-deck This Enhancement or Bug is currently being worked on. label May 7, 2020
michaeltout pushed a commit to michaeltout/ethers.js that referenced this issue Aug 23, 2020
@progsmile
Copy link

progsmile commented Jan 28, 2022

@ricmoo I'm still facing this issue with attempts to return string to js.

    function hashCollaborators(Collaborator[] memory collaborators) public view returns(string memory) {
        bytes32 digest = keccak256(abi.encode(collaborators));
        return string(abi.encode(digest));
    }

And your approach with try-catch ethers.utils.toUtf8String(error.value, ethers.utils.Utf8ErrorFuncs.ignore) works!
But, is there better solution?
Thanks in advance!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or improvement.
Projects
None yet
Development

No branches or pull requests

2 participants