Skip to content

fix(assets): read a dotted unit in getByUnit, as fromUnit writes one - #710

Open
jorbuedo wants to merge 1 commit into
IntersectMBO:mainfrom
jorbuedo:slice/assets-dotted-unit
Open

jorbuedo wants to merge 1 commit into
IntersectMBO:mainfrom
jorbuedo:slice/assets-dotted-unit

Conversation

@jorbuedo

@jorbuedo jorbuedo commented Oct 22, 2026 •

Copy link
Copy Markdown

One of the four fixes asked for in the closing comment on
#619 — smallest fix plus its test, nothing else.

fromUnit and getByUnit disagree on a dotted unit, so getByUnit reads 0n for an asset that is there.

The defect

fromUnit accepts both policyId+assetName and policyId.assetName, and has since it was written.
getByUnit and fromRecord sliced at 56 characters unconditionally, so an asset built from a dotted
unit read back as 0n with the same string it was built from.

0n is why this was worth finding: it reads as "this asset is not here" rather than as a parse
failure, so a balance is simply wrong and nothing reports it.

The fix

One splitUnit now serves fromUnit, fromRecord and getByUnit. A dot cannot occur in hex, so
the two spellings stay unambiguous.

Contents

  • packages/evolution/src/Assets.ts — the shared parse.
  • packages/evolution/test/AssetsUnitSpelling.test.ts — both spellings through all three entry
    points, including the round-trip that answered 0n.
  • A patch changeset.

Deliberately left out: the two CIP-21 ordering hunks from the original PR.

Verification

All six CI tasks run locally off ci.yml against this base (lint, type-check, test, build,
check:dts, docgen): green. packages/evolution: 1920 passed, 75 skipped, 0 failed. The new test file fails on this base without the source change, so it does exercise it.
devnet's suite needs Docker and was not run here.

@solidsnakedev solidsnakedev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, the shared split fixes it, and undotted input reads back the same as before everywhere.

One change before merge: fromUnit now also accepts the joined spelling, policyId followed by assetName with no dot, which main rejected with a PolicyId ParseError. That's a good change, but please say so in the changeset and in the fromUnit JSDoc, and update the "First 56 chars are policyId" comment in fromRecord.

One more of the same kind, if you want it here: a unit in uppercase hex still reads back as 0n, because getByUnit compares it against lowercase hex. Main does the same. Lowercasing both halves in splitUnit fixes it, and a follow-up PR is fine too.

`fromUnit` accepts both `policyId+assetName` and `policyId.assetName`, and has
since it was written. `getByUnit` and `fromRecord` sliced at 56 characters
unconditionally, so an asset built from a dotted unit read back as `0n` with the
same string it was built from.

`0n` is why this was worth finding: it reads as "this asset is not here" rather
than as a parse failure, so a balance is simply wrong and nothing reports it.

The shared parse also lowercases both halves, for the same symptom from the same
cause: `Bytes.fromHex` takes either case, so `fromUnit` built an asset from an
uppercase unit that `getByUnit` answered `0n` for, because it compares against
`PolicyId.toHex`, which emits lowercase. `getUnits` still emits the canonical
lowercase spelling.

One `splitUnit` now serves all three. A dot cannot occur in hex, so the two
spellings stay unambiguous.

This widens what `fromUnit` accepts, which the changeset and its doc comment now
say: a dotless unit longer than 56 characters splits at 56, where before the
whole string was read as the policy id and rejected with a `PolicyId` parse
error -- for the very spelling `getUnits` emits.
@jorbuedo
jorbuedo force-pushed the slice/assets-dotted-unit branch from ef66c72 to 450705e Compare November 5, 2026 21:15
@jorbuedo

jorbuedo commented Nov 5, 2026

Copy link
Copy Markdown
Author

Both in, including the one you offered. Rebased onto fcf30275.

fromUnit now also accepts the joined spelling […] please say so in the changeset and in the fromUnit JSDoc, and update the "First 56 chars are policyId" comment in fromRecord

Done. The changeset now carries it as its own paragraph, marked as the one input this widens: a dotless unit longer than 56 characters splits at 56, where before the whole string was read as the policy id and rejected with a PolicyId parse error — for the very spelling getUnits emits. fromUnit's JSDoc says the same, and getByUnit's now names all three accepted forms. The stale fromRecord comment is gone; the addByHex line next to it stayed, since it is still true. There is a test for the joined spelling specifically, and it fails on main.

a unit in uppercase hex still reads back as 0n […] a follow-up PR is fine too

Taken here, since it is the same sentence of behaviour as the dotted case — a unit that built an asset must read that asset back, and a spelling is a spelling. Reproduced first: Bytes.fromHex accepts either case, so fromUnit built the asset from an uppercase unit and getByUnit then answered 0n for it, against lowercase-emitting PolicyId.toHex. splitUnit lowercases both halves, which fixes getByUnit, fromUnit and fromRecord together. getUnits still emits the canonical lowercase spelling, and there is a test pinning that. Say the word if you would rather read it as a separate PR and I will split it back out.

Gate, after a clean dist and .tsbuildinfo: lint, type-check, build, check:dts and docgen pass, and packages/evolution is 2030 passed / 75 skipped, 0 failed. Of the nine tests in the file, eight fail on unfixed main; the uppercase one also fails on the commit you reviewed.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants