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

Add ownKeys trap to Result

Open drortirosh opened this issue 2 years ago • 4 comments

Ethers Version

6.6.0

Search Terms

abi, decode

Describe the Problem

The following sample (executable in both ethers 5.7 and ethers 6.x), shows how AbiCoder.decode of v6 returns the correct value, but removes type information. The returned struct is only valid as a 2-entry array, but field names are lost.

v5 returned data:

decoded struct= [
  BigNumber { _hex: '0x01', _isBigNumber: true },
  BigNumber { _hex: '0x02', _isBigNumber: true },
  stake: BigNumber { _hex: '0x01', _isBigNumber: true },
  unstakeDelaySec: BigNumber { _hex: '0x02', _isBigNumber: true }
]
decomposed struct: {
  '0': BigNumber { _hex: '0x01', _isBigNumber: true },
  '1': BigNumber { _hex: '0x02', _isBigNumber: true },
  stake: BigNumber { _hex: '0x01', _isBigNumber: true },
  unstakeDelaySec: BigNumber { _hex: '0x02', _isBigNumber: true }
}
decomposed manually: {
  stake: BigNumber { _hex: '0x01', _isBigNumber: true },
  unstakeDelaySec: BigNumber { _hex: '0x02', _isBigNumber: true }
}

(that is, can be parsed both as an array and as a struct)

v6 returned data:

decoded struct= Result(2) [ 1n, 2n ]
decomposed struct: { '0': 1n, '1': 2n }
decomposed manually: { stake: 1n, unstakeDelaySec: 2n }

(that is, it is a 2-element array, with no field names - they if referenced manually, the value by field can be extracted)

The following sample can be executed as both ethers v5 and ethers v6:

Code Snippet

// save the following code as "testDecode.js" and execute with "node testDecode.js"
// works with either ethers v5.7 and ethers v6.6
// ethers v6 decodes the struct into an array, and lose the original field names.
// ethers v5 returns a struct can can be used both as an array AND as a struct (mapping)
const ethers = require('ethers')
const { AbiCoder } = require('ethers')

function run () {
  let coder
  //ethers v6:
  if (AbiCoder) {
    coder = AbiCoder.defaultAbiCoder()
  } else {
    //ethers v5:
    coder = ethers.utils.defaultAbiCoder
  }

  const info = {
    stake: 1,
    unstakeDelaySec: 2
  }
  const stakeInfoType = {
    name: 'stakeInfo',
    components: [
      {
        internalType: 'uint256',
        name: 'stake',
        type: 'uint256'
      },
      {
        internalType: 'uint256',
        name: 'unstakeDelaySec',
        type: 'uint256'
      }
    ],
    internalType: 'struct StakeInfo',
    type: 'tuple'
  } 

  console.log('input struct:', info)
  const enc = coder.encode([stakeInfoType], [info])
  console.log('enc=', enc)
  const dec = coder.decode([stakeInfoType], enc)[0]
  console.log('decoded struct=', dec)
  console.log('decomposed struct:',{...dec })
  const {stake, unstakeDelaySec} = dec 
  console.log('decomposed manually:',{ stake, unstakeDelaySec })

}

run()

Contract ABI

No response

Errors

No response

Environment

node.js (v12 or newer), Hardhat

Environment (Other)

No response

drortirosh avatar Jun 22 '23 13:06 drortirosh

Can I work on this issue ?

vishesh0123 avatar Jun 24 '23 11:06 vishesh0123

UPDATED: The returned object DOES contain internally the field names - it is just very shy about it, and doesn't expose them. caller must know field names and ask them explicitly, but { ...dec } decompose operator doesn't return the field names, only by numerical indices

drortirosh avatar Jun 25 '23 15:06 drortirosh

Yes, all named parameters are available on the Result object. You may also use result.toObject() to flatten it into a put JavaScript object.

The keys aren't visible because getters are not enumerable. I will research whether adding the ownKeys trap to make the names show up to the Object.keys() function, which should make console.log more noisy, which sounds like what you want.

ricmoo avatar Jun 27 '23 14:06 ricmoo

is there any update here?

emretepedev avatar Nov 07 '23 13:11 emretepedev