From 03cbebe8b0b982fc07ff2062b8e7dc1669ed9533 Mon Sep 17 00:00:00 2001 From: Kyle Cox Date: Fri, 9 Oct 2026 01:42:15 +0000 Subject: [PATCH] fix(core): track the minting token by reference in TokensCollection --- .changeset/tidy-minting-tracks.md | 5 ++ .../collections/tokensCollection.spec.ts | 46 +++++++++++++++++++ .../models/collections/tokensCollection.ts | 19 ++++++-- 3 files changed, 65 insertions(+), 5 deletions(-) create mode 100644 .changeset/tidy-minting-tracks.md diff --git a/.changeset/tidy-minting-tracks.md b/.changeset/tidy-minting-tracks.md new file mode 100644 index 00000000..35822cd9 --- /dev/null +++ b/.changeset/tidy-minting-tracks.md @@ -0,0 +1,5 @@ +--- +"@fleet-sdk/core": patch +--- + +fix(core): keep TokensCollection minting metadata attached to the minting token when other tokens are inserted or removed, instead of reporting another token's amount, reporting a minting token that was removed, or throwing a TypeError diff --git a/packages/core/src/models/collections/tokensCollection.spec.ts b/packages/core/src/models/collections/tokensCollection.spec.ts index 2daf2f82..01271fec 100644 --- a/packages/core/src/models/collections/tokensCollection.spec.ts +++ b/packages/core/src/models/collections/tokensCollection.spec.ts @@ -276,6 +276,52 @@ describe("Tokens collection", () => { }).toThrow(NotFoundError); }); + it("Should keep tracking the minting token after a preceding token is removed", () => { + const collection = new TokensCollection(); + collection.add({ tokenId: tokenA, amount: 50n }); + collection.mint({ amount: 100n, name: "test" }); + collection.add({ tokenId: tokenB, amount: 10n }); + + collection.remove(tokenA); + + expect(collection.minting?.amount).toEqual(100n); + expect(collection.minting?.name).toEqual("test"); + }); + + it("Should keep tracking the minting token after a token is inserted before it", () => { + const collection = new TokensCollection(); + collection.add({ tokenId: tokenA, amount: 50n }); + collection.mint({ amount: 100n, name: "test" }); + + collection.add({ tokenId: tokenB, amount: 10n }, { index: 0 }); + + expect(collection.minting?.amount).toEqual(100n); + expect(collection.minting?.name).toEqual("test"); + }); + + it("Should not report a minting token after the minting token is removed", () => { + const collection = new TokensCollection(); + collection.mint({ amount: 100n, name: "test" }); + collection.add({ tokenId: tokenA, amount: 50n }); + + collection.remove(0); + + expect(collection.minting).toBeUndefined(); + expect(collection.toArray()).toEqual([{ tokenId: tokenA, amount: 50n }]); + expect(() => collection.mint({ amount: 1n, name: "test_2" })).not.toThrow(); + expect(collection.minting?.name).toEqual("test_2"); + }); + + it("Should not throw when reading minting after the last token is removed", () => { + const collection = new TokensCollection(); + collection.add({ tokenId: tokenA, amount: 50n }); + collection.mint({ amount: 100n, name: "test" }); + + collection.remove(tokenA); + + expect(collection.minting?.amount).toEqual(100n); + }); + it("Should throw when trying to remove by an out of bounds index", () => { const collection = new TokensCollection(); collection.add([ diff --git a/packages/core/src/models/collections/tokensCollection.ts b/packages/core/src/models/collections/tokensCollection.ts index fa8b10e0..0fdd3c93 100644 --- a/packages/core/src/models/collections/tokensCollection.ts +++ b/packages/core/src/models/collections/tokensCollection.ts @@ -23,7 +23,7 @@ export type OutputToken = { amount: T; }; -type MintingData = { index: number; metadata: NewToken }; +type MintingData = { item: OutputToken; metadata: NewToken }; export class TokensCollection extends Collection, OutputToken> { #minting: MintingData | undefined; @@ -38,10 +38,10 @@ export class TokensCollection extends Collection, OutputToke } public get minting(): NewToken | undefined { - if (!this.#minting) return undefined; + if (!this.#minting || !this._items.includes(this.#minting.item)) return undefined; return { ...this.#minting.metadata, - amount: this._items[this.#minting.index].amount + amount: this.#minting.item.amount }; } @@ -71,12 +71,21 @@ export class TokensCollection extends Collection, OutputToke } public mint(token: NewToken): number { - if (isDefined(this.#minting)) { + if (isDefined(this.minting)) { throw new FleetError("Only one minting token is allowed per transaction."); } + const previousLength = this.length; const len = super.add({ tokenId: token.tokenId, amount: token.amount }); - this.#minting = { index: len - 1, metadata: token }; + // If the amount was summed into an existing item, the minting item is that + // item; otherwise it is the newly appended one. Track the item itself, not + // its index, so later insertions and removals cannot detach the minting + // metadata from the token it belongs to. + const item = + len > previousLength + ? this._items[len - 1] + : (this._items.find((x) => x.tokenId === token.tokenId) ?? this._items[len - 1]); + this.#minting = { item, metadata: token }; return this.length; }