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
- 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
- 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
- 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)
- 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
What happened?
Description
UniqueTokenID(the branded type behindAsset.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 aboveNumber.MAX_SAFE_INTEGERsilently loses precision when passed throughNumber(), corrupting the token ID used for cross-chain assettransfer — e.g. in the bridge execution layer's LOCK/UNLOCK paths and
in
besu-leaf.ts/ethereum-leaf.ts'sgetAsset().Proposed fix
Rebrand
UniqueTokenIDasbigintand remove theNumber()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
UniqueTokenIDis defined asBrand<number, "UniqueTokenID">in packages/cactus-plugin satp-hermes/src/main/typescript/cross-chain-mechanisms/bridge/ontology/assets/asset.tsconst onChainTokenId = "9007199254740993"; // 2^53 + 1
console.log(Number(onChainTokenId));
// -> 9007199254740992 (wrong: lost the last bit of precision)
Cacti version or commit SHA
8747ce1
Environment
node -v && uname -srm v24.18.0 Linux 7.0.0-30-generic x86_64Affected package(s) / connector(s)
No response
Logs or stack traces
Additional context
No response