From 1a1e265ea6fca000a9688141a730aa75c2b68f1a Mon Sep 17 00:00:00 2001 From: hinsonsidan Date: Tue, 13 Jan 2026 18:51:54 +0800 Subject: [PATCH 1/2] feat: add sort value in MeshValue --- packages/mesh-common/src/data/value.ts | 72 +++++++++++++++-- .../test/data/value/convertor.test.ts | 77 +++++++++++++++++++ 2 files changed, 142 insertions(+), 7 deletions(-) diff --git a/packages/mesh-common/src/data/value.ts b/packages/mesh-common/src/data/value.ts index 2553f4c1f..5d3d4d689 100644 --- a/packages/mesh-common/src/data/value.ts +++ b/packages/mesh-common/src/data/value.ts @@ -16,6 +16,13 @@ import { tokenName, } from "./json"; +/** + * Compare two hex strings by byte ordering (lexicographic comparison) + * Works because hex digits 0-9, a-f are in ascending ASCII order + */ +const compareByteOrder = (a: string, b: string): number => + a < b ? -1 : a > b ? 1 : 0; + /** * Aiken alias * Value is the JSON representation of Cardano data Value @@ -60,6 +67,29 @@ export class MeshValue { this.value = value; } + /** + * Sort a Value (JSON representation) by policy ID then token name + * @param plutusValue The Value to sort + * @returns Sorted Value + */ + static sortValue = (plutusValue: Value): Value => { + const sortedPolicies = [...plutusValue.map].sort((a, b) => + compareByteOrder(a.k.bytes, b.k.bytes), + ); + + const sortedMap = sortedPolicies.map((policyEntry) => { + const sortedTokens = [...policyEntry.v.map].sort((a, b) => + compareByteOrder(a.k.bytes, b.k.bytes), + ); + return { + k: policyEntry.k, + v: { map: sortedTokens }, + }; + }); + + return { map: sortedMap }; + }; + /** * Converting assets into MeshValue * @param assets The assets to convert @@ -296,19 +326,20 @@ export class MeshValue { /** * Convert the MeshValue object into Cardano data Value in Mesh Data type + * Entries are sorted by byte ordering of policy ID, then token name */ toData = (): MValue => { - const valueMap: MValue = new Map(); + const unsortedMap: Map> = new Map(); this.toAssets().forEach((asset) => { const sanitizedName = asset.unit.replace("lovelace", ""); const policy = sanitizedName.slice(0, 56) || ""; const token = sanitizedName.slice(56) || ""; - if (!valueMap.has(policy)) { - valueMap.set(policy, new Map()); + if (!unsortedMap.has(policy)) { + unsortedMap.set(policy, new Map()); } - const tokenMap = valueMap.get(policy)!; + const tokenMap = unsortedMap.get(policy)!; const quantity = tokenMap?.get(token); if (!quantity) { tokenMap.set(token, BigInt(asset.quantity)); @@ -317,11 +348,30 @@ export class MeshValue { } }); + // Sort by policy ID (byte ordering), then by token name (byte ordering) + const sortedPolicies = Array.from(unsortedMap.keys()).sort(compareByteOrder); + const valueMap: MValue = new Map(); + + sortedPolicies.forEach((policy) => { + const unsortedTokenMap = unsortedMap.get(policy)!; + const sortedTokens = Array.from(unsortedTokenMap.keys()).sort( + compareByteOrder, + ); + const sortedTokenMap = new Map(); + + sortedTokens.forEach((token) => { + sortedTokenMap.set(token, unsortedTokenMap.get(token)!); + }); + + valueMap.set(policy, sortedTokenMap); + }); + return valueMap; }; /** * Convert the MeshValue object into a JSON representation of Cardano data Value + * Entries are sorted by byte ordering of policy ID, then token name * @returns Cardano data Value in JSON */ toJSON = (): Value => { @@ -345,11 +395,19 @@ export class MeshValue { } }); - Object.keys(valueMap).forEach((policy) => { + // Sort policies by byte ordering + const sortedPolicies = Object.keys(valueMap).sort(compareByteOrder); + + sortedPolicies.forEach((policy) => { const policyByte = currencySymbol(policy); - const tokens: [TokenName, Integer][] = Object.keys(valueMap[policy]!).map( - (name) => [tokenName(name), integer(valueMap[policy]![name]!)], + // Sort tokens by byte ordering + const sortedTokenNames = Object.keys(valueMap[policy]!).sort( + compareByteOrder, ); + const tokens: [TokenName, Integer][] = sortedTokenNames.map((name) => [ + tokenName(name), + integer(valueMap[policy]![name]!), + ]); const policyMap = assocMap(tokens); valueMapToParse.push([policyByte, policyMap]); diff --git a/packages/mesh-common/test/data/value/convertor.test.ts b/packages/mesh-common/test/data/value/convertor.test.ts index 0ce51cebf..673e8700f 100644 --- a/packages/mesh-common/test/data/value/convertor.test.ts +++ b/packages/mesh-common/test/data/value/convertor.test.ts @@ -17,6 +17,54 @@ import { import { mockUnit } from "./common"; +const unsortedValue: Value = assocMap([ + [currencySymbol(""), assocMap([[tokenName(""), integer(200000000)]])], + [ + currencySymbol("c69b981db7a65e339a6d783755f85a2e03afa1cece9714c55fe4c913"), + assocMap([[tokenName("5553444d"), integer(200000000)]]), + ], + [ + currencySymbol("a2818ba06a88bb6c08d10f4f9b897c09768f28d274093628ad7086fc"), + assocMap([[tokenName("484f534b59"), integer(100000000)]]), + ], + [ + currencySymbol("82e46eb16633bf8bfa820c83ffeb63192c6e21757d2bf91290b2f41d"), + assocMap([[tokenName("494147"), integer(100000000)]]), + ], + [ + currencySymbol("378f9732c755ed6f4fc8d406f1461d0cca95d7d2e69416784684df39"), + assocMap([[tokenName("534e454b"), integer(100000000)]]), + ], + [ + currencySymbol("3363b99384d6ee4c4b009068af396c8fdf92dafd111e58a857af0429"), + assocMap([[tokenName("4e49474854"), integer(100000000)]]), + ], +]); + +const sortedValue: Value = assocMap([ + [currencySymbol(""), assocMap([[tokenName(""), integer(200000000)]])], + [ + currencySymbol("3363b99384d6ee4c4b009068af396c8fdf92dafd111e58a857af0429"), + assocMap([[tokenName("4e49474854"), integer(100000000)]]), + ], + [ + currencySymbol("378f9732c755ed6f4fc8d406f1461d0cca95d7d2e69416784684df39"), + assocMap([[tokenName("534e454b"), integer(100000000)]]), + ], + [ + currencySymbol("82e46eb16633bf8bfa820c83ffeb63192c6e21757d2bf91290b2f41d"), + assocMap([[tokenName("494147"), integer(100000000)]]), + ], + [ + currencySymbol("a2818ba06a88bb6c08d10f4f9b897c09768f28d274093628ad7086fc"), + assocMap([[tokenName("484f534b59"), integer(100000000)]]), + ], + [ + currencySymbol("c69b981db7a65e339a6d783755f85a2e03afa1cece9714c55fe4c913"), + assocMap([[tokenName("5553444d"), integer(200000000)]]), + ], +]); + describe("value", () => { it("should create a new Value instance with the correct value", () => { const val: Asset[] = [{ unit: "lovelace", quantity: "1000000" }]; @@ -269,4 +317,33 @@ describe("MeshValue class", () => { expect(JSON.stringify(jsonValue)).toEqual(JSON.stringify(expectedValue)); }); }); + describe("sortValue", () => { + test("should sort policies and tokens by byte ordering", () => { + const sortedValue = MeshValue.sortValue(unsortedValue); + expect(JSON.stringify(sortedValue)).toEqual(JSON.stringify(sortedValue)); + }); + + test("should handle empty Value", () => { + const emptyValue: Value = assocMap([]); + const sortedValue = MeshValue.sortValue(emptyValue); + expect(JSON.stringify(sortedValue)).toEqual(JSON.stringify(emptyValue)); + }); + + test("should handle already sorted Value", () => { + const alreadySorted: Value = assocMap([ + [currencySymbol(""), assocMap([[tokenName(""), integer(200000000)]])], + [ + currencySymbol( + "3363b99384d6ee4c4b009068af396c8fdf92dafd111e58a857af0429", + ), + assocMap([[tokenName("4e49474854"), integer(100000000)]]), + ], + ]); + + const sortedValue = MeshValue.sortValue(alreadySorted); + expect(JSON.stringify(sortedValue)).toEqual( + JSON.stringify(alreadySorted), + ); + }); + }); }); From 87ac3e987092584e29f417eca25b92cb98f8f75c Mon Sep 17 00:00:00 2001 From: hinsonsidan Date: Tue, 13 Jan 2026 18:52:42 +0800 Subject: [PATCH 2/2] chore: release --- packages/bitcoin/package.json | 2 +- packages/mesh-common/package.json | 2 +- packages/mesh-contract/package.json | 6 +++--- packages/mesh-core-csl/package.json | 6 +++--- packages/mesh-core-cst/package.json | 4 ++-- packages/mesh-core/package.json | 12 ++++++------ packages/mesh-hydra/package.json | 8 ++++---- packages/mesh-provider/package.json | 8 ++++---- packages/mesh-react/package.json | 10 +++++----- packages/mesh-svelte/package.json | 4 ++-- packages/mesh-transaction/package.json | 6 +++--- packages/mesh-wallet/package.json | 8 ++++---- packages/midnight-contracts-wizard/package.json | 2 +- packages/midnight-setup/package.json | 2 +- scripts/mesh-cli/package.json | 2 +- 15 files changed, 41 insertions(+), 41 deletions(-) diff --git a/packages/bitcoin/package.json b/packages/bitcoin/package.json index f40314e74..e55253d84 100644 --- a/packages/bitcoin/package.json +++ b/packages/bitcoin/package.json @@ -1,6 +1,6 @@ { "name": "@meshsdk/bitcoin", - "version": "1.9.0-beta.94", + "version": "1.9.0-beta.95", "description": "Mesh Bitcoin package", "main": "./dist/index.cjs", "browser": "./dist/index.js", diff --git a/packages/mesh-common/package.json b/packages/mesh-common/package.json index 10f493382..26c8f0655 100644 --- a/packages/mesh-common/package.json +++ b/packages/mesh-common/package.json @@ -1,6 +1,6 @@ { "name": "@meshsdk/common", - "version": "1.9.0-beta.94", + "version": "1.9.0-beta.95", "description": "Contains constants, types and interfaces used across the SDK and different serialization libraries", "main": "./dist/index.cjs", "browser": "./dist/index.js", diff --git a/packages/mesh-contract/package.json b/packages/mesh-contract/package.json index 6614bedd8..1368f3179 100644 --- a/packages/mesh-contract/package.json +++ b/packages/mesh-contract/package.json @@ -1,6 +1,6 @@ { "name": "@meshsdk/contract", - "version": "1.9.0-beta.94", + "version": "1.9.0-beta.95", "description": "List of open-source smart contracts, complete with documentation, live demos, and end-to-end source code. https://meshjs.dev/smart-contracts", "main": "./dist/index.cjs", "browser": "./dist/index.js", @@ -34,8 +34,8 @@ "typescript": "^5.3.3" }, "dependencies": { - "@meshsdk/common": "1.9.0-beta.94", - "@meshsdk/core": "1.9.0-beta.94", + "@meshsdk/common": "1.9.0-beta.95", + "@meshsdk/core": "1.9.0-beta.95", "libsodium-wrappers-sumo": "0.7.15" }, "prettier": "@meshsdk/configs/prettier", diff --git a/packages/mesh-core-csl/package.json b/packages/mesh-core-csl/package.json index 5476a1d83..4fd9dc66b 100644 --- a/packages/mesh-core-csl/package.json +++ b/packages/mesh-core-csl/package.json @@ -1,6 +1,6 @@ { "name": "@meshsdk/core-csl", - "version": "1.9.0-beta.94", + "version": "1.9.0-beta.95", "description": "Types and utilities functions between Mesh and cardano-serialization-lib", "main": "./dist/index.cjs", "module": "./dist/index.js", @@ -31,7 +31,7 @@ }, "devDependencies": { "@meshsdk/configs": "*", - "@meshsdk/provider": "1.9.0-beta.94", + "@meshsdk/provider": "1.9.0-beta.95", "@types/json-bigint": "^1.0.4", "eslint": "^8.57.0", "ts-jest": "^29.1.4", @@ -39,7 +39,7 @@ "typescript": "^5.3.3" }, "dependencies": { - "@meshsdk/common": "1.9.0-beta.94", + "@meshsdk/common": "1.9.0-beta.95", "@sidan-lab/whisky-js-browser": "^1.0.11", "@sidan-lab/whisky-js-nodejs": "^1.0.11", "@types/base32-encoding": "^1.0.2", diff --git a/packages/mesh-core-cst/package.json b/packages/mesh-core-cst/package.json index 53f73812b..ff6466d4d 100644 --- a/packages/mesh-core-cst/package.json +++ b/packages/mesh-core-cst/package.json @@ -1,6 +1,6 @@ { "name": "@meshsdk/core-cst", - "version": "1.9.0-beta.94", + "version": "1.9.0-beta.95", "description": "Types and utilities functions between Mesh and cardano-js-sdk", "main": "./dist/index.cjs", "browser": "./dist/index.js", @@ -44,7 +44,7 @@ "@harmoniclabs/plutus-data": "1.2.6", "@harmoniclabs/uplc": "1.4.1", "@harmoniclabs/pair": "^1.0.0", - "@meshsdk/common": "1.9.0-beta.94", + "@meshsdk/common": "1.9.0-beta.95", "@types/base32-encoding": "^1.0.2", "base32-encoding": "^1.0.0", "bech32": "^2.0.0", diff --git a/packages/mesh-core/package.json b/packages/mesh-core/package.json index 0c0710bc3..8c72920a0 100644 --- a/packages/mesh-core/package.json +++ b/packages/mesh-core/package.json @@ -1,6 +1,6 @@ { "name": "@meshsdk/core", - "version": "1.9.0-beta.94", + "version": "1.9.0-beta.95", "description": "Mesh SDK Core - https://meshjs.dev/", "main": "./dist/index.cjs", "browser": "./dist/index.js", @@ -33,11 +33,11 @@ "typescript": "^5.3.3" }, "dependencies": { - "@meshsdk/common": "1.9.0-beta.94", - "@meshsdk/core-cst": "1.9.0-beta.94", - "@meshsdk/provider": "1.9.0-beta.94", - "@meshsdk/transaction": "1.9.0-beta.94", - "@meshsdk/wallet": "1.9.0-beta.94", + "@meshsdk/common": "1.9.0-beta.95", + "@meshsdk/core-cst": "1.9.0-beta.95", + "@meshsdk/provider": "1.9.0-beta.95", + "@meshsdk/transaction": "1.9.0-beta.95", + "@meshsdk/wallet": "1.9.0-beta.95", "libsodium-wrappers-sumo": "0.7.15" }, "prettier": "@meshsdk/configs/prettier", diff --git a/packages/mesh-hydra/package.json b/packages/mesh-hydra/package.json index 1c940a2c0..b89f91ea0 100644 --- a/packages/mesh-hydra/package.json +++ b/packages/mesh-hydra/package.json @@ -1,6 +1,6 @@ { "name": "@meshsdk/hydra", - "version": "1.9.0-beta.94", + "version": "1.9.0-beta.95", "description": "Mesh Hydra package", "main": "./dist/index.cjs", "browser": "./dist/index.js", @@ -27,9 +27,9 @@ "test": "jest" }, "dependencies": { - "@meshsdk/common": "1.9.0-beta.94", - "@meshsdk/core": "1.9.0-beta.94", - "@meshsdk/core-cst": "1.9.0-beta.94", + "@meshsdk/common": "1.9.0-beta.95", + "@meshsdk/core": "1.9.0-beta.95", + "@meshsdk/core-cst": "1.9.0-beta.95", "axios": "^1.7.2" }, "devDependencies": { diff --git a/packages/mesh-provider/package.json b/packages/mesh-provider/package.json index 165232fd0..314d02cff 100644 --- a/packages/mesh-provider/package.json +++ b/packages/mesh-provider/package.json @@ -1,6 +1,6 @@ { "name": "@meshsdk/provider", - "version": "1.9.0-beta.94", + "version": "1.9.0-beta.95", "description": "Blockchain data providers - https://meshjs.dev/providers", "main": "./dist/index.cjs", "browser": "./dist/index.js", @@ -35,9 +35,9 @@ "typescript": "^5.3.3" }, "dependencies": { - "@meshsdk/bitcoin": "1.9.0-beta.94", - "@meshsdk/common": "1.9.0-beta.94", - "@meshsdk/core-cst": "1.9.0-beta.94", + "@meshsdk/bitcoin": "1.9.0-beta.95", + "@meshsdk/common": "1.9.0-beta.95", + "@meshsdk/core-cst": "1.9.0-beta.95", "@utxorpc/sdk": "^0.6.7", "@utxorpc/spec": "^0.16.0", "axios": "^1.7.2", diff --git a/packages/mesh-react/package.json b/packages/mesh-react/package.json index 2ac919a11..a4df70d42 100644 --- a/packages/mesh-react/package.json +++ b/packages/mesh-react/package.json @@ -1,6 +1,6 @@ { "name": "@meshsdk/react", - "version": "1.9.0-beta.94", + "version": "1.9.0-beta.95", "description": "React component library - https://meshjs.dev/react", "main": "./dist/index.cjs", "browser": "./dist/index.js", @@ -30,10 +30,10 @@ }, "dependencies": { "@cardananium/cardano-peer-connect": "^1.2.19", - "@meshsdk/bitcoin": "1.9.0-beta.94", - "@meshsdk/common": "1.9.0-beta.94", - "@meshsdk/transaction": "1.9.0-beta.94", - "@meshsdk/wallet": "1.9.0-beta.94", + "@meshsdk/bitcoin": "1.9.0-beta.95", + "@meshsdk/common": "1.9.0-beta.95", + "@meshsdk/transaction": "1.9.0-beta.95", + "@meshsdk/wallet": "1.9.0-beta.95", "@meshsdk/web3-sdk": "0.0.50", "@radix-ui/react-dialog": "^1.1.2", "@radix-ui/react-dropdown-menu": "^2.1.2", diff --git a/packages/mesh-svelte/package.json b/packages/mesh-svelte/package.json index c8cdbd9e5..9a11964fc 100644 --- a/packages/mesh-svelte/package.json +++ b/packages/mesh-svelte/package.json @@ -1,6 +1,6 @@ { "name": "@meshsdk/svelte", - "version": "1.9.0-beta.94", + "version": "1.9.0-beta.95", "description": "Svelte component library - https://meshjs.dev/svelte", "type": "module", "exports": { @@ -26,7 +26,7 @@ "dev": "vite dev" }, "dependencies": { - "@meshsdk/core": "1.9.0-beta.94", + "@meshsdk/core": "1.9.0-beta.95", "bits-ui": "1.0.0-next.65" }, "devDependencies": { diff --git a/packages/mesh-transaction/package.json b/packages/mesh-transaction/package.json index 83522ac14..decf1fa24 100644 --- a/packages/mesh-transaction/package.json +++ b/packages/mesh-transaction/package.json @@ -1,6 +1,6 @@ { "name": "@meshsdk/transaction", - "version": "1.9.0-beta.94", + "version": "1.9.0-beta.95", "description": "Transactions - https://meshjs.dev/apis/transaction", "main": "./dist/index.cjs", "browser": "./dist/index.js", @@ -35,8 +35,8 @@ "typescript": "^5.3.3" }, "dependencies": { - "@meshsdk/common": "1.9.0-beta.94", - "@meshsdk/core-cst": "1.9.0-beta.94", + "@meshsdk/common": "1.9.0-beta.95", + "@meshsdk/core-cst": "1.9.0-beta.95", "@cardano-sdk/core": "^0.46.11", "@cardano-sdk/util": "^0.17.1", "@cardano-sdk/input-selection": "^0.14.27", diff --git a/packages/mesh-wallet/package.json b/packages/mesh-wallet/package.json index 277c916b3..26a51dd47 100644 --- a/packages/mesh-wallet/package.json +++ b/packages/mesh-wallet/package.json @@ -1,6 +1,6 @@ { "name": "@meshsdk/wallet", - "version": "1.9.0-beta.94", + "version": "1.9.0-beta.95", "description": "Wallets - https://meshjs.dev/apis/wallets", "main": "./dist/index.cjs", "browser": "./dist/index.js", @@ -35,9 +35,9 @@ "typescript": "^5.3.3" }, "dependencies": { - "@meshsdk/common": "1.9.0-beta.94", - "@meshsdk/core-cst": "1.9.0-beta.94", - "@meshsdk/transaction": "1.9.0-beta.94", + "@meshsdk/common": "1.9.0-beta.95", + "@meshsdk/core-cst": "1.9.0-beta.95", + "@meshsdk/transaction": "1.9.0-beta.95", "@simplewebauthn/browser": "^13.0.0", "libsodium-wrappers-sumo": "0.7.15" }, diff --git a/packages/midnight-contracts-wizard/package.json b/packages/midnight-contracts-wizard/package.json index 73df72f68..9e469cb9f 100644 --- a/packages/midnight-contracts-wizard/package.json +++ b/packages/midnight-contracts-wizard/package.json @@ -1,6 +1,6 @@ { "name": "@meshsdk/midnight-contracts-wizard", - "version": "1.9.0-beta.94", + "version": "1.9.0-beta.95", "description": "Create a new Midnight contracts project with selected smart contracts", "main": "dist/index.js", "bin": { diff --git a/packages/midnight-setup/package.json b/packages/midnight-setup/package.json index 60bf9629d..563864404 100644 --- a/packages/midnight-setup/package.json +++ b/packages/midnight-setup/package.json @@ -1,6 +1,6 @@ { "name": "@meshsdk/midnight-setup", - "version": "1.9.0-beta.94", + "version": "1.9.0-beta.95", "description": "Midnight Network integration SDK for MeshSDK - https://meshjs.dev/midnight", "main": "./dist/index.cjs", "browser": "./dist/index.js", diff --git a/scripts/mesh-cli/package.json b/scripts/mesh-cli/package.json index 90572e08f..56a64efb8 100644 --- a/scripts/mesh-cli/package.json +++ b/scripts/mesh-cli/package.json @@ -3,7 +3,7 @@ "description": "A quick and easy way to bootstrap your Web3 app using Mesh.", "homepage": "https://meshjs.dev", "author": "MeshJS", - "version": "1.9.0-beta.94", + "version": "1.9.0-beta.95", "license": "Apache-2.0", "type": "module", "main": "./dist/index.cjs",