ethers.js icon indicating copy to clipboard operation
ethers.js copied to clipboard

`invalid BytesLike value` on `createRandom` in jest

Open mattiaferrari02 opened this issue 1 year ago • 9 comments

Ethers Version

6.6.2

Search Terms

No response

Describe the Problem

Calling Wallet.createRandom() in jest environment causes an error, the same thing applies to when I try to create a Wallet using a mnemonic phrase Wallet.fromPhrase(phrase)

Code Snippet

describe("evmTests", () => {
  test("setup", async() => {
    const w = Wallet.createRandom()
    expect(1).toBe(1)
  })
})

Contract ABI

No response

Errors

TypeError: invalid BytesLike value (argument="value", value={ "data": [ 136, 147, 113, 168, 255, 56, 15, 137, 131, 87, 45, 124, 173, 28, 199, 107, 61, 103, 192, 19, 159, 170, 93, 228, 76, 59, 243, 25, 29, 144, 63, 194 ], "type": "Buffer" }, code=INVALID_ARGUMENT, version=6.6.7)

Environment

No response

Environment (Other)

No response

mattiaferrari02 avatar Sep 14 '23 09:09 mattiaferrari02

Hey, i think i have a similar problem when using ethers.sha256 inside a test. Interestingly, this only happens when using the jsdom test environment.

ethers.sha256('0x0001')

I suspect that this happens because the hexlify method expects a BytesLike: Screenshot 2023-09-15 at 15 46 26

but the _sha256 implementation returns a Buffer: Screenshot 2023-09-15 at 15 45 15

From looking at the ethers code, I figured that i could register a slightly adjusted sha256 implementation as a workaround in my test setup. I am not sure if this has any unintended side-effects, so please use with caution:

// https://github.com/ethers-io/ethers.js/issues/4365#issuecomment-1721313836
ethers.sha256.register((data) => {
  return new Uint8Array(crypto.createHash('sha256').update(data).digest());
});

niklasnatter avatar Sep 15 '23 13:09 niklasnatter

A Buffer is a sub-class of Uint8Array though. Does Jest perhaps replace the Buffer class with its own? Can you try console.log(Buffer.from([]) instanceof Uint8Array)?

ricmoo avatar Sep 15 '23 18:09 ricmoo

@ricmoo I tried logging that in jest environment, it gives out false

mattiaferrari02 avatar Sep 18 '23 07:09 mattiaferrari02

@ricmoo I tried logging that in jest environment, it gives out false

I can confirm that this is the case with testEnvironment: 'jsdom'. It prints out true if i remove the testEnvironment configuration in my jest config.

I suspect that the jest-environment-jsdom environment somehow changes the Buffer or Uint8Array implementation? Unfortunately I dont really have an idea how to debug this further.

niklasnatter avatar Sep 18 '23 10:09 niklasnatter

Okay I investigated this a bit more and it seems like this is a known problem with jest and how it uses executions contexts: https://github.com/jestjs/jest/issues/2549, https://github.com/jestjs/jest/issues/7780#issuecomment-669828353, https://github.com/jestjs/jest/issues/14363#issuecomment-1651658124

As far as I understand, there are two sets of globals, one in the Node.js context and one in the Jest context. I suspect that this line in the jest-environment-jsdom package sets the Buffer from the Node.js context into the Jest context, but it does not do the same for Uint8Array. The instanceof check then fails because the passed buffer instance extends the Uint8Array from the Node.js context, but ethers checks against the Uint8Array from the Jest context: https://github.com/jestjs/jest/blob/81cc9f07e8f0c664f8664d22feab9bec2a226882/packages/jest-environment-jsdom/src/index.ts#L85

The problem does not appear when using jest-environment-node (which is the default environment) because this environment also sets the Uint8Array of the Jest context: https://github.com/jestjs/jest/blob/81cc9f07e8f0c664f8664d22feab9bec2a226882/packages/jest-environment-node/src/index.ts#L134-L139

niklasnatter avatar Sep 18 '23 12:09 niklasnatter

Okay but the test that I'm writing require jest-environment-jsdom

mattiaferrari02 avatar Sep 18 '23 12:09 mattiaferrari02

if someone has the same issue, yet. I was able to workaround it by:

  1. Creating a jest.setup.ts;
Object.defineProperty(Uint8Array, Symbol.hasInstance, {
  value(potentialInstance: unknown) {
    return this === Uint8Array
      ? Object.prototype.toString.call(potentialInstance) ===
          '[object Uint8Array]'
      : Uint8Array[Symbol.hasInstance].call(this, potentialInstance);
  },
});
  1. Adding the setup to jest.config.ts on prop setupFilesAfterEnv;
setupFilesAfterEnv: ['./jest.setup.ts'],

luizstacio avatar Oct 31 '23 17:10 luizstacio

Oh wow. So, in Jest instanceof Uint8Array doesn’t work??

ricmoo avatar Oct 31 '23 17:10 ricmoo

Same error is also occurring while calling any contract call inside of Vitest unit test suite.

Workaround is not to use environment: 'jsdom' but instead environment: 'happy-dom' inside of defineConfig({ test: { environment: '...',

Btw. This issue might be related with https://github.com/ethers-io/ethers.js/issues/4436

jurosh avatar Dec 12 '23 22:12 jurosh