@ledgerhq/context-module 2.3.0 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/package.json +1 -1
- package/lib/cjs/src/modules/solana/lifi/domain/LifiContextLoader.js +1 -1
- package/lib/cjs/src/modules/solana/lifi/domain/LifiContextLoader.js.map +3 -3
- package/lib/cjs/src/modules/solana/lifi/domain/LifiContextLoader.test.js +1 -1
- package/lib/cjs/src/modules/solana/lifi/domain/LifiContextLoader.test.js.map +3 -3
- package/lib/cjs/src/modules/solana/model/SolanaContextTypes.js.map +1 -1
- package/lib/cjs/src/modules/solana/model/SolanaPayloads.js.map +2 -2
- package/lib/cjs/src/modules/solana/owner-info/data/HttpOwnerInfoDataSource.js +1 -1
- package/lib/cjs/src/modules/solana/owner-info/data/HttpOwnerInfoDataSource.js.map +2 -2
- package/lib/cjs/src/modules/solana/owner-info/data/HttpOwnerInfoDataSource.test.js +1 -1
- package/lib/cjs/src/modules/solana/owner-info/data/HttpOwnerInfoDataSource.test.js.map +2 -2
- package/lib/cjs/src/modules/solana/trusted-name/data/HttpTrustedNameDataSource.js +1 -1
- package/lib/cjs/src/modules/solana/trusted-name/data/HttpTrustedNameDataSource.js.map +3 -3
- package/lib/cjs/src/modules/solana/trusted-name/data/HttpTrustedNameDataSource.test.js +1 -1
- package/lib/cjs/src/modules/solana/trusted-name/data/HttpTrustedNameDataSource.test.js.map +2 -2
- package/lib/cjs/src/modules/solana/trusted-name/data/TrustedNameDataSource.js +1 -1
- package/lib/cjs/src/modules/solana/trusted-name/data/TrustedNameDataSource.js.map +1 -1
- package/lib/cjs/src/modules/solana/trusted-name/domain/TrustedNameContextLoader.js +1 -1
- package/lib/cjs/src/modules/solana/trusted-name/domain/TrustedNameContextLoader.js.map +2 -2
- package/lib/cjs/src/modules/solana/trusted-name/domain/TrustedNameContextLoader.test.js +1 -1
- package/lib/cjs/src/modules/solana/trusted-name/domain/TrustedNameContextLoader.test.js.map +2 -2
- package/lib/esm/package.json +1 -1
- package/lib/esm/src/modules/solana/lifi/domain/LifiContextLoader.js +1 -1
- package/lib/esm/src/modules/solana/lifi/domain/LifiContextLoader.js.map +3 -3
- package/lib/esm/src/modules/solana/lifi/domain/LifiContextLoader.test.js +1 -1
- package/lib/esm/src/modules/solana/lifi/domain/LifiContextLoader.test.js.map +3 -3
- package/lib/esm/src/modules/solana/model/SolanaPayloads.js.map +2 -2
- package/lib/esm/src/modules/solana/owner-info/data/HttpOwnerInfoDataSource.js +1 -1
- package/lib/esm/src/modules/solana/owner-info/data/HttpOwnerInfoDataSource.js.map +2 -2
- package/lib/esm/src/modules/solana/owner-info/data/HttpOwnerInfoDataSource.test.js +1 -1
- package/lib/esm/src/modules/solana/owner-info/data/HttpOwnerInfoDataSource.test.js.map +2 -2
- package/lib/esm/src/modules/solana/trusted-name/data/HttpTrustedNameDataSource.js +1 -1
- package/lib/esm/src/modules/solana/trusted-name/data/HttpTrustedNameDataSource.js.map +3 -3
- package/lib/esm/src/modules/solana/trusted-name/data/HttpTrustedNameDataSource.test.js +1 -1
- package/lib/esm/src/modules/solana/trusted-name/data/HttpTrustedNameDataSource.test.js.map +2 -2
- package/lib/esm/src/modules/solana/trusted-name/domain/TrustedNameContextLoader.js +1 -1
- package/lib/esm/src/modules/solana/trusted-name/domain/TrustedNameContextLoader.js.map +2 -2
- package/lib/esm/src/modules/solana/trusted-name/domain/TrustedNameContextLoader.test.js +1 -1
- package/lib/esm/src/modules/solana/trusted-name/domain/TrustedNameContextLoader.test.js.map +2 -2
- package/lib/types/src/modules/solana/lifi/domain/LifiContextLoader.d.ts.map +1 -1
- package/lib/types/src/modules/solana/model/SolanaContextTypes.d.ts +5 -1
- package/lib/types/src/modules/solana/model/SolanaContextTypes.d.ts.map +1 -1
- package/lib/types/src/modules/solana/model/SolanaPayloads.d.ts +3 -1
- package/lib/types/src/modules/solana/model/SolanaPayloads.d.ts.map +1 -1
- package/lib/types/src/modules/solana/owner-info/data/HttpOwnerInfoDataSource.d.ts.map +1 -1
- package/lib/types/src/modules/solana/trusted-name/data/HttpTrustedNameDataSource.d.ts +1 -1
- package/lib/types/src/modules/solana/trusted-name/data/HttpTrustedNameDataSource.d.ts.map +1 -1
- package/lib/types/src/modules/solana/trusted-name/data/TrustedNameDataSource.d.ts +1 -0
- package/lib/types/src/modules/solana/trusted-name/data/TrustedNameDataSource.d.ts.map +1 -1
- package/lib/types/src/modules/solana/trusted-name/domain/TrustedNameContextLoader.d.ts +1 -0
- package/lib/types/src/modules/solana/trusted-name/domain/TrustedNameContextLoader.d.ts.map +1 -1
- package/lib/types/tsconfig.prod.tsbuildinfo +1 -1
- package/package.json +4 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/solana/lifi/domain/LifiContextLoader.test.ts"],
|
|
4
|
-
"sourcesContent": ["/* eslint-disable @typescript-eslint/no-unsafe-member-access */\n/* eslint-disable @typescript-eslint/no-unsafe-call */\n/* eslint-disable @typescript-eslint/no-unsafe-assignment */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { Left, Right } from \"purify-ts\";\nimport { beforeEach, describe, expect, it, vi } from \"vitest\";\n\nimport { type ContextModuleServiceConfig } from \"@/config/model/ContextModuleConfig\";\nimport { type PkiCertificateLoader } from \"@/modules/multichain/pki/domain/PkiCertificateLoader\";\nimport {\n type GetTransactionDescriptorsResponse,\n type LifiDataSource,\n} from \"@/modules/solana/lifi/data/LifiDataSource\";\nimport {\n type SolanaTransactionDescriptorList,\n type SolanaTransactionDescriptorRaw,\n} from \"@/modules/solana/model/SolanaContextTypes\";\nimport { type SolanaTransactionDescriptor } from \"@/modules/solana/model/SolanaPayloads\";\nimport { ClearSignContextType } from \"@/shared/model/ClearSignContext\";\n\nimport { LifiContextLoader } from \"./LifiContextLoader\";\n\nconst mockLoggerFactory = () => ({\n debug: vi.fn(),\n info: vi.fn(),\n warn: vi.fn(),\n error: vi.fn(),\n subscribers: [],\n});\n\nconst mockCertificate = {\n keyUsageNumber: 13,\n payload: new Uint8Array([0x01, 0x02]),\n};\n\nconst mockConfig = {\n cal: {\n url: \"https://global.api.prd.ledger.com/cal/v1\",\n mode: \"test\",\n branch: \"main\",\n },\n} as ContextModuleServiceConfig;\n\ndescribe(\"LifiContextLoader\", () => {\n let mockDataSource: LifiDataSource;\n let mockCertificateLoader: PkiCertificateLoader;\n\n const makeRawDescriptor = (\n data: string,\n sig = \"deadbeef\",\n ): SolanaTransactionDescriptorRaw => ({\n data,\n descriptorType: \"swap_template\",\n descriptorVersion: \"v1\",\n signatures: { test: sig, prod: `prod_${sig}` },\n });\n\n const makeResolvedDescriptor = (\n data: string,\n sig = \"deadbeef\",\n ): SolanaTransactionDescriptor => ({\n data,\n descriptorType: \"swap_template\",\n descriptorVersion: \"v1\",\n signature: sig,\n });\n\n const responseDescriptorsArray: GetTransactionDescriptorsResponse[\"descriptors\"] =\n [\n {\n program_id: \"SomeProgram\",\n discriminator_hex: \"1\",\n descriptor: makeRawDescriptor(\"abc123\"),\n },\n {\n program_id: \"AnotherProgram\",\n // discriminator_hex intentionally omitted -> defaults to \"\"\n descriptor: makeRawDescriptor(\"def456\"),\n },\n ];\n\n const expectedPlucked: SolanaTransactionDescriptorList = {\n \"SomeProgram:1\": makeResolvedDescriptor(\"abc123\"),\n \"AnotherProgram:\": makeResolvedDescriptor(\"def456\"),\n };\n\n const responseInstructionsArray: GetTransactionDescriptorsResponse[\"instructions\"] =\n [\n {\n program_id: \"SomeProgram\",\n discriminator: 1,\n discriminator_hex: \"1\",\n },\n {\n program_id: \"AnotherProgram\",\n // discriminator_hex intentionally omitted\n },\n ];\n\n const txDescriptorsResponse: GetTransactionDescriptorsResponse = {\n id: \"tpl-1\",\n chain_id: 101,\n instructions: responseInstructionsArray,\n descriptors: responseDescriptorsArray,\n };\n\n beforeEach(() => {\n vi.restoreAllMocks();\n mockDataSource = {\n getTransactionDescriptorsPayload: vi.fn(),\n } as unknown as LifiDataSource;\n mockCertificateLoader = {\n loadCertificate: vi.fn().mockResolvedValue(mockCertificate),\n } as unknown as PkiCertificateLoader;\n });\n\n const makeLoader = () =>\n new LifiContextLoader(\n mockDataSource,\n mockConfig,\n mockCertificateLoader,\n mockLoggerFactory,\n );\n\n describe(\"canHandle\", () => {\n it(\"returns true when templateId is provided and SOLANA_LIFI is in expectedTypes\", () => {\n const loader = makeLoader();\n\n expect(\n loader.canHandle(\n {\n templateId: \"tpl-123\",\n deviceModelId: \"nanoS\" as any,\n },\n [ClearSignContextType.SOLANA_LIFI],\n ),\n ).toBe(true);\n });\n\n it(\"returns false when SOLANA_LIFI is not in expectedTypes\", () => {\n const loader = makeLoader();\n\n expect(\n loader.canHandle({ templateId: \"tpl-123\" } as any, [\n ClearSignContextType.SOLANA_TOKEN,\n ]),\n ).toBe(false);\n });\n\n it(\"returns false when templateId is missing or falsy\", () => {\n const loader = makeLoader();\n\n expect(\n loader.canHandle({ templateId: \"\" } as any, [\n ClearSignContextType.SOLANA_LIFI,\n ]),\n ).toBe(false);\n expect(\n loader.canHandle({ templateId: undefined } as any, [\n ClearSignContextType.SOLANA_LIFI,\n ]),\n ).toBe(false);\n expect(\n loader.canHandle({} as any, [ClearSignContextType.SOLANA_LIFI]),\n ).toBe(false);\n });\n });\n\n describe(\"load\", () => {\n it(\"returns an array with an error when datasource returns Left(error)\", async () => {\n const loader = makeLoader();\n const error = new Error(\"boom\");\n\n vi.spyOn(\n mockDataSource,\n \"getTransactionDescriptorsPayload\",\n ).mockResolvedValue(Left(error));\n\n const input = { templateId: \"tpl-err\", deviceModelId: \"nanoS\" as any };\n const result = await loader.load(input);\n\n expect(\n mockDataSource.getTransactionDescriptorsPayload,\n ).toHaveBeenCalledWith({\n templateId: \"tpl-err\",\n });\n\n expect(result).toEqual([\n {\n type: ClearSignContextType.ERROR,\n error,\n },\n ]);\n });\n\n it(\"returns SOLANA_LIFI array with plucked descriptors and certificate when datasource returns Right(value)\", async () => {\n const loader = makeLoader();\n\n vi.spyOn(\n mockDataSource,\n \"getTransactionDescriptorsPayload\",\n ).mockResolvedValue(Right(txDescriptorsResponse));\n\n const input = { templateId: \"tpl-ok\", deviceModelId: \"nanoS\" as any };\n const result = await loader.load(input);\n\n expect(\n mockDataSource.getTransactionDescriptorsPayload,\n ).toHaveBeenCalledWith({\n templateId: \"tpl-ok\",\n });\n\n expect(mockCertificateLoader.loadCertificate).toHaveBeenCalledWith({\n keyId: \"swap_template_key\",\n keyUsage: \"swap_template\",\n targetDevice: \"nanoS\",\n });\n\n expect(result).toEqual([\n {\n type: ClearSignContextType.SOLANA_LIFI,\n payload: {\n descriptors: expectedPlucked,\n instructions: [\n { program_id: \"SomeProgram\", discriminator_hex: \"1\" },\n { program_id: \"AnotherProgram\" },\n ],\n },\n certificate: mockCertificate,\n },\n ]);\n });\n });\n\n describe(\"pluckTransactionData (private)\", () => {\n it(\"returns a map keyed by `${program_id}:${discriminator_hex ?? '0'}`\", () => {\n const loader = makeLoader();\n const pluck = (loader as any).pluckTransactionData.bind(loader);\n\n const result: SolanaTransactionDescriptorList = pluck(\n txDescriptorsResponse,\n );\n\n expect(result).toEqual(expectedPlucked);\n });\n });\n\n describe(\"extractInstructionsMeta (private)\", () => {\n it(\"returns instruction metadata from the API response\", () => {\n const loader = makeLoader();\n const extract = (loader as any).extractInstructionsMeta.bind(loader);\n\n const result = extract(txDescriptorsResponse);\n\n expect(result).toEqual([\n { program_id: \"SomeProgram\", discriminator_hex: \"1\" },\n { program_id: \"AnotherProgram\" },\n ]);\n });\n\n it(\"returns empty array when instructions are missing\", () => {\n const loader = makeLoader();\n const extract = (loader as any).extractInstructionsMeta.bind(loader);\n\n const result = extract({\n ...txDescriptorsResponse,\n instructions: undefined,\n });\n\n expect(result).toEqual([]);\n });\n });\n\n describe(\"pluckTransactionData mode resolution (private)\", () => {\n it(\"resolves signature using config.cal.mode\", () => {\n const loader = makeLoader();\n const pluck = (loader as any).pluckTransactionData.bind(loader);\n\n const result: SolanaTransactionDescriptorList = pluck(\n txDescriptorsResponse,\n );\n\n expect(result[\"SomeProgram:1\"]?.signature).toBe(\"deadbeef\");\n });\n });\n\n describe(\"real CAL payload (LiFi template 4c694669)\", () => {\n const realApiResponse: GetTransactionDescriptorsResponse = {\n id: \"4c694669\",\n chain_id: 101,\n instructions: [\n {\n program_id: \"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL\",\n discriminator: 1,\n discriminator_hex: \"1\",\n },\n {\n program_id: \"11111111111111111111111111111111\",\n discriminator: 2,\n discriminator_hex: \"2\",\n },\n {\n program_id: \"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4\",\n discriminator: 3.0753642362361016e18,\n discriminator_hex: \"2aade37a97cb17e0\",\n },\n {\n program_id: \"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4\",\n discriminator: 9.339575302786589e18,\n discriminator_hex: \"819cd641339b2148\",\n },\n { program_id: \"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr\" },\n {\n program_id: \"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL\",\n discriminator: 1,\n discriminator_hex: \"1\",\n },\n { program_id: \"3i5JeuZuUxeKtVysUnwQNGerJP2bSMX9fTFfS4Nxe3Br\" },\n {\n program_id: \"ComputeBudget111111111111111111111111111111\",\n discriminator: 2,\n discriminator_hex: \"2\",\n },\n {\n program_id: \"ComputeBudget111111111111111111111111111111\",\n discriminator: 3,\n discriminator_hex: \"3\",\n },\n {\n program_id: \"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA\",\n discriminator: 3,\n discriminator_hex: \"3\",\n },\n {\n program_id: \"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA\",\n discriminator: 17,\n discriminator_hex: \"11\",\n },\n { program_id: \"BrdgN2RPzEMWF96ZbnnJaUtQDQx7VRXYaHHbYCBvceWB\" },\n ],\n descriptors: [\n {\n program_id: \"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL\",\n discriminator_hex: \"1\",\n descriptor: makeRawDescriptor(\"atoken_1\", \"sig_atoken\"),\n },\n {\n program_id: \"11111111111111111111111111111111\",\n discriminator_hex: \"2\",\n descriptor: makeRawDescriptor(\"system_2\", \"sig_system\"),\n },\n {\n program_id: \"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4\",\n discriminator_hex: \"2aade37a97cb17e0\",\n descriptor: makeRawDescriptor(\"jup_route\", \"sig_jup_route\"),\n },\n {\n program_id: \"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4\",\n discriminator_hex: \"819cd641339b2148\",\n descriptor: makeRawDescriptor(\"jup_shared\", \"sig_jup_shared\"),\n },\n {\n program_id: \"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr\",\n descriptor: makeRawDescriptor(\"memo\", \"sig_memo\"),\n },\n {\n program_id: \"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL\",\n discriminator_hex: \"1\",\n descriptor: makeRawDescriptor(\"atoken_1\", \"sig_atoken\"),\n },\n {\n program_id: \"3i5JeuZuUxeKtVysUnwQNGerJP2bSMX9fTFfS4Nxe3Br\",\n descriptor: makeRawDescriptor(\"3i5jeu\", \"sig_3i5jeu\"),\n },\n {\n program_id: \"ComputeBudget111111111111111111111111111111\",\n discriminator_hex: \"2\",\n descriptor: makeRawDescriptor(\"cb_2\", \"sig_cb2\"),\n },\n {\n program_id: \"ComputeBudget111111111111111111111111111111\",\n discriminator_hex: \"3\",\n descriptor: makeRawDescriptor(\"cb_3\", \"sig_cb3\"),\n },\n {\n program_id: \"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA\",\n discriminator_hex: \"3\",\n descriptor: makeRawDescriptor(\"tokenkeg_3\", \"sig_tk3\"),\n },\n {\n program_id: \"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA\",\n discriminator_hex: \"11\",\n descriptor: makeRawDescriptor(\"tokenkeg_11\", \"sig_tk11\"),\n },\n {\n program_id: \"BrdgN2RPzEMWF96ZbnnJaUtQDQx7VRXYaHHbYCBvceWB\",\n descriptor: makeRawDescriptor(\"brdg\", \"sig_brdg\"),\n },\n ],\n };\n\n it(\"produces 11 unique descriptor keys (ATokenGP:1 appears twice but deduplicates)\", () => {\n const loader = makeLoader();\n const pluck = (loader as any).pluckTransactionData.bind(loader);\n\n const result: SolanaTransactionDescriptorList = pluck(realApiResponse);\n const keys = Object.keys(result);\n\n // 12 descriptors but ATokenGP:1 appears twice -> 11 unique keys\n expect(keys).toHaveLength(11);\n });\n\n it(\"creates distinct keys for same program_id with different discriminators\", () => {\n const loader = makeLoader();\n const pluck = (loader as any).pluckTransactionData.bind(loader);\n\n const result: SolanaTransactionDescriptorList = pluck(realApiResponse);\n\n // JUP6 has two different discriminators -> two distinct entries\n expect(\n result[\"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4:2aade37a97cb17e0\"]\n ?.data,\n ).toBe(\"jup_route\");\n expect(\n result[\"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4:819cd641339b2148\"]\n ?.data,\n ).toBe(\"jup_shared\");\n\n // ComputeBudget has discriminator 2 and 3\n expect(\n result[\"ComputeBudget111111111111111111111111111111:2\"]?.data,\n ).toBe(\"cb_2\");\n expect(\n result[\"ComputeBudget111111111111111111111111111111:3\"]?.data,\n ).toBe(\"cb_3\");\n\n // TokenkegQ has discriminator 3 and 11\n expect(\n result[\"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA:3\"]?.data,\n ).toBe(\"tokenkeg_3\");\n expect(\n result[\"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA:11\"]?.data,\n ).toBe(\"tokenkeg_11\");\n });\n\n it(\"uses :0 suffix for programs without a discriminator\", () => {\n const loader = makeLoader();\n const pluck = (loader as any).pluckTransactionData.bind(loader);\n\n const result: SolanaTransactionDescriptorList = pluck(realApiResponse);\n\n expect(result[\"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr:\"]?.data).toBe(\n \"memo\",\n );\n expect(\n result[\"3i5JeuZuUxeKtVysUnwQNGerJP2bSMX9fTFfS4Nxe3Br:\"]?.data,\n ).toBe(\"3i5jeu\");\n expect(\n result[\"BrdgN2RPzEMWF96ZbnnJaUtQDQx7VRXYaHHbYCBvceWB:\"]?.data,\n ).toBe(\"brdg\");\n });\n\n it(\"extracts 12 instruction metadata entries preserving order and discriminators\", () => {\n const loader = makeLoader();\n const extract = (loader as any).extractInstructionsMeta.bind(loader);\n\n const result = extract(realApiResponse);\n\n expect(result).toHaveLength(12);\n\n // First: ATokenGP with discriminator\n expect(result[0]).toEqual({\n program_id: \"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL\",\n discriminator_hex: \"1\",\n });\n // JUP6 route (8-byte discriminator)\n expect(result[2]).toEqual({\n program_id: \"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4\",\n discriminator_hex: \"2aade37a97cb17e0\",\n });\n // JUP6 shared (8-byte discriminator)\n expect(result[3]).toEqual({\n program_id: \"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4\",\n discriminator_hex: \"819cd641339b2148\",\n });\n // Memo: no discriminator\n expect(result[4]).toEqual({\n program_id: \"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr\",\n });\n // BrdgN2: no discriminator (last entry)\n expect(result[11]).toEqual({\n program_id: \"BrdgN2RPzEMWF96ZbnnJaUtQDQx7VRXYaHHbYCBvceWB\",\n });\n });\n\n it(\"load returns full payload with descriptors and instructions from real API response\", async () => {\n const loader = makeLoader();\n\n vi.spyOn(\n mockDataSource,\n \"getTransactionDescriptorsPayload\",\n ).mockResolvedValue(Right(realApiResponse));\n\n const input = { templateId: \"4c694669\", deviceModelId: \"nanoS\" as any };\n const result = await loader.load(input);\n\n expect(result).toHaveLength(1);\n expect(result[0]).toMatchObject({\n type: ClearSignContextType.SOLANA_LIFI,\n payload: {\n descriptors: expect.objectContaining({\n \"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4:2aade37a97cb17e0\":\n expect.objectContaining({ data: \"jup_route\" }),\n \"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4:819cd641339b2148\":\n expect.objectContaining({ data: \"jup_shared\" }),\n }),\n instructions: expect.arrayContaining([\n expect.objectContaining({\n program_id: \"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4\",\n discriminator_hex: \"2aade37a97cb17e0\",\n }),\n expect.objectContaining({\n program_id: \"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4\",\n discriminator_hex: \"819cd641339b2148\",\n }),\n ]),\n },\n });\n\n expect(Object.keys((result[0] as any).payload.descriptors)).toHaveLength(\n 11,\n );\n expect((result[0] as any).payload.instructions).toHaveLength(12);\n });\n });\n});\n"],
|
|
5
|
-
"mappings": "AAIA,OAAS,QAAAA,EAAM,SAAAC,MAAa,YAC5B,OAAS,cAAAC,EAAY,YAAAC,EAAU,UAAAC,EAAQ,MAAAC,EAAI,MAAAC,MAAU,SAarD,OAAS,wBAAAC,MAA4B,kCAErC,OAAS,qBAAAC,MAAyB,sBAElC,MAAMC,EAAoB,KAAO,CAC/B,MAAOH,EAAG,GAAG,EACb,KAAMA,EAAG,GAAG,EACZ,KAAMA,EAAG,GAAG,EACZ,MAAOA,EAAG,GAAG,EACb,YAAa,CAAC,CAChB,GAEMI,EAAkB,CACtB,eAAgB,GAChB,QAAS,IAAI,WAAW,CAAC,EAAM,CAAI,CAAC,CACtC,EAEMC,EAAa,CACjB,IAAK,CACH,IAAK,2CACL,KAAM,OACN,OAAQ,MACV,CACF,EAEAR,EAAS,oBAAqB,IAAM,CAClC,IAAIS,EACAC,EAEJ,MAAMC,EAAoB,CACxBC,EACAC,EAAM,cAC8B,CACpC,KAAAD,EACA,eAAgB,gBAChB,kBAAmB,KACnB,WAAY,CAAE,KAAMC,EAAK,KAAM,QAAQA,CAAG,EAAG,CAC/C,GAEMC,EAAyB,CAC7BF,EACAC,EAAM,cAC2B,CACjC,KAAAD,EACA,eAAgB,gBAChB,kBAAmB,KACnB,UAAWC,CACb,GAEME,EACJ,CACE,CACE,WAAY,cACZ,kBAAmB,IACnB,WAAYJ,EAAkB,QAAQ,CACxC,EACA,CACE,WAAY,iBAEZ,WAAYA,EAAkB,QAAQ,CACxC,CACF,EAEIK,EAAmD,CACvD,
|
|
6
|
-
"names": ["Left", "Right", "beforeEach", "describe", "expect", "it", "vi", "ClearSignContextType", "LifiContextLoader", "mockLoggerFactory", "mockCertificate", "mockConfig", "mockDataSource", "mockCertificateLoader", "makeRawDescriptor", "data", "sig", "makeResolvedDescriptor", "responseDescriptorsArray", "expectedPlucked", "txDescriptorsResponse", "makeLoader", "loader", "error", "input", "result", "realApiResponse", "keys"]
|
|
4
|
+
"sourcesContent": ["/* eslint-disable @typescript-eslint/no-unsafe-member-access */\n/* eslint-disable @typescript-eslint/no-unsafe-call */\n/* eslint-disable @typescript-eslint/no-unsafe-assignment */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { Left, Right } from \"purify-ts\";\nimport { beforeEach, describe, expect, it, vi } from \"vitest\";\n\nimport { type ContextModuleServiceConfig } from \"@/config/model/ContextModuleConfig\";\nimport { type PkiCertificateLoader } from \"@/modules/multichain/pki/domain/PkiCertificateLoader\";\nimport {\n type GetTransactionDescriptorsResponse,\n type LifiDataSource,\n} from \"@/modules/solana/lifi/data/LifiDataSource\";\nimport {\n type SolanaTransactionDescriptorList,\n type SolanaTransactionDescriptorRaw,\n} from \"@/modules/solana/model/SolanaContextTypes\";\nimport { type SolanaTransactionDescriptor } from \"@/modules/solana/model/SolanaPayloads\";\nimport { ClearSignContextType } from \"@/shared/model/ClearSignContext\";\n\nimport { LifiContextLoader } from \"./LifiContextLoader\";\n\nconst mockLoggerFactory = () => ({\n debug: vi.fn(),\n info: vi.fn(),\n warn: vi.fn(),\n error: vi.fn(),\n subscribers: [],\n});\n\nconst mockCertificate = {\n keyUsageNumber: 13,\n payload: new Uint8Array([0x01, 0x02]),\n};\n\nconst mockConfig = {\n cal: {\n url: \"https://global.api.prd.ledger.com/cal/v1\",\n mode: \"test\",\n branch: \"main\",\n },\n} as ContextModuleServiceConfig;\n\ndescribe(\"LifiContextLoader\", () => {\n let mockDataSource: LifiDataSource;\n let mockCertificateLoader: PkiCertificateLoader;\n\n const makeRawDescriptor = (\n data: string,\n sig = \"deadbeef\",\n ): SolanaTransactionDescriptorRaw => ({\n data,\n descriptorType: \"swap_template\",\n descriptorVersion: \"v1\",\n signatures: { test: sig, prod: `prod_${sig}` },\n });\n\n const makeResolvedDescriptor = (\n data: string,\n sig = \"deadbeef\",\n ): SolanaTransactionDescriptor => ({\n data,\n descriptorType: \"swap_template\",\n descriptorVersion: \"v1\",\n signature: sig,\n });\n\n const responseDescriptorsArray: GetTransactionDescriptorsResponse[\"descriptors\"] =\n [\n {\n program_id: \"SomeProgram\",\n discriminator_hex: \"1\",\n descriptor: makeRawDescriptor(\"abc123\"),\n },\n {\n program_id: \"AnotherProgram\",\n // discriminator_hex intentionally omitted -> defaults to \"\"\n descriptor: makeRawDescriptor(\"def456\"),\n },\n ];\n\n const expectedPlucked: SolanaTransactionDescriptorList = {\n \"SomeProgram:1\": [makeResolvedDescriptor(\"abc123\")],\n \"AnotherProgram:\": [makeResolvedDescriptor(\"def456\")],\n };\n\n const responseInstructionsArray: GetTransactionDescriptorsResponse[\"instructions\"] =\n [\n {\n program_id: \"SomeProgram\",\n discriminator: 1,\n discriminator_hex: \"1\",\n },\n {\n program_id: \"AnotherProgram\",\n // discriminator_hex intentionally omitted\n },\n ];\n\n const txDescriptorsResponse: GetTransactionDescriptorsResponse = {\n id: \"tpl-1\",\n chain_id: 101,\n instructions: responseInstructionsArray,\n descriptors: responseDescriptorsArray,\n };\n\n beforeEach(() => {\n vi.restoreAllMocks();\n mockDataSource = {\n getTransactionDescriptorsPayload: vi.fn(),\n } as unknown as LifiDataSource;\n mockCertificateLoader = {\n loadCertificate: vi.fn().mockResolvedValue(mockCertificate),\n } as unknown as PkiCertificateLoader;\n });\n\n const makeLoader = () =>\n new LifiContextLoader(\n mockDataSource,\n mockConfig,\n mockCertificateLoader,\n mockLoggerFactory,\n );\n\n describe(\"canHandle\", () => {\n it(\"returns true when templateId is provided and SOLANA_LIFI is in expectedTypes\", () => {\n const loader = makeLoader();\n\n expect(\n loader.canHandle(\n {\n templateId: \"tpl-123\",\n deviceModelId: \"nanoS\" as any,\n },\n [ClearSignContextType.SOLANA_LIFI],\n ),\n ).toBe(true);\n });\n\n it(\"returns false when SOLANA_LIFI is not in expectedTypes\", () => {\n const loader = makeLoader();\n\n expect(\n loader.canHandle({ templateId: \"tpl-123\" } as any, [\n ClearSignContextType.SOLANA_TOKEN,\n ]),\n ).toBe(false);\n });\n\n it(\"returns false when templateId is missing or falsy\", () => {\n const loader = makeLoader();\n\n expect(\n loader.canHandle({ templateId: \"\" } as any, [\n ClearSignContextType.SOLANA_LIFI,\n ]),\n ).toBe(false);\n expect(\n loader.canHandle({ templateId: undefined } as any, [\n ClearSignContextType.SOLANA_LIFI,\n ]),\n ).toBe(false);\n expect(\n loader.canHandle({} as any, [ClearSignContextType.SOLANA_LIFI]),\n ).toBe(false);\n });\n });\n\n describe(\"load\", () => {\n it(\"returns an array with an error when datasource returns Left(error)\", async () => {\n const loader = makeLoader();\n const error = new Error(\"boom\");\n\n vi.spyOn(\n mockDataSource,\n \"getTransactionDescriptorsPayload\",\n ).mockResolvedValue(Left(error));\n\n const input = { templateId: \"tpl-err\", deviceModelId: \"nanoS\" as any };\n const result = await loader.load(input);\n\n expect(\n mockDataSource.getTransactionDescriptorsPayload,\n ).toHaveBeenCalledWith({\n templateId: \"tpl-err\",\n });\n\n expect(result).toEqual([\n {\n type: ClearSignContextType.ERROR,\n error,\n },\n ]);\n });\n\n it(\"returns SOLANA_LIFI array with plucked descriptors and certificate when datasource returns Right(value)\", async () => {\n const loader = makeLoader();\n\n vi.spyOn(\n mockDataSource,\n \"getTransactionDescriptorsPayload\",\n ).mockResolvedValue(Right(txDescriptorsResponse));\n\n const input = { templateId: \"tpl-ok\", deviceModelId: \"nanoS\" as any };\n const result = await loader.load(input);\n\n expect(\n mockDataSource.getTransactionDescriptorsPayload,\n ).toHaveBeenCalledWith({\n templateId: \"tpl-ok\",\n });\n\n expect(mockCertificateLoader.loadCertificate).toHaveBeenCalledWith({\n keyId: \"swap_template_key\",\n keyUsage: \"swap_template\",\n targetDevice: \"nanoS\",\n });\n\n expect(result).toEqual([\n {\n type: ClearSignContextType.SOLANA_LIFI,\n payload: {\n descriptors: expectedPlucked,\n instructions: [\n { program_id: \"SomeProgram\", discriminator_hex: \"1\" },\n { program_id: \"AnotherProgram\" },\n ],\n },\n certificate: mockCertificate,\n },\n ]);\n });\n });\n\n describe(\"pluckTransactionData (private)\", () => {\n it(\"returns a map keyed by `${program_id}:${discriminator_hex ?? '0'}`\", () => {\n const loader = makeLoader();\n const pluck = (loader as any).pluckTransactionData.bind(loader);\n\n const result: SolanaTransactionDescriptorList = pluck(\n txDescriptorsResponse,\n );\n\n expect(result).toEqual(expectedPlucked);\n });\n });\n\n describe(\"extractInstructionsMeta (private)\", () => {\n it(\"returns instruction metadata from the API response\", () => {\n const loader = makeLoader();\n const extract = (loader as any).extractInstructionsMeta.bind(loader);\n\n const result = extract(txDescriptorsResponse);\n\n expect(result).toEqual([\n { program_id: \"SomeProgram\", discriminator_hex: \"1\" },\n { program_id: \"AnotherProgram\" },\n ]);\n });\n\n it(\"returns empty array when instructions are missing\", () => {\n const loader = makeLoader();\n const extract = (loader as any).extractInstructionsMeta.bind(loader);\n\n const result = extract({\n ...txDescriptorsResponse,\n instructions: undefined,\n });\n\n expect(result).toEqual([]);\n });\n });\n\n describe(\"pluckTransactionData mode resolution (private)\", () => {\n it(\"resolves signature using config.cal.mode\", () => {\n const loader = makeLoader();\n const pluck = (loader as any).pluckTransactionData.bind(loader);\n\n const result: SolanaTransactionDescriptorList = pluck(\n txDescriptorsResponse,\n );\n\n expect(result[\"SomeProgram:1\"]?.[0]?.signature).toBe(\"deadbeef\");\n });\n });\n\n describe(\"duplicate discriminator handling (private)\", () => {\n it(\"collects both descriptors into an array when same (program_id, discriminator_hex) appears twice\", () => {\n // Models the LiFi CAL PR fix: System Program Transfer has two descriptors \u2014\n // one for the transfer amount and one for fees (distinguished by capped_bps).\n const loader = makeLoader();\n const pluck = (loader as any).pluckTransactionData.bind(loader);\n\n const response: GetTransactionDescriptorsResponse = {\n id: \"dup-test\",\n chain_id: 101,\n instructions: [\n {\n program_id: \"11111111111111111111111111111111\",\n discriminator: 2,\n discriminator_hex: \"02\",\n },\n {\n program_id: \"11111111111111111111111111111111\",\n discriminator: 2,\n discriminator_hex: \"02\",\n },\n ],\n descriptors: [\n {\n program_id: \"11111111111111111111111111111111\",\n discriminator_hex: \"02\",\n has_basis_point: false,\n descriptor: makeRawDescriptor(\n \"transfer_descriptor\",\n \"sig_transfer\",\n ),\n },\n {\n program_id: \"11111111111111111111111111111111\",\n discriminator_hex: \"02\",\n has_basis_point: true,\n descriptor: makeRawDescriptor(\"fee_descriptor\", \"sig_fee\"),\n },\n ],\n };\n\n const result: SolanaTransactionDescriptorList = pluck(response);\n\n expect(Object.keys(result)).toHaveLength(1);\n expect(result[\"11111111111111111111111111111111:02\"]).toHaveLength(2);\n expect(result[\"11111111111111111111111111111111:02\"]?.[0]?.data).toBe(\n \"transfer_descriptor\",\n );\n expect(\n result[\"11111111111111111111111111111111:02\"]?.[0]?.has_basis_point,\n ).toBe(false);\n expect(result[\"11111111111111111111111111111111:02\"]?.[1]?.data).toBe(\n \"fee_descriptor\",\n );\n expect(\n result[\"11111111111111111111111111111111:02\"]?.[1]?.has_basis_point,\n ).toBe(true);\n });\n });\n\n describe(\"real CAL payload (LiFi template 4c694669)\", () => {\n const realApiResponse: GetTransactionDescriptorsResponse = {\n id: \"4c694669\",\n chain_id: 101,\n instructions: [\n {\n program_id: \"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL\",\n discriminator: 1,\n discriminator_hex: \"1\",\n },\n {\n program_id: \"11111111111111111111111111111111\",\n discriminator: 2,\n discriminator_hex: \"2\",\n },\n {\n program_id: \"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4\",\n discriminator: 3.0753642362361016e18,\n discriminator_hex: \"2aade37a97cb17e0\",\n },\n {\n program_id: \"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4\",\n discriminator: 9.339575302786589e18,\n discriminator_hex: \"819cd641339b2148\",\n },\n { program_id: \"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr\" },\n {\n program_id: \"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL\",\n discriminator: 1,\n discriminator_hex: \"1\",\n },\n { program_id: \"3i5JeuZuUxeKtVysUnwQNGerJP2bSMX9fTFfS4Nxe3Br\" },\n {\n program_id: \"ComputeBudget111111111111111111111111111111\",\n discriminator: 2,\n discriminator_hex: \"2\",\n },\n {\n program_id: \"ComputeBudget111111111111111111111111111111\",\n discriminator: 3,\n discriminator_hex: \"3\",\n },\n {\n program_id: \"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA\",\n discriminator: 3,\n discriminator_hex: \"3\",\n },\n {\n program_id: \"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA\",\n discriminator: 17,\n discriminator_hex: \"11\",\n },\n { program_id: \"BrdgN2RPzEMWF96ZbnnJaUtQDQx7VRXYaHHbYCBvceWB\" },\n ],\n descriptors: [\n {\n program_id: \"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL\",\n discriminator_hex: \"1\",\n descriptor: makeRawDescriptor(\"atoken_1\", \"sig_atoken\"),\n },\n {\n program_id: \"11111111111111111111111111111111\",\n discriminator_hex: \"2\",\n descriptor: makeRawDescriptor(\"system_2\", \"sig_system\"),\n },\n {\n program_id: \"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4\",\n discriminator_hex: \"2aade37a97cb17e0\",\n descriptor: makeRawDescriptor(\"jup_route\", \"sig_jup_route\"),\n },\n {\n program_id: \"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4\",\n discriminator_hex: \"819cd641339b2148\",\n descriptor: makeRawDescriptor(\"jup_shared\", \"sig_jup_shared\"),\n },\n {\n program_id: \"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr\",\n descriptor: makeRawDescriptor(\"memo\", \"sig_memo\"),\n },\n {\n program_id: \"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL\",\n discriminator_hex: \"1\",\n descriptor: makeRawDescriptor(\"atoken_1\", \"sig_atoken\"),\n },\n {\n program_id: \"3i5JeuZuUxeKtVysUnwQNGerJP2bSMX9fTFfS4Nxe3Br\",\n descriptor: makeRawDescriptor(\"3i5jeu\", \"sig_3i5jeu\"),\n },\n {\n program_id: \"ComputeBudget111111111111111111111111111111\",\n discriminator_hex: \"2\",\n descriptor: makeRawDescriptor(\"cb_2\", \"sig_cb2\"),\n },\n {\n program_id: \"ComputeBudget111111111111111111111111111111\",\n discriminator_hex: \"3\",\n descriptor: makeRawDescriptor(\"cb_3\", \"sig_cb3\"),\n },\n {\n program_id: \"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA\",\n discriminator_hex: \"3\",\n descriptor: makeRawDescriptor(\"tokenkeg_3\", \"sig_tk3\"),\n },\n {\n program_id: \"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA\",\n discriminator_hex: \"11\",\n descriptor: makeRawDescriptor(\"tokenkeg_11\", \"sig_tk11\"),\n },\n {\n program_id: \"BrdgN2RPzEMWF96ZbnnJaUtQDQx7VRXYaHHbYCBvceWB\",\n descriptor: makeRawDescriptor(\"brdg\", \"sig_brdg\"),\n },\n ],\n };\n\n it(\"produces 11 unique descriptor keys (ATokenGP:1 appears twice -> array of 2)\", () => {\n const loader = makeLoader();\n const pluck = (loader as any).pluckTransactionData.bind(loader);\n\n const result: SolanaTransactionDescriptorList = pluck(realApiResponse);\n const keys = Object.keys(result);\n\n // 12 descriptors but ATokenGP:1 appears twice -> 11 unique keys, ATokenGP:1 has 2 entries\n expect(keys).toHaveLength(11);\n expect(\n result[\"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL:1\"],\n ).toHaveLength(2);\n });\n\n it(\"creates distinct keys for same program_id with different discriminators\", () => {\n const loader = makeLoader();\n const pluck = (loader as any).pluckTransactionData.bind(loader);\n\n const result: SolanaTransactionDescriptorList = pluck(realApiResponse);\n\n // JUP6 has two different discriminators -> two distinct entries\n expect(\n result[\n \"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4:2aade37a97cb17e0\"\n ]?.[0]?.data,\n ).toBe(\"jup_route\");\n expect(\n result[\n \"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4:819cd641339b2148\"\n ]?.[0]?.data,\n ).toBe(\"jup_shared\");\n\n // ComputeBudget has discriminator 2 and 3\n expect(\n result[\"ComputeBudget111111111111111111111111111111:2\"]?.[0]?.data,\n ).toBe(\"cb_2\");\n expect(\n result[\"ComputeBudget111111111111111111111111111111:3\"]?.[0]?.data,\n ).toBe(\"cb_3\");\n\n // TokenkegQ has discriminator 3 and 11\n expect(\n result[\"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA:3\"]?.[0]?.data,\n ).toBe(\"tokenkeg_3\");\n expect(\n result[\"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA:11\"]?.[0]?.data,\n ).toBe(\"tokenkeg_11\");\n });\n\n it(\"uses :0 suffix for programs without a discriminator\", () => {\n const loader = makeLoader();\n const pluck = (loader as any).pluckTransactionData.bind(loader);\n\n const result: SolanaTransactionDescriptorList = pluck(realApiResponse);\n\n expect(\n result[\"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr:\"]?.[0]?.data,\n ).toBe(\"memo\");\n expect(\n result[\"3i5JeuZuUxeKtVysUnwQNGerJP2bSMX9fTFfS4Nxe3Br:\"]?.[0]?.data,\n ).toBe(\"3i5jeu\");\n expect(\n result[\"BrdgN2RPzEMWF96ZbnnJaUtQDQx7VRXYaHHbYCBvceWB:\"]?.[0]?.data,\n ).toBe(\"brdg\");\n });\n\n it(\"extracts 12 instruction metadata entries preserving order and discriminators\", () => {\n const loader = makeLoader();\n const extract = (loader as any).extractInstructionsMeta.bind(loader);\n\n const result = extract(realApiResponse);\n\n expect(result).toHaveLength(12);\n\n // First: ATokenGP with discriminator\n expect(result[0]).toEqual({\n program_id: \"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL\",\n discriminator_hex: \"1\",\n });\n // JUP6 route (8-byte discriminator)\n expect(result[2]).toEqual({\n program_id: \"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4\",\n discriminator_hex: \"2aade37a97cb17e0\",\n });\n // JUP6 shared (8-byte discriminator)\n expect(result[3]).toEqual({\n program_id: \"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4\",\n discriminator_hex: \"819cd641339b2148\",\n });\n // Memo: no discriminator\n expect(result[4]).toEqual({\n program_id: \"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr\",\n });\n // BrdgN2: no discriminator (last entry)\n expect(result[11]).toEqual({\n program_id: \"BrdgN2RPzEMWF96ZbnnJaUtQDQx7VRXYaHHbYCBvceWB\",\n });\n });\n\n it(\"load returns full payload with descriptors and instructions from real API response\", async () => {\n const loader = makeLoader();\n\n vi.spyOn(\n mockDataSource,\n \"getTransactionDescriptorsPayload\",\n ).mockResolvedValue(Right(realApiResponse));\n\n const input = { templateId: \"4c694669\", deviceModelId: \"nanoS\" as any };\n const result = await loader.load(input);\n\n expect(result).toHaveLength(1);\n expect(result[0]).toMatchObject({\n type: ClearSignContextType.SOLANA_LIFI,\n payload: {\n descriptors: expect.objectContaining({\n \"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4:2aade37a97cb17e0\":\n expect.arrayContaining([\n expect.objectContaining({ data: \"jup_route\" }),\n ]),\n \"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4:819cd641339b2148\":\n expect.arrayContaining([\n expect.objectContaining({ data: \"jup_shared\" }),\n ]),\n }),\n instructions: expect.arrayContaining([\n expect.objectContaining({\n program_id: \"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4\",\n discriminator_hex: \"2aade37a97cb17e0\",\n }),\n expect.objectContaining({\n program_id: \"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4\",\n discriminator_hex: \"819cd641339b2148\",\n }),\n ]),\n },\n });\n\n expect(Object.keys((result[0] as any).payload.descriptors)).toHaveLength(\n 11,\n );\n expect((result[0] as any).payload.instructions).toHaveLength(12);\n });\n });\n});\n"],
|
|
5
|
+
"mappings": "AAIA,OAAS,QAAAA,EAAM,SAAAC,MAAa,YAC5B,OAAS,cAAAC,EAAY,YAAAC,EAAU,UAAAC,EAAQ,MAAAC,EAAI,MAAAC,MAAU,SAarD,OAAS,wBAAAC,MAA4B,kCAErC,OAAS,qBAAAC,MAAyB,sBAElC,MAAMC,EAAoB,KAAO,CAC/B,MAAOH,EAAG,GAAG,EACb,KAAMA,EAAG,GAAG,EACZ,KAAMA,EAAG,GAAG,EACZ,MAAOA,EAAG,GAAG,EACb,YAAa,CAAC,CAChB,GAEMI,EAAkB,CACtB,eAAgB,GAChB,QAAS,IAAI,WAAW,CAAC,EAAM,CAAI,CAAC,CACtC,EAEMC,EAAa,CACjB,IAAK,CACH,IAAK,2CACL,KAAM,OACN,OAAQ,MACV,CACF,EAEAR,EAAS,oBAAqB,IAAM,CAClC,IAAIS,EACAC,EAEJ,MAAMC,EAAoB,CACxBC,EACAC,EAAM,cAC8B,CACpC,KAAAD,EACA,eAAgB,gBAChB,kBAAmB,KACnB,WAAY,CAAE,KAAMC,EAAK,KAAM,QAAQA,CAAG,EAAG,CAC/C,GAEMC,EAAyB,CAC7BF,EACAC,EAAM,cAC2B,CACjC,KAAAD,EACA,eAAgB,gBAChB,kBAAmB,KACnB,UAAWC,CACb,GAEME,EACJ,CACE,CACE,WAAY,cACZ,kBAAmB,IACnB,WAAYJ,EAAkB,QAAQ,CACxC,EACA,CACE,WAAY,iBAEZ,WAAYA,EAAkB,QAAQ,CACxC,CACF,EAEIK,EAAmD,CACvD,gBAAiB,CAACF,EAAuB,QAAQ,CAAC,EAClD,kBAAmB,CAACA,EAAuB,QAAQ,CAAC,CACtD,EAeMG,EAA2D,CAC/D,GAAI,QACJ,SAAU,IACV,aAfA,CACE,CACE,WAAY,cACZ,cAAe,EACf,kBAAmB,GACrB,EACA,CACE,WAAY,gBAEd,CACF,EAMA,YAAaF,CACf,EAEAhB,EAAW,IAAM,CACfI,EAAG,gBAAgB,EACnBM,EAAiB,CACf,iCAAkCN,EAAG,GAAG,CAC1C,EACAO,EAAwB,CACtB,gBAAiBP,EAAG,GAAG,EAAE,kBAAkBI,CAAe,CAC5D,CACF,CAAC,EAED,MAAMW,EAAa,IACjB,IAAIb,EACFI,EACAD,EACAE,EACAJ,CACF,EAEFN,EAAS,YAAa,IAAM,CAC1BE,EAAG,+EAAgF,IAAM,CACvF,MAAMiB,EAASD,EAAW,EAE1BjB,EACEkB,EAAO,UACL,CACE,WAAY,UACZ,cAAe,OACjB,EACA,CAACf,EAAqB,WAAW,CACnC,CACF,EAAE,KAAK,EAAI,CACb,CAAC,EAEDF,EAAG,yDAA0D,IAAM,CACjE,MAAMiB,EAASD,EAAW,EAE1BjB,EACEkB,EAAO,UAAU,CAAE,WAAY,SAAU,EAAU,CACjDf,EAAqB,YACvB,CAAC,CACH,EAAE,KAAK,EAAK,CACd,CAAC,EAEDF,EAAG,oDAAqD,IAAM,CAC5D,MAAMiB,EAASD,EAAW,EAE1BjB,EACEkB,EAAO,UAAU,CAAE,WAAY,EAAG,EAAU,CAC1Cf,EAAqB,WACvB,CAAC,CACH,EAAE,KAAK,EAAK,EACZH,EACEkB,EAAO,UAAU,CAAE,WAAY,MAAU,EAAU,CACjDf,EAAqB,WACvB,CAAC,CACH,EAAE,KAAK,EAAK,EACZH,EACEkB,EAAO,UAAU,CAAC,EAAU,CAACf,EAAqB,WAAW,CAAC,CAChE,EAAE,KAAK,EAAK,CACd,CAAC,CACH,CAAC,EAEDJ,EAAS,OAAQ,IAAM,CACrBE,EAAG,qEAAsE,SAAY,CACnF,MAAMiB,EAASD,EAAW,EACpBE,EAAQ,IAAI,MAAM,MAAM,EAE9BjB,EAAG,MACDM,EACA,kCACF,EAAE,kBAAkBZ,EAAKuB,CAAK,CAAC,EAE/B,MAAMC,EAAQ,CAAE,WAAY,UAAW,cAAe,OAAe,EAC/DC,EAAS,MAAMH,EAAO,KAAKE,CAAK,EAEtCpB,EACEQ,EAAe,gCACjB,EAAE,qBAAqB,CACrB,WAAY,SACd,CAAC,EAEDR,EAAOqB,CAAM,EAAE,QAAQ,CACrB,CACE,KAAMlB,EAAqB,MAC3B,MAAAgB,CACF,CACF,CAAC,CACH,CAAC,EAEDlB,EAAG,0GAA2G,SAAY,CACxH,MAAMiB,EAASD,EAAW,EAE1Bf,EAAG,MACDM,EACA,kCACF,EAAE,kBAAkBX,EAAMmB,CAAqB,CAAC,EAEhD,MAAMI,EAAQ,CAAE,WAAY,SAAU,cAAe,OAAe,EAC9DC,EAAS,MAAMH,EAAO,KAAKE,CAAK,EAEtCpB,EACEQ,EAAe,gCACjB,EAAE,qBAAqB,CACrB,WAAY,QACd,CAAC,EAEDR,EAAOS,EAAsB,eAAe,EAAE,qBAAqB,CACjE,MAAO,oBACP,SAAU,gBACV,aAAc,OAChB,CAAC,EAEDT,EAAOqB,CAAM,EAAE,QAAQ,CACrB,CACE,KAAMlB,EAAqB,YAC3B,QAAS,CACP,YAAaY,EACb,aAAc,CACZ,CAAE,WAAY,cAAe,kBAAmB,GAAI,EACpD,CAAE,WAAY,gBAAiB,CACjC,CACF,EACA,YAAaT,CACf,CACF,CAAC,CACH,CAAC,CACH,CAAC,EAEDP,EAAS,iCAAkC,IAAM,CAC/CE,EAAG,qEAAsE,IAAM,CAC7E,MAAMiB,EAASD,EAAW,EAGpBI,EAFSH,EAAe,qBAAqB,KAAKA,CAAM,EAG5DF,CACF,EAEAhB,EAAOqB,CAAM,EAAE,QAAQN,CAAe,CACxC,CAAC,CACH,CAAC,EAEDhB,EAAS,oCAAqC,IAAM,CAClDE,EAAG,qDAAsD,IAAM,CAC7D,MAAMiB,EAASD,EAAW,EAGpBI,EAFWH,EAAe,wBAAwB,KAAKA,CAAM,EAE5CF,CAAqB,EAE5ChB,EAAOqB,CAAM,EAAE,QAAQ,CACrB,CAAE,WAAY,cAAe,kBAAmB,GAAI,EACpD,CAAE,WAAY,gBAAiB,CACjC,CAAC,CACH,CAAC,EAEDpB,EAAG,oDAAqD,IAAM,CAC5D,MAAMiB,EAASD,EAAW,EAGpBI,EAFWH,EAAe,wBAAwB,KAAKA,CAAM,EAE5C,CACrB,GAAGF,EACH,aAAc,MAChB,CAAC,EAEDhB,EAAOqB,CAAM,EAAE,QAAQ,CAAC,CAAC,CAC3B,CAAC,CACH,CAAC,EAEDtB,EAAS,iDAAkD,IAAM,CAC/DE,EAAG,2CAA4C,IAAM,CACnD,MAAMiB,EAASD,EAAW,EAGpBI,EAFSH,EAAe,qBAAqB,KAAKA,CAAM,EAG5DF,CACF,EAEAhB,EAAOqB,EAAO,eAAe,IAAI,CAAC,GAAG,SAAS,EAAE,KAAK,UAAU,CACjE,CAAC,CACH,CAAC,EAEDtB,EAAS,6CAA8C,IAAM,CAC3DE,EAAG,kGAAmG,IAAM,CAG1G,MAAMiB,EAASD,EAAW,EACpBK,EAASJ,EAAe,qBAAqB,KAAKA,CAAM,EAExDK,EAA8C,CAClD,GAAI,WACJ,SAAU,IACV,aAAc,CACZ,CACE,WAAY,mCACZ,cAAe,EACf,kBAAmB,IACrB,EACA,CACE,WAAY,mCACZ,cAAe,EACf,kBAAmB,IACrB,CACF,EACA,YAAa,CACX,CACE,WAAY,mCACZ,kBAAmB,KACnB,gBAAiB,GACjB,WAAYb,EACV,sBACA,cACF,CACF,EACA,CACE,WAAY,mCACZ,kBAAmB,KACnB,gBAAiB,GACjB,WAAYA,EAAkB,iBAAkB,SAAS,CAC3D,CACF,CACF,EAEMW,EAA0CC,EAAMC,CAAQ,EAE9DvB,EAAO,OAAO,KAAKqB,CAAM,CAAC,EAAE,aAAa,CAAC,EAC1CrB,EAAOqB,EAAO,qCAAqC,CAAC,EAAE,aAAa,CAAC,EACpErB,EAAOqB,EAAO,qCAAqC,IAAI,CAAC,GAAG,IAAI,EAAE,KAC/D,qBACF,EACArB,EACEqB,EAAO,qCAAqC,IAAI,CAAC,GAAG,eACtD,EAAE,KAAK,EAAK,EACZrB,EAAOqB,EAAO,qCAAqC,IAAI,CAAC,GAAG,IAAI,EAAE,KAC/D,gBACF,EACArB,EACEqB,EAAO,qCAAqC,IAAI,CAAC,GAAG,eACtD,EAAE,KAAK,EAAI,CACb,CAAC,CACH,CAAC,EAEDtB,EAAS,4CAA6C,IAAM,CAC1D,MAAMyB,EAAqD,CACzD,GAAI,WACJ,SAAU,IACV,aAAc,CACZ,CACE,WAAY,+CACZ,cAAe,EACf,kBAAmB,GACrB,EACA,CACE,WAAY,mCACZ,cAAe,EACf,kBAAmB,GACrB,EACA,CACE,WAAY,8CACZ,cAAe,mBACf,kBAAmB,kBACrB,EACA,CACE,WAAY,8CACZ,cAAe,mBACf,kBAAmB,kBACrB,EACA,CAAE,WAAY,6CAA8C,EAC5D,CACE,WAAY,+CACZ,cAAe,EACf,kBAAmB,GACrB,EACA,CAAE,WAAY,8CAA+C,EAC7D,CACE,WAAY,8CACZ,cAAe,EACf,kBAAmB,GACrB,EACA,CACE,WAAY,8CACZ,cAAe,EACf,kBAAmB,GACrB,EACA,CACE,WAAY,8CACZ,cAAe,EACf,kBAAmB,GACrB,EACA,CACE,WAAY,8CACZ,cAAe,GACf,kBAAmB,IACrB,EACA,CAAE,WAAY,8CAA+C,CAC/D,EACA,YAAa,CACX,CACE,WAAY,+CACZ,kBAAmB,IACnB,WAAYd,EAAkB,WAAY,YAAY,CACxD,EACA,CACE,WAAY,mCACZ,kBAAmB,IACnB,WAAYA,EAAkB,WAAY,YAAY,CACxD,EACA,CACE,WAAY,8CACZ,kBAAmB,mBACnB,WAAYA,EAAkB,YAAa,eAAe,CAC5D,EACA,CACE,WAAY,8CACZ,kBAAmB,mBACnB,WAAYA,EAAkB,aAAc,gBAAgB,CAC9D,EACA,CACE,WAAY,8CACZ,WAAYA,EAAkB,OAAQ,UAAU,CAClD,EACA,CACE,WAAY,+CACZ,kBAAmB,IACnB,WAAYA,EAAkB,WAAY,YAAY,CACxD,EACA,CACE,WAAY,+CACZ,WAAYA,EAAkB,SAAU,YAAY,CACtD,EACA,CACE,WAAY,8CACZ,kBAAmB,IACnB,WAAYA,EAAkB,OAAQ,SAAS,CACjD,EACA,CACE,WAAY,8CACZ,kBAAmB,IACnB,WAAYA,EAAkB,OAAQ,SAAS,CACjD,EACA,CACE,WAAY,8CACZ,kBAAmB,IACnB,WAAYA,EAAkB,aAAc,SAAS,CACvD,EACA,CACE,WAAY,8CACZ,kBAAmB,KACnB,WAAYA,EAAkB,cAAe,UAAU,CACzD,EACA,CACE,WAAY,+CACZ,WAAYA,EAAkB,OAAQ,UAAU,CAClD,CACF,CACF,EAEAT,EAAG,8EAA+E,IAAM,CACtF,MAAMiB,EAASD,EAAW,EAGpBI,EAFSH,EAAe,qBAAqB,KAAKA,CAAM,EAERM,CAAe,EAC/DC,EAAO,OAAO,KAAKJ,CAAM,EAG/BrB,EAAOyB,CAAI,EAAE,aAAa,EAAE,EAC5BzB,EACEqB,EAAO,gDAAgD,CACzD,EAAE,aAAa,CAAC,CAClB,CAAC,EAEDpB,EAAG,0EAA2E,IAAM,CAClF,MAAMiB,EAASD,EAAW,EAGpBI,EAFSH,EAAe,qBAAqB,KAAKA,CAAM,EAERM,CAAe,EAGrExB,EACEqB,EACE,8DACF,IAAI,CAAC,GAAG,IACV,EAAE,KAAK,WAAW,EAClBrB,EACEqB,EACE,8DACF,IAAI,CAAC,GAAG,IACV,EAAE,KAAK,YAAY,EAGnBrB,EACEqB,EAAO,+CAA+C,IAAI,CAAC,GAAG,IAChE,EAAE,KAAK,MAAM,EACbrB,EACEqB,EAAO,+CAA+C,IAAI,CAAC,GAAG,IAChE,EAAE,KAAK,MAAM,EAGbrB,EACEqB,EAAO,+CAA+C,IAAI,CAAC,GAAG,IAChE,EAAE,KAAK,YAAY,EACnBrB,EACEqB,EAAO,gDAAgD,IAAI,CAAC,GAAG,IACjE,EAAE,KAAK,aAAa,CACtB,CAAC,EAEDpB,EAAG,sDAAuD,IAAM,CAC9D,MAAMiB,EAASD,EAAW,EAGpBI,EAFSH,EAAe,qBAAqB,KAAKA,CAAM,EAERM,CAAe,EAErExB,EACEqB,EAAO,8CAA8C,IAAI,CAAC,GAAG,IAC/D,EAAE,KAAK,MAAM,EACbrB,EACEqB,EAAO,+CAA+C,IAAI,CAAC,GAAG,IAChE,EAAE,KAAK,QAAQ,EACfrB,EACEqB,EAAO,+CAA+C,IAAI,CAAC,GAAG,IAChE,EAAE,KAAK,MAAM,CACf,CAAC,EAEDpB,EAAG,+EAAgF,IAAM,CACvF,MAAMiB,EAASD,EAAW,EAGpBI,EAFWH,EAAe,wBAAwB,KAAKA,CAAM,EAE5CM,CAAe,EAEtCxB,EAAOqB,CAAM,EAAE,aAAa,EAAE,EAG9BrB,EAAOqB,EAAO,CAAC,CAAC,EAAE,QAAQ,CACxB,WAAY,+CACZ,kBAAmB,GACrB,CAAC,EAEDrB,EAAOqB,EAAO,CAAC,CAAC,EAAE,QAAQ,CACxB,WAAY,8CACZ,kBAAmB,kBACrB,CAAC,EAEDrB,EAAOqB,EAAO,CAAC,CAAC,EAAE,QAAQ,CACxB,WAAY,8CACZ,kBAAmB,kBACrB,CAAC,EAEDrB,EAAOqB,EAAO,CAAC,CAAC,EAAE,QAAQ,CACxB,WAAY,6CACd,CAAC,EAEDrB,EAAOqB,EAAO,EAAE,CAAC,EAAE,QAAQ,CACzB,WAAY,8CACd,CAAC,CACH,CAAC,EAEDpB,EAAG,qFAAsF,SAAY,CACnG,MAAMiB,EAASD,EAAW,EAE1Bf,EAAG,MACDM,EACA,kCACF,EAAE,kBAAkBX,EAAM2B,CAAe,CAAC,EAE1C,MAAMJ,EAAQ,CAAE,WAAY,WAAY,cAAe,OAAe,EAChEC,EAAS,MAAMH,EAAO,KAAKE,CAAK,EAEtCpB,EAAOqB,CAAM,EAAE,aAAa,CAAC,EAC7BrB,EAAOqB,EAAO,CAAC,CAAC,EAAE,cAAc,CAC9B,KAAMlB,EAAqB,YAC3B,QAAS,CACP,YAAaH,EAAO,iBAAiB,CACnC,+DACEA,EAAO,gBAAgB,CACrBA,EAAO,iBAAiB,CAAE,KAAM,WAAY,CAAC,CAC/C,CAAC,EACH,+DACEA,EAAO,gBAAgB,CACrBA,EAAO,iBAAiB,CAAE,KAAM,YAAa,CAAC,CAChD,CAAC,CACL,CAAC,EACD,aAAcA,EAAO,gBAAgB,CACnCA,EAAO,iBAAiB,CACtB,WAAY,8CACZ,kBAAmB,kBACrB,CAAC,EACDA,EAAO,iBAAiB,CACtB,WAAY,8CACZ,kBAAmB,kBACrB,CAAC,CACH,CAAC,CACH,CACF,CAAC,EAEDA,EAAO,OAAO,KAAMqB,EAAO,CAAC,EAAU,QAAQ,WAAW,CAAC,EAAE,aAC1D,EACF,EACArB,EAAQqB,EAAO,CAAC,EAAU,QAAQ,YAAY,EAAE,aAAa,EAAE,CACjE,CAAC,CACH,CAAC,CACH,CAAC",
|
|
6
|
+
"names": ["Left", "Right", "beforeEach", "describe", "expect", "it", "vi", "ClearSignContextType", "LifiContextLoader", "mockLoggerFactory", "mockCertificate", "mockConfig", "mockDataSource", "mockCertificateLoader", "makeRawDescriptor", "data", "sig", "makeResolvedDescriptor", "responseDescriptorsArray", "expectedPlucked", "txDescriptorsResponse", "makeLoader", "loader", "error", "input", "result", "pluck", "response", "realApiResponse", "keys"]
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/solana/model/SolanaPayloads.ts"],
|
|
4
|
-
"sourcesContent": ["// Solana payload types \u2014 used both by Solana loaders and (type-only)\n// referenced by the shared ClearSignContext union for typed-payload variants.\n\nexport type SolanaTokenData = {\n solanaTokenDescriptor: {\n data: string;\n signature: string;\n };\n};\n\nexport type SolanaTransactionDescriptor = {\n data: string;\n descriptorType: string;\n descriptorVersion: string;\n signature: string;\n};\n\nexport type SolanaLifiInstructionMeta = {\n program_id: string;\n discriminator_hex?: string;\n};\n\nexport type SolanaLifiPayload = {\n descriptors: Record<string, SolanaTransactionDescriptor>;\n instructions: SolanaLifiInstructionMeta[];\n};\n\nexport type SolanaTransactionCheckPayload = {\n descriptor: string;\n};\n\n// --- Generic clear-signing payloads ---\n\n// CAL static descriptor payloads. `data` is the signed TLV (hex string);\n// `signature` is the resolved signature for the configured CAL signature\n// kind (`prod` | `test`).\n\nexport type SolanaSignedDescriptor = {\n data: string;\n signature: string;\n};\n\n/**\n * One enum-variant descriptor bundled with an instruction descriptor by CAL.\n * The signed TLV (`descriptor.data`) feeds the host-side type-pool decode\n * cache, `enumId`/`variantIndex` identify which variant it describes (and let\n * the host stream the selected variant's signed descriptor to the device).\n */\nexport type SolanaInstructionEnumVariant = {\n enumId: string;\n variantIndex: number;\n descriptor: SolanaSignedDescriptor;\n};\n\n/**\n * Payload for ClearSignContextType.SOLANA_INSTRUCTION_INFO.\n *\n * `instructionInfo` + `substructures` are the signed TLVs streamed to the\n * device, and `enumVariants` are the signed enum-variant descriptors CAL\n * bundles for the program's enums (used to build the host decode cache). The\n * remaining fields are CAL's decoded JSON consumed host-side by the\n * requirement builder.\n */\nexport type SolanaInstructionInfoPayload = {\n programId: string;\n discriminator: string;\n instructionInfo: SolanaSignedDescriptor;\n substructures: SolanaInstructionSubstructure[];\n enumVariants: SolanaInstructionEnumVariant[];\n idlDescriptor: {\n typePool: SolanaCalTypePoolEntry[];\n rootType: number;\n };\n mintAssociations: SolanaCalMintAssociation[];\n valueFlowPorts: SolanaCalValueFlowPort[];\n accountResets: SolanaCalAccountReset[];\n displayFields: SolanaCalDisplayField[];\n};\n\nexport enum SolanaInstructionSubstructureKind {\n DISPLAY_FIELD = 0x00,\n VALUE_FLOW_PORT = 0x01,\n HIDE_RULE = 0x02,\n ACCOUNT_RESET = 0x03,\n}\n\nexport type SolanaInstructionSubstructure = {\n kind: SolanaInstructionSubstructureKind;\n // TLV payload (hex string) for the substructure.\n data: string;\n};\n\n// --- Decoded CAL JSON (snake_case mirrors the CAL response) ---\n\nexport type SolanaCalValue = {\n source: string;\n /** ACCOUNT_PATH: index into the instruction's accounts. */\n account_index?: number;\n /** CONSTANT: hex-encoded bytes. */\n data?: string;\n /** ARGUMENT_PATH: decoded steps. */\n path?: { steps: number[] };\n};\n\nexport type SolanaCalTokenValue = {\n kind: string;\n value?: SolanaCalValue;\n account_index?: number;\n};\n\nexport type SolanaCalValueFlowPort = {\n /** Ordered candidate list of account indices (length 1 for a single-account port). */\n account_indices: number[];\n optional_account_strategy?: string;\n token_value: SolanaCalTokenValue;\n};\n\nexport type SolanaCalAccountReset = {\n account_index: number;\n require_pre_balance_zero?: boolean;\n};\n\nexport type SolanaCalDisplayField = {\n name?: string;\n param: { type: string; value?: SolanaCalValue; token?: SolanaCalValue };\n};\n\nexport type SolanaCalTypePoolEntry = {\n index: number;\n kind: string;\n refs?: number[];\n size?: number;\n encoding?: number;\n len_kind?: string;\n flag_kind?: string;\n sentinel?: string;\n disc_kind?: string;\n total_variants?: number;\n enum_id?: string;\n};\n\nexport type SolanaCalMintAssociation = {\n account_index: number;\n mint_index: number;\n};\n\n/**\n * Payload for ClearSignContextType.SOLANA_ENUM_VARIANT: one signed\n * ENUM_VARIANT descriptor for a `(programId, enumId, variantIndex)` triple.\n */\nexport type SolanaEnumVariantPayload = {\n programId: string;\n enumId: string;\n variantIndex: number;\n descriptor: SolanaSignedDescriptor;\n};\n\n/**\n * Payload for ClearSignContextType.SOLANA_TOKEN_INFO, keyed by mint pubkey.\n * Distinct from the legacy SOLANA_TOKEN context.\n */\nexport type SolanaTokenInfoPayload = {\n mint: string;\n descriptor: SolanaSignedDescriptor;\n};\n\n// --- Dynamic descriptor payloads (challenge-bound) ---\n//\n// Each payload carries the raw signed TLV bytes decoded from the backend's\n// hex `signedDescriptor`.\n\nexport type SolanaTokenAccountStatePayload = {\n descriptor: Uint8Array;\n};\n\nexport type SolanaAltResolutionPayload = {\n descriptor: Uint8Array;\n};\n\n// SOLANA_TRUSTED_NAME (generic flow, with 2-byte length prefix) is emitted by\n// SolanaTrustedNameContextLoader. SOLANA_BASIC_TRUSTED_NAME (basic flow, raw\n// bytes) is emitted by OwnerInfoContextLoader. Both carry `payload: Uint8Array`.\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["// Solana payload types \u2014 used both by Solana loaders and (type-only)\n// referenced by the shared ClearSignContext union for typed-payload variants.\n\nexport type SolanaTokenData = {\n solanaTokenDescriptor: {\n data: string;\n signature: string;\n };\n};\n\nexport type SolanaTransactionDescriptor = {\n data: string;\n descriptorType: string;\n descriptorVersion: string;\n signature: string;\n has_basis_point?: boolean;\n};\n\nexport type SolanaLifiInstructionMeta = {\n program_id: string;\n discriminator_hex?: string;\n has_basis_point?: boolean;\n};\n\nexport type SolanaLifiPayload = {\n descriptors: Record<string, SolanaTransactionDescriptor[]>;\n instructions: SolanaLifiInstructionMeta[];\n};\n\nexport type SolanaTransactionCheckPayload = {\n descriptor: string;\n};\n\n// --- Generic clear-signing payloads ---\n\n// CAL static descriptor payloads. `data` is the signed TLV (hex string);\n// `signature` is the resolved signature for the configured CAL signature\n// kind (`prod` | `test`).\n\nexport type SolanaSignedDescriptor = {\n data: string;\n signature: string;\n};\n\n/**\n * One enum-variant descriptor bundled with an instruction descriptor by CAL.\n * The signed TLV (`descriptor.data`) feeds the host-side type-pool decode\n * cache, `enumId`/`variantIndex` identify which variant it describes (and let\n * the host stream the selected variant's signed descriptor to the device).\n */\nexport type SolanaInstructionEnumVariant = {\n enumId: string;\n variantIndex: number;\n descriptor: SolanaSignedDescriptor;\n};\n\n/**\n * Payload for ClearSignContextType.SOLANA_INSTRUCTION_INFO.\n *\n * `instructionInfo` + `substructures` are the signed TLVs streamed to the\n * device, and `enumVariants` are the signed enum-variant descriptors CAL\n * bundles for the program's enums (used to build the host decode cache). The\n * remaining fields are CAL's decoded JSON consumed host-side by the\n * requirement builder.\n */\nexport type SolanaInstructionInfoPayload = {\n programId: string;\n discriminator: string;\n instructionInfo: SolanaSignedDescriptor;\n substructures: SolanaInstructionSubstructure[];\n enumVariants: SolanaInstructionEnumVariant[];\n idlDescriptor: {\n typePool: SolanaCalTypePoolEntry[];\n rootType: number;\n };\n mintAssociations: SolanaCalMintAssociation[];\n valueFlowPorts: SolanaCalValueFlowPort[];\n accountResets: SolanaCalAccountReset[];\n displayFields: SolanaCalDisplayField[];\n};\n\nexport enum SolanaInstructionSubstructureKind {\n DISPLAY_FIELD = 0x00,\n VALUE_FLOW_PORT = 0x01,\n HIDE_RULE = 0x02,\n ACCOUNT_RESET = 0x03,\n}\n\nexport type SolanaInstructionSubstructure = {\n kind: SolanaInstructionSubstructureKind;\n // TLV payload (hex string) for the substructure.\n data: string;\n};\n\n// --- Decoded CAL JSON (snake_case mirrors the CAL response) ---\n\nexport type SolanaCalValue = {\n source: string;\n /** ACCOUNT_PATH: index into the instruction's accounts. */\n account_index?: number;\n /** CONSTANT: hex-encoded bytes. */\n data?: string;\n /** ARGUMENT_PATH: decoded steps. */\n path?: { steps: number[] };\n};\n\nexport type SolanaCalTokenValue = {\n kind: string;\n value?: SolanaCalValue;\n account_index?: number;\n};\n\nexport type SolanaCalValueFlowPort = {\n /** Ordered candidate list of account indices (length 1 for a single-account port). */\n account_indices: number[];\n optional_account_strategy?: string;\n token_value: SolanaCalTokenValue;\n};\n\nexport type SolanaCalAccountReset = {\n account_index: number;\n require_pre_balance_zero?: boolean;\n};\n\nexport type SolanaCalDisplayField = {\n name?: string;\n param: { type: string; value?: SolanaCalValue; token?: SolanaCalValue };\n};\n\nexport type SolanaCalTypePoolEntry = {\n index: number;\n kind: string;\n refs?: number[];\n size?: number;\n encoding?: number;\n len_kind?: string;\n flag_kind?: string;\n sentinel?: string;\n disc_kind?: string;\n total_variants?: number;\n enum_id?: string;\n};\n\nexport type SolanaCalMintAssociation = {\n account_index: number;\n mint_index: number;\n};\n\n/**\n * Payload for ClearSignContextType.SOLANA_ENUM_VARIANT: one signed\n * ENUM_VARIANT descriptor for a `(programId, enumId, variantIndex)` triple.\n */\nexport type SolanaEnumVariantPayload = {\n programId: string;\n enumId: string;\n variantIndex: number;\n descriptor: SolanaSignedDescriptor;\n};\n\n/**\n * Payload for ClearSignContextType.SOLANA_TOKEN_INFO, keyed by mint pubkey.\n * Distinct from the legacy SOLANA_TOKEN context.\n */\nexport type SolanaTokenInfoPayload = {\n mint: string;\n descriptor: SolanaSignedDescriptor;\n};\n\n// --- Dynamic descriptor payloads (challenge-bound) ---\n//\n// Each payload carries the raw signed TLV bytes decoded from the backend's\n// hex `signedDescriptor`.\n\nexport type SolanaTokenAccountStatePayload = {\n descriptor: Uint8Array;\n};\n\nexport type SolanaAltResolutionPayload = {\n descriptor: Uint8Array;\n};\n\n// SOLANA_TRUSTED_NAME (generic flow, with 2-byte length prefix) is emitted by\n// SolanaTrustedNameContextLoader. SOLANA_BASIC_TRUSTED_NAME (basic flow, raw\n// bytes) is emitted by OwnerInfoContextLoader. Both carry `payload: Uint8Array`.\n"],
|
|
5
|
+
"mappings": "AAiFO,IAAKA,OACVA,IAAA,cAAgB,GAAhB,gBACAA,IAAA,gBAAkB,GAAlB,kBACAA,IAAA,UAAY,GAAZ,YACAA,IAAA,cAAgB,GAAhB,gBAJUA,OAAA",
|
|
6
6
|
"names": ["SolanaInstructionSubstructureKind"]
|
|
7
7
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var w=Object.defineProperty;var m=Object.getOwnPropertyDescriptor;var u=(i,e,n,t)=>{for(var r=t>1?void 0:t?m(e,n):e,o=i.length-1,s;o>=0;o--)(s=i[o])&&(r=(t?s(e,n,r):s(r))||r);return t&&r&&w(e,n,r),r},d=(i,e)=>(n,t)=>e(n,t,i);import{hexaStringToBuffer as S}from"@ledgerhq/device-management-kit";import{inject as p,injectable as h}from"inversify";import{Left as a,Right as f}from"purify-ts";import{configTypes as g}from"../../../../config/di/configTypes";import{networkTypes as I}from"../../../../shared/network/di/networkTypes";let c=class{constructor(e,n){this.config=e;this.http=n
|
|
1
|
+
var w=Object.defineProperty;var m=Object.getOwnPropertyDescriptor;var u=(i,e,n,t)=>{for(var r=t>1?void 0:t?m(e,n):e,o=i.length-1,s;o>=0;o--)(s=i[o])&&(r=(t?s(e,n,r):s(r))||r);return t&&r&&w(e,n,r),r},d=(i,e)=>(n,t)=>e(n,t,i);import{hexaStringToBuffer as S}from"@ledgerhq/device-management-kit";import{inject as p,injectable as h}from"inversify";import{Left as a,Right as f}from"purify-ts";import{configTypes as g}from"../../../../config/di/configTypes";import{networkTypes as I}from"../../../../shared/network/di/networkTypes";let c=class{constructor(e,n){this.config=e;this.http=n}isSolanaSPLOwnerInfo(e){return typeof e!="object"||e===null?!1:typeof e.tokenAccount=="string"&&typeof e.owner=="string"&&typeof e.contract=="string"&&typeof e.signedDescriptor=="string"}async fetchAddressMetadata(e,n){try{const t=await this.http.get(`${this.config.metadataServiceDomain.url}/v2/solana/owner/${e}`,{params:{challenge:n}});return this.isSolanaSPLOwnerInfo(t)?f(t):a(new Error("[ContextModule] - HttpOwnerInfoDataSource: invalid fetchAddressMetadata response shape"))}catch{return a(new Error("[ContextModule] - HttpOwnerInfoDataSource: Failed to fetch address metadata"))}}async computeAddressMetadata(e,n,t){try{const r=await this.http.get(`${this.config.metadataServiceDomain.url}/v2/solana/computed-token-account/${e}/${n}`,{params:{challenge:t}});return this.isSolanaSPLOwnerInfo(r)?f(r):a(new Error("[ContextModule] - HttpOwnerInfoDataSource: invalid computeAddressMetadata response shape"))}catch{return a(new Error("[ContextModule] - HttpOwnerInfoDataSource: Failed to compute address metadata"))}}async getOwnerInfo(e){const{tokenAddress:n,challenge:t,createATA:r}=e;if(!t)return a(new Error("[ContextModule] - HttpOwnerInfoDataSource: challenge is required"));let o;if(n)o=await this.fetchAddressMetadata(n,t);else if(r?.address&&r?.mintAddress)o=await this.computeAddressMetadata(r.address,r.mintAddress,t);else return a(new Error("[ContextModule] - HttpOwnerInfoDataSource: either tokenAddress or valid createATA must be provided"));return o.chain(s=>{const l=S(s.signedDescriptor);return l?f({tlvDescriptor:l}):a(new Error("[ContextModule] - HttpOwnerInfoDataSource: invalid base64 tlvDescriptor received"))})}};c=u([h(),d(0,p(g.Config)),d(1,p(I.NetworkClient))],c);export{c as HttpOwnerInfoDataSource};
|
|
2
2
|
//# sourceMappingURL=HttpOwnerInfoDataSource.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/solana/owner-info/data/HttpOwnerInfoDataSource.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n DmkNetworkClient,\n hexaStringToBuffer,\n} from \"@ledgerhq/device-management-kit\";\nimport { inject, injectable } from \"inversify\";\nimport { Either, Left, Right } from \"purify-ts\";\n\nimport { configTypes } from \"@/config/di/configTypes\";\nimport type { ContextModuleServiceConfig } from \"@/config/model/ContextModuleConfig\";\nimport {\n type SolanaSPLOwnerInfo,\n type SolanaTransactionContext,\n} from \"@/modules/solana/model/SolanaTransactionContext\";\nimport { networkTypes } from \"@/shared/network/di/networkTypes\";\n\nimport {\n HttpOwnerInfoDataSourceResult,\n OwnerInfoDataSource,\n} from \"./OwnerInfoDataSource\";\n\n@injectable()\nexport class HttpOwnerInfoDataSource implements OwnerInfoDataSource {\n constructor(\n @inject(configTypes.Config)\n private readonly config: ContextModuleServiceConfig,\n @inject(networkTypes.NetworkClient)\n private readonly http: DmkNetworkClient,\n ) {
|
|
5
|
-
"mappings": "iOAAA,OAEE,sBAAAA,MACK,kCACP,OAAS,UAAAC,EAAQ,cAAAC,MAAkB,YACnC,OAAiB,QAAAC,EAAM,SAAAC,MAAa,YAEpC,OAAS,eAAAC,MAAmB,0BAM5B,OAAS,gBAAAC,MAAoB,mCAQtB,IAAMC,EAAN,KAA6D,CAClE,YAEmBC,EAEAC,EACjB,CAHiB,YAAAD,EAEA,UAAAC,
|
|
4
|
+
"sourcesContent": ["import {\n DmkNetworkClient,\n hexaStringToBuffer,\n} from \"@ledgerhq/device-management-kit\";\nimport { inject, injectable } from \"inversify\";\nimport { Either, Left, Right } from \"purify-ts\";\n\nimport { configTypes } from \"@/config/di/configTypes\";\nimport type { ContextModuleServiceConfig } from \"@/config/model/ContextModuleConfig\";\nimport {\n type SolanaSPLOwnerInfo,\n type SolanaTransactionContext,\n} from \"@/modules/solana/model/SolanaTransactionContext\";\nimport { networkTypes } from \"@/shared/network/di/networkTypes\";\n\nimport {\n HttpOwnerInfoDataSourceResult,\n OwnerInfoDataSource,\n} from \"./OwnerInfoDataSource\";\n\n@injectable()\nexport class HttpOwnerInfoDataSource implements OwnerInfoDataSource {\n constructor(\n @inject(configTypes.Config)\n private readonly config: ContextModuleServiceConfig,\n @inject(networkTypes.NetworkClient)\n private readonly http: DmkNetworkClient,\n ) {}\n\n private isSolanaSPLOwnerInfo(data: {\n tokenAccount?: unknown;\n owner?: unknown;\n contract?: unknown;\n signedDescriptor?: unknown;\n }): data is SolanaSPLOwnerInfo {\n if (typeof data !== \"object\" || data === null) return false;\n return (\n typeof data.tokenAccount === \"string\" &&\n typeof data.owner === \"string\" &&\n typeof data.contract === \"string\" &&\n typeof data.signedDescriptor === \"string\"\n );\n }\n\n async fetchAddressMetadata(\n tokenAddress: string,\n challenge: string,\n ): Promise<Either<Error, SolanaSPLOwnerInfo>> {\n try {\n const data = (await this.http.get(\n `${this.config.metadataServiceDomain.url}/v2/solana/owner/${tokenAddress}`,\n { params: { challenge } },\n )) as SolanaSPLOwnerInfo;\n if (!this.isSolanaSPLOwnerInfo(data))\n return Left(\n new Error(\n \"[ContextModule] - HttpOwnerInfoDataSource: invalid fetchAddressMetadata response shape\",\n ),\n );\n return Right(data);\n } catch {\n return Left(\n new Error(\n \"[ContextModule] - HttpOwnerInfoDataSource: Failed to fetch address metadata\",\n ),\n );\n }\n }\n\n async computeAddressMetadata(\n address: string,\n mintAddress: string,\n challenge: string,\n ): Promise<Either<Error, SolanaSPLOwnerInfo>> {\n try {\n const data = (await this.http.get(\n `${this.config.metadataServiceDomain.url}/v2/solana/computed-token-account/${address}/${mintAddress}`,\n { params: { challenge } },\n )) as SolanaSPLOwnerInfo;\n if (!this.isSolanaSPLOwnerInfo(data))\n return Left(\n new Error(\n \"[ContextModule] - HttpOwnerInfoDataSource: invalid computeAddressMetadata response shape\",\n ),\n );\n return Right(data);\n } catch {\n return Left(\n new Error(\n \"[ContextModule] - HttpOwnerInfoDataSource: Failed to compute address metadata\",\n ),\n );\n }\n }\n\n async getOwnerInfo(\n context: SolanaTransactionContext,\n ): Promise<Either<Error, HttpOwnerInfoDataSourceResult>> {\n const { tokenAddress, challenge, createATA } = context;\n\n if (!challenge) {\n return Left(\n new Error(\n \"[ContextModule] - HttpOwnerInfoDataSource: challenge is required\",\n ),\n );\n }\n\n let ownerInfoResult: Either<Error, SolanaSPLOwnerInfo>;\n\n if (tokenAddress) {\n ownerInfoResult = await this.fetchAddressMetadata(\n tokenAddress,\n challenge,\n );\n } else if (createATA?.address && createATA?.mintAddress) {\n ownerInfoResult = await this.computeAddressMetadata(\n createATA.address,\n createATA.mintAddress,\n challenge,\n );\n } else {\n return Left(\n new Error(\n \"[ContextModule] - HttpOwnerInfoDataSource: either tokenAddress or valid createATA must be provided\",\n ),\n );\n }\n\n return ownerInfoResult.chain((ownerInfo) => {\n const tlvDescriptor = hexaStringToBuffer(ownerInfo.signedDescriptor);\n if (!tlvDescriptor) {\n return Left(\n new Error(\n \"[ContextModule] - HttpOwnerInfoDataSource: invalid base64 tlvDescriptor received\",\n ),\n );\n }\n return Right({\n tlvDescriptor,\n });\n });\n }\n}\n"],
|
|
5
|
+
"mappings": "iOAAA,OAEE,sBAAAA,MACK,kCACP,OAAS,UAAAC,EAAQ,cAAAC,MAAkB,YACnC,OAAiB,QAAAC,EAAM,SAAAC,MAAa,YAEpC,OAAS,eAAAC,MAAmB,0BAM5B,OAAS,gBAAAC,MAAoB,mCAQtB,IAAMC,EAAN,KAA6D,CAClE,YAEmBC,EAEAC,EACjB,CAHiB,YAAAD,EAEA,UAAAC,CAChB,CAEK,qBAAqBC,EAKE,CAC7B,OAAI,OAAOA,GAAS,UAAYA,IAAS,KAAa,GAEpD,OAAOA,EAAK,cAAiB,UAC7B,OAAOA,EAAK,OAAU,UACtB,OAAOA,EAAK,UAAa,UACzB,OAAOA,EAAK,kBAAqB,QAErC,CAEA,MAAM,qBACJC,EACAC,EAC4C,CAC5C,GAAI,CACF,MAAMF,EAAQ,MAAM,KAAK,KAAK,IAC5B,GAAG,KAAK,OAAO,sBAAsB,GAAG,oBAAoBC,CAAY,GACxE,CAAE,OAAQ,CAAE,UAAAC,CAAU,CAAE,CAC1B,EACA,OAAK,KAAK,qBAAqBF,CAAI,EAM5BG,EAAMH,CAAI,EALRI,EACL,IAAI,MACF,wFACF,CACF,CAEJ,MAAQ,CACN,OAAOA,EACL,IAAI,MACF,6EACF,CACF,CACF,CACF,CAEA,MAAM,uBACJC,EACAC,EACAJ,EAC4C,CAC5C,GAAI,CACF,MAAMF,EAAQ,MAAM,KAAK,KAAK,IAC5B,GAAG,KAAK,OAAO,sBAAsB,GAAG,qCAAqCK,CAAO,IAAIC,CAAW,GACnG,CAAE,OAAQ,CAAE,UAAAJ,CAAU,CAAE,CAC1B,EACA,OAAK,KAAK,qBAAqBF,CAAI,EAM5BG,EAAMH,CAAI,EALRI,EACL,IAAI,MACF,0FACF,CACF,CAEJ,MAAQ,CACN,OAAOA,EACL,IAAI,MACF,+EACF,CACF,CACF,CACF,CAEA,MAAM,aACJG,EACuD,CACvD,KAAM,CAAE,aAAAN,EAAc,UAAAC,EAAW,UAAAM,CAAU,EAAID,EAE/C,GAAI,CAACL,EACH,OAAOE,EACL,IAAI,MACF,kEACF,CACF,EAGF,IAAIK,EAEJ,GAAIR,EACFQ,EAAkB,MAAM,KAAK,qBAC3BR,EACAC,CACF,UACSM,GAAW,SAAWA,GAAW,YAC1CC,EAAkB,MAAM,KAAK,uBAC3BD,EAAU,QACVA,EAAU,YACVN,CACF,MAEA,QAAOE,EACL,IAAI,MACF,oGACF,CACF,EAGF,OAAOK,EAAgB,MAAOC,GAAc,CAC1C,MAAMC,EAAgBC,EAAmBF,EAAU,gBAAgB,EACnE,OAAKC,EAOER,EAAM,CACX,cAAAQ,CACF,CAAC,EARQP,EACL,IAAI,MACF,kFACF,CACF,CAKJ,CAAC,CACH,CACF,EA1HaP,EAANgB,EAAA,CADNC,EAAW,EAGPC,EAAA,EAAAC,EAAOC,EAAY,MAAM,GAEzBF,EAAA,EAAAC,EAAOE,EAAa,aAAa,IAJzBrB",
|
|
6
6
|
"names": ["hexaStringToBuffer", "inject", "injectable", "Left", "Right", "configTypes", "networkTypes", "HttpOwnerInfoDataSource", "config", "http", "data", "tokenAddress", "challenge", "Right", "Left", "address", "mintAddress", "context", "createATA", "ownerInfoResult", "ownerInfo", "tlvDescriptor", "hexaStringToBuffer", "__decorateClass", "injectable", "__decorateParam", "inject", "configTypes", "networkTypes"]
|
|
7
7
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{DeviceModelId as
|
|
1
|
+
import{DeviceModelId as a,hexaStringToBuffer as l}from"@ledgerhq/device-management-kit";import{Left as d}from"purify-ts";import{HttpOwnerInfoDataSource as r}from"../../../../modules/solana/owner-info/data/HttpOwnerInfoDataSource";function u(o){const c=new TextEncoder().encode(o);return Array.from(c).map(e=>e.toString(16).padStart(2,"0")).join("")}describe("HttpOwnerInfoDataSource",()=>{const o={metadataServiceDomain:{url:"https://some.doma.in"},originToken:"mock-origin-token"},i=u("mock-descriptor"),c={tokenAccount:"token-account",owner:"owner-address",contract:"contract-address",signedDescriptor:i};let e;beforeEach(()=>{vi.resetAllMocks(),e={get:vi.fn()}}),it("should fetch address metadata via tokenAddress",async()=>{const n={deviceModelId:a.FLEX,tokenAddress:"some-token",challenge:"random",createATA:void 0};e.get.mockResolvedValueOnce(c);const t=await new r(o,e).getOwnerInfo(n);expect(e.get).toHaveBeenCalledWith(`${o.metadataServiceDomain.url}/v2/solana/owner/some-token`,{params:{challenge:"random"}}),expect(t.isRight()).toBe(!0),expect(t.extract()).toEqual({tlvDescriptor:l(i)})}),it("should return an error if both tokenAddress and createATA are missing or invalid",async()=>{const n={deviceModelId:a.FLEX,tokenAddress:void 0,challenge:"random",createATA:void 0},t=await new r(o,e).getOwnerInfo(n);expect(t).toEqual(d(new Error("[ContextModule] - HttpOwnerInfoDataSource: either tokenAddress or valid createATA must be provided")))}),it("should return an error if challenge is missing",async()=>{const n={deviceModelId:a.FLEX,tokenAddress:"some-token",challenge:void 0,createATA:void 0},t=await new r(o,e).getOwnerInfo(n);expect(t).toEqual(d(new Error("[ContextModule] - HttpOwnerInfoDataSource: challenge is required")))}),it("should return an error if the descriptor is not valid base64",async()=>{e.get.mockResolvedValueOnce({...c,signedDescriptor:"!!!not-valid-base64!!!"});const n={deviceModelId:a.FLEX,tokenAddress:"some-token",challenge:"random",createATA:void 0},t=await new r(o,e).getOwnerInfo(n);expect(t).toEqual(d(new Error("[ContextModule] - HttpOwnerInfoDataSource: invalid base64 tlvDescriptor received")))}),it("should return an error if the metadata request fails",async()=>{e.get.mockRejectedValueOnce(new Error("Network error"));const n={deviceModelId:a.FLEX,tokenAddress:"some-token",challenge:"random",createATA:void 0},t=await new r(o,e).getOwnerInfo(n);expect(t).toEqual(d(new Error("[ContextModule] - HttpOwnerInfoDataSource: Failed to fetch address metadata")))}),it("should return an error if fetch request return wrong shape for fetchAddressMetadata",async()=>{e.get.mockResolvedValueOnce({wrong:"field"});const n={deviceModelId:a.FLEX,tokenAddress:"some-token",challenge:"random",createATA:void 0},t=await new r(o,e).getOwnerInfo(n);expect(t).toEqual(d(new Error("[ContextModule] - HttpOwnerInfoDataSource: invalid fetchAddressMetadata response shape")))}),it("should return an error if fetch request return wrong shape for computeAddressMetadata",async()=>{e.get.mockResolvedValueOnce({wrong:"field"});const n={deviceModelId:a.FLEX,tokenAddress:void 0,challenge:"random",createATA:{address:"some-address",mintAddress:"some-mint"}},t=await new r(o,e).getOwnerInfo(n);expect(e.get).toHaveBeenCalledWith(`${o.metadataServiceDomain.url}/v2/solana/computed-token-account/some-address/some-mint`,{params:{challenge:"random"}}),expect(t).toEqual(d(new Error("[ContextModule] - HttpOwnerInfoDataSource: invalid computeAddressMetadata response shape")))})});
|
|
2
2
|
//# sourceMappingURL=HttpOwnerInfoDataSource.test.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/solana/owner-info/data/HttpOwnerInfoDataSource.test.ts"],
|
|
4
|
-
"sourcesContent": ["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport {\n DeviceModelId,\n type DmkNetworkClient,\n hexaStringToBuffer,\n} from \"@ledgerhq/device-management-kit\";\nimport { Left } from \"purify-ts\";\n\nimport type { ContextModuleServiceConfig } from \"@/config/model/ContextModuleConfig\";\nimport { HttpOwnerInfoDataSource } from \"@/modules/solana/owner-info/data/HttpOwnerInfoDataSource\";\nimport type { SolanaTransactionContext } from \"@/modules/solana/owner-info/domain/solanaContextTypes\";\n\nfunction stringToHex(str: string): string {\n const encoder = new TextEncoder();\n const bytes = encoder.encode(str); // Uint8Array\n return Array.from(bytes)\n .map((byte) => byte.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\ndescribe(\"HttpOwnerInfoDataSource\", () => {\n const config = {\n metadataServiceDomain: { url: \"https://some.doma.in\" },\n originToken: \"mock-origin-token\",\n } as ContextModuleServiceConfig;\n\n const signedDescriptorHex = stringToHex(\"mock-descriptor\");\n const responseData = {\n tokenAccount: \"token-account\",\n owner: \"owner-address\",\n contract: \"contract-address\",\n signedDescriptor: signedDescriptorHex,\n };\n\n let httpMock: { get: ReturnType<typeof vi.fn> };\n\n beforeEach(() => {\n vi.resetAllMocks();\n httpMock = { get: vi.fn() };\n });\n\n it(\"should fetch address metadata via tokenAddress\", async () => {\n const context: SolanaTransactionContext = {\n deviceModelId: DeviceModelId.FLEX,\n tokenAddress: \"some-token\",\n challenge: \"random\",\n createATA: undefined,\n };\n httpMock.get.mockResolvedValueOnce(responseData);\n\n const dataSource = new HttpOwnerInfoDataSource(\n config,\n httpMock as unknown as DmkNetworkClient,\n );\n const result = await dataSource.getOwnerInfo(context);\n\n expect(httpMock.get).toHaveBeenCalledWith(\n `${config.metadataServiceDomain.url}/v2/solana/owner/some-token`,\n { params: { challenge: \"random\" } },\n );\n expect(result.isRight()).toBe(true);\n expect(result.extract()).toEqual({\n tlvDescriptor: hexaStringToBuffer(signedDescriptorHex),\n });\n });\n\n it(\"should return an error if both tokenAddress and createATA are missing or invalid\", async () => {\n const context: SolanaTransactionContext = {\n deviceModelId: DeviceModelId.FLEX,\n tokenAddress: undefined,\n challenge: \"random\",\n createATA: undefined,\n };\n\n const dataSource = new HttpOwnerInfoDataSource(\n config,\n httpMock as unknown as DmkNetworkClient,\n );\n const result = await dataSource.getOwnerInfo(context);\n\n expect(result).toEqual(\n Left(\n new Error(\n \"[ContextModule] - HttpOwnerInfoDataSource: either tokenAddress or valid createATA must be provided\",\n ),\n ),\n );\n });\n\n it(\"should return an error if challenge is missing\", async () => {\n const context: SolanaTransactionContext = {\n deviceModelId: DeviceModelId.FLEX,\n tokenAddress: \"some-token\",\n challenge: undefined,\n createATA: undefined,\n };\n\n const dataSource = new HttpOwnerInfoDataSource(\n config,\n httpMock as unknown as DmkNetworkClient,\n );\n const result = await dataSource.getOwnerInfo(context);\n\n expect(result).toEqual(\n Left(\n new Error(\n \"[ContextModule] - HttpOwnerInfoDataSource: challenge is required\",\n ),\n ),\n );\n });\n\n it(\"should return an error if the descriptor is not valid base64\", async () => {\n httpMock.get.mockResolvedValueOnce({\n ...responseData,\n signedDescriptor: \"!!!not-valid-base64!!!\",\n });\n const context: SolanaTransactionContext = {\n deviceModelId: DeviceModelId.FLEX,\n tokenAddress: \"some-token\",\n challenge: \"random\",\n createATA: undefined,\n };\n\n const dataSource = new HttpOwnerInfoDataSource(\n config,\n httpMock as unknown as DmkNetworkClient,\n );\n const result = await dataSource.getOwnerInfo(context);\n\n expect(result).toEqual(\n Left(\n new Error(\n \"[ContextModule] - HttpOwnerInfoDataSource: invalid base64 tlvDescriptor received\",\n ),\n ),\n );\n });\n\n it(\"should return an error if the metadata request fails\", async () => {\n httpMock.get.mockRejectedValueOnce(new Error(\"Network error\"));\n const context: SolanaTransactionContext = {\n deviceModelId: DeviceModelId.FLEX,\n tokenAddress: \"some-token\",\n challenge: \"random\",\n createATA: undefined,\n };\n\n const dataSource = new HttpOwnerInfoDataSource(\n config,\n httpMock as unknown as DmkNetworkClient,\n );\n const result = await dataSource.getOwnerInfo(context);\n\n expect(result).toEqual(\n Left(\n new Error(\n \"[ContextModule] - HttpOwnerInfoDataSource: Failed to fetch address metadata\",\n ),\n ),\n );\n });\n\n it(\"should return an error if fetch request return wrong shape for fetchAddressMetadata\", async () => {\n httpMock.get.mockResolvedValueOnce({ wrong: \"field\" });\n\n const context: SolanaTransactionContext = {\n deviceModelId: DeviceModelId.FLEX,\n tokenAddress: \"some-token\",\n challenge: \"random\",\n createATA: undefined,\n };\n\n const dataSource = new HttpOwnerInfoDataSource(\n config,\n httpMock as unknown as DmkNetworkClient,\n );\n const result = await dataSource.getOwnerInfo(context);\n\n expect(result).toEqual(\n Left(\n new Error(\n \"[ContextModule] - HttpOwnerInfoDataSource: invalid fetchAddressMetadata response shape\",\n ),\n ),\n );\n });\n\n it(\"should return an error if fetch request return wrong shape for computeAddressMetadata\", async () => {\n httpMock.get.mockResolvedValueOnce({ wrong: \"field\" });\n\n const context: SolanaTransactionContext = {\n deviceModelId: DeviceModelId.FLEX,\n tokenAddress: undefined,\n challenge: \"random\",\n createATA: {\n address: \"some-address\",\n mintAddress: \"some-mint\",\n },\n };\n\n const dataSource = new HttpOwnerInfoDataSource(\n config,\n httpMock as unknown as DmkNetworkClient,\n );\n const result = await dataSource.getOwnerInfo(context);\n\n expect(httpMock.get).toHaveBeenCalledWith(\n `${config.metadataServiceDomain.url}/v2/solana/computed-token-account/some-address/some-mint`,\n { params: { challenge: \"random\" } },\n );\n expect(result).toEqual(\n Left(\n new Error(\n \"[ContextModule] - HttpOwnerInfoDataSource: invalid computeAddressMetadata response shape\",\n ),\n ),\n );\n });\n
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport {\n DeviceModelId,\n type DmkNetworkClient,\n hexaStringToBuffer,\n} from \"@ledgerhq/device-management-kit\";\nimport { Left } from \"purify-ts\";\n\nimport type { ContextModuleServiceConfig } from \"@/config/model/ContextModuleConfig\";\nimport { HttpOwnerInfoDataSource } from \"@/modules/solana/owner-info/data/HttpOwnerInfoDataSource\";\nimport type { SolanaTransactionContext } from \"@/modules/solana/owner-info/domain/solanaContextTypes\";\n\nfunction stringToHex(str: string): string {\n const encoder = new TextEncoder();\n const bytes = encoder.encode(str); // Uint8Array\n return Array.from(bytes)\n .map((byte) => byte.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\ndescribe(\"HttpOwnerInfoDataSource\", () => {\n const config = {\n metadataServiceDomain: { url: \"https://some.doma.in\" },\n originToken: \"mock-origin-token\",\n } as ContextModuleServiceConfig;\n\n const signedDescriptorHex = stringToHex(\"mock-descriptor\");\n const responseData = {\n tokenAccount: \"token-account\",\n owner: \"owner-address\",\n contract: \"contract-address\",\n signedDescriptor: signedDescriptorHex,\n };\n\n let httpMock: { get: ReturnType<typeof vi.fn> };\n\n beforeEach(() => {\n vi.resetAllMocks();\n httpMock = { get: vi.fn() };\n });\n\n it(\"should fetch address metadata via tokenAddress\", async () => {\n const context: SolanaTransactionContext = {\n deviceModelId: DeviceModelId.FLEX,\n tokenAddress: \"some-token\",\n challenge: \"random\",\n createATA: undefined,\n };\n httpMock.get.mockResolvedValueOnce(responseData);\n\n const dataSource = new HttpOwnerInfoDataSource(\n config,\n httpMock as unknown as DmkNetworkClient,\n );\n const result = await dataSource.getOwnerInfo(context);\n\n expect(httpMock.get).toHaveBeenCalledWith(\n `${config.metadataServiceDomain.url}/v2/solana/owner/some-token`,\n { params: { challenge: \"random\" } },\n );\n expect(result.isRight()).toBe(true);\n expect(result.extract()).toEqual({\n tlvDescriptor: hexaStringToBuffer(signedDescriptorHex),\n });\n });\n\n it(\"should return an error if both tokenAddress and createATA are missing or invalid\", async () => {\n const context: SolanaTransactionContext = {\n deviceModelId: DeviceModelId.FLEX,\n tokenAddress: undefined,\n challenge: \"random\",\n createATA: undefined,\n };\n\n const dataSource = new HttpOwnerInfoDataSource(\n config,\n httpMock as unknown as DmkNetworkClient,\n );\n const result = await dataSource.getOwnerInfo(context);\n\n expect(result).toEqual(\n Left(\n new Error(\n \"[ContextModule] - HttpOwnerInfoDataSource: either tokenAddress or valid createATA must be provided\",\n ),\n ),\n );\n });\n\n it(\"should return an error if challenge is missing\", async () => {\n const context: SolanaTransactionContext = {\n deviceModelId: DeviceModelId.FLEX,\n tokenAddress: \"some-token\",\n challenge: undefined,\n createATA: undefined,\n };\n\n const dataSource = new HttpOwnerInfoDataSource(\n config,\n httpMock as unknown as DmkNetworkClient,\n );\n const result = await dataSource.getOwnerInfo(context);\n\n expect(result).toEqual(\n Left(\n new Error(\n \"[ContextModule] - HttpOwnerInfoDataSource: challenge is required\",\n ),\n ),\n );\n });\n\n it(\"should return an error if the descriptor is not valid base64\", async () => {\n httpMock.get.mockResolvedValueOnce({\n ...responseData,\n signedDescriptor: \"!!!not-valid-base64!!!\",\n });\n const context: SolanaTransactionContext = {\n deviceModelId: DeviceModelId.FLEX,\n tokenAddress: \"some-token\",\n challenge: \"random\",\n createATA: undefined,\n };\n\n const dataSource = new HttpOwnerInfoDataSource(\n config,\n httpMock as unknown as DmkNetworkClient,\n );\n const result = await dataSource.getOwnerInfo(context);\n\n expect(result).toEqual(\n Left(\n new Error(\n \"[ContextModule] - HttpOwnerInfoDataSource: invalid base64 tlvDescriptor received\",\n ),\n ),\n );\n });\n\n it(\"should return an error if the metadata request fails\", async () => {\n httpMock.get.mockRejectedValueOnce(new Error(\"Network error\"));\n const context: SolanaTransactionContext = {\n deviceModelId: DeviceModelId.FLEX,\n tokenAddress: \"some-token\",\n challenge: \"random\",\n createATA: undefined,\n };\n\n const dataSource = new HttpOwnerInfoDataSource(\n config,\n httpMock as unknown as DmkNetworkClient,\n );\n const result = await dataSource.getOwnerInfo(context);\n\n expect(result).toEqual(\n Left(\n new Error(\n \"[ContextModule] - HttpOwnerInfoDataSource: Failed to fetch address metadata\",\n ),\n ),\n );\n });\n\n it(\"should return an error if fetch request return wrong shape for fetchAddressMetadata\", async () => {\n httpMock.get.mockResolvedValueOnce({ wrong: \"field\" });\n\n const context: SolanaTransactionContext = {\n deviceModelId: DeviceModelId.FLEX,\n tokenAddress: \"some-token\",\n challenge: \"random\",\n createATA: undefined,\n };\n\n const dataSource = new HttpOwnerInfoDataSource(\n config,\n httpMock as unknown as DmkNetworkClient,\n );\n const result = await dataSource.getOwnerInfo(context);\n\n expect(result).toEqual(\n Left(\n new Error(\n \"[ContextModule] - HttpOwnerInfoDataSource: invalid fetchAddressMetadata response shape\",\n ),\n ),\n );\n });\n\n it(\"should return an error if fetch request return wrong shape for computeAddressMetadata\", async () => {\n httpMock.get.mockResolvedValueOnce({ wrong: \"field\" });\n\n const context: SolanaTransactionContext = {\n deviceModelId: DeviceModelId.FLEX,\n tokenAddress: undefined,\n challenge: \"random\",\n createATA: {\n address: \"some-address\",\n mintAddress: \"some-mint\",\n },\n };\n\n const dataSource = new HttpOwnerInfoDataSource(\n config,\n httpMock as unknown as DmkNetworkClient,\n );\n const result = await dataSource.getOwnerInfo(context);\n\n expect(httpMock.get).toHaveBeenCalledWith(\n `${config.metadataServiceDomain.url}/v2/solana/computed-token-account/some-address/some-mint`,\n { params: { challenge: \"random\" } },\n );\n expect(result).toEqual(\n Left(\n new Error(\n \"[ContextModule] - HttpOwnerInfoDataSource: invalid computeAddressMetadata response shape\",\n ),\n ),\n );\n });\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["DeviceModelId", "hexaStringToBuffer", "Left", "HttpOwnerInfoDataSource", "stringToHex", "str", "bytes", "byte", "config", "signedDescriptorHex", "responseData", "httpMock", "context", "result"]
|
|
7
7
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
1
|
+
var N=Object.defineProperty;var k=Object.getOwnPropertyDescriptor;var y=(i,e,o,n)=>{for(var r=n>1?void 0:n?k(e,o):e,s=i.length-1,a;s>=0;s--)(a=i[s])&&(r=(n?a(e,o,r):a(r))||r);return n&&r&&N(e,o,r),r},p=(i,e)=>(o,n)=>e(o,n,i);import{inject as C,injectable as E}from"inversify";import{Codec as d,Left as u,optional as S,Right as x,string as m}from"purify-ts";import{configTypes as w}from"../../../../config/di/configTypes";import{SIGNATURE_TAG as M}from"../../../../shared/model/SignatureTags";import{networkTypes as $}from"../../../../shared/network/di/networkTypes";import{HexStringUtils as T}from"../../../../shared/utils/HexStringUtils";const D=d.interface({signedDescriptor:d.interface({data:m,signatures:d.interface({prod:S(m),test:S(m)})}),keyId:m,keyUsage:m});let l=class{constructor(e,o){this.config=e;this.http=o}async getTrustedName({address:e,network:o,challenge:n,types:r,sources:s}){let a;try{a=await this.http.get(`${this.config.metadataServiceDomain.url}/v2/names/solana/${o}/reverse/${e}`,{params:{types:r.join(","),sources:s.join(","),challenge:n}})}catch(t){const c=t instanceof Error?t.message:String(t);return u(new Error(`[ContextModule] HttpSolanaTrustedNameDataSource: Failed to fetch trusted name: ${c}`))}return D.decode(a).caseOf({Left:t=>u(new Error(`[ContextModule] HttpSolanaTrustedNameDataSource: malformed response for ${e}: ${t}`)),Right:t=>{const c=this.config.cal.mode??"prod",f=t.signedDescriptor.signatures[c];if(!f)return u(new Error(`[ContextModule] HttpSolanaTrustedNameDataSource: missing ${c} signature for ${e}`));let g;try{g=T.hexToBytes(T.appendSignatureToPayload(t.signedDescriptor.data,f,M))}catch(h){return u(new Error(`[ContextModule] HttpSolanaTrustedNameDataSource: invalid signed descriptor for ${e}: ${h.message}`))}return x({address:e,descriptor:g,keyId:t.keyId,keyUsage:t.keyUsage})}})}};l=y([E(),p(0,C(w.Config)),p(1,C($.NetworkClient))],l);export{l as HttpSolanaTrustedNameDataSource};
|
|
2
2
|
//# sourceMappingURL=HttpTrustedNameDataSource.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/solana/trusted-name/data/HttpTrustedNameDataSource.ts"],
|
|
4
|
-
"sourcesContent": ["import { DmkNetworkClient } from \"@ledgerhq/device-management-kit\";\nimport { inject, injectable } from \"inversify\";\nimport { Codec, Either, Left, optional, Right, string } from \"purify-ts\";\n\nimport { configTypes } from \"@/config/di/configTypes\";\nimport {\n type ContextModuleCalMode,\n type ContextModuleServiceConfig,\n} from \"@/config/model/ContextModuleConfig\";\nimport { SIGNATURE_TAG } from \"@/shared/model/SignatureTags\";\nimport { networkTypes } from \"@/shared/network/di/networkTypes\";\nimport { HexStringUtils } from \"@/shared/utils/HexStringUtils\";\n\nimport {\n type GetSolanaTrustedNameParams,\n type SolanaTrustedNameDataSource,\n type SolanaTrustedNameResult,\n} from \"./TrustedNameDataSource\";\n\nconst trustedNameResponseCodec = Codec.interface({\n signedDescriptor: Codec.interface({\n data: string,\n signatures: Codec.interface({\n prod: optional(string),\n test: optional(string),\n }),\n }),\n keyId: string,\n keyUsage: string,\n});\n\n@injectable()\nexport class HttpSolanaTrustedNameDataSource\n implements SolanaTrustedNameDataSource\n{\n constructor(\n @inject(configTypes.Config)\n private readonly config: ContextModuleServiceConfig,\n @inject(networkTypes.NetworkClient)\n private readonly http: DmkNetworkClient,\n ) {}\n\n public async getTrustedName({\n address,\n network,\n challenge,\n sources,\n }: GetSolanaTrustedNameParams): Promise<\n Either<Error, SolanaTrustedNameResult>\n > {\n let dto: unknown;\n try {\n dto = await this.http.get(\n `${this.config.metadataServiceDomain.url}/v2/names/solana/${network}/reverse/${address}`,\n {\n params: {\n sources: sources.join(\",\"),\n challenge,\n },\n },\n );\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n return Left(\n new Error(\n `[ContextModule] HttpSolanaTrustedNameDataSource: Failed to fetch trusted name: ${reason}`,\n ),\n );\n }\n\n return trustedNameResponseCodec\n .decode(dto)\n .caseOf<Either<Error, SolanaTrustedNameResult>>({\n Left: (error) =>\n Left(\n new Error(\n `[ContextModule] HttpSolanaTrustedNameDataSource: malformed response for ${address}: ${error}`,\n ),\n ),\n Right: (validated) => {\n const mode: ContextModuleCalMode = this.config.cal.mode ?? \"prod\";\n const signature = validated.signedDescriptor.signatures[mode];\n if (!signature) {\n return Left(\n new Error(\n `[ContextModule] HttpSolanaTrustedNameDataSource: missing ${mode} signature for ${address}`,\n ),\n );\n }\n\n let descriptor: Uint8Array;\n try {\n descriptor = HexStringUtils.hexToBytes(\n HexStringUtils.appendSignatureToPayload(\n validated.signedDescriptor.data,\n signature,\n SIGNATURE_TAG,\n ),\n );\n } catch (error) {\n return Left(\n new Error(\n `[ContextModule] HttpSolanaTrustedNameDataSource: invalid signed descriptor for ${address}: ${(error as Error).message}`,\n ),\n );\n }\n\n return Right({\n address,\n descriptor,\n keyId: validated.keyId,\n keyUsage: validated.keyUsage,\n });\n },\n });\n }\n}\n"],
|
|
5
|
-
"mappings": "iOACA,OAAS,UAAAA,EAAQ,cAAAC,MAAkB,YACnC,OAAS,SAAAC,EAAe,QAAAC,EAAM,YAAAC,EAAU,SAAAC,EAAO,UAAAC,MAAc,YAE7D,OAAS,eAAAC,MAAmB,0BAK5B,OAAS,iBAAAC,MAAqB,+BAC9B,OAAS,gBAAAC,MAAoB,mCAC7B,OAAS,kBAAAC,MAAsB,gCAQ/B,MAAMC,EAA2BC,EAAM,UAAU,CAC/C,iBAAkBA,EAAM,UAAU,CAChC,KAAMC,EACN,WAAYD,EAAM,UAAU,CAC1B,KAAME,EAASD,CAAM,EACrB,KAAMC,EAASD,CAAM,CACvB,CAAC,CACH,CAAC,EACD,MAAOA,EACP,SAAUA,CACZ,CAAC,EAGM,IAAME,EAAN,KAEP,CACE,YAEmBC,EAEAC,EACjB,CAHiB,YAAAD,EAEA,UAAAC,CAChB,CAEH,MAAa,eAAe,CAC1B,QAAAC,EACA,QAAAC,EACA,UAAAC,EACA,QAAAC,CACF,EAEE,CACA,IAAIC,EACJ,GAAI,CACFA,EAAM,MAAM,KAAK,KAAK,IACpB,GAAG,KAAK,OAAO,sBAAsB,GAAG,
|
|
6
|
-
"names": ["inject", "injectable", "Codec", "Left", "optional", "Right", "string", "configTypes", "SIGNATURE_TAG", "networkTypes", "HexStringUtils", "trustedNameResponseCodec", "Codec", "string", "optional", "HttpSolanaTrustedNameDataSource", "config", "http", "address", "network", "challenge", "sources", "dto", "error", "reason", "Left", "validated", "mode", "signature", "descriptor", "HexStringUtils", "SIGNATURE_TAG", "Right", "__decorateClass", "injectable", "__decorateParam", "inject", "configTypes", "networkTypes"]
|
|
4
|
+
"sourcesContent": ["import { DmkNetworkClient } from \"@ledgerhq/device-management-kit\";\nimport { inject, injectable } from \"inversify\";\nimport { Codec, Either, Left, optional, Right, string } from \"purify-ts\";\n\nimport { configTypes } from \"@/config/di/configTypes\";\nimport {\n type ContextModuleCalMode,\n type ContextModuleServiceConfig,\n} from \"@/config/model/ContextModuleConfig\";\nimport { SIGNATURE_TAG } from \"@/shared/model/SignatureTags\";\nimport { networkTypes } from \"@/shared/network/di/networkTypes\";\nimport { HexStringUtils } from \"@/shared/utils/HexStringUtils\";\n\nimport {\n type GetSolanaTrustedNameParams,\n type SolanaTrustedNameDataSource,\n type SolanaTrustedNameResult,\n} from \"./TrustedNameDataSource\";\n\nconst trustedNameResponseCodec = Codec.interface({\n signedDescriptor: Codec.interface({\n data: string,\n signatures: Codec.interface({\n prod: optional(string),\n test: optional(string),\n }),\n }),\n keyId: string,\n keyUsage: string,\n});\n\n@injectable()\nexport class HttpSolanaTrustedNameDataSource\n implements SolanaTrustedNameDataSource\n{\n constructor(\n @inject(configTypes.Config)\n private readonly config: ContextModuleServiceConfig,\n @inject(networkTypes.NetworkClient)\n private readonly http: DmkNetworkClient,\n ) {}\n\n public async getTrustedName({\n address,\n network,\n challenge,\n types,\n sources,\n }: GetSolanaTrustedNameParams): Promise<\n Either<Error, SolanaTrustedNameResult>\n > {\n let dto: unknown;\n try {\n dto = await this.http.get(\n `${this.config.metadataServiceDomain.url}/v2/names/solana/${network}/reverse/${address}`,\n {\n params: {\n types: types.join(\",\"),\n sources: sources.join(\",\"),\n challenge,\n },\n },\n );\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n return Left(\n new Error(\n `[ContextModule] HttpSolanaTrustedNameDataSource: Failed to fetch trusted name: ${reason}`,\n ),\n );\n }\n\n return trustedNameResponseCodec\n .decode(dto)\n .caseOf<Either<Error, SolanaTrustedNameResult>>({\n Left: (error) =>\n Left(\n new Error(\n `[ContextModule] HttpSolanaTrustedNameDataSource: malformed response for ${address}: ${error}`,\n ),\n ),\n Right: (validated) => {\n const mode: ContextModuleCalMode = this.config.cal.mode ?? \"prod\";\n const signature = validated.signedDescriptor.signatures[mode];\n if (!signature) {\n return Left(\n new Error(\n `[ContextModule] HttpSolanaTrustedNameDataSource: missing ${mode} signature for ${address}`,\n ),\n );\n }\n\n let descriptor: Uint8Array;\n try {\n descriptor = HexStringUtils.hexToBytes(\n HexStringUtils.appendSignatureToPayload(\n validated.signedDescriptor.data,\n signature,\n SIGNATURE_TAG,\n ),\n );\n } catch (error) {\n return Left(\n new Error(\n `[ContextModule] HttpSolanaTrustedNameDataSource: invalid signed descriptor for ${address}: ${(error as Error).message}`,\n ),\n );\n }\n\n return Right({\n address,\n descriptor,\n keyId: validated.keyId,\n keyUsage: validated.keyUsage,\n });\n },\n });\n }\n}\n"],
|
|
5
|
+
"mappings": "iOACA,OAAS,UAAAA,EAAQ,cAAAC,MAAkB,YACnC,OAAS,SAAAC,EAAe,QAAAC,EAAM,YAAAC,EAAU,SAAAC,EAAO,UAAAC,MAAc,YAE7D,OAAS,eAAAC,MAAmB,0BAK5B,OAAS,iBAAAC,MAAqB,+BAC9B,OAAS,gBAAAC,MAAoB,mCAC7B,OAAS,kBAAAC,MAAsB,gCAQ/B,MAAMC,EAA2BC,EAAM,UAAU,CAC/C,iBAAkBA,EAAM,UAAU,CAChC,KAAMC,EACN,WAAYD,EAAM,UAAU,CAC1B,KAAME,EAASD,CAAM,EACrB,KAAMC,EAASD,CAAM,CACvB,CAAC,CACH,CAAC,EACD,MAAOA,EACP,SAAUA,CACZ,CAAC,EAGM,IAAME,EAAN,KAEP,CACE,YAEmBC,EAEAC,EACjB,CAHiB,YAAAD,EAEA,UAAAC,CAChB,CAEH,MAAa,eAAe,CAC1B,QAAAC,EACA,QAAAC,EACA,UAAAC,EACA,MAAAC,EACA,QAAAC,CACF,EAEE,CACA,IAAIC,EACJ,GAAI,CACFA,EAAM,MAAM,KAAK,KAAK,IACpB,GAAG,KAAK,OAAO,sBAAsB,GAAG,oBAAoBJ,CAAO,YAAYD,CAAO,GACtF,CACE,OAAQ,CACN,MAAOG,EAAM,KAAK,GAAG,EACrB,QAASC,EAAQ,KAAK,GAAG,EACzB,UAAAF,CACF,CACF,CACF,CACF,OAASI,EAAO,CACd,MAAMC,EAASD,aAAiB,MAAQA,EAAM,QAAU,OAAOA,CAAK,EACpE,OAAOE,EACL,IAAI,MACF,kFAAkFD,CAAM,EAC1F,CACF,CACF,CAEA,OAAOd,EACJ,OAAOY,CAAG,EACV,OAA+C,CAC9C,KAAOC,GACLE,EACE,IAAI,MACF,2EAA2ER,CAAO,KAAKM,CAAK,EAC9F,CACF,EACF,MAAQG,GAAc,CACpB,MAAMC,EAA6B,KAAK,OAAO,IAAI,MAAQ,OACrDC,EAAYF,EAAU,iBAAiB,WAAWC,CAAI,EAC5D,GAAI,CAACC,EACH,OAAOH,EACL,IAAI,MACF,4DAA4DE,CAAI,kBAAkBV,CAAO,EAC3F,CACF,EAGF,IAAIY,EACJ,GAAI,CACFA,EAAaC,EAAe,WAC1BA,EAAe,yBACbJ,EAAU,iBAAiB,KAC3BE,EACAG,CACF,CACF,CACF,OAASR,EAAO,CACd,OAAOE,EACL,IAAI,MACF,kFAAkFR,CAAO,KAAMM,EAAgB,OAAO,EACxH,CACF,CACF,CAEA,OAAOS,EAAM,CACX,QAAAf,EACA,WAAAY,EACA,MAAOH,EAAU,MACjB,SAAUA,EAAU,QACtB,CAAC,CACH,CACF,CAAC,CACL,CACF,EAtFaZ,EAANmB,EAAA,CADNC,EAAW,EAKPC,EAAA,EAAAC,EAAOC,EAAY,MAAM,GAEzBF,EAAA,EAAAC,EAAOE,EAAa,aAAa,IANzBxB",
|
|
6
|
+
"names": ["inject", "injectable", "Codec", "Left", "optional", "Right", "string", "configTypes", "SIGNATURE_TAG", "networkTypes", "HexStringUtils", "trustedNameResponseCodec", "Codec", "string", "optional", "HttpSolanaTrustedNameDataSource", "config", "http", "address", "network", "challenge", "types", "sources", "dto", "error", "reason", "Left", "validated", "mode", "signature", "descriptor", "HexStringUtils", "SIGNATURE_TAG", "Right", "__decorateClass", "injectable", "__decorateParam", "inject", "configTypes", "networkTypes"]
|
|
7
7
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{Left as g,Right as u}from"purify-ts";import{beforeEach as
|
|
1
|
+
import{Left as g,Right as u}from"purify-ts";import{beforeEach as k,describe as y,expect as s,it as c,vi as l}from"vitest";import{SolanaTransactionScanChainId as n}from"../../../../modules/solana/model/SolanaTransactionScanChainId";import{HttpSolanaTrustedNameDataSource as f}from"./HttpTrustedNameDataSource";y("HttpSolanaTrustedNameDataSource",()=>{let r,a;const t="vitalik.sol",o="deadbeef",m=(e="prod")=>({metadataServiceDomain:{url:"https://nft.api.ledger.com"},cal:{mode:e}}),d=(e="prod")=>new f(m(e),a),i=(e,p="ff00")=>({descriptorType:"name",descriptorVersion:"2",signedDescriptor:{data:p,signatures:e},keyId:"cal_trusted_name",keyUsage:"trusted_name"});k(()=>{l.clearAllMocks(),a={get:l.fn()},r=d()}),c("calls the names endpoint with network, types, sources and challenge",async()=>{a.get.mockResolvedValue(i({prod:"aabb"})),await r.getTrustedName({address:t,network:String(n.MAINNET),challenge:o,types:["token","smart_contract"],sources:["crypto_asset_list"]}),s(a.get).toHaveBeenCalledWith(`https://nft.api.ledger.com/v2/names/solana/${n.MAINNET}/reverse/${t}`,{params:{types:"token,smart_contract",sources:"crypto_asset_list",challenge:o}})}),c("appends the prod signature TLV and decodes into a Uint8Array",async()=>{a.get.mockResolvedValue(i({prod:"aabb",test:"cc"}));const e=await r.getTrustedName({address:t,network:String(n.MAINNET),challenge:o,types:["token","smart_contract"],sources:["crypto_asset_list"]});s(e).toEqual(u({address:t,descriptor:new Uint8Array([255,0,21,2,170,187]),keyId:"cal_trusted_name",keyUsage:"trusted_name"}))}),c("selects the test signature when cal.mode is test",async()=>{r=d("test"),a.get.mockResolvedValue(i({prod:"aabb",test:"cc"}));const e=await r.getTrustedName({address:t,network:String(n.MAINNET),challenge:o,types:["token","smart_contract"],sources:["crypto_asset_list"]});s(e).toEqual(u({address:t,descriptor:new Uint8Array([255,0,21,1,204]),keyId:"cal_trusted_name",keyUsage:"trusted_name"}))}),c("returns Left when the signature for the active mode is missing",async()=>{r=d("prod"),a.get.mockResolvedValue(i({test:"cc"}));const e=await r.getTrustedName({address:t,network:String(n.MAINNET),challenge:o,types:["token","smart_contract"],sources:["crypto_asset_list"]});s(e.isLeft()).toBe(!0),s(e.extract().message).toMatch(new RegExp(String.raw`\[ContextModule\] HttpSolanaTrustedNameDataSource: missing prod signature for ${t}`))}),c("returns Left on malformed response (hex-string signedDescriptor)",async()=>{a.get.mockResolvedValue({signedDescriptor:"01020304",keyId:"k",keyUsage:"u"});const e=await r.getTrustedName({address:t,network:String(n.MAINNET),challenge:o,types:[],sources:[]});s(e.isLeft()).toBe(!0),s(e.extract().message).toMatch(new RegExp(String.raw`\[ContextModule\] HttpSolanaTrustedNameDataSource: malformed response for ${t}:`))}),c("returns Left when HTTP client throws",async()=>{a.get.mockRejectedValue(new Error("net"));const e=await r.getTrustedName({address:t,network:String(n.MAINNET),challenge:o,types:[],sources:[]});s(e).toEqual(g(new Error("[ContextModule] HttpSolanaTrustedNameDataSource: Failed to fetch trusted name: net")))})});
|
|
2
2
|
//# sourceMappingURL=HttpTrustedNameDataSource.test.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/solana/trusted-name/data/HttpTrustedNameDataSource.test.ts"],
|
|
4
|
-
"sourcesContent": ["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { type DmkNetworkClient } from \"@ledgerhq/device-management-kit\";\nimport { Left, Right } from \"purify-ts\";\nimport { beforeEach, describe, expect, it, vi } from \"vitest\";\n\nimport { type ContextModuleServiceConfig } from \"@/config/model/ContextModuleConfig\";\nimport { SolanaTransactionScanChainId } from \"@/modules/solana/model/SolanaTransactionScanChainId\";\n\nimport { HttpSolanaTrustedNameDataSource } from \"./HttpTrustedNameDataSource\";\nimport { type SolanaTrustedNameDataSource } from \"./TrustedNameDataSource\";\n\ndescribe(\"HttpSolanaTrustedNameDataSource\", () => {\n let datasource: SolanaTrustedNameDataSource;\n let httpMock: { get: ReturnType<typeof vi.fn> };\n const address = \"vitalik.sol\";\n const challenge = \"deadbeef\";\n\n const makeConfig = (mode: \"prod\" | \"test\" = \"prod\") =>\n ({\n metadataServiceDomain: { url: \"https://nft.api.ledger.com\" },\n cal: { mode },\n }) as ContextModuleServiceConfig;\n\n const makeDatasource = (mode: \"prod\" | \"test\" = \"prod\") =>\n new HttpSolanaTrustedNameDataSource(\n makeConfig(mode),\n httpMock as unknown as DmkNetworkClient,\n );\n\n const objectResponse = (\n signatures: { prod?: string; test?: string },\n data = \"ff00\",\n ) => ({\n descriptorType: \"name\",\n descriptorVersion: \"2\",\n signedDescriptor: { data, signatures },\n keyId: \"cal_trusted_name\",\n keyUsage: \"trusted_name\",\n });\n\n beforeEach(() => {\n vi.clearAllMocks();\n httpMock = { get: vi.fn() };\n datasource = makeDatasource();\n });\n\n it(\"calls the names endpoint with network, sources and challenge
|
|
5
|
-
"mappings": "AAEA,OAAS,QAAAA,EAAM,SAAAC,MAAa,YAC5B,OAAS,cAAAC,EAAY,YAAAC,EAAU,UAAAC,EAAQ,MAAAC,EAAI,MAAAC,MAAU,SAGrD,OAAS,gCAAAC,MAAoC,sDAE7C,OAAS,mCAAAC,MAAuC,8BAGhDL,EAAS,kCAAmC,IAAM,CAChD,IAAIM,EACAC,EACJ,MAAMC,EAAU,cACVC,EAAY,WAEZC,EAAa,CAACC,EAAwB,UACzC,CACC,sBAAuB,CAAE,IAAK,4BAA6B,EAC3D,IAAK,CAAE,KAAAA,CAAK,CACd,GAEIC,EAAiB,CAACD,EAAwB,SAC9C,IAAIN,EACFK,EAAWC,CAAI,EACfJ,CACF,EAEIM,EAAiB,CACrBC,EACAC,EAAO,UACH,CACJ,eAAgB,OAChB,kBAAmB,IACnB,iBAAkB,CAAE,KAAAA,EAAM,WAAAD,CAAW,EACrC,MAAO,mBACP,SAAU,cACZ,GAEAf,EAAW,IAAM,CACfI,EAAG,cAAc,EACjBI,EAAW,CAAE,IAAKJ,EAAG,GAAG,CAAE,EAC1BG,EAAaM,EAAe,CAC9B,CAAC,EAEDV,EAAG,
|
|
4
|
+
"sourcesContent": ["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { type DmkNetworkClient } from \"@ledgerhq/device-management-kit\";\nimport { Left, Right } from \"purify-ts\";\nimport { beforeEach, describe, expect, it, vi } from \"vitest\";\n\nimport { type ContextModuleServiceConfig } from \"@/config/model/ContextModuleConfig\";\nimport { SolanaTransactionScanChainId } from \"@/modules/solana/model/SolanaTransactionScanChainId\";\n\nimport { HttpSolanaTrustedNameDataSource } from \"./HttpTrustedNameDataSource\";\nimport { type SolanaTrustedNameDataSource } from \"./TrustedNameDataSource\";\n\ndescribe(\"HttpSolanaTrustedNameDataSource\", () => {\n let datasource: SolanaTrustedNameDataSource;\n let httpMock: { get: ReturnType<typeof vi.fn> };\n const address = \"vitalik.sol\";\n const challenge = \"deadbeef\";\n\n const makeConfig = (mode: \"prod\" | \"test\" = \"prod\") =>\n ({\n metadataServiceDomain: { url: \"https://nft.api.ledger.com\" },\n cal: { mode },\n }) as ContextModuleServiceConfig;\n\n const makeDatasource = (mode: \"prod\" | \"test\" = \"prod\") =>\n new HttpSolanaTrustedNameDataSource(\n makeConfig(mode),\n httpMock as unknown as DmkNetworkClient,\n );\n\n const objectResponse = (\n signatures: { prod?: string; test?: string },\n data = \"ff00\",\n ) => ({\n descriptorType: \"name\",\n descriptorVersion: \"2\",\n signedDescriptor: { data, signatures },\n keyId: \"cal_trusted_name\",\n keyUsage: \"trusted_name\",\n });\n\n beforeEach(() => {\n vi.clearAllMocks();\n httpMock = { get: vi.fn() };\n datasource = makeDatasource();\n });\n\n it(\"calls the names endpoint with network, types, sources and challenge\", async () => {\n httpMock.get.mockResolvedValue(objectResponse({ prod: \"aabb\" }));\n\n await datasource.getTrustedName({\n address,\n network: String(SolanaTransactionScanChainId.MAINNET),\n challenge,\n types: [\"token\", \"smart_contract\"],\n sources: [\"crypto_asset_list\"],\n });\n\n expect(httpMock.get).toHaveBeenCalledWith(\n `https://nft.api.ledger.com/v2/names/solana/${SolanaTransactionScanChainId.MAINNET}/reverse/${address}`,\n {\n params: {\n types: \"token,smart_contract\",\n sources: \"crypto_asset_list\",\n challenge,\n },\n },\n );\n });\n\n it(\"appends the prod signature TLV and decodes into a Uint8Array\", async () => {\n httpMock.get.mockResolvedValue(\n objectResponse({ prod: \"aabb\", test: \"cc\" }),\n );\n\n const result = await datasource.getTrustedName({\n address,\n network: String(SolanaTransactionScanChainId.MAINNET),\n challenge,\n types: [\"token\", \"smart_contract\"],\n sources: [\"crypto_asset_list\"],\n });\n\n // data(ff00) + SIGNATURE_TAG(15) + len(02) + sig(aabb)\n expect(result).toEqual(\n Right({\n address,\n descriptor: new Uint8Array([0xff, 0x00, 0x15, 0x02, 0xaa, 0xbb]),\n keyId: \"cal_trusted_name\",\n keyUsage: \"trusted_name\",\n }),\n );\n });\n\n it(\"selects the test signature when cal.mode is test\", async () => {\n datasource = makeDatasource(\"test\");\n httpMock.get.mockResolvedValue(\n objectResponse({ prod: \"aabb\", test: \"cc\" }),\n );\n\n const result = await datasource.getTrustedName({\n address,\n network: String(SolanaTransactionScanChainId.MAINNET),\n challenge,\n types: [\"token\", \"smart_contract\"],\n sources: [\"crypto_asset_list\"],\n });\n\n // data(ff00) + SIGNATURE_TAG(15) + len(01) + sig(cc)\n expect(result).toEqual(\n Right({\n address,\n descriptor: new Uint8Array([0xff, 0x00, 0x15, 0x01, 0xcc]),\n keyId: \"cal_trusted_name\",\n keyUsage: \"trusted_name\",\n }),\n );\n });\n\n it(\"returns Left when the signature for the active mode is missing\", async () => {\n datasource = makeDatasource(\"prod\");\n httpMock.get.mockResolvedValue(objectResponse({ test: \"cc\" }));\n\n const result = await datasource.getTrustedName({\n address,\n network: String(SolanaTransactionScanChainId.MAINNET),\n challenge,\n types: [\"token\", \"smart_contract\"],\n sources: [\"crypto_asset_list\"],\n });\n\n expect(result.isLeft()).toBe(true);\n expect((result.extract() as Error).message).toMatch(\n new RegExp(\n String.raw`\\[ContextModule\\] HttpSolanaTrustedNameDataSource: missing prod signature for ${address}`,\n ),\n );\n });\n\n it(\"returns Left on malformed response (hex-string signedDescriptor)\", async () => {\n httpMock.get.mockResolvedValue({\n signedDescriptor: \"01020304\",\n keyId: \"k\",\n keyUsage: \"u\",\n } as any);\n\n const result = await datasource.getTrustedName({\n address,\n network: String(SolanaTransactionScanChainId.MAINNET),\n challenge,\n types: [],\n sources: [],\n });\n\n expect(result.isLeft()).toBe(true);\n expect((result.extract() as Error).message).toMatch(\n new RegExp(\n String.raw`\\[ContextModule\\] HttpSolanaTrustedNameDataSource: malformed response for ${address}:`,\n ),\n );\n });\n\n it(\"returns Left when HTTP client throws\", async () => {\n httpMock.get.mockRejectedValue(new Error(\"net\"));\n\n const result = await datasource.getTrustedName({\n address,\n network: String(SolanaTransactionScanChainId.MAINNET),\n challenge,\n types: [],\n sources: [],\n });\n\n expect(result).toEqual(\n Left(\n new Error(\n \"[ContextModule] HttpSolanaTrustedNameDataSource: Failed to fetch trusted name: net\",\n ),\n ),\n );\n });\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["Left", "Right", "beforeEach", "describe", "expect", "it", "vi", "SolanaTransactionScanChainId", "HttpSolanaTrustedNameDataSource", "datasource", "httpMock", "address", "challenge", "makeConfig", "mode", "makeDatasource", "objectResponse", "signatures", "data", "result"]
|
|
7
7
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var f=Object.defineProperty;var
|
|
1
|
+
var f=Object.defineProperty;var y=Object.getOwnPropertyDescriptor;var m=(a,r,o,t)=>{for(var e=t>1?void 0:t?y(r,o):r,n=a.length-1,l;n>=0;n--)(l=a[n])&&(e=(t?l(r,o,e):l(e))||e);return t&&e&&f(r,o,e),e},i=(a,r)=>(o,t)=>r(o,t,a);import{inject as p,injectable as C}from"inversify";import{array as g,Codec as T,optional as N,string as s}from"purify-ts";import{configTypes as h}from"../../../../config/di/configTypes";import{pkiTypes as I}from"../../../../modules/multichain/pki/di/pkiTypes";import{SolanaTransactionScanChainId as c}from"../../../../modules/solana/model/SolanaTransactionScanChainId";import{solanaTrustedNameTypes as E}from"../../../../modules/solana/trusted-name/di/trustedNameTypes";import{ClearSignContextType as S}from"../../../../shared/model/ClearSignContext";import{loadChallengeBoundContexts as v}from"../../../../shared/utils/challengeBoundLoader";import{deviceModelIdCodec as x}from"../../../../shared/utils/deviceModelIdCodec";const L=[S.SOLANA_TRUSTED_NAME],M={"solana-mainnet":c.MAINNET,"solana-devnet":c.DEVNET,"solana-testnet":c.TESTNET},u=c.MAINNET,D=T.interface({address:s,challenge:s,types:g(s),sources:g(s)}),A=T.interface({deviceModelId:x,network:N(s),requests:g(D)});let d=class{constructor(r,o,t){this.dataSource=r;this.certificateLoader=o;this.logger=t("SolanaTrustedNameContextLoader")}logger;canHandle(r,o){return L.every(t=>o.includes(t))?A.decode(r).caseOf({Left:()=>!1,Right:({requests:t})=>t.length>0&&t.every(e=>e.address.length>0&&e.challenge.length>0)}):!1}async load(r){const o=r.network?M[r.network]??u:u,t=String(o);return v({requests:r.requests,deviceModelId:r.deviceModelId,certificateLoader:this.certificateLoader,logger:this.logger,label:"TRUSTED_NAME",fetch:e=>this.dataSource.getTrustedName({address:e.address,challenge:e.challenge,types:e.types,sources:e.sources,network:t}),toContext:(e,n)=>({type:S.SOLANA_TRUSTED_NAME,payload:e.descriptor,certificate:n}),describe:e=>({address:e.address})})}};d=m([C(),i(0,p(E.SolanaTrustedNameDataSource)),i(1,p(I.PkiCertificateLoader)),i(2,p(h.ContextModuleLoggerFactory))],d);export{d as SolanaTrustedNameContextLoader};
|
|
2
2
|
//# sourceMappingURL=TrustedNameContextLoader.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/solana/trusted-name/domain/TrustedNameContextLoader.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n type DeviceModelId,\n type LoggerPublisherService,\n} from \"@ledgerhq/device-management-kit\";\nimport { inject, injectable } from \"inversify\";\nimport { array, Codec, optional, string } from \"purify-ts\";\n\nimport { configTypes } from \"@/config/di/configTypes\";\nimport { pkiTypes } from \"@/modules/multichain/pki/di/pkiTypes\";\nimport { type PkiCertificateLoader } from \"@/modules/multichain/pki/domain/PkiCertificateLoader\";\nimport { SolanaTransactionScanChainId } from \"@/modules/solana/model/SolanaTransactionScanChainId\";\nimport { type SolanaTrustedNameDataSource } from \"@/modules/solana/trusted-name/data/TrustedNameDataSource\";\nimport { solanaTrustedNameTypes } from \"@/modules/solana/trusted-name/di/trustedNameTypes\";\nimport { type ContextLoader } from \"@/shared/domain/ContextLoader\";\nimport {\n type ClearSignContext,\n ClearSignContextType,\n} from \"@/shared/model/ClearSignContext\";\nimport { loadChallengeBoundContexts } from \"@/shared/utils/challengeBoundLoader\";\nimport { deviceModelIdCodec } from \"@/shared/utils/deviceModelIdCodec\";\n\nconst SUPPORTED_TYPES: ClearSignContextType[] = [\n ClearSignContextType.SOLANA_TRUSTED_NAME,\n];\n\nconst CHAIN_ID_BY_NETWORK: Record<string, SolanaTransactionScanChainId> = {\n \"solana-mainnet\": SolanaTransactionScanChainId.MAINNET,\n \"solana-devnet\": SolanaTransactionScanChainId.DEVNET,\n \"solana-testnet\": SolanaTransactionScanChainId.TESTNET,\n};\n\nconst NETWORK_DEFAULT = SolanaTransactionScanChainId.MAINNET;\n\n/**\n * One trusted-name request, resolved to a Uint8Array TLV descriptor. The\n * caller must obtain a fresh challenge immediately before each fetch.\n */\nexport type SolanaTrustedNameRequest = {\n address: string;\n challenge: string;\n sources: string[];\n};\n\nexport type SolanaTrustedNameContextInput = {\n deviceModelId: DeviceModelId;\n network?: string; // \"mainnet\" | \"devnet\" | \"testnet\"; defaults to mainnet\n requests: SolanaTrustedNameRequest[];\n};\n\nconst trustedNameRequestCodec = Codec.interface({\n address: string,\n challenge: string,\n sources: array(string),\n});\n\nconst solanaTrustedNameInputCodec = Codec.interface({\n deviceModelId: deviceModelIdCodec,\n network: optional(string),\n requests: array(trustedNameRequestCodec),\n});\n\n@injectable()\nexport class SolanaTrustedNameContextLoader\n implements ContextLoader<SolanaTrustedNameContextInput>\n{\n private readonly logger: LoggerPublisherService;\n\n constructor(\n @inject(solanaTrustedNameTypes.SolanaTrustedNameDataSource)\n private readonly dataSource: SolanaTrustedNameDataSource,\n @inject(pkiTypes.PkiCertificateLoader)\n private readonly certificateLoader: PkiCertificateLoader,\n @inject(configTypes.ContextModuleLoggerFactory)\n loggerFactory: (tag: string) => LoggerPublisherService,\n ) {\n this.logger = loggerFactory(\"SolanaTrustedNameContextLoader\");\n }\n\n public canHandle(\n input: unknown,\n expectedTypes: ClearSignContextType[],\n ): input is SolanaTrustedNameContextInput {\n if (!SUPPORTED_TYPES.every((t) => expectedTypes.includes(t))) return false;\n return solanaTrustedNameInputCodec.decode(input).caseOf({\n Left: () => false,\n Right: ({ requests }) =>\n requests.length > 0 &&\n requests.every((r) => r.address.length > 0 && r.challenge.length > 0),\n });\n }\n\n public async load(\n input: SolanaTrustedNameContextInput,\n ): Promise<ClearSignContext[]> {\n const chainId = input.network\n ? (CHAIN_ID_BY_NETWORK[input.network] ?? NETWORK_DEFAULT)\n : NETWORK_DEFAULT;\n const network = String(chainId);\n\n return loadChallengeBoundContexts({\n requests: input.requests,\n deviceModelId: input.deviceModelId,\n certificateLoader: this.certificateLoader,\n logger: this.logger,\n label: \"TRUSTED_NAME\",\n fetch: (request) =>\n this.dataSource.getTrustedName({\n address: request.address,\n challenge: request.challenge,\n sources: request.sources,\n network,\n }),\n toContext: (value, certificate) => ({\n type: ClearSignContextType.SOLANA_TRUSTED_NAME,\n payload: value.descriptor,\n certificate,\n }),\n describe: (request) => ({ address: request.address }),\n });\n }\n}\n"],
|
|
5
|
-
"mappings": "iOAIA,OAAS,UAAAA,EAAQ,cAAAC,MAAkB,YACnC,OAAS,SAAAC,EAAO,SAAAC,EAAO,YAAAC,EAAU,UAAAC,MAAc,YAE/C,OAAS,eAAAC,MAAmB,0BAC5B,OAAS,YAAAC,MAAgB,uCAEzB,OAAS,gCAAAC,MAAoC,sDAE7C,OAAS,0BAAAC,MAA8B,oDAEvC,OAEE,wBAAAC,MACK,kCACP,OAAS,8BAAAC,MAAkC,sCAC3C,OAAS,sBAAAC,MAA0B,oCAEnC,MAAMC,EAA0C,CAC9CC,EAAqB,mBACvB,EAEMC,EAAoE,CACxE,iBAAkBC,EAA6B,QAC/C,gBAAiBA,EAA6B,OAC9C,iBAAkBA,EAA6B,OACjD,EAEMC,EAAkBD,EAA6B,
|
|
4
|
+
"sourcesContent": ["import {\n type DeviceModelId,\n type LoggerPublisherService,\n} from \"@ledgerhq/device-management-kit\";\nimport { inject, injectable } from \"inversify\";\nimport { array, Codec, optional, string } from \"purify-ts\";\n\nimport { configTypes } from \"@/config/di/configTypes\";\nimport { pkiTypes } from \"@/modules/multichain/pki/di/pkiTypes\";\nimport { type PkiCertificateLoader } from \"@/modules/multichain/pki/domain/PkiCertificateLoader\";\nimport { SolanaTransactionScanChainId } from \"@/modules/solana/model/SolanaTransactionScanChainId\";\nimport { type SolanaTrustedNameDataSource } from \"@/modules/solana/trusted-name/data/TrustedNameDataSource\";\nimport { solanaTrustedNameTypes } from \"@/modules/solana/trusted-name/di/trustedNameTypes\";\nimport { type ContextLoader } from \"@/shared/domain/ContextLoader\";\nimport {\n type ClearSignContext,\n ClearSignContextType,\n} from \"@/shared/model/ClearSignContext\";\nimport { loadChallengeBoundContexts } from \"@/shared/utils/challengeBoundLoader\";\nimport { deviceModelIdCodec } from \"@/shared/utils/deviceModelIdCodec\";\n\nconst SUPPORTED_TYPES: ClearSignContextType[] = [\n ClearSignContextType.SOLANA_TRUSTED_NAME,\n];\n\nconst CHAIN_ID_BY_NETWORK: Record<string, SolanaTransactionScanChainId> = {\n \"solana-mainnet\": SolanaTransactionScanChainId.MAINNET,\n \"solana-devnet\": SolanaTransactionScanChainId.DEVNET,\n \"solana-testnet\": SolanaTransactionScanChainId.TESTNET,\n};\n\nconst NETWORK_DEFAULT = SolanaTransactionScanChainId.MAINNET;\n\n/**\n * One trusted-name request, resolved to a Uint8Array TLV descriptor. The\n * caller must obtain a fresh challenge immediately before each fetch.\n */\nexport type SolanaTrustedNameRequest = {\n address: string;\n challenge: string;\n types: string[];\n sources: string[];\n};\n\nexport type SolanaTrustedNameContextInput = {\n deviceModelId: DeviceModelId;\n network?: string; // \"mainnet\" | \"devnet\" | \"testnet\"; defaults to mainnet\n requests: SolanaTrustedNameRequest[];\n};\n\nconst trustedNameRequestCodec = Codec.interface({\n address: string,\n challenge: string,\n types: array(string),\n sources: array(string),\n});\n\nconst solanaTrustedNameInputCodec = Codec.interface({\n deviceModelId: deviceModelIdCodec,\n network: optional(string),\n requests: array(trustedNameRequestCodec),\n});\n\n@injectable()\nexport class SolanaTrustedNameContextLoader\n implements ContextLoader<SolanaTrustedNameContextInput>\n{\n private readonly logger: LoggerPublisherService;\n\n constructor(\n @inject(solanaTrustedNameTypes.SolanaTrustedNameDataSource)\n private readonly dataSource: SolanaTrustedNameDataSource,\n @inject(pkiTypes.PkiCertificateLoader)\n private readonly certificateLoader: PkiCertificateLoader,\n @inject(configTypes.ContextModuleLoggerFactory)\n loggerFactory: (tag: string) => LoggerPublisherService,\n ) {\n this.logger = loggerFactory(\"SolanaTrustedNameContextLoader\");\n }\n\n public canHandle(\n input: unknown,\n expectedTypes: ClearSignContextType[],\n ): input is SolanaTrustedNameContextInput {\n if (!SUPPORTED_TYPES.every((t) => expectedTypes.includes(t))) return false;\n return solanaTrustedNameInputCodec.decode(input).caseOf({\n Left: () => false,\n Right: ({ requests }) =>\n requests.length > 0 &&\n requests.every((r) => r.address.length > 0 && r.challenge.length > 0),\n });\n }\n\n public async load(\n input: SolanaTrustedNameContextInput,\n ): Promise<ClearSignContext[]> {\n const chainId = input.network\n ? (CHAIN_ID_BY_NETWORK[input.network] ?? NETWORK_DEFAULT)\n : NETWORK_DEFAULT;\n const network = String(chainId);\n\n return loadChallengeBoundContexts({\n requests: input.requests,\n deviceModelId: input.deviceModelId,\n certificateLoader: this.certificateLoader,\n logger: this.logger,\n label: \"TRUSTED_NAME\",\n fetch: (request) =>\n this.dataSource.getTrustedName({\n address: request.address,\n challenge: request.challenge,\n types: request.types,\n sources: request.sources,\n network,\n }),\n toContext: (value, certificate) => ({\n type: ClearSignContextType.SOLANA_TRUSTED_NAME,\n payload: value.descriptor,\n certificate,\n }),\n describe: (request) => ({ address: request.address }),\n });\n }\n}\n"],
|
|
5
|
+
"mappings": "iOAIA,OAAS,UAAAA,EAAQ,cAAAC,MAAkB,YACnC,OAAS,SAAAC,EAAO,SAAAC,EAAO,YAAAC,EAAU,UAAAC,MAAc,YAE/C,OAAS,eAAAC,MAAmB,0BAC5B,OAAS,YAAAC,MAAgB,uCAEzB,OAAS,gCAAAC,MAAoC,sDAE7C,OAAS,0BAAAC,MAA8B,oDAEvC,OAEE,wBAAAC,MACK,kCACP,OAAS,8BAAAC,MAAkC,sCAC3C,OAAS,sBAAAC,MAA0B,oCAEnC,MAAMC,EAA0C,CAC9CC,EAAqB,mBACvB,EAEMC,EAAoE,CACxE,iBAAkBC,EAA6B,QAC/C,gBAAiBA,EAA6B,OAC9C,iBAAkBA,EAA6B,OACjD,EAEMC,EAAkBD,EAA6B,QAmB/CE,EAA0BC,EAAM,UAAU,CAC9C,QAASC,EACT,UAAWA,EACX,MAAOC,EAAMD,CAAM,EACnB,QAASC,EAAMD,CAAM,CACvB,CAAC,EAEKE,EAA8BH,EAAM,UAAU,CAClD,cAAeI,EACf,QAASC,EAASJ,CAAM,EACxB,SAAUC,EAAMH,CAAuB,CACzC,CAAC,EAGM,IAAMO,EAAN,KAEP,CAGE,YAEmBC,EAEAC,EAEjBC,EACA,CALiB,gBAAAF,EAEA,uBAAAC,EAIjB,KAAK,OAASC,EAAc,gCAAgC,CAC9D,CAXiB,OAaV,UACLC,EACAC,EACwC,CACxC,OAAKjB,EAAgB,MAAO,GAAMiB,EAAc,SAAS,CAAC,CAAC,EACpDR,EAA4B,OAAOO,CAAK,EAAE,OAAO,CACtD,KAAM,IAAM,GACZ,MAAO,CAAC,CAAE,SAAAE,CAAS,IACjBA,EAAS,OAAS,GAClBA,EAAS,MAAOC,GAAMA,EAAE,QAAQ,OAAS,GAAKA,EAAE,UAAU,OAAS,CAAC,CACxE,CAAC,EANoE,EAOvE,CAEA,MAAa,KACXH,EAC6B,CAC7B,MAAMI,EAAUJ,EAAM,QACjBd,EAAoBc,EAAM,OAAO,GAAKZ,EACvCA,EACEiB,EAAU,OAAOD,CAAO,EAE9B,OAAOE,EAA2B,CAChC,SAAUN,EAAM,SAChB,cAAeA,EAAM,cACrB,kBAAmB,KAAK,kBACxB,OAAQ,KAAK,OACb,MAAO,eACP,MAAQO,GACN,KAAK,WAAW,eAAe,CAC7B,QAASA,EAAQ,QACjB,UAAWA,EAAQ,UACnB,MAAOA,EAAQ,MACf,QAASA,EAAQ,QACjB,QAAAF,CACF,CAAC,EACH,UAAW,CAACG,EAAOC,KAAiB,CAClC,KAAMxB,EAAqB,oBAC3B,QAASuB,EAAM,WACf,YAAAC,CACF,GACA,SAAWF,IAAa,CAAE,QAASA,EAAQ,OAAQ,EACrD,CAAC,CACH,CACF,EA3DaX,EAANc,EAAA,CADNC,EAAW,EAOPC,EAAA,EAAAC,EAAOC,EAAuB,2BAA2B,GAEzDF,EAAA,EAAAC,EAAOE,EAAS,oBAAoB,GAEpCH,EAAA,EAAAC,EAAOG,EAAY,0BAA0B,IAVrCpB",
|
|
6
6
|
"names": ["inject", "injectable", "array", "Codec", "optional", "string", "configTypes", "pkiTypes", "SolanaTransactionScanChainId", "solanaTrustedNameTypes", "ClearSignContextType", "loadChallengeBoundContexts", "deviceModelIdCodec", "SUPPORTED_TYPES", "ClearSignContextType", "CHAIN_ID_BY_NETWORK", "SolanaTransactionScanChainId", "NETWORK_DEFAULT", "trustedNameRequestCodec", "Codec", "string", "array", "solanaTrustedNameInputCodec", "deviceModelIdCodec", "optional", "SolanaTrustedNameContextLoader", "dataSource", "certificateLoader", "loggerFactory", "input", "expectedTypes", "requests", "r", "chainId", "network", "loadChallengeBoundContexts", "request", "value", "certificate", "__decorateClass", "injectable", "__decorateParam", "inject", "solanaTrustedNameTypes", "pkiTypes", "configTypes"]
|
|
7
7
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{DeviceModelId as i}from"@ledgerhq/device-management-kit";import{Left as
|
|
1
|
+
import{DeviceModelId as i}from"@ledgerhq/device-management-kit";import{Left as A,Right as l}from"purify-ts";import{beforeEach as N,describe as u,expect as t,it as o,vi as s}from"vitest";import{SolanaTransactionScanChainId as f}from"../../../../modules/solana/model/SolanaTransactionScanChainId";import{ClearSignContextType as c}from"../../../../shared/model/ClearSignContext";import{SolanaTrustedNameContextLoader as k}from"./TrustedNameContextLoader";const g=()=>({debug:s.fn(),info:s.fn(),warn:s.fn(),error:s.fn(),subscribers:[]}),m={keyUsageNumber:4,payload:new Uint8Array([7])},a="Addr11111111111111111111111111111111111111",p="Addr22222222222222222222222222222222222222";u("SolanaTrustedNameContextLoader",()=>{let d,y;N(()=>{s.restoreAllMocks(),d={getTrustedName:s.fn()},y={loadCertificate:s.fn().mockResolvedValue(m)}});const n=()=>new k(d,y,g);u("canHandle",()=>{o("returns true when SOLANA_TRUSTED_NAME is requested with valid requests",()=>{t(n().canHandle({deviceModelId:i.NANO_X,requests:[{address:a,challenge:"c",types:["token","smart_contract"],sources:["sns"]}]},[c.SOLANA_TRUSTED_NAME])).toBe(!0)}),o("returns false when SOLANA_TRUSTED_NAME is not requested",()=>{t(n().canHandle({requests:[{address:a,challenge:"c",types:["token","smart_contract"],sources:["sns"]}]},[c.SOLANA_TOKEN])).toBe(!1)}),o("returns false on malformed input",()=>{const e=n(),r=[c.SOLANA_TRUSTED_NAME];t(e.canHandle({requests:[]},r)).toBe(!1),t(e.canHandle({requests:[{address:"",challenge:"c",types:[],sources:[]}]},r)).toBe(!1),t(e.canHandle({requests:[{address:a,challenge:"",types:[],sources:[]}]},r)).toBe(!1)})}),u("load",()=>{o("emits one SOLANA_TRUSTED_NAME per request with Uint8Array payload",async()=>{s.spyOn(d,"getTrustedName").mockImplementation(async({address:r})=>l({address:r,descriptor:new Uint8Array([r===a?170:187]),keyId:"name_key",keyUsage:"trusted_name"}));const e=await n().load({deviceModelId:i.NANO_X,requests:[{address:a,challenge:"c1",types:["token","smart_contract"],sources:["sns"]},{address:p,challenge:"c2",types:["token","smart_contract"],sources:["cal"]}]});t(e).toHaveLength(2),t(e[0]).toMatchObject({type:c.SOLANA_TRUSTED_NAME,payload:new Uint8Array([170]),certificate:m}),t(e[1]).toMatchObject({type:c.SOLANA_TRUSTED_NAME,payload:new Uint8Array([187])})}),o("emits an ERROR per failing request and keeps the others",async()=>{s.spyOn(d,"getTrustedName").mockImplementation(async({address:r})=>r===a?l({address:r,descriptor:new Uint8Array([170]),keyId:"k",keyUsage:"u"}):A(new Error("upstream-404")));const e=await n().load({deviceModelId:i.NANO_X,requests:[{address:a,challenge:"c1",types:[],sources:[]},{address:p,challenge:"c2",types:[],sources:[]}]});t(e[0]?.type).toBe(c.SOLANA_TRUSTED_NAME),t(e[1]?.type).toBe(c.ERROR)}),o("forwards default chain id 900 when input.network is omitted",async()=>{const e=s.spyOn(d,"getTrustedName").mockResolvedValue(l({address:a,descriptor:new Uint8Array([1]),keyId:"k",keyUsage:"u"}));await n().load({deviceModelId:i.NANO_X,requests:[{address:a,challenge:"c",types:[],sources:[]}]}),t(e).toHaveBeenCalledWith(t.objectContaining({network:String(f.MAINNET)}))}),o("maps a cluster name to its numeric chain id",async()=>{const e=s.spyOn(d,"getTrustedName").mockResolvedValue(l({address:a,descriptor:new Uint8Array([1]),keyId:"k",keyUsage:"u"}));await n().load({deviceModelId:i.NANO_X,network:"solana-devnet",requests:[{address:a,challenge:"c",types:[],sources:[]}]}),t(e).toHaveBeenCalledWith(t.objectContaining({network:"901"}))})})});
|
|
2
2
|
//# sourceMappingURL=TrustedNameContextLoader.test.js.map
|