Skip to content

fix(satp-hermes): ERC-6909/ERC-721 uniqueDescriptor truncates large uint256 token IDs #4703

Description

@lukacoff

What happened?

Description

UniqueTokenID (the branded type behind Asset.uniqueDescriptor,
used for ERC-6909 multi-token type IDs and ERC-721 NFT IDs) is
currently branded as number:

https://github.com/hyperledger-cacti/cacti/blob/main/packages/cactus-plugin-satp-hermes/src/main/typescript/cross-chain-mechanisms/bridge/ontology/assets/asset.ts

Since on-chain token IDs are uint256, any value above
Number.MAX_SAFE_INTEGER silently loses precision when passed through
Number(), corrupting the token ID used for cross-chain asset
transfer — e.g. in the bridge execution layer's LOCK/UNLOCK paths and
in besu-leaf.ts / ethereum-leaf.ts's getAsset().

Proposed fix

Rebrand UniqueTokenID as bigint and remove the Number()
conversions on the bridge execution layer / EVM leafs, updating the
affected tests accordingly.

Expected behavior

Asset uniqueDescriptor / tokenId values representing on-chain uint256 token IDs (ERC-6909 type IDs, ERC-721 NFT IDs) should preserve full precision throughout the bridge — from the wire format (proto string) through the internal Asset representation to the contract call — even for values larger than Number.MAX_SAFE_INTEGER (2^53 - 1).

Steps to reproduce

  1. Note that UniqueTokenID is defined as Brand<number, "UniqueTokenID"> in packages/cactus-plugin satp-hermes/src/main/typescript/cross-chain-mechanisms/bridge/ontology/assets/asset.ts
  2. In besu-leaf.ts / ethereum-leaf.ts's getAsset(), the on-chain uint256 token id (returned as a string) is converted like this: uniqueDescriptor: Number(token.amount) as UniqueTokenID
  3. Reproduce the precision loss directly in a Node REPL:
    const onChainTokenId = "9007199254740993"; // 2^53 + 1
    console.log(Number(onChainTokenId));
    // -> 9007199254740992 (wrong: lost the last bit of precision)
  4. The same pattern (Number(...) as UniqueTokenID) is used in satp-bridge-execution-layer-implementation.ts for the LOCK/UNLOCK paths of NonFungibleAsset, silently corrupting the token id used to identify the asset being locked/unlocked cross-chain.

Cacti version or commit SHA

8747ce1

Environment

node -v && uname -srm
v24.18.0
Linux 7.0.0-30-generic x86_64

Affected package(s) / connector(s)

No response

Logs or stack traces

Additional context

No response

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Triage_ReadyIssue approved to be worked onbugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions