@ledgerhq/context-module 0.0.0-develop-20260625003437 → 0.0.0-develop-20260626003549
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/instruction-info/domain/InstructionInfoContextLoader.js +1 -1
- package/lib/cjs/src/modules/solana/instruction-info/domain/InstructionInfoContextLoader.js.map +3 -3
- package/lib/cjs/src/modules/solana/instruction-info/domain/InstructionInfoContextLoader.test.js +1 -1
- package/lib/cjs/src/modules/solana/instruction-info/domain/InstructionInfoContextLoader.test.js.map +2 -2
- package/lib/cjs/src/modules/solana/model/SolanaClearSignContext.js.map +2 -2
- package/lib/cjs/src/modules/solana/model/SolanaContextTypes.js +1 -1
- package/lib/cjs/src/modules/solana/model/SolanaContextTypes.js.map +1 -1
- package/lib/cjs/src/modules/solana/model/SolanaPayloads.js +1 -1
- package/lib/cjs/src/modules/solana/model/SolanaPayloads.js.map +2 -2
- package/lib/esm/package.json +1 -1
- package/lib/esm/src/modules/solana/instruction-info/domain/InstructionInfoContextLoader.js +1 -1
- package/lib/esm/src/modules/solana/instruction-info/domain/InstructionInfoContextLoader.js.map +3 -3
- package/lib/esm/src/modules/solana/instruction-info/domain/InstructionInfoContextLoader.test.js +1 -1
- package/lib/esm/src/modules/solana/instruction-info/domain/InstructionInfoContextLoader.test.js.map +2 -2
- package/lib/esm/src/modules/solana/model/SolanaClearSignContext.js.map +2 -2
- package/lib/esm/src/modules/solana/model/SolanaPayloads.js.map +2 -2
- package/lib/types/src/modules/solana/instruction-info/domain/InstructionInfoContextLoader.d.ts.map +1 -1
- package/lib/types/src/modules/solana/model/SolanaClearSignContext.d.ts +1 -1
- package/lib/types/src/modules/solana/model/SolanaClearSignContext.d.ts.map +1 -1
- package/lib/types/src/modules/solana/model/SolanaContextTypes.d.ts +7 -1
- package/lib/types/src/modules/solana/model/SolanaContextTypes.d.ts.map +1 -1
- package/lib/types/src/modules/solana/model/SolanaPayloads.d.ts +15 -1
- package/lib/types/src/modules/solana/model/SolanaPayloads.d.ts.map +1 -1
- package/lib/types/tsconfig.prod.tsbuildinfo +1 -1
- package/package.json +6 -6
package/lib/cjs/package.json
CHANGED
|
@@ -58,5 +58,5 @@
|
|
|
58
58
|
"watch:builds": "pnpm ldmk-tool watch --entryPoints index.ts,src/**/*.ts --tsconfig tsconfig.prod.json",
|
|
59
59
|
"watch:types": "concurrently \"tsc --watch -p tsconfig.prod.json\" \"tsc-alias --watch -p tsconfig.prod.json\""
|
|
60
60
|
},
|
|
61
|
-
"version": "0.0.0-develop-
|
|
61
|
+
"version": "0.0.0-develop-20260626003549"
|
|
62
62
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var y=Object.defineProperty;var R=Object.getOwnPropertyDescriptor;var b=Object.getOwnPropertyNames;var N=Object.prototype.hasOwnProperty;var M=(i,t)=>{for(var r in t)y(i,r,{get:t[r],enumerable:!0})},U=(i,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let e of b(t))!N.call(i,e)&&e!==r&&y(i,e,{get:()=>t[e],enumerable:!(o=R(t,e))||o.enumerable});return i};var A=i=>U(y({},"__esModule",{value:!0}),i),_=(i,t,r,o)=>{for(var e=o>1?void 0:o?R(t,r):t,a=i.length-1,d;a>=0;a--)(d=i[a])&&(e=(o?d(t,r,e):d(e))||e);return o&&e&&y(t,r,e),e},f=(i,t)=>(r,o)=>t(r,o,i);var W={};M(W,{InstructionInfoContextLoader:()=>I});module.exports=A(W);var l=require("inversify"),c=require("purify-ts"),v=require("../../../../config/di/configTypes"),k=require("../../../../modules/multichain/pki/di/pkiTypes"),L=require("../../../../modules/multichain/pki/model/KeyId"),O=require("../../../../modules/multichain/pki/model/KeyUsage"),T=require("../../../../modules/solana/instruction-info/di/instructionInfoTypes"),m=require("../../../../modules/solana/model/SolanaPayloads"),p=require("../../../../shared/model/ClearSignContext"),P=require("../../../../shared/utils/certificateResult"),w=require("../../../../shared/utils/deviceModelIdCodec"),D=require("../../../../shared/utils/uIntCodec");const F=[p.ClearSignContextType.SOLANA_INSTRUCTION_INFO],j="solana-mainnet",K=c.Codec.interface({programId:c.string,discriminator:(0,c.optional)(c.string)}),V=c.Codec.interface({deviceModelId:w.deviceModelIdCodec,instructions:(0,c.array)(K),network:(0,c.optional)(c.string)});let I=class{constructor(t,r,o,e){this.dataSource=t;this.config=r;this.certificateLoader=o;this.logger=e("InstructionInfoContextLoader")}logger;canHandle(t,r){return F.every(o=>r.includes(o))?V.decode(t).caseOf({Left:()=>!1,Right:({instructions:o})=>o.length>0&&o.every(e=>e.programId.length>0)}):!1}async load(t){const r=t.network||j,o=Array.from(new Set(t.instructions.map(s=>s.programId)));this.logger.debug("[load] Fetching instruction descriptors",{data:{network:r,programCount:o.length,instructionCount:t.instructions.length}});const e=(0,P.loadCertificateResult)(this.certificateLoader,{keyId:L.KeyId.TokenMetadataKey,keyUsage:O.KeyUsage.CoinMeta,targetDevice:t.deviceModelId}),a=Promise.all(o.map(async s=>({programId:s,either:await this.dataSource.getInstructionInfo({programId:s,network:r})}))),[d,C]=await Promise.all([e,a]);if(!d.ok||!d.value){const s=d.ok?new Error("[ContextModule] InstructionInfoContextLoader: certificate is missing"):d.error;return this.logger.warn("[load] INSTRUCTION_INFO certificate unavailable",{data:{error:s.message}}),o.map(()=>({type:p.ClearSignContextType.ERROR,error:s}))}const h=d.value,n=[];for(const{programId:s,either:S}of C)S.caseOf({Left:u=>{this.logger.warn("[load] Failed to fetch instruction descriptors for program",{data:{programId:s,error:u.message}}),n.push({type:p.ClearSignContextType.ERROR,error:u})},Right:u=>{const g=this.collectWantedDiscriminators(s,t.instructions);for(const[x,E]of Object.entries(u.descriptors))g!==null&&!g.has(x)||n.push(this.toInstructionInfoContext(x,E,h))}});return this.logger.debug("[load] Emitted contexts",{data:{count:n.length}}),n}collectWantedDiscriminators(t,r){const o=new Set;let e=!1;for(const a of r)a.programId===t&&(a.discriminator===void 0?e=!0:o.add(a.discriminator));return e?null:o}toInstructionInfoContext(t,r,o){const e=r.instruction_info,a=this.pickSignature(e.descriptor.signatures);if(!a){const n=new Error(`[ContextModule] InstructionInfoContextLoader: missing '${this.config.cal.mode??"prod"}' signature for (${e.program_id}, ${t})`);return this.logger.warn("[load] INSTRUCTION_INFO missing signature",{data:{programId:e.program_id,discriminator:t}}),{type:p.ClearSignContextType.ERROR,error:n}}const d=[...(r.display_fields??[]).map(n=>({kind:m.SolanaInstructionSubstructureKind.DISPLAY_FIELD,data:n.descriptor})),...(r.value_flow_ports??[]).map(n=>({kind:m.SolanaInstructionSubstructureKind.VALUE_FLOW_PORT,data:n.descriptor})),...(r.hide_rules??[]).map(n=>({kind:m.SolanaInstructionSubstructureKind.HIDE_RULE,data:n.descriptor})),...(r.account_resets??[]).map(n=>({kind:m.SolanaInstructionSubstructureKind.ACCOUNT_RESET,data:n.descriptor}))],C=[];for(const[n,s]of Object.entries(r.enum_variants??{}))for(const[S,u]of Object.entries(s)){const g=Number(S);if(D.u16Codec.decode(g).isLeft()){this.logger.warn("[load] skipping malformed enum variant index",{data:{programId:e.program_id,enumId:n,variantIndex:S}});continue}C.push({enumId:n,variantIndex:g,descriptor:{data:u.data,signature:this.pickSignature(u.signatures)??""}})}const h={programId:e.program_id,discriminator:t,instructionInfo:{data:e.descriptor.data,signature:a},substructures:d,enumVariants:C};return{type:p.ClearSignContextType.SOLANA_INSTRUCTION_INFO,payload:h,certificate:o}}pickSignature(t){const r=this.config.cal.mode??"prod";return t[r]}};I=_([(0,l.injectable)(),f(0,(0,l.inject)(T.instructionInfoTypes.InstructionInfoDataSource)),f(1,(0,l.inject)(v.configTypes.Config)),f(2,(0,l.inject)(k.pkiTypes.PkiCertificateLoader)),f(3,(0,l.inject)(v.configTypes.ContextModuleLoggerFactory))],I);0&&(module.exports={InstructionInfoContextLoader});
|
|
2
2
|
//# sourceMappingURL=InstructionInfoContextLoader.js.map
|
package/lib/cjs/src/modules/solana/instruction-info/domain/InstructionInfoContextLoader.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/solana/instruction-info/domain/InstructionInfoContextLoader.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 { type ContextModuleServiceConfig } from \"@/config/model/ContextModuleConfig\";\nimport { pkiTypes } from \"@/modules/multichain/pki/di/pkiTypes\";\nimport { type PkiCertificateLoader } from \"@/modules/multichain/pki/domain/PkiCertificateLoader\";\nimport { KeyId } from \"@/modules/multichain/pki/model/KeyId\";\nimport { KeyUsage } from \"@/modules/multichain/pki/model/KeyUsage\";\nimport { type InstructionInfoDataSource } from \"@/modules/solana/instruction-info/data/InstructionInfoDataSource\";\nimport {\n type CalInstructionDescriptorDto,\n type CalSignatures,\n} from \"@/modules/solana/instruction-info/data/InstructionInfoDto\";\nimport { instructionInfoTypes } from \"@/modules/solana/instruction-info/di/instructionInfoTypes\";\nimport {\n type SolanaInstructionInfoPayload,\n type SolanaInstructionSubstructure,\n SolanaInstructionSubstructureKind,\n} from \"@/modules/solana/model/SolanaPayloads\";\nimport { type ContextLoader } from \"@/shared/domain/ContextLoader\";\nimport {\n type ClearSignContext,\n ClearSignContextType,\n} from \"@/shared/model/ClearSignContext\";\nimport { loadCertificateResult } from \"@/shared/utils/certificateResult\";\nimport { deviceModelIdCodec } from \"@/shared/utils/deviceModelIdCodec\";\n\nconst SUPPORTED_TYPES: ClearSignContextType[] = [\n ClearSignContextType.SOLANA_INSTRUCTION_INFO,\n];\n\nconst NETWORK_DEFAULT = \"solana-mainnet\";\n\n/**\n * Per-instruction reference the caller provides for the TX it wants to\n * clear-sign. Discriminator is optional: when omitted, the loader emits\n * every descriptor CAL returns for that `programId`. When present, the\n * loader filters down to descriptors whose `discriminator` matches.\n */\nexport type SolanaInstructionRef = {\n programId: string;\n discriminator?: string;\n};\n\nexport type SolanaInstructionInfoContextInput = {\n deviceModelId: DeviceModelId;\n instructions: SolanaInstructionRef[];\n network?: string;\n};\n\nconst instructionRefCodec = Codec.interface({\n programId: string,\n discriminator: optional(string),\n});\n\nconst solanaInstructionInfoInputCodec = Codec.interface({\n deviceModelId: deviceModelIdCodec,\n instructions: array(instructionRefCodec),\n network: optional(string),\n});\n\n/**\n * Emits SOLANA_INSTRUCTION_INFO contexts for the requested instructions.\n * Enum-variant emission lives in the sibling {@link EnumVariantContextLoader}.\n */\n@injectable()\nexport class InstructionInfoContextLoader\n implements ContextLoader<SolanaInstructionInfoContextInput>\n{\n private readonly logger: LoggerPublisherService;\n\n constructor(\n @inject(instructionInfoTypes.InstructionInfoDataSource)\n private readonly dataSource: InstructionInfoDataSource,\n @inject(configTypes.Config)\n private readonly config: ContextModuleServiceConfig,\n @inject(pkiTypes.PkiCertificateLoader)\n private readonly certificateLoader: PkiCertificateLoader,\n @inject(configTypes.ContextModuleLoggerFactory)\n loggerFactory: (tag: string) => LoggerPublisherService,\n ) {\n this.logger = loggerFactory(\"InstructionInfoContextLoader\");\n }\n\n public canHandle(\n input: unknown,\n expectedTypes: ClearSignContextType[],\n ): input is SolanaInstructionInfoContextInput {\n if (!SUPPORTED_TYPES.every((t) => expectedTypes.includes(t))) {\n return false;\n }\n return solanaInstructionInfoInputCodec.decode(input).caseOf({\n Left: () => false,\n Right: ({ instructions }) =>\n instructions.length > 0 &&\n instructions.every((ix) => ix.programId.length > 0),\n });\n }\n\n public async load(\n input: SolanaInstructionInfoContextInput,\n ): Promise<ClearSignContext[]> {\n const network = input.network || NETWORK_DEFAULT;\n const distinctProgramIds = Array.from(\n new Set(input.instructions.map((ix) => ix.programId)),\n );\n\n this.logger.debug(\"[load] Fetching instruction descriptors\", {\n data: {\n network,\n programCount: distinctProgramIds.length,\n instructionCount: input.instructions.length,\n },\n });\n\n // Run cert load in parallel with CAL fetches \u2014 neither depends on the\n // other. The tagged-Result wrapper lets us preserve the data fetches\n // if cert fails so we can surface a per-program ERROR rather than\n // silently dropping them.\n const certPromise = loadCertificateResult(this.certificateLoader, {\n keyId: KeyId.TokenMetadataKey,\n keyUsage: KeyUsage.CoinMeta,\n targetDevice: input.deviceModelId,\n });\n\n // One CAL call per program, fanned out in parallel.\n const dataPromise = Promise.all(\n distinctProgramIds.map(async (programId) => ({\n programId,\n either: await this.dataSource.getInstructionInfo({\n programId,\n network,\n }),\n })),\n );\n\n const [certResult, results] = await Promise.all([certPromise, dataPromise]);\n\n if (!certResult.ok || !certResult.value) {\n const error = certResult.ok\n ? new Error(\n \"[ContextModule] InstructionInfoContextLoader: certificate is missing\",\n )\n : certResult.error;\n this.logger.warn(\"[load] INSTRUCTION_INFO certificate unavailable\", {\n data: { error: error.message },\n });\n return distinctProgramIds.map(() => ({\n type: ClearSignContextType.ERROR,\n error,\n }));\n }\n const certificate = certResult.value;\n\n const contexts: ClearSignContext[] = [];\n\n for (const { programId, either } of results) {\n either.caseOf<void>({\n Left: (err) => {\n this.logger.warn(\n \"[load] Failed to fetch instruction descriptors for program\",\n { data: { programId, error: err.message } },\n );\n contexts.push({\n type: ClearSignContextType.ERROR,\n error: err,\n });\n },\n Right: (value) => {\n const wantedDiscriminators = this.collectWantedDiscriminators(\n programId,\n input.instructions,\n );\n\n for (const [discriminator, dto] of Object.entries(\n value.descriptors,\n )) {\n if (\n wantedDiscriminators !== null &&\n !wantedDiscriminators.has(discriminator)\n ) {\n continue;\n }\n contexts.push(\n this.toInstructionInfoContext(discriminator, dto, certificate),\n );\n }\n },\n });\n }\n\n this.logger.debug(\"[load] Emitted contexts\", {\n data: { count: contexts.length },\n });\n\n return contexts;\n }\n\n /**\n * Returns the set of discriminators the caller has explicitly asked\n * about for this `programId`, or `null` when the caller wants every\n * descriptor CAL has for the program.\n */\n private collectWantedDiscriminators(\n programId: string,\n instructions: SolanaInstructionRef[],\n ): Set<string> | null {\n const set = new Set<string>();\n let hasUnconstrained = false;\n for (const ix of instructions) {\n if (ix.programId !== programId) continue;\n if (ix.discriminator === undefined) {\n hasUnconstrained = true;\n } else {\n set.add(ix.discriminator);\n }\n }\n return hasUnconstrained ? null : set;\n }\n\n private toInstructionInfoContext(\n discriminator: string,\n dto: CalInstructionDescriptorDto,\n certificate: Awaited<ReturnType<PkiCertificateLoader[\"loadCertificate\"]>>,\n ): ClearSignContext {\n const info = dto.instruction_info;\n const signature = this.pickSignature(info.descriptor.signatures);\n if (!signature) {\n const error = new Error(\n `[ContextModule] InstructionInfoContextLoader: missing '${this.config.cal.mode ?? \"prod\"}' signature for (${info.program_id}, ${discriminator})`,\n );\n this.logger.warn(\"[load] INSTRUCTION_INFO missing signature\", {\n data: { programId: info.program_id, discriminator },\n });\n return { type: ClearSignContextType.ERROR, error };\n }\n\n const substructures: SolanaInstructionSubstructure[] = [\n ...(dto.display_fields ?? []).map((s) => ({\n kind: SolanaInstructionSubstructureKind.DISPLAY_FIELD,\n data: s.descriptor,\n })),\n ...(dto.value_flow_ports ?? []).map((s) => ({\n kind: SolanaInstructionSubstructureKind.VALUE_FLOW_PORT,\n data: s.descriptor,\n })),\n ...(dto.hide_rules ?? []).map((s) => ({\n kind: SolanaInstructionSubstructureKind.HIDE_RULE,\n data: s.descriptor,\n })),\n ...(dto.account_resets ?? []).map((s) => ({\n kind: SolanaInstructionSubstructureKind.ACCOUNT_RESET,\n data: s.descriptor,\n })),\n ];\n\n const payload: SolanaInstructionInfoPayload = {\n programId: info.program_id,\n discriminator,\n instructionInfo: {\n data: info.descriptor.data,\n signature,\n },\n substructures,\n };\n\n return {\n type: ClearSignContextType.SOLANA_INSTRUCTION_INFO,\n payload,\n certificate,\n };\n }\n\n private pickSignature(signatures: CalSignatures): string | undefined {\n const mode = this.config.cal.mode ?? \"prod\";\n return signatures[mode];\n }\n}\n"],
|
|
5
|
-
"mappings": "okBAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,kCAAAE,IAAA,eAAAC,EAAAH,GAIA,IAAAI,EAAmC,qBACnCC,EAA+C,qBAE/CC,EAA4B,mCAE5BC,EAAyB,gDAEzBC,EAAsB,gDACtBC,EAAyB,mDAMzBC,EAAqC,qEACrCC,
|
|
6
|
-
"names": ["InstructionInfoContextLoader_exports", "__export", "InstructionInfoContextLoader", "__toCommonJS", "import_inversify", "import_purify_ts", "import_configTypes", "import_pkiTypes", "import_KeyId", "import_KeyUsage", "import_instructionInfoTypes", "import_SolanaPayloads", "import_ClearSignContext", "import_certificateResult", "import_deviceModelIdCodec", "SUPPORTED_TYPES", "NETWORK_DEFAULT", "instructionRefCodec", "solanaInstructionInfoInputCodec", "InstructionInfoContextLoader", "dataSource", "config", "certificateLoader", "loggerFactory", "input", "expectedTypes", "t", "instructions", "ix", "network", "distinctProgramIds", "certPromise", "dataPromise", "programId", "certResult", "results", "error", "certificate", "contexts", "either", "err", "value", "wantedDiscriminators", "discriminator", "dto", "set", "hasUnconstrained", "info", "signature", "substructures", "s", "payload", "signatures", "mode", "__decorateClass", "__decorateParam"]
|
|
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 { type ContextModuleServiceConfig } from \"@/config/model/ContextModuleConfig\";\nimport { pkiTypes } from \"@/modules/multichain/pki/di/pkiTypes\";\nimport { type PkiCertificateLoader } from \"@/modules/multichain/pki/domain/PkiCertificateLoader\";\nimport { KeyId } from \"@/modules/multichain/pki/model/KeyId\";\nimport { KeyUsage } from \"@/modules/multichain/pki/model/KeyUsage\";\nimport { type InstructionInfoDataSource } from \"@/modules/solana/instruction-info/data/InstructionInfoDataSource\";\nimport {\n type CalInstructionDescriptorDto,\n type CalSignatures,\n} from \"@/modules/solana/instruction-info/data/InstructionInfoDto\";\nimport { instructionInfoTypes } from \"@/modules/solana/instruction-info/di/instructionInfoTypes\";\nimport {\n type SolanaInstructionEnumVariant,\n type SolanaInstructionInfoPayload,\n type SolanaInstructionSubstructure,\n SolanaInstructionSubstructureKind,\n} from \"@/modules/solana/model/SolanaPayloads\";\nimport { type ContextLoader } from \"@/shared/domain/ContextLoader\";\nimport {\n type ClearSignContext,\n ClearSignContextType,\n} from \"@/shared/model/ClearSignContext\";\nimport { loadCertificateResult } from \"@/shared/utils/certificateResult\";\nimport { deviceModelIdCodec } from \"@/shared/utils/deviceModelIdCodec\";\nimport { u16Codec } from \"@/shared/utils/uIntCodec\";\n\nconst SUPPORTED_TYPES: ClearSignContextType[] = [\n ClearSignContextType.SOLANA_INSTRUCTION_INFO,\n];\n\nconst NETWORK_DEFAULT = \"solana-mainnet\";\n\n/**\n * Per-instruction reference the caller provides for the TX it wants to\n * clear-sign. Discriminator is optional: when omitted, the loader emits\n * every descriptor CAL returns for that `programId`. When present, the\n * loader filters down to descriptors whose `discriminator` matches.\n */\nexport type SolanaInstructionRef = {\n programId: string;\n discriminator?: string;\n};\n\nexport type SolanaInstructionInfoContextInput = {\n deviceModelId: DeviceModelId;\n instructions: SolanaInstructionRef[];\n network?: string;\n};\n\nconst instructionRefCodec = Codec.interface({\n programId: string,\n discriminator: optional(string),\n});\n\nconst solanaInstructionInfoInputCodec = Codec.interface({\n deviceModelId: deviceModelIdCodec,\n instructions: array(instructionRefCodec),\n network: optional(string),\n});\n\n/**\n * Emits SOLANA_INSTRUCTION_INFO contexts for the requested instructions.\n * Enum-variant emission lives in the sibling {@link EnumVariantContextLoader}.\n */\n@injectable()\nexport class InstructionInfoContextLoader\n implements ContextLoader<SolanaInstructionInfoContextInput>\n{\n private readonly logger: LoggerPublisherService;\n\n constructor(\n @inject(instructionInfoTypes.InstructionInfoDataSource)\n private readonly dataSource: InstructionInfoDataSource,\n @inject(configTypes.Config)\n private readonly config: ContextModuleServiceConfig,\n @inject(pkiTypes.PkiCertificateLoader)\n private readonly certificateLoader: PkiCertificateLoader,\n @inject(configTypes.ContextModuleLoggerFactory)\n loggerFactory: (tag: string) => LoggerPublisherService,\n ) {\n this.logger = loggerFactory(\"InstructionInfoContextLoader\");\n }\n\n public canHandle(\n input: unknown,\n expectedTypes: ClearSignContextType[],\n ): input is SolanaInstructionInfoContextInput {\n if (!SUPPORTED_TYPES.every((t) => expectedTypes.includes(t))) {\n return false;\n }\n return solanaInstructionInfoInputCodec.decode(input).caseOf({\n Left: () => false,\n Right: ({ instructions }) =>\n instructions.length > 0 &&\n instructions.every((ix) => ix.programId.length > 0),\n });\n }\n\n public async load(\n input: SolanaInstructionInfoContextInput,\n ): Promise<ClearSignContext[]> {\n const network = input.network || NETWORK_DEFAULT;\n const distinctProgramIds = Array.from(\n new Set(input.instructions.map((ix) => ix.programId)),\n );\n\n this.logger.debug(\"[load] Fetching instruction descriptors\", {\n data: {\n network,\n programCount: distinctProgramIds.length,\n instructionCount: input.instructions.length,\n },\n });\n\n // Run cert load in parallel with CAL fetches \u2014 neither depends on the\n // other. The tagged-Result wrapper lets us preserve the data fetches\n // if cert fails so we can surface a per-program ERROR rather than\n // silently dropping them.\n const certPromise = loadCertificateResult(this.certificateLoader, {\n keyId: KeyId.TokenMetadataKey,\n keyUsage: KeyUsage.CoinMeta,\n targetDevice: input.deviceModelId,\n });\n\n // One CAL call per program, fanned out in parallel.\n const dataPromise = Promise.all(\n distinctProgramIds.map(async (programId) => ({\n programId,\n either: await this.dataSource.getInstructionInfo({\n programId,\n network,\n }),\n })),\n );\n\n const [certResult, results] = await Promise.all([certPromise, dataPromise]);\n\n if (!certResult.ok || !certResult.value) {\n const error = certResult.ok\n ? new Error(\n \"[ContextModule] InstructionInfoContextLoader: certificate is missing\",\n )\n : certResult.error;\n this.logger.warn(\"[load] INSTRUCTION_INFO certificate unavailable\", {\n data: { error: error.message },\n });\n return distinctProgramIds.map(() => ({\n type: ClearSignContextType.ERROR,\n error,\n }));\n }\n const certificate = certResult.value;\n\n const contexts: ClearSignContext[] = [];\n\n for (const { programId, either } of results) {\n either.caseOf<void>({\n Left: (err) => {\n this.logger.warn(\n \"[load] Failed to fetch instruction descriptors for program\",\n { data: { programId, error: err.message } },\n );\n contexts.push({\n type: ClearSignContextType.ERROR,\n error: err,\n });\n },\n Right: (value) => {\n const wantedDiscriminators = this.collectWantedDiscriminators(\n programId,\n input.instructions,\n );\n\n for (const [discriminator, dto] of Object.entries(\n value.descriptors,\n )) {\n if (\n wantedDiscriminators !== null &&\n !wantedDiscriminators.has(discriminator)\n ) {\n continue;\n }\n contexts.push(\n this.toInstructionInfoContext(discriminator, dto, certificate),\n );\n }\n },\n });\n }\n\n this.logger.debug(\"[load] Emitted contexts\", {\n data: { count: contexts.length },\n });\n\n return contexts;\n }\n\n /**\n * Returns the set of discriminators the caller has explicitly asked\n * about for this `programId`, or `null` when the caller wants every\n * descriptor CAL has for the program.\n */\n private collectWantedDiscriminators(\n programId: string,\n instructions: SolanaInstructionRef[],\n ): Set<string> | null {\n const set = new Set<string>();\n let hasUnconstrained = false;\n for (const ix of instructions) {\n if (ix.programId !== programId) continue;\n if (ix.discriminator === undefined) {\n hasUnconstrained = true;\n } else {\n set.add(ix.discriminator);\n }\n }\n return hasUnconstrained ? null : set;\n }\n\n private toInstructionInfoContext(\n discriminator: string,\n dto: CalInstructionDescriptorDto,\n certificate: Awaited<ReturnType<PkiCertificateLoader[\"loadCertificate\"]>>,\n ): ClearSignContext {\n const info = dto.instruction_info;\n const signature = this.pickSignature(info.descriptor.signatures);\n if (!signature) {\n const error = new Error(\n `[ContextModule] InstructionInfoContextLoader: missing '${this.config.cal.mode ?? \"prod\"}' signature for (${info.program_id}, ${discriminator})`,\n );\n this.logger.warn(\"[load] INSTRUCTION_INFO missing signature\", {\n data: { programId: info.program_id, discriminator },\n });\n return { type: ClearSignContextType.ERROR, error };\n }\n\n const substructures: SolanaInstructionSubstructure[] = [\n ...(dto.display_fields ?? []).map((s) => ({\n kind: SolanaInstructionSubstructureKind.DISPLAY_FIELD,\n data: s.descriptor,\n })),\n ...(dto.value_flow_ports ?? []).map((s) => ({\n kind: SolanaInstructionSubstructureKind.VALUE_FLOW_PORT,\n data: s.descriptor,\n })),\n ...(dto.hide_rules ?? []).map((s) => ({\n kind: SolanaInstructionSubstructureKind.HIDE_RULE,\n data: s.descriptor,\n })),\n ...(dto.account_resets ?? []).map((s) => ({\n kind: SolanaInstructionSubstructureKind.ACCOUNT_RESET,\n data: s.descriptor,\n })),\n ];\n\n // Flatten the CAL-bundled enum variants (keyed enumId to variantIndex) into\n // a flat list. The signed TLV feeds the host's type-pool decode cache, the\n // signature is best-effort (only needed when streaming the selected\n // variant, not for decoding).\n const enumVariants: SolanaInstructionEnumVariant[] = [];\n for (const [enumId, variants] of Object.entries(dto.enum_variants ?? {})) {\n for (const [variantIndex, variant] of Object.entries(variants)) {\n // CAL keys variants by their stringified u16 index. Validate through\n // the codec so malformed keys (\"7abc\", \"-1\", \"1e10\") are skipped rather\n // than leaking NaN/out-of-range indices into the payload (matches\n // EnumVariantContextLoader).\n const index = Number(variantIndex);\n if (u16Codec.decode(index).isLeft()) {\n this.logger.warn(\"[load] skipping malformed enum variant index\", {\n data: { programId: info.program_id, enumId, variantIndex },\n });\n continue;\n }\n enumVariants.push({\n enumId,\n variantIndex: index,\n descriptor: {\n data: variant.data,\n signature: this.pickSignature(variant.signatures) ?? \"\",\n },\n });\n }\n }\n\n const payload: SolanaInstructionInfoPayload = {\n programId: info.program_id,\n discriminator,\n instructionInfo: {\n data: info.descriptor.data,\n signature,\n },\n substructures,\n enumVariants,\n };\n\n return {\n type: ClearSignContextType.SOLANA_INSTRUCTION_INFO,\n payload,\n certificate,\n };\n }\n\n private pickSignature(signatures: CalSignatures): string | undefined {\n const mode = this.config.cal.mode ?? \"prod\";\n return signatures[mode];\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["InstructionInfoContextLoader_exports", "__export", "InstructionInfoContextLoader", "__toCommonJS", "import_inversify", "import_purify_ts", "import_configTypes", "import_pkiTypes", "import_KeyId", "import_KeyUsage", "import_instructionInfoTypes", "import_SolanaPayloads", "import_ClearSignContext", "import_certificateResult", "import_deviceModelIdCodec", "import_uIntCodec", "SUPPORTED_TYPES", "NETWORK_DEFAULT", "instructionRefCodec", "solanaInstructionInfoInputCodec", "InstructionInfoContextLoader", "dataSource", "config", "certificateLoader", "loggerFactory", "input", "expectedTypes", "t", "instructions", "ix", "network", "distinctProgramIds", "certPromise", "dataPromise", "programId", "certResult", "results", "error", "certificate", "contexts", "either", "err", "value", "wantedDiscriminators", "discriminator", "dto", "set", "hasUnconstrained", "info", "signature", "substructures", "s", "enumVariants", "enumId", "variants", "variantIndex", "variant", "index", "payload", "signatures", "mode", "__decorateClass", "__decorateParam"]
|
|
7
7
|
}
|
package/lib/cjs/src/modules/solana/instruction-info/domain/InstructionInfoContextLoader.test.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var c=require("@ledgerhq/device-management-kit"),i=require("purify-ts"),e=require("vitest"),s=require("../../../../shared/model/ClearSignContext"),y=require("./InstructionInfoContextLoader");const N=()=>({debug:e.vi.fn(),info:e.vi.fn(),warn:e.vi.fn(),error:e.vi.fn(),subscribers:[]}),O={keyUsageNumber:8,payload:new Uint8Array([66])},f={cal:{url:"https://crypto-assets-service.api.ledger.com/v1",mode:"prod",branch:"main"}},a="11111111111111111111111111111111",u="JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4";function m(){return{programId:a,descriptors:{"00000000":{type:"instruction",instruction_info:{version:1,program_id:a,discriminator:"00000000",hash:"feedface",descriptor:{data:"0001sys_data",signatures:{prod:"prodsig_sys",test:"testsig_sys"}}},display_fields:[{descriptor:"df1"},{descriptor:"df2"}],value_flow_ports:[{descriptor:"vfp1"}],hide_rules:[],account_resets:[]}}}}function g(){return{programId:u,descriptors:{e517cb977ae3ad2a:{type:"instruction",instruction_info:{version:1,program_id:u,discriminator:"e517cb977ae3ad2a",hash:"deadbeef",descriptor:{data:"0001jup_data",signatures:{prod:"prodsig_jup",test:"testsig_jup"}}},display_fields:[],value_flow_ports:[{descriptor:"vfp_in"},{descriptor:"vfp_out"}],hide_rules:[],account_resets:[]}}}}(0,e.describe)("InstructionInfoContextLoader",()=>{let d,I;(0,e.beforeEach)(()=>{e.vi.restoreAllMocks(),d={getInstructionInfo:e.vi.fn()},I={loadCertificate:e.vi.fn().mockResolvedValue(O)}});const l=(o=f)=>new y.InstructionInfoContextLoader(d,o,I,N);(0,e.describe)("canHandle",()=>{(0,e.it)("returns true when SOLANA_INSTRUCTION_INFO is requested and instructions are present",()=>{(0,e.expect)(l().canHandle({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:a}]},[s.ClearSignContextType.SOLANA_INSTRUCTION_INFO])).toBe(!0)}),(0,e.it)("returns false when SOLANA_INSTRUCTION_INFO is not requested",()=>{(0,e.expect)(l().canHandle({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:a}]},[s.ClearSignContextType.SOLANA_TOKEN])).toBe(!1)}),(0,e.it)("returns false on missing / empty / malformed instructions",()=>{const o=l(),t=[s.ClearSignContextType.SOLANA_INSTRUCTION_INFO];(0,e.expect)(o.canHandle({},t)).toBe(!1),(0,e.expect)(o.canHandle({instructions:[]},t)).toBe(!1),(0,e.expect)(o.canHandle({instructions:[{programId:""}]},t)).toBe(!1),(0,e.expect)(o.canHandle(null,t)).toBe(!1),(0,e.expect)(o.canHandle("string",t)).toBe(!1)})}),(0,e.describe)("load",()=>{(0,e.it)("fans out one CAL call per distinct programId in parallel",async()=>{const o=l(),t=e.vi.spyOn(d,"getInstructionInfo").mockImplementation(async({programId:r})=>r===a?(0,i.Right)(m()):r===u?(0,i.Right)(g()):(0,i.Left)(new Error("unknown program"))),n=await o.load({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:a,discriminator:"00000000"},{programId:a,discriminator:"00000000"},{programId:u,discriminator:"e517cb977ae3ad2a"}],network:"solana-mainnet"});(0,e.expect)(t).toHaveBeenCalledTimes(2),(0,e.expect)(t).toHaveBeenCalledWith({programId:a,network:"solana-mainnet"}),(0,e.expect)(t).toHaveBeenCalledWith({programId:u,network:"solana-mainnet"}),(0,e.expect)(n.every(r=>r.type===s.ClearSignContextType.SOLANA_INSTRUCTION_INFO||r.type===s.ClearSignContextType.ERROR)).toBe(!0),(0,e.expect)(n.filter(r=>r.type===s.ClearSignContextType.SOLANA_INSTRUCTION_INFO)).toHaveLength(2)}),(0,e.it)("emits one SOLANA_INSTRUCTION_INFO per matching (programId, discriminator) with substructures and certificate bundled",async()=>{const o=l();e.vi.spyOn(d,"getInstructionInfo").mockResolvedValue((0,i.Right)(m()));const n=(await o.load({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:a,discriminator:"00000000"}]})).find(r=>r.type===s.ClearSignContextType.SOLANA_INSTRUCTION_INFO);(0,e.expect)(n).toBeDefined(),(0,e.expect)(n).toMatchObject({type:s.ClearSignContextType.SOLANA_INSTRUCTION_INFO,certificate:O}),(0,e.expect)(n.payload).toMatchObject({programId:a,discriminator:"00000000",instructionInfo:{data:"0001sys_data",signature:"prodsig_sys"},substructures:[{kind:0,data:"df1"},{kind:0,data:"df2"},{kind:1,data:"vfp1"}]})}),(0,e.it)("does NOT emit SOLANA_ENUM_VARIANT contexts (those come from EnumVariantContextLoader)",async()=>{const o=l(),t=g(),n=t.descriptors.e517cb977ae3ad2a;if(!n)throw new Error("fixture: descriptor must exist");n.enum_variants={swap:{46:{variant_name:"raydiumCP",data:"01_raydium_tlv",signatures:{prod:"prodsig_v46",test:"testsig_v46"}}}},e.vi.spyOn(d,"getInstructionInfo").mockResolvedValue((0,i.Right)(t));const r=await o.load({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:u,discriminator:"e517cb977ae3ad2a"}]});(0,e.expect)(r.filter(p=>p.type===s.ClearSignContextType.SOLANA_ENUM_VARIANT)).toHaveLength(0)}),(0,e.it)("filters descriptors by requested discriminator",async()=>{const o=l(),t={programId:a,descriptors:{...m().descriptors,"00000001":{type:"instruction",instruction_info:{version:1,program_id:a,discriminator:"00000001",hash:"feedface2",descriptor:{data:"0001other",signatures:{prod:"p",test:"t"}}},display_fields:[{descriptor:"shouldnotappear"}]}}};e.vi.spyOn(d,"getInstructionInfo").mockResolvedValue((0,i.Right)(t));const r=(await o.load({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:a,discriminator:"00000000"}]})).filter(p=>p.type===s.ClearSignContextType.SOLANA_INSTRUCTION_INFO);(0,e.expect)(r).toHaveLength(1),(0,e.expect)(r[0].payload.discriminator).toBe("00000000")}),(0,e.it)("emits one ERROR context when CAL fails for a program but keeps processing the others",async()=>{const o=l();e.vi.spyOn(d,"getInstructionInfo").mockImplementation(async({programId:p})=>p===a?(0,i.Right)(m()):(0,i.Left)(new Error("upstream-500")));const t=await o.load({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:a,discriminator:"00000000"},{programId:u,discriminator:"e517cb977ae3ad2a"}]}),n=t.filter(p=>p.type===s.ClearSignContextType.ERROR),r=t.filter(p=>p.type===s.ClearSignContextType.SOLANA_INSTRUCTION_INFO);(0,e.expect)(n).toHaveLength(1),(0,e.expect)(r).toHaveLength(1)}),(0,e.it)("emits ERROR (not empty signature) when configured CAL mode signature is missing",async()=>{const o=new y.InstructionInfoContextLoader(d,{...f,cal:{...f.cal,mode:"test"}},I,N),t=g(),n=t.descriptors.e517cb977ae3ad2a;if(!n)throw new Error("fixture: descriptor must exist");delete n.instruction_info.descriptor.signatures.test,e.vi.spyOn(d,"getInstructionInfo").mockResolvedValue((0,i.Right)(t));const r=await o.load({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:u,discriminator:"e517cb977ae3ad2a"}]});(0,e.expect)(r).toHaveLength(1),(0,e.expect)(r[0]?.type).toBe(s.ClearSignContextType.ERROR),(0,e.expect)(r[0].error.message).toMatch(/missing 'test' signature/)}),(0,e.it)("degrades every program to ERROR when certificate loading fails",async()=>{e.vi.spyOn(I,"loadCertificate").mockRejectedValue(new Error("pki-down")),e.vi.spyOn(d,"getInstructionInfo").mockResolvedValue((0,i.Right)(m()));const t=await l().load({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:a},{programId:u}]});(0,e.expect)(t).toHaveLength(2),(0,e.expect)(t.every(n=>n.type===s.ClearSignContextType.ERROR)).toBe(!0)}),(0,e.it)("degrades every program to ERROR when the certificate resolves undefined",async()=>{e.vi.spyOn(I,"loadCertificate").mockResolvedValue(void 0),e.vi.spyOn(d,"getInstructionInfo").mockResolvedValue((0,i.Right)(m()));const t=await l().load({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:a},{programId:u}]});(0,e.expect)(t).toHaveLength(2),(0,e.expect)(t.every(n=>n.type===s.ClearSignContextType.ERROR)).toBe(!0),(0,e.expect)(t[0].error.message).toMatch(/certificate is missing/)}),(0,e.it)("selects signatures based on config.cal.mode (test)",async()=>{const o=new y.InstructionInfoContextLoader(d,{...f,cal:{...f.cal,mode:"test"}},I,N);e.vi.spyOn(d,"getInstructionInfo").mockResolvedValue((0,i.Right)(g()));const n=(await o.load({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:u,discriminator:"e517cb977ae3ad2a"}]})).find(r=>r.type===s.ClearSignContextType.SOLANA_INSTRUCTION_INFO);(0,e.expect)(n.payload.instructionInfo.signature).toBe("testsig_jup")}),(0,e.it)("falls back to default network when input.network is omitted or empty string",async()=>{const o=l(),t=e.vi.spyOn(d,"getInstructionInfo").mockResolvedValue((0,i.Right)(m()));await o.load({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:a}]}),(0,e.expect)(t).toHaveBeenLastCalledWith({programId:a,network:"solana-mainnet"}),await o.load({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:a}],network:""}),(0,e.expect)(t).toHaveBeenLastCalledWith({programId:a,network:"solana-mainnet"})})})});
|
|
1
|
+
"use strict";var c=require("@ledgerhq/device-management-kit"),i=require("purify-ts"),t=require("vitest"),s=require("../../../../shared/model/ClearSignContext"),_=require("./InstructionInfoContextLoader");const N=()=>({debug:t.vi.fn(),info:t.vi.fn(),warn:t.vi.fn(),error:t.vi.fn(),subscribers:[]}),O={keyUsageNumber:8,payload:new Uint8Array([66])},g={cal:{url:"https://crypto-assets-service.api.ledger.com/v1",mode:"prod",branch:"main"}},n="11111111111111111111111111111111",p="JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4";function m(){return{programId:n,descriptors:{"00000000":{type:"instruction",instruction_info:{version:1,program_id:n,discriminator:"00000000",hash:"feedface",descriptor:{data:"0001sys_data",signatures:{prod:"prodsig_sys",test:"testsig_sys"}}},display_fields:[{descriptor:"df1"},{descriptor:"df2"}],value_flow_ports:[{descriptor:"vfp1"}],hide_rules:[],account_resets:[]}}}}function f(){return{programId:p,descriptors:{e517cb977ae3ad2a:{type:"instruction",instruction_info:{version:1,program_id:p,discriminator:"e517cb977ae3ad2a",hash:"deadbeef",descriptor:{data:"0001jup_data",signatures:{prod:"prodsig_jup",test:"testsig_jup"}}},display_fields:[],value_flow_ports:[{descriptor:"vfp_in"},{descriptor:"vfp_out"}],hide_rules:[],account_resets:[]}}}}(0,t.describe)("InstructionInfoContextLoader",()=>{let d,I;(0,t.beforeEach)(()=>{t.vi.restoreAllMocks(),d={getInstructionInfo:t.vi.fn()},I={loadCertificate:t.vi.fn().mockResolvedValue(O)}});const u=(o=g)=>new _.InstructionInfoContextLoader(d,o,I,N);(0,t.describe)("canHandle",()=>{(0,t.it)("returns true when SOLANA_INSTRUCTION_INFO is requested and instructions are present",()=>{(0,t.expect)(u().canHandle({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:n}]},[s.ClearSignContextType.SOLANA_INSTRUCTION_INFO])).toBe(!0)}),(0,t.it)("returns false when SOLANA_INSTRUCTION_INFO is not requested",()=>{(0,t.expect)(u().canHandle({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:n}]},[s.ClearSignContextType.SOLANA_TOKEN])).toBe(!1)}),(0,t.it)("returns false on missing / empty / malformed instructions",()=>{const o=u(),e=[s.ClearSignContextType.SOLANA_INSTRUCTION_INFO];(0,t.expect)(o.canHandle({},e)).toBe(!1),(0,t.expect)(o.canHandle({instructions:[]},e)).toBe(!1),(0,t.expect)(o.canHandle({instructions:[{programId:""}]},e)).toBe(!1),(0,t.expect)(o.canHandle(null,e)).toBe(!1),(0,t.expect)(o.canHandle("string",e)).toBe(!1)})}),(0,t.describe)("load",()=>{(0,t.it)("fans out one CAL call per distinct programId in parallel",async()=>{const o=u(),e=t.vi.spyOn(d,"getInstructionInfo").mockImplementation(async({programId:a})=>a===n?(0,i.Right)(m()):a===p?(0,i.Right)(f()):(0,i.Left)(new Error("unknown program"))),r=await o.load({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:n,discriminator:"00000000"},{programId:n,discriminator:"00000000"},{programId:p,discriminator:"e517cb977ae3ad2a"}],network:"solana-mainnet"});(0,t.expect)(e).toHaveBeenCalledTimes(2),(0,t.expect)(e).toHaveBeenCalledWith({programId:n,network:"solana-mainnet"}),(0,t.expect)(e).toHaveBeenCalledWith({programId:p,network:"solana-mainnet"}),(0,t.expect)(r.every(a=>a.type===s.ClearSignContextType.SOLANA_INSTRUCTION_INFO||a.type===s.ClearSignContextType.ERROR)).toBe(!0),(0,t.expect)(r.filter(a=>a.type===s.ClearSignContextType.SOLANA_INSTRUCTION_INFO)).toHaveLength(2)}),(0,t.it)("emits one SOLANA_INSTRUCTION_INFO per matching (programId, discriminator) with substructures and certificate bundled",async()=>{const o=u();t.vi.spyOn(d,"getInstructionInfo").mockResolvedValue((0,i.Right)(m()));const r=(await o.load({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:n,discriminator:"00000000"}]})).find(a=>a.type===s.ClearSignContextType.SOLANA_INSTRUCTION_INFO);(0,t.expect)(r).toBeDefined(),(0,t.expect)(r).toMatchObject({type:s.ClearSignContextType.SOLANA_INSTRUCTION_INFO,certificate:O}),(0,t.expect)(r.payload).toMatchObject({programId:n,discriminator:"00000000",instructionInfo:{data:"0001sys_data",signature:"prodsig_sys"},substructures:[{kind:0,data:"df1"},{kind:0,data:"df2"},{kind:1,data:"vfp1"}]})}),(0,t.it)("does NOT emit SOLANA_ENUM_VARIANT contexts (those come from EnumVariantContextLoader)",async()=>{const o=u(),e=f(),r=e.descriptors.e517cb977ae3ad2a;if(!r)throw new Error("fixture: descriptor must exist");r.enum_variants={swap:{46:{variant_name:"raydiumCP",data:"01_raydium_tlv",signatures:{prod:"prodsig_v46",test:"testsig_v46"}}}},t.vi.spyOn(d,"getInstructionInfo").mockResolvedValue((0,i.Right)(e));const a=await o.load({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:p,discriminator:"e517cb977ae3ad2a"}]});(0,t.expect)(a.filter(l=>l.type===s.ClearSignContextType.SOLANA_ENUM_VARIANT)).toHaveLength(0)}),(0,t.it)("bundles CAL enum_variants into the SOLANA_INSTRUCTION_INFO payload (host decode cache)",async()=>{const o=u(),e=f(),r=e.descriptors.e517cb977ae3ad2a;if(!r)throw new Error("fixture: descriptor must exist");r.enum_variants={swap:{46:{variant_name:"raydiumCP",data:"01_raydium_tlv",signatures:{prod:"prodsig_v46",test:"testsig_v46"}}}},t.vi.spyOn(d,"getInstructionInfo").mockResolvedValue((0,i.Right)(e));const l=(await o.load({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:p,discriminator:"e517cb977ae3ad2a"}]})).find(y=>y.type===s.ClearSignContextType.SOLANA_INSTRUCTION_INFO);(0,t.expect)(l.payload.enumVariants).toEqual([{enumId:"swap",variantIndex:46,descriptor:{data:"01_raydium_tlv",signature:"prodsig_v46"}}])}),(0,t.it)("skips enum_variants keyed by a non-u16 index instead of leaking NaN",async()=>{const o=u(),e=f(),r=e.descriptors.e517cb977ae3ad2a;if(!r)throw new Error("fixture: descriptor must exist");r.enum_variants={swap:{46:{variant_name:"raydiumCP",data:"01_raydium_tlv",signatures:{prod:"prodsig_v46",test:"testsig_v46"}},"7abc":{variant_name:"junk",data:"junk_tlv",signatures:{prod:"x",test:"x"}},7e4:{variant_name:"overflow",data:"overflow_tlv",signatures:{prod:"x",test:"x"}}}},t.vi.spyOn(d,"getInstructionInfo").mockResolvedValue((0,i.Right)(e));const l=(await o.load({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:p,discriminator:"e517cb977ae3ad2a"}]})).find(y=>y.type===s.ClearSignContextType.SOLANA_INSTRUCTION_INFO);(0,t.expect)(l.payload.enumVariants).toEqual([{enumId:"swap",variantIndex:46,descriptor:{data:"01_raydium_tlv",signature:"prodsig_v46"}}])}),(0,t.it)("filters descriptors by requested discriminator",async()=>{const o=u(),e={programId:n,descriptors:{...m().descriptors,"00000001":{type:"instruction",instruction_info:{version:1,program_id:n,discriminator:"00000001",hash:"feedface2",descriptor:{data:"0001other",signatures:{prod:"p",test:"t"}}},display_fields:[{descriptor:"shouldnotappear"}]}}};t.vi.spyOn(d,"getInstructionInfo").mockResolvedValue((0,i.Right)(e));const a=(await o.load({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:n,discriminator:"00000000"}]})).filter(l=>l.type===s.ClearSignContextType.SOLANA_INSTRUCTION_INFO);(0,t.expect)(a).toHaveLength(1),(0,t.expect)(a[0].payload.discriminator).toBe("00000000")}),(0,t.it)("emits one ERROR context when CAL fails for a program but keeps processing the others",async()=>{const o=u();t.vi.spyOn(d,"getInstructionInfo").mockImplementation(async({programId:l})=>l===n?(0,i.Right)(m()):(0,i.Left)(new Error("upstream-500")));const e=await o.load({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:n,discriminator:"00000000"},{programId:p,discriminator:"e517cb977ae3ad2a"}]}),r=e.filter(l=>l.type===s.ClearSignContextType.ERROR),a=e.filter(l=>l.type===s.ClearSignContextType.SOLANA_INSTRUCTION_INFO);(0,t.expect)(r).toHaveLength(1),(0,t.expect)(a).toHaveLength(1)}),(0,t.it)("emits ERROR (not empty signature) when configured CAL mode signature is missing",async()=>{const o=new _.InstructionInfoContextLoader(d,{...g,cal:{...g.cal,mode:"test"}},I,N),e=f(),r=e.descriptors.e517cb977ae3ad2a;if(!r)throw new Error("fixture: descriptor must exist");delete r.instruction_info.descriptor.signatures.test,t.vi.spyOn(d,"getInstructionInfo").mockResolvedValue((0,i.Right)(e));const a=await o.load({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:p,discriminator:"e517cb977ae3ad2a"}]});(0,t.expect)(a).toHaveLength(1),(0,t.expect)(a[0]?.type).toBe(s.ClearSignContextType.ERROR),(0,t.expect)(a[0].error.message).toMatch(/missing 'test' signature/)}),(0,t.it)("degrades every program to ERROR when certificate loading fails",async()=>{t.vi.spyOn(I,"loadCertificate").mockRejectedValue(new Error("pki-down")),t.vi.spyOn(d,"getInstructionInfo").mockResolvedValue((0,i.Right)(m()));const e=await u().load({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:n},{programId:p}]});(0,t.expect)(e).toHaveLength(2),(0,t.expect)(e.every(r=>r.type===s.ClearSignContextType.ERROR)).toBe(!0)}),(0,t.it)("degrades every program to ERROR when the certificate resolves undefined",async()=>{t.vi.spyOn(I,"loadCertificate").mockResolvedValue(void 0),t.vi.spyOn(d,"getInstructionInfo").mockResolvedValue((0,i.Right)(m()));const e=await u().load({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:n},{programId:p}]});(0,t.expect)(e).toHaveLength(2),(0,t.expect)(e.every(r=>r.type===s.ClearSignContextType.ERROR)).toBe(!0),(0,t.expect)(e[0].error.message).toMatch(/certificate is missing/)}),(0,t.it)("selects signatures based on config.cal.mode (test)",async()=>{const o=new _.InstructionInfoContextLoader(d,{...g,cal:{...g.cal,mode:"test"}},I,N);t.vi.spyOn(d,"getInstructionInfo").mockResolvedValue((0,i.Right)(f()));const r=(await o.load({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:p,discriminator:"e517cb977ae3ad2a"}]})).find(a=>a.type===s.ClearSignContextType.SOLANA_INSTRUCTION_INFO);(0,t.expect)(r.payload.instructionInfo.signature).toBe("testsig_jup")}),(0,t.it)("falls back to default network when input.network is omitted or empty string",async()=>{const o=u(),e=t.vi.spyOn(d,"getInstructionInfo").mockResolvedValue((0,i.Right)(m()));await o.load({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:n}]}),(0,t.expect)(e).toHaveBeenLastCalledWith({programId:n,network:"solana-mainnet"}),await o.load({deviceModelId:c.DeviceModelId.NANO_X,instructions:[{programId:n}],network:""}),(0,t.expect)(e).toHaveBeenLastCalledWith({programId:n,network:"solana-mainnet"})})})});
|
|
2
2
|
//# sourceMappingURL=InstructionInfoContextLoader.test.js.map
|
package/lib/cjs/src/modules/solana/instruction-info/domain/InstructionInfoContextLoader.test.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/solana/instruction-info/domain/InstructionInfoContextLoader.test.ts"],
|
|
4
|
-
"sourcesContent": ["/* eslint-disable @typescript-eslint/no-explicit-any */\n/* eslint-disable @typescript-eslint/no-unsafe-member-access */\nimport { DeviceModelId } 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 { type PkiCertificateLoader } from \"@/modules/multichain/pki/domain/PkiCertificateLoader\";\nimport {\n type InstructionInfoDataSource,\n type InstructionInfoResult,\n} from \"@/modules/solana/instruction-info/data/InstructionInfoDataSource\";\nimport { ClearSignContextType } from \"@/shared/model/ClearSignContext\";\n\nimport { InstructionInfoContextLoader } from \"./InstructionInfoContextLoader\";\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: 8,\n payload: new Uint8Array([0x42]),\n};\n\nconst mockConfig = {\n cal: {\n url: \"https://crypto-assets-service.api.ledger.com/v1\",\n mode: \"prod\",\n branch: \"main\",\n },\n} as ContextModuleServiceConfig;\n\nconst SYSTEM_PROGRAM = \"11111111111111111111111111111111\";\nconst JUPITER_PROGRAM = \"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4\";\n\nfunction makeSystemCreateAccountResult(): InstructionInfoResult {\n return {\n programId: SYSTEM_PROGRAM,\n descriptors: {\n \"00000000\": {\n type: \"instruction\",\n instruction_info: {\n version: 1,\n program_id: SYSTEM_PROGRAM,\n discriminator: \"00000000\",\n hash: \"feedface\",\n descriptor: {\n data: \"0001sys_data\",\n signatures: { prod: \"prodsig_sys\", test: \"testsig_sys\" },\n },\n },\n display_fields: [{ descriptor: \"df1\" }, { descriptor: \"df2\" }],\n value_flow_ports: [{ descriptor: \"vfp1\" }],\n hide_rules: [],\n account_resets: [],\n },\n },\n };\n}\n\nfunction makeJupiterRouteResult(): InstructionInfoResult {\n return {\n programId: JUPITER_PROGRAM,\n descriptors: {\n e517cb977ae3ad2a: {\n type: \"instruction\",\n instruction_info: {\n version: 1,\n program_id: JUPITER_PROGRAM,\n discriminator: \"e517cb977ae3ad2a\",\n hash: \"deadbeef\",\n descriptor: {\n data: \"0001jup_data\",\n signatures: { prod: \"prodsig_jup\", test: \"testsig_jup\" },\n },\n },\n display_fields: [],\n value_flow_ports: [{ descriptor: \"vfp_in\" }, { descriptor: \"vfp_out\" }],\n hide_rules: [],\n account_resets: [],\n },\n },\n };\n}\n\ndescribe(\"InstructionInfoContextLoader\", () => {\n let dataSource: InstructionInfoDataSource;\n let certificateLoader: PkiCertificateLoader;\n\n beforeEach(() => {\n vi.restoreAllMocks();\n dataSource = {\n getInstructionInfo: vi.fn(),\n };\n certificateLoader = {\n loadCertificate: vi.fn().mockResolvedValue(mockCertificate),\n };\n });\n\n const makeLoader = (config = mockConfig) =>\n new InstructionInfoContextLoader(\n dataSource,\n config,\n certificateLoader,\n mockLoggerFactory,\n );\n\n describe(\"canHandle\", () => {\n it(\"returns true when SOLANA_INSTRUCTION_INFO is requested and instructions are present\", () => {\n expect(\n makeLoader().canHandle(\n {\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [{ programId: SYSTEM_PROGRAM }],\n },\n [ClearSignContextType.SOLANA_INSTRUCTION_INFO],\n ),\n ).toBe(true);\n });\n\n it(\"returns false when SOLANA_INSTRUCTION_INFO is not requested\", () => {\n expect(\n makeLoader().canHandle(\n {\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [{ programId: SYSTEM_PROGRAM }],\n },\n [ClearSignContextType.SOLANA_TOKEN],\n ),\n ).toBe(false);\n });\n\n it(\"returns false on missing / empty / malformed instructions\", () => {\n const loader = makeLoader();\n const types = [ClearSignContextType.SOLANA_INSTRUCTION_INFO];\n expect(loader.canHandle({} as any, types)).toBe(false);\n expect(loader.canHandle({ instructions: [] } as any, types)).toBe(false);\n expect(\n loader.canHandle({ instructions: [{ programId: \"\" }] } as any, types),\n ).toBe(false);\n expect(loader.canHandle(null, types)).toBe(false);\n expect(loader.canHandle(\"string\", types)).toBe(false);\n });\n });\n\n describe(\"load\", () => {\n it(\"fans out one CAL call per distinct programId in parallel\", async () => {\n const loader = makeLoader();\n const spy = vi\n .spyOn(dataSource, \"getInstructionInfo\")\n .mockImplementation(async ({ programId }) => {\n if (programId === SYSTEM_PROGRAM)\n return Right(makeSystemCreateAccountResult());\n if (programId === JUPITER_PROGRAM)\n return Right(makeJupiterRouteResult());\n return Left(new Error(\"unknown program\"));\n });\n\n const result = await loader.load({\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [\n { programId: SYSTEM_PROGRAM, discriminator: \"00000000\" },\n { programId: SYSTEM_PROGRAM, discriminator: \"00000000\" }, // dup\n { programId: JUPITER_PROGRAM, discriminator: \"e517cb977ae3ad2a\" },\n ],\n network: \"solana-mainnet\",\n });\n\n expect(spy).toHaveBeenCalledTimes(2);\n expect(spy).toHaveBeenCalledWith({\n programId: SYSTEM_PROGRAM,\n network: \"solana-mainnet\",\n });\n expect(spy).toHaveBeenCalledWith({\n programId: JUPITER_PROGRAM,\n network: \"solana-mainnet\",\n });\n\n // Only INSTRUCTION_INFO contexts now \u2014 enum variants moved to\n // EnumVariantContextLoader.\n expect(\n result.every(\n (c) =>\n c.type === ClearSignContextType.SOLANA_INSTRUCTION_INFO ||\n c.type === ClearSignContextType.ERROR,\n ),\n ).toBe(true);\n expect(\n result.filter(\n (c) => c.type === ClearSignContextType.SOLANA_INSTRUCTION_INFO,\n ),\n ).toHaveLength(2);\n });\n\n it(\"emits one SOLANA_INSTRUCTION_INFO per matching (programId, discriminator) with substructures and certificate bundled\", async () => {\n const loader = makeLoader();\n vi.spyOn(dataSource, \"getInstructionInfo\").mockResolvedValue(\n Right(makeSystemCreateAccountResult()),\n );\n\n const result = await loader.load({\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [\n { programId: SYSTEM_PROGRAM, discriminator: \"00000000\" },\n ],\n });\n\n const infoCtx = result.find(\n (c) => c.type === ClearSignContextType.SOLANA_INSTRUCTION_INFO,\n );\n expect(infoCtx).toBeDefined();\n expect(infoCtx).toMatchObject({\n type: ClearSignContextType.SOLANA_INSTRUCTION_INFO,\n certificate: mockCertificate,\n });\n expect((infoCtx as any).payload).toMatchObject({\n programId: SYSTEM_PROGRAM,\n discriminator: \"00000000\",\n instructionInfo: { data: \"0001sys_data\", signature: \"prodsig_sys\" },\n substructures: [\n { kind: 0x00, data: \"df1\" },\n { kind: 0x00, data: \"df2\" },\n { kind: 0x01, data: \"vfp1\" },\n ],\n });\n });\n\n it(\"does NOT emit SOLANA_ENUM_VARIANT contexts (those come from EnumVariantContextLoader)\", async () => {\n const loader = makeLoader();\n const jupiter = makeJupiterRouteResult();\n // Stuff an enum_variants entry into the descriptor to confirm it's ignored.\n const descriptor = jupiter.descriptors[\"e517cb977ae3ad2a\"];\n if (!descriptor) throw new Error(\"fixture: descriptor must exist\");\n descriptor.enum_variants = {\n swap: {\n \"46\": {\n variant_name: \"raydiumCP\",\n data: \"01_raydium_tlv\",\n signatures: { prod: \"prodsig_v46\", test: \"testsig_v46\" },\n },\n },\n };\n vi.spyOn(dataSource, \"getInstructionInfo\").mockResolvedValue(\n Right(jupiter),\n );\n\n const result = await loader.load({\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [\n { programId: JUPITER_PROGRAM, discriminator: \"e517cb977ae3ad2a\" },\n ],\n });\n\n expect(\n result.filter(\n (c) => c.type === ClearSignContextType.SOLANA_ENUM_VARIANT,\n ),\n ).toHaveLength(0);\n });\n\n it(\"filters descriptors by requested discriminator\", async () => {\n const loader = makeLoader();\n const result: InstructionInfoResult = {\n programId: SYSTEM_PROGRAM,\n descriptors: {\n ...makeSystemCreateAccountResult().descriptors,\n \"00000001\": {\n type: \"instruction\",\n instruction_info: {\n version: 1,\n program_id: SYSTEM_PROGRAM,\n discriminator: \"00000001\",\n hash: \"feedface2\",\n descriptor: {\n data: \"0001other\",\n signatures: { prod: \"p\", test: \"t\" },\n },\n },\n display_fields: [{ descriptor: \"shouldnotappear\" }],\n },\n },\n };\n vi.spyOn(dataSource, \"getInstructionInfo\").mockResolvedValue(\n Right(result),\n );\n\n const out = await loader.load({\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [\n { programId: SYSTEM_PROGRAM, discriminator: \"00000000\" },\n ],\n });\n\n const infos = out.filter(\n (c) => c.type === ClearSignContextType.SOLANA_INSTRUCTION_INFO,\n );\n expect(infos).toHaveLength(1);\n expect((infos[0] as any).payload.discriminator).toBe(\"00000000\");\n });\n\n it(\"emits one ERROR context when CAL fails for a program but keeps processing the others\", async () => {\n const loader = makeLoader();\n vi.spyOn(dataSource, \"getInstructionInfo\").mockImplementation(\n async ({ programId }) => {\n if (programId === SYSTEM_PROGRAM)\n return Right(makeSystemCreateAccountResult());\n return Left(new Error(\"upstream-500\"));\n },\n );\n\n const out = await loader.load({\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [\n { programId: SYSTEM_PROGRAM, discriminator: \"00000000\" },\n { programId: JUPITER_PROGRAM, discriminator: \"e517cb977ae3ad2a\" },\n ],\n });\n\n const errors = out.filter((c) => c.type === ClearSignContextType.ERROR);\n const infos = out.filter(\n (c) => c.type === ClearSignContextType.SOLANA_INSTRUCTION_INFO,\n );\n expect(errors).toHaveLength(1);\n expect(infos).toHaveLength(1);\n });\n\n it(\"emits ERROR (not empty signature) when configured CAL mode signature is missing\", async () => {\n const loader = new InstructionInfoContextLoader(\n dataSource,\n { ...mockConfig, cal: { ...mockConfig.cal, mode: \"test\" } } as any,\n certificateLoader,\n mockLoggerFactory,\n );\n // Only prod signature in CAL response; loader is configured for test.\n const result = makeJupiterRouteResult();\n const descriptor = result.descriptors[\"e517cb977ae3ad2a\"];\n if (!descriptor) throw new Error(\"fixture: descriptor must exist\");\n delete (descriptor.instruction_info.descriptor.signatures as any).test;\n vi.spyOn(dataSource, \"getInstructionInfo\").mockResolvedValue(\n Right(result),\n );\n\n const out = await loader.load({\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [\n { programId: JUPITER_PROGRAM, discriminator: \"e517cb977ae3ad2a\" },\n ],\n });\n\n expect(out).toHaveLength(1);\n expect(out[0]?.type).toBe(ClearSignContextType.ERROR);\n expect((out[0] as any).error.message).toMatch(/missing 'test' signature/);\n });\n\n it(\"degrades every program to ERROR when certificate loading fails\", async () => {\n vi.spyOn(certificateLoader, \"loadCertificate\").mockRejectedValue(\n new Error(\"pki-down\"),\n );\n // Data fetches happen in parallel with the cert load, so they may run\n // before the cert rejection settles. Resolve them so allSettled-like\n // logic doesn't dangle.\n vi.spyOn(dataSource, \"getInstructionInfo\").mockResolvedValue(\n Right(makeSystemCreateAccountResult()),\n );\n const loader = makeLoader();\n\n const out = await loader.load({\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [\n { programId: SYSTEM_PROGRAM },\n { programId: JUPITER_PROGRAM },\n ],\n });\n\n expect(out).toHaveLength(2);\n expect(out.every((c) => c.type === ClearSignContextType.ERROR)).toBe(\n true,\n );\n });\n\n it(\"degrades every program to ERROR when the certificate resolves undefined\", async () => {\n vi.spyOn(certificateLoader, \"loadCertificate\").mockResolvedValue(\n undefined,\n );\n vi.spyOn(dataSource, \"getInstructionInfo\").mockResolvedValue(\n Right(makeSystemCreateAccountResult()),\n );\n const loader = makeLoader();\n\n const out = await loader.load({\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [\n { programId: SYSTEM_PROGRAM },\n { programId: JUPITER_PROGRAM },\n ],\n });\n\n expect(out).toHaveLength(2);\n expect(out.every((c) => c.type === ClearSignContextType.ERROR)).toBe(\n true,\n );\n expect((out[0] as any).error.message).toMatch(/certificate is missing/);\n });\n\n it(\"selects signatures based on config.cal.mode (test)\", async () => {\n const loader = new InstructionInfoContextLoader(\n dataSource,\n { ...mockConfig, cal: { ...mockConfig.cal, mode: \"test\" } } as any,\n certificateLoader,\n mockLoggerFactory,\n );\n vi.spyOn(dataSource, \"getInstructionInfo\").mockResolvedValue(\n Right(makeJupiterRouteResult()),\n );\n\n const out = await loader.load({\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [\n { programId: JUPITER_PROGRAM, discriminator: \"e517cb977ae3ad2a\" },\n ],\n });\n\n const info = out.find(\n (c) => c.type === ClearSignContextType.SOLANA_INSTRUCTION_INFO,\n );\n expect((info as any).payload.instructionInfo.signature).toBe(\n \"testsig_jup\",\n );\n });\n\n it(\"falls back to default network when input.network is omitted or empty string\", async () => {\n const loader = makeLoader();\n const spy = vi\n .spyOn(dataSource, \"getInstructionInfo\")\n .mockResolvedValue(Right(makeSystemCreateAccountResult()));\n\n await loader.load({\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [{ programId: SYSTEM_PROGRAM }],\n });\n expect(spy).toHaveBeenLastCalledWith({\n programId: SYSTEM_PROGRAM,\n network: \"solana-mainnet\",\n });\n\n await loader.load({\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [{ programId: SYSTEM_PROGRAM }],\n network: \"\",\n });\n expect(spy).toHaveBeenLastCalledWith({\n programId: SYSTEM_PROGRAM,\n network: \"solana-mainnet\",\n });\n });\n });\n});\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["/* eslint-disable @typescript-eslint/no-explicit-any */\n/* eslint-disable @typescript-eslint/no-unsafe-member-access */\nimport { DeviceModelId } 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 { type PkiCertificateLoader } from \"@/modules/multichain/pki/domain/PkiCertificateLoader\";\nimport {\n type InstructionInfoDataSource,\n type InstructionInfoResult,\n} from \"@/modules/solana/instruction-info/data/InstructionInfoDataSource\";\nimport { ClearSignContextType } from \"@/shared/model/ClearSignContext\";\n\nimport { InstructionInfoContextLoader } from \"./InstructionInfoContextLoader\";\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: 8,\n payload: new Uint8Array([0x42]),\n};\n\nconst mockConfig = {\n cal: {\n url: \"https://crypto-assets-service.api.ledger.com/v1\",\n mode: \"prod\",\n branch: \"main\",\n },\n} as ContextModuleServiceConfig;\n\nconst SYSTEM_PROGRAM = \"11111111111111111111111111111111\";\nconst JUPITER_PROGRAM = \"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4\";\n\nfunction makeSystemCreateAccountResult(): InstructionInfoResult {\n return {\n programId: SYSTEM_PROGRAM,\n descriptors: {\n \"00000000\": {\n type: \"instruction\",\n instruction_info: {\n version: 1,\n program_id: SYSTEM_PROGRAM,\n discriminator: \"00000000\",\n hash: \"feedface\",\n descriptor: {\n data: \"0001sys_data\",\n signatures: { prod: \"prodsig_sys\", test: \"testsig_sys\" },\n },\n },\n display_fields: [{ descriptor: \"df1\" }, { descriptor: \"df2\" }],\n value_flow_ports: [{ descriptor: \"vfp1\" }],\n hide_rules: [],\n account_resets: [],\n },\n },\n };\n}\n\nfunction makeJupiterRouteResult(): InstructionInfoResult {\n return {\n programId: JUPITER_PROGRAM,\n descriptors: {\n e517cb977ae3ad2a: {\n type: \"instruction\",\n instruction_info: {\n version: 1,\n program_id: JUPITER_PROGRAM,\n discriminator: \"e517cb977ae3ad2a\",\n hash: \"deadbeef\",\n descriptor: {\n data: \"0001jup_data\",\n signatures: { prod: \"prodsig_jup\", test: \"testsig_jup\" },\n },\n },\n display_fields: [],\n value_flow_ports: [{ descriptor: \"vfp_in\" }, { descriptor: \"vfp_out\" }],\n hide_rules: [],\n account_resets: [],\n },\n },\n };\n}\n\ndescribe(\"InstructionInfoContextLoader\", () => {\n let dataSource: InstructionInfoDataSource;\n let certificateLoader: PkiCertificateLoader;\n\n beforeEach(() => {\n vi.restoreAllMocks();\n dataSource = {\n getInstructionInfo: vi.fn(),\n };\n certificateLoader = {\n loadCertificate: vi.fn().mockResolvedValue(mockCertificate),\n };\n });\n\n const makeLoader = (config = mockConfig) =>\n new InstructionInfoContextLoader(\n dataSource,\n config,\n certificateLoader,\n mockLoggerFactory,\n );\n\n describe(\"canHandle\", () => {\n it(\"returns true when SOLANA_INSTRUCTION_INFO is requested and instructions are present\", () => {\n expect(\n makeLoader().canHandle(\n {\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [{ programId: SYSTEM_PROGRAM }],\n },\n [ClearSignContextType.SOLANA_INSTRUCTION_INFO],\n ),\n ).toBe(true);\n });\n\n it(\"returns false when SOLANA_INSTRUCTION_INFO is not requested\", () => {\n expect(\n makeLoader().canHandle(\n {\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [{ programId: SYSTEM_PROGRAM }],\n },\n [ClearSignContextType.SOLANA_TOKEN],\n ),\n ).toBe(false);\n });\n\n it(\"returns false on missing / empty / malformed instructions\", () => {\n const loader = makeLoader();\n const types = [ClearSignContextType.SOLANA_INSTRUCTION_INFO];\n expect(loader.canHandle({} as any, types)).toBe(false);\n expect(loader.canHandle({ instructions: [] } as any, types)).toBe(false);\n expect(\n loader.canHandle({ instructions: [{ programId: \"\" }] } as any, types),\n ).toBe(false);\n expect(loader.canHandle(null, types)).toBe(false);\n expect(loader.canHandle(\"string\", types)).toBe(false);\n });\n });\n\n describe(\"load\", () => {\n it(\"fans out one CAL call per distinct programId in parallel\", async () => {\n const loader = makeLoader();\n const spy = vi\n .spyOn(dataSource, \"getInstructionInfo\")\n .mockImplementation(async ({ programId }) => {\n if (programId === SYSTEM_PROGRAM)\n return Right(makeSystemCreateAccountResult());\n if (programId === JUPITER_PROGRAM)\n return Right(makeJupiterRouteResult());\n return Left(new Error(\"unknown program\"));\n });\n\n const result = await loader.load({\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [\n { programId: SYSTEM_PROGRAM, discriminator: \"00000000\" },\n { programId: SYSTEM_PROGRAM, discriminator: \"00000000\" }, // dup\n { programId: JUPITER_PROGRAM, discriminator: \"e517cb977ae3ad2a\" },\n ],\n network: \"solana-mainnet\",\n });\n\n expect(spy).toHaveBeenCalledTimes(2);\n expect(spy).toHaveBeenCalledWith({\n programId: SYSTEM_PROGRAM,\n network: \"solana-mainnet\",\n });\n expect(spy).toHaveBeenCalledWith({\n programId: JUPITER_PROGRAM,\n network: \"solana-mainnet\",\n });\n\n // Only INSTRUCTION_INFO contexts now \u2014 enum variants moved to\n // EnumVariantContextLoader.\n expect(\n result.every(\n (c) =>\n c.type === ClearSignContextType.SOLANA_INSTRUCTION_INFO ||\n c.type === ClearSignContextType.ERROR,\n ),\n ).toBe(true);\n expect(\n result.filter(\n (c) => c.type === ClearSignContextType.SOLANA_INSTRUCTION_INFO,\n ),\n ).toHaveLength(2);\n });\n\n it(\"emits one SOLANA_INSTRUCTION_INFO per matching (programId, discriminator) with substructures and certificate bundled\", async () => {\n const loader = makeLoader();\n vi.spyOn(dataSource, \"getInstructionInfo\").mockResolvedValue(\n Right(makeSystemCreateAccountResult()),\n );\n\n const result = await loader.load({\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [\n { programId: SYSTEM_PROGRAM, discriminator: \"00000000\" },\n ],\n });\n\n const infoCtx = result.find(\n (c) => c.type === ClearSignContextType.SOLANA_INSTRUCTION_INFO,\n );\n expect(infoCtx).toBeDefined();\n expect(infoCtx).toMatchObject({\n type: ClearSignContextType.SOLANA_INSTRUCTION_INFO,\n certificate: mockCertificate,\n });\n expect((infoCtx as any).payload).toMatchObject({\n programId: SYSTEM_PROGRAM,\n discriminator: \"00000000\",\n instructionInfo: { data: \"0001sys_data\", signature: \"prodsig_sys\" },\n substructures: [\n { kind: 0x00, data: \"df1\" },\n { kind: 0x00, data: \"df2\" },\n { kind: 0x01, data: \"vfp1\" },\n ],\n });\n });\n\n it(\"does NOT emit SOLANA_ENUM_VARIANT contexts (those come from EnumVariantContextLoader)\", async () => {\n const loader = makeLoader();\n const jupiter = makeJupiterRouteResult();\n // Stuff an enum_variants entry into the descriptor to confirm it's ignored.\n const descriptor = jupiter.descriptors[\"e517cb977ae3ad2a\"];\n if (!descriptor) throw new Error(\"fixture: descriptor must exist\");\n descriptor.enum_variants = {\n swap: {\n \"46\": {\n variant_name: \"raydiumCP\",\n data: \"01_raydium_tlv\",\n signatures: { prod: \"prodsig_v46\", test: \"testsig_v46\" },\n },\n },\n };\n vi.spyOn(dataSource, \"getInstructionInfo\").mockResolvedValue(\n Right(jupiter),\n );\n\n const result = await loader.load({\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [\n { programId: JUPITER_PROGRAM, discriminator: \"e517cb977ae3ad2a\" },\n ],\n });\n\n expect(\n result.filter(\n (c) => c.type === ClearSignContextType.SOLANA_ENUM_VARIANT,\n ),\n ).toHaveLength(0);\n });\n\n it(\"bundles CAL enum_variants into the SOLANA_INSTRUCTION_INFO payload (host decode cache)\", async () => {\n const loader = makeLoader();\n const jupiter = makeJupiterRouteResult();\n const descriptor = jupiter.descriptors[\"e517cb977ae3ad2a\"];\n if (!descriptor) throw new Error(\"fixture: descriptor must exist\");\n descriptor.enum_variants = {\n swap: {\n \"46\": {\n variant_name: \"raydiumCP\",\n data: \"01_raydium_tlv\",\n signatures: { prod: \"prodsig_v46\", test: \"testsig_v46\" },\n },\n },\n };\n vi.spyOn(dataSource, \"getInstructionInfo\").mockResolvedValue(\n Right(jupiter),\n );\n\n const result = await loader.load({\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [\n { programId: JUPITER_PROGRAM, discriminator: \"e517cb977ae3ad2a\" },\n ],\n });\n\n const infoCtx = result.find(\n (c) => c.type === ClearSignContextType.SOLANA_INSTRUCTION_INFO,\n );\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n expect((infoCtx as any).payload.enumVariants).toEqual([\n {\n enumId: \"swap\",\n variantIndex: 46,\n descriptor: { data: \"01_raydium_tlv\", signature: \"prodsig_v46\" },\n },\n ]);\n });\n\n it(\"skips enum_variants keyed by a non-u16 index instead of leaking NaN\", async () => {\n const loader = makeLoader();\n const jupiter = makeJupiterRouteResult();\n const descriptor = jupiter.descriptors[\"e517cb977ae3ad2a\"];\n if (!descriptor) throw new Error(\"fixture: descriptor must exist\");\n descriptor.enum_variants = {\n swap: {\n \"46\": {\n variant_name: \"raydiumCP\",\n data: \"01_raydium_tlv\",\n signatures: { prod: \"prodsig_v46\", test: \"testsig_v46\" },\n },\n // malformed keys CAL must never produce, but the codec guards anyway\n \"7abc\": {\n variant_name: \"junk\",\n data: \"junk_tlv\",\n signatures: { prod: \"x\", test: \"x\" },\n },\n \"70000\": {\n variant_name: \"overflow\",\n data: \"overflow_tlv\",\n signatures: { prod: \"x\", test: \"x\" },\n },\n },\n };\n vi.spyOn(dataSource, \"getInstructionInfo\").mockResolvedValue(\n Right(jupiter),\n );\n\n const result = await loader.load({\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [\n { programId: JUPITER_PROGRAM, discriminator: \"e517cb977ae3ad2a\" },\n ],\n });\n\n const infoCtx = result.find(\n (c) => c.type === ClearSignContextType.SOLANA_INSTRUCTION_INFO,\n );\n // only the valid u16 key survives\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n expect((infoCtx as any).payload.enumVariants).toEqual([\n {\n enumId: \"swap\",\n variantIndex: 46,\n descriptor: { data: \"01_raydium_tlv\", signature: \"prodsig_v46\" },\n },\n ]);\n });\n\n it(\"filters descriptors by requested discriminator\", async () => {\n const loader = makeLoader();\n const result: InstructionInfoResult = {\n programId: SYSTEM_PROGRAM,\n descriptors: {\n ...makeSystemCreateAccountResult().descriptors,\n \"00000001\": {\n type: \"instruction\",\n instruction_info: {\n version: 1,\n program_id: SYSTEM_PROGRAM,\n discriminator: \"00000001\",\n hash: \"feedface2\",\n descriptor: {\n data: \"0001other\",\n signatures: { prod: \"p\", test: \"t\" },\n },\n },\n display_fields: [{ descriptor: \"shouldnotappear\" }],\n },\n },\n };\n vi.spyOn(dataSource, \"getInstructionInfo\").mockResolvedValue(\n Right(result),\n );\n\n const out = await loader.load({\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [\n { programId: SYSTEM_PROGRAM, discriminator: \"00000000\" },\n ],\n });\n\n const infos = out.filter(\n (c) => c.type === ClearSignContextType.SOLANA_INSTRUCTION_INFO,\n );\n expect(infos).toHaveLength(1);\n expect((infos[0] as any).payload.discriminator).toBe(\"00000000\");\n });\n\n it(\"emits one ERROR context when CAL fails for a program but keeps processing the others\", async () => {\n const loader = makeLoader();\n vi.spyOn(dataSource, \"getInstructionInfo\").mockImplementation(\n async ({ programId }) => {\n if (programId === SYSTEM_PROGRAM)\n return Right(makeSystemCreateAccountResult());\n return Left(new Error(\"upstream-500\"));\n },\n );\n\n const out = await loader.load({\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [\n { programId: SYSTEM_PROGRAM, discriminator: \"00000000\" },\n { programId: JUPITER_PROGRAM, discriminator: \"e517cb977ae3ad2a\" },\n ],\n });\n\n const errors = out.filter((c) => c.type === ClearSignContextType.ERROR);\n const infos = out.filter(\n (c) => c.type === ClearSignContextType.SOLANA_INSTRUCTION_INFO,\n );\n expect(errors).toHaveLength(1);\n expect(infos).toHaveLength(1);\n });\n\n it(\"emits ERROR (not empty signature) when configured CAL mode signature is missing\", async () => {\n const loader = new InstructionInfoContextLoader(\n dataSource,\n { ...mockConfig, cal: { ...mockConfig.cal, mode: \"test\" } } as any,\n certificateLoader,\n mockLoggerFactory,\n );\n // Only prod signature in CAL response; loader is configured for test.\n const result = makeJupiterRouteResult();\n const descriptor = result.descriptors[\"e517cb977ae3ad2a\"];\n if (!descriptor) throw new Error(\"fixture: descriptor must exist\");\n delete (descriptor.instruction_info.descriptor.signatures as any).test;\n vi.spyOn(dataSource, \"getInstructionInfo\").mockResolvedValue(\n Right(result),\n );\n\n const out = await loader.load({\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [\n { programId: JUPITER_PROGRAM, discriminator: \"e517cb977ae3ad2a\" },\n ],\n });\n\n expect(out).toHaveLength(1);\n expect(out[0]?.type).toBe(ClearSignContextType.ERROR);\n expect((out[0] as any).error.message).toMatch(/missing 'test' signature/);\n });\n\n it(\"degrades every program to ERROR when certificate loading fails\", async () => {\n vi.spyOn(certificateLoader, \"loadCertificate\").mockRejectedValue(\n new Error(\"pki-down\"),\n );\n // Data fetches happen in parallel with the cert load, so they may run\n // before the cert rejection settles. Resolve them so allSettled-like\n // logic doesn't dangle.\n vi.spyOn(dataSource, \"getInstructionInfo\").mockResolvedValue(\n Right(makeSystemCreateAccountResult()),\n );\n const loader = makeLoader();\n\n const out = await loader.load({\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [\n { programId: SYSTEM_PROGRAM },\n { programId: JUPITER_PROGRAM },\n ],\n });\n\n expect(out).toHaveLength(2);\n expect(out.every((c) => c.type === ClearSignContextType.ERROR)).toBe(\n true,\n );\n });\n\n it(\"degrades every program to ERROR when the certificate resolves undefined\", async () => {\n vi.spyOn(certificateLoader, \"loadCertificate\").mockResolvedValue(\n undefined,\n );\n vi.spyOn(dataSource, \"getInstructionInfo\").mockResolvedValue(\n Right(makeSystemCreateAccountResult()),\n );\n const loader = makeLoader();\n\n const out = await loader.load({\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [\n { programId: SYSTEM_PROGRAM },\n { programId: JUPITER_PROGRAM },\n ],\n });\n\n expect(out).toHaveLength(2);\n expect(out.every((c) => c.type === ClearSignContextType.ERROR)).toBe(\n true,\n );\n expect((out[0] as any).error.message).toMatch(/certificate is missing/);\n });\n\n it(\"selects signatures based on config.cal.mode (test)\", async () => {\n const loader = new InstructionInfoContextLoader(\n dataSource,\n { ...mockConfig, cal: { ...mockConfig.cal, mode: \"test\" } } as any,\n certificateLoader,\n mockLoggerFactory,\n );\n vi.spyOn(dataSource, \"getInstructionInfo\").mockResolvedValue(\n Right(makeJupiterRouteResult()),\n );\n\n const out = await loader.load({\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [\n { programId: JUPITER_PROGRAM, discriminator: \"e517cb977ae3ad2a\" },\n ],\n });\n\n const info = out.find(\n (c) => c.type === ClearSignContextType.SOLANA_INSTRUCTION_INFO,\n );\n expect((info as any).payload.instructionInfo.signature).toBe(\n \"testsig_jup\",\n );\n });\n\n it(\"falls back to default network when input.network is omitted or empty string\", async () => {\n const loader = makeLoader();\n const spy = vi\n .spyOn(dataSource, \"getInstructionInfo\")\n .mockResolvedValue(Right(makeSystemCreateAccountResult()));\n\n await loader.load({\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [{ programId: SYSTEM_PROGRAM }],\n });\n expect(spy).toHaveBeenLastCalledWith({\n programId: SYSTEM_PROGRAM,\n network: \"solana-mainnet\",\n });\n\n await loader.load({\n deviceModelId: DeviceModelId.NANO_X,\n instructions: [{ programId: SYSTEM_PROGRAM }],\n network: \"\",\n });\n expect(spy).toHaveBeenLastCalledWith({\n programId: SYSTEM_PROGRAM,\n network: \"solana-mainnet\",\n });\n });\n });\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["import_device_management_kit", "import_purify_ts", "import_vitest", "import_ClearSignContext", "import_InstructionInfoContextLoader", "mockLoggerFactory", "mockCertificate", "mockConfig", "SYSTEM_PROGRAM", "JUPITER_PROGRAM", "makeSystemCreateAccountResult", "makeJupiterRouteResult", "dataSource", "certificateLoader", "makeLoader", "config", "loader", "types", "spy", "programId", "result", "c", "infoCtx", "jupiter", "descriptor", "infos", "out", "errors", "info"]
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/solana/model/SolanaClearSignContext.ts"],
|
|
4
|
-
"sourcesContent": ["import { type PkiCertificate } from \"@/modules/multichain/pki/model/PkiCertificate\";\nimport {\n type SolanaAltResolutionPayload,\n type SolanaEnumVariantPayload,\n type SolanaInstructionInfoPayload,\n type SolanaLifiPayload,\n type SolanaTokenAccountStatePayload,\n type SolanaTokenData,\n type SolanaTokenInfoPayload,\n type SolanaTransactionCheckPayload,\n} from \"@/modules/solana/model/SolanaPayloads\";\nimport {\n type ClearSignContext,\n type ClearSignContextSuccess,\n ClearSignContextType,\n} from \"@/shared/model/ClearSignContext\";\n\n// Re-export Solana payload types so consumers can import from this file\nexport type {\n SolanaAltResolutionPayload,\n SolanaEnumVariantPayload,\n SolanaInstructionInfoPayload,\n SolanaInstructionSubstructure,\n SolanaLifiInstructionMeta,\n SolanaLifiPayload,\n SolanaSignedDescriptor,\n SolanaTokenAccountStatePayload,\n SolanaTokenData,\n SolanaTokenInfoPayload,\n SolanaTransactionCheckPayload,\n SolanaTransactionDescriptor,\n} from \"@/modules/solana/model/SolanaPayloads\";\nexport { SolanaInstructionSubstructureKind } from \"@/modules/solana/model/SolanaPayloads\";\n\n/**\n * Solana-specific payload overrides \u2014 contributed to the shared\n * ClearSignContextSuccessPayloads map at the integration boundary.\n */\nexport type SolanaPayloadOverrides = {\n [ClearSignContextType.SOLANA_TOKEN]: {\n payload: SolanaTokenData;\n certificate?: PkiCertificate;\n };\n [ClearSignContextType.SOLANA_LIFI]: {\n payload: SolanaLifiPayload;\n certificate?: PkiCertificate;\n };\n [ClearSignContextType.SOLANA_TRUSTED_NAME]: {\n payload: Uint8Array;\n certificate?: PkiCertificate;\n };\n [ClearSignContextType.SOLANA_TRANSACTION_CHECK]: {\n payload: SolanaTransactionCheckPayload;\n certificate?: PkiCertificate;\n };\n [ClearSignContextType.SOLANA_INSTRUCTION_INFO]: {\n payload: SolanaInstructionInfoPayload;\n certificate?: PkiCertificate;\n };\n [ClearSignContextType.SOLANA_ENUM_VARIANT]: {\n payload: SolanaEnumVariantPayload;\n certificate?: PkiCertificate;\n };\n [ClearSignContextType.SOLANA_TOKEN_INFO]: {\n payload: SolanaTokenInfoPayload;\n certificate?: PkiCertificate;\n };\n [ClearSignContextType.SOLANA_TOKEN_ACCOUNT_STATE]: {\n payload: SolanaTokenAccountStatePayload;\n certificate?: PkiCertificate;\n };\n [ClearSignContextType.SOLANA_ALT_RESOLUTION]: {\n payload: SolanaAltResolutionPayload;\n certificate?: PkiCertificate;\n };\n};\n\nexport const SolanaContextType = {\n TOKEN: ClearSignContextType.SOLANA_TOKEN,\n LIFI: ClearSignContextType.SOLANA_LIFI,\n TRUSTED_NAME: ClearSignContextType.SOLANA_TRUSTED_NAME,\n TRANSACTION_CHECK: ClearSignContextType.SOLANA_TRANSACTION_CHECK,\n INSTRUCTION_INFO: ClearSignContextType.SOLANA_INSTRUCTION_INFO,\n ENUM_VARIANT: ClearSignContextType.SOLANA_ENUM_VARIANT,\n TOKEN_INFO: ClearSignContextType.SOLANA_TOKEN_INFO,\n TOKEN_ACCOUNT_STATE: ClearSignContextType.SOLANA_TOKEN_ACCOUNT_STATE,\n ALT_RESOLUTION: ClearSignContextType.SOLANA_ALT_RESOLUTION,\n} as const;\n\n/**\n * Union of all Solana-relevant success context types.\n */\nexport type SolanaClearSignContextSuccessType =\n (typeof SolanaContextType)[keyof typeof SolanaContextType];\n\n/**\n * A ClearSignContextSuccess narrowed to only Solana-relevant types.\n */\nexport type SolanaClearSignContextSuccess =\n ClearSignContextSuccess<SolanaClearSignContextSuccessType>;\n\n/**\n * Set of all Solana-relevant success ClearSignContextType values.\n * Used by the type guard below to filter out non-Solana contexts at runtime.\n */\nexport const SOLANA_CLEAR_SIGN_CONTEXT_SUCCESS_TYPES =\n new Set<ClearSignContextType>(Object.values(SolanaContextType));\n\n/**\n * Type guard that narrows a ClearSignContextSuccess to a\n * SolanaClearSignContextSuccess, filtering out non-Solana types.\n */\nexport function isSolanaContextSuccess(\n ctx: ClearSignContext,\n): ctx is SolanaClearSignContextSuccess {\n return SOLANA_CLEAR_SIGN_CONTEXT_SUCCESS_TYPES.has(ctx.type);\n}\n"],
|
|
5
|
-
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,6CAAAE,EAAA,sBAAAC,EAAA,qGAAAC,IAAA,eAAAC,EAAAL,GAWA,IAAAM,EAIO,
|
|
4
|
+
"sourcesContent": ["import { type PkiCertificate } from \"@/modules/multichain/pki/model/PkiCertificate\";\nimport {\n type SolanaAltResolutionPayload,\n type SolanaEnumVariantPayload,\n type SolanaInstructionInfoPayload,\n type SolanaLifiPayload,\n type SolanaTokenAccountStatePayload,\n type SolanaTokenData,\n type SolanaTokenInfoPayload,\n type SolanaTransactionCheckPayload,\n} from \"@/modules/solana/model/SolanaPayloads\";\nimport {\n type ClearSignContext,\n type ClearSignContextSuccess,\n ClearSignContextType,\n} from \"@/shared/model/ClearSignContext\";\n\n// Re-export Solana payload types so consumers can import from this file\nexport type {\n SolanaAltResolutionPayload,\n SolanaEnumVariantPayload,\n SolanaInstructionEnumVariant,\n SolanaInstructionInfoPayload,\n SolanaInstructionSubstructure,\n SolanaLifiInstructionMeta,\n SolanaLifiPayload,\n SolanaSignedDescriptor,\n SolanaTokenAccountStatePayload,\n SolanaTokenData,\n SolanaTokenInfoPayload,\n SolanaTransactionCheckPayload,\n SolanaTransactionDescriptor,\n} from \"@/modules/solana/model/SolanaPayloads\";\nexport { SolanaInstructionSubstructureKind } from \"@/modules/solana/model/SolanaPayloads\";\n\n/**\n * Solana-specific payload overrides \u2014 contributed to the shared\n * ClearSignContextSuccessPayloads map at the integration boundary.\n */\nexport type SolanaPayloadOverrides = {\n [ClearSignContextType.SOLANA_TOKEN]: {\n payload: SolanaTokenData;\n certificate?: PkiCertificate;\n };\n [ClearSignContextType.SOLANA_LIFI]: {\n payload: SolanaLifiPayload;\n certificate?: PkiCertificate;\n };\n [ClearSignContextType.SOLANA_TRUSTED_NAME]: {\n payload: Uint8Array;\n certificate?: PkiCertificate;\n };\n [ClearSignContextType.SOLANA_TRANSACTION_CHECK]: {\n payload: SolanaTransactionCheckPayload;\n certificate?: PkiCertificate;\n };\n [ClearSignContextType.SOLANA_INSTRUCTION_INFO]: {\n payload: SolanaInstructionInfoPayload;\n certificate?: PkiCertificate;\n };\n [ClearSignContextType.SOLANA_ENUM_VARIANT]: {\n payload: SolanaEnumVariantPayload;\n certificate?: PkiCertificate;\n };\n [ClearSignContextType.SOLANA_TOKEN_INFO]: {\n payload: SolanaTokenInfoPayload;\n certificate?: PkiCertificate;\n };\n [ClearSignContextType.SOLANA_TOKEN_ACCOUNT_STATE]: {\n payload: SolanaTokenAccountStatePayload;\n certificate?: PkiCertificate;\n };\n [ClearSignContextType.SOLANA_ALT_RESOLUTION]: {\n payload: SolanaAltResolutionPayload;\n certificate?: PkiCertificate;\n };\n};\n\nexport const SolanaContextType = {\n TOKEN: ClearSignContextType.SOLANA_TOKEN,\n LIFI: ClearSignContextType.SOLANA_LIFI,\n TRUSTED_NAME: ClearSignContextType.SOLANA_TRUSTED_NAME,\n TRANSACTION_CHECK: ClearSignContextType.SOLANA_TRANSACTION_CHECK,\n INSTRUCTION_INFO: ClearSignContextType.SOLANA_INSTRUCTION_INFO,\n ENUM_VARIANT: ClearSignContextType.SOLANA_ENUM_VARIANT,\n TOKEN_INFO: ClearSignContextType.SOLANA_TOKEN_INFO,\n TOKEN_ACCOUNT_STATE: ClearSignContextType.SOLANA_TOKEN_ACCOUNT_STATE,\n ALT_RESOLUTION: ClearSignContextType.SOLANA_ALT_RESOLUTION,\n} as const;\n\n/**\n * Union of all Solana-relevant success context types.\n */\nexport type SolanaClearSignContextSuccessType =\n (typeof SolanaContextType)[keyof typeof SolanaContextType];\n\n/**\n * A ClearSignContextSuccess narrowed to only Solana-relevant types.\n */\nexport type SolanaClearSignContextSuccess =\n ClearSignContextSuccess<SolanaClearSignContextSuccessType>;\n\n/**\n * Set of all Solana-relevant success ClearSignContextType values.\n * Used by the type guard below to filter out non-Solana contexts at runtime.\n */\nexport const SOLANA_CLEAR_SIGN_CONTEXT_SUCCESS_TYPES =\n new Set<ClearSignContextType>(Object.values(SolanaContextType));\n\n/**\n * Type guard that narrows a ClearSignContextSuccess to a\n * SolanaClearSignContextSuccess, filtering out non-Solana types.\n */\nexport function isSolanaContextSuccess(\n ctx: ClearSignContext,\n): ctx is SolanaClearSignContextSuccess {\n return SOLANA_CLEAR_SIGN_CONTEXT_SUCCESS_TYPES.has(ctx.type);\n}\n"],
|
|
5
|
+
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,6CAAAE,EAAA,sBAAAC,EAAA,qGAAAC,IAAA,eAAAC,EAAAL,GAWA,IAAAM,EAIO,2CAkBPC,EAAkD,iDA6C3C,MAAMJ,EAAoB,CAC/B,MAAO,uBAAqB,aAC5B,KAAM,uBAAqB,YAC3B,aAAc,uBAAqB,oBACnC,kBAAmB,uBAAqB,yBACxC,iBAAkB,uBAAqB,wBACvC,aAAc,uBAAqB,oBACnC,WAAY,uBAAqB,kBACjC,oBAAqB,uBAAqB,2BAC1C,eAAgB,uBAAqB,qBACvC,EAkBaD,EACX,IAAI,IAA0B,OAAO,OAAOC,CAAiB,CAAC,EAMzD,SAASC,EACdI,EACsC,CACtC,OAAON,EAAwC,IAAIM,EAAI,IAAI,CAC7D",
|
|
6
6
|
"names": ["SolanaClearSignContext_exports", "__export", "SOLANA_CLEAR_SIGN_CONTEXT_SUCCESS_TYPES", "SolanaContextType", "isSolanaContextSuccess", "__toCommonJS", "import_ClearSignContext", "import_SolanaPayloads", "ctx"]
|
|
7
7
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var r=Object.defineProperty;var
|
|
1
|
+
"use strict";var r=Object.defineProperty;var S=Object.getOwnPropertyDescriptor;var s=Object.getOwnPropertyNames;var C=Object.prototype.hasOwnProperty;var i=(t,e,a,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of s(e))!C.call(t,n)&&n!==a&&r(t,n,{get:()=>e[n],enumerable:!(o=S(e,n))||o.enumerable});return t};var c=t=>i(r({},"__esModule",{value:!0}),t);var p={};module.exports=c(p);
|
|
2
2
|
//# sourceMappingURL=SolanaContextTypes.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/solana/model/SolanaContextTypes.ts"],
|
|
4
|
-
"sourcesContent": ["import { type SolanaTransactionDescriptor } from \"@/modules/solana/model/SolanaPayloads\";\nimport {\n type ClearSignContextError,\n type ClearSignContextSuccess,\n type ClearSignContextType,\n} from \"@/shared/model/ClearSignContext\";\n\nexport type SolanaTransactionDescriptorRaw = {\n data: string;\n descriptorType: string;\n descriptorVersion: string;\n signatures: {\n prod?: string;\n test?: string;\n };\n};\n\nexport type SolanaLifiDescriptorEntry = {\n program_id: string;\n discriminator_hex?: string;\n descriptor: SolanaTransactionDescriptorRaw;\n};\n\nexport type GetTransactionDescriptorsResponse = {\n id: string;\n chain_id: number;\n instructions: Array<{\n program_id: string;\n discriminator?: number;\n discriminator_hex?: string;\n }>;\n descriptors: SolanaLifiDescriptorEntry[];\n};\n\nexport type SolanaTransactionDescriptorList = Record<\n string,\n SolanaTransactionDescriptor\n>;\n\n//
|
|
4
|
+
"sourcesContent": ["import { type SolanaTransactionDescriptor } from \"@/modules/solana/model/SolanaPayloads\";\nimport {\n type ClearSignContextError,\n type ClearSignContextSuccess,\n type ClearSignContextType,\n} from \"@/shared/model/ClearSignContext\";\n\nexport type SolanaTransactionDescriptorRaw = {\n data: string;\n descriptorType: string;\n descriptorVersion: string;\n signatures: {\n prod?: string;\n test?: string;\n };\n};\n\nexport type SolanaLifiDescriptorEntry = {\n program_id: string;\n discriminator_hex?: string;\n descriptor: SolanaTransactionDescriptorRaw;\n};\n\nexport type GetTransactionDescriptorsResponse = {\n id: string;\n chain_id: number;\n instructions: Array<{\n program_id: string;\n discriminator?: number;\n discriminator_hex?: string;\n }>;\n descriptors: SolanaLifiDescriptorEntry[];\n};\n\nexport type SolanaTransactionDescriptorList = Record<\n string,\n SolanaTransactionDescriptor\n>;\n\n// The signer registry (`provideContextRegistry.ts`) uses a required mapped\n// type over this union, so every member listed here MUST have a matching\n// provide handler in the signer. The generic clear-signing types below are\n// declared together with their handlers\nexport type SolanaContextSuccessType =\n | ClearSignContextType.SOLANA_TOKEN\n | ClearSignContextType.SOLANA_LIFI\n | ClearSignContextType.SOLANA_TRANSACTION_CHECK\n | ClearSignContextType.SOLANA_INSTRUCTION_INFO\n | ClearSignContextType.SOLANA_ENUM_VARIANT\n | ClearSignContextType.SOLANA_TOKEN_INFO\n | ClearSignContextType.SOLANA_TOKEN_ACCOUNT_STATE\n | ClearSignContextType.SOLANA_ALT_RESOLUTION\n | ClearSignContextType.SOLANA_TRUSTED_NAME;\n\nexport type SolanaContextSuccess<\n T extends SolanaContextSuccessType = SolanaContextSuccessType,\n> = ClearSignContextSuccess<T>;\n\nexport type SolanaContextError = ClearSignContextError;\n\nexport type SolanaContext = SolanaContextSuccess | SolanaContextError;\n\nexport type SolanaTokenContextSuccess =\n ClearSignContextSuccess<ClearSignContextType.SOLANA_TOKEN>;\n\nexport type SolanaLifiContextSuccess =\n ClearSignContextSuccess<ClearSignContextType.SOLANA_LIFI>;\n\nexport type SolanaTransactionCheckContextSuccess =\n ClearSignContextSuccess<ClearSignContextType.SOLANA_TRANSACTION_CHECK>;\n\nexport type SolanaInstructionInfoContextSuccess =\n ClearSignContextSuccess<ClearSignContextType.SOLANA_INSTRUCTION_INFO>;\n\nexport type SolanaEnumVariantContextSuccess =\n ClearSignContextSuccess<ClearSignContextType.SOLANA_ENUM_VARIANT>;\n\nexport type SolanaTokenInfoContextSuccess =\n ClearSignContextSuccess<ClearSignContextType.SOLANA_TOKEN_INFO>;\n\nexport type SolanaTokenAccountStateContextSuccess =\n ClearSignContextSuccess<ClearSignContextType.SOLANA_TOKEN_ACCOUNT_STATE>;\n\nexport type SolanaAltResolutionContextSuccess =\n ClearSignContextSuccess<ClearSignContextType.SOLANA_ALT_RESOLUTION>;\n\nexport type SolanaTrustedNameContextSuccess =\n ClearSignContextSuccess<ClearSignContextType.SOLANA_TRUSTED_NAME>;\n\nexport type SolanaTokenContextResult =\n | SolanaTokenContextSuccess\n | SolanaContextError;\n\nexport type SolanaLifiContextResult =\n | SolanaLifiContextSuccess\n | SolanaContextError;\n\nexport type SolanaTransactionCheckContextResult =\n | SolanaTransactionCheckContextSuccess\n | SolanaContextError;\n\nexport type LoaderResult =\n | SolanaTokenContextResult\n | SolanaLifiContextResult\n | SolanaTransactionCheckContextResult;\n"],
|
|
5
5
|
"mappings": "+WAAA,IAAAA,EAAA,kBAAAC,EAAAD",
|
|
6
6
|
"names": ["SolanaContextTypes_exports", "__toCommonJS"]
|
|
7
7
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var o=Object.defineProperty;var s=Object.getOwnPropertyDescriptor;var p=Object.getOwnPropertyNames;var g=Object.prototype.hasOwnProperty;var d=(a,t)=>{for(var n in t)o(a,n,{get:t[n],enumerable:!0})},c=(a,t,n,e)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of p(t))!g.call(a,r)&&r!==n&&o(a,r,{get:()=>t[r],enumerable:!(e=s(t,r))||e.enumerable});return a};var l=a=>c(o({},"__esModule",{value:!0}),a);var y={};d(y,{SolanaInstructionSubstructureKind:()=>i});module.exports=l(y);var i=(r=>(r[r.DISPLAY_FIELD=0]="DISPLAY_FIELD",r[r.VALUE_FLOW_PORT=1]="VALUE_FLOW_PORT",r[r.HIDE_RULE=2]="HIDE_RULE",r[r.ACCOUNT_RESET=3]="ACCOUNT_RESET",r))(i||{});0&&(module.exports={SolanaInstructionSubstructureKind});
|
|
2
2
|
//# sourceMappingURL=SolanaPayloads.js.map
|
|
@@ -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 * Payload for ClearSignContextType.SOLANA_INSTRUCTION_INFO: one signed\n * INSTRUCTION_INFO descriptor
|
|
5
|
-
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,uCAAAE,IAAA,eAAAC,EAAAH,
|
|
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: one signed\n * INSTRUCTION_INFO descriptor, its substructures, and the enum-variant\n * descriptors CAL bundles for the program's enums (used to build the host\n * decode cache).\n */\nexport type SolanaInstructionInfoPayload = {\n programId: string;\n discriminator: string;\n instructionInfo: SolanaSignedDescriptor;\n substructures: SolanaInstructionSubstructure[];\n enumVariants: SolanaInstructionEnumVariant[];\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/**\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 already exists with `payload: Uint8Array`, emitted by\n// both OwnerInfoContextLoader and SolanaTrustedNameContextLoader.\n"],
|
|
5
|
+
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,uCAAAE,IAAA,eAAAC,EAAAH,GAoEO,IAAKE,OACVA,IAAA,cAAgB,GAAhB,gBACAA,IAAA,gBAAkB,GAAlB,kBACAA,IAAA,UAAY,GAAZ,YACAA,IAAA,cAAgB,GAAhB,gBAJUA,OAAA",
|
|
6
6
|
"names": ["SolanaPayloads_exports", "__export", "SolanaInstructionSubstructureKind", "__toCommonJS"]
|
|
7
7
|
}
|
package/lib/esm/package.json
CHANGED
|
@@ -58,5 +58,5 @@
|
|
|
58
58
|
"watch:builds": "pnpm ldmk-tool watch --entryPoints index.ts,src/**/*.ts --tsconfig tsconfig.prod.json",
|
|
59
59
|
"watch:types": "concurrently \"tsc --watch -p tsconfig.prod.json\" \"tsc-alias --watch -p tsconfig.prod.json\""
|
|
60
60
|
},
|
|
61
|
-
"version": "0.0.0-develop-
|
|
61
|
+
"version": "0.0.0-develop-20260626003549"
|
|
62
62
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
1
|
+
var L=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var v=(d,t,r,o)=>{for(var e=o>1?void 0:o?O(t,r):t,i=d.length-1,s;i>=0;i--)(s=d[i])&&(e=(o?s(t,r,e):s(e))||e);return o&&e&&L(t,r,e),e},l=(d,t)=>(r,o)=>t(r,o,d);import{inject as m,injectable as T}from"inversify";import{array as P,Codec as R,optional as _,string as y}from"purify-ts";import{configTypes as x}from"../../../../config/di/configTypes";import{pkiTypes as w}from"../../../../modules/multichain/pki/di/pkiTypes";import{KeyId as D}from"../../../../modules/multichain/pki/model/KeyId";import{KeyUsage as E}from"../../../../modules/multichain/pki/model/KeyUsage";import{instructionInfoTypes as b}from"../../../../modules/solana/instruction-info/di/instructionInfoTypes";import{SolanaInstructionSubstructureKind as I}from"../../../../modules/solana/model/SolanaPayloads";import{ClearSignContextType as p}from"../../../../shared/model/ClearSignContext";import{loadCertificateResult as N}from"../../../../shared/utils/certificateResult";import{deviceModelIdCodec as M}from"../../../../shared/utils/deviceModelIdCodec";import{u16Codec as U}from"../../../../shared/utils/uIntCodec";const A=[p.SOLANA_INSTRUCTION_INFO],F="solana-mainnet",j=R.interface({programId:y,discriminator:_(y)}),K=R.interface({deviceModelId:M,instructions:P(j),network:_(y)});let C=class{constructor(t,r,o,e){this.dataSource=t;this.config=r;this.certificateLoader=o;this.logger=e("InstructionInfoContextLoader")}logger;canHandle(t,r){return A.every(o=>r.includes(o))?K.decode(t).caseOf({Left:()=>!1,Right:({instructions:o})=>o.length>0&&o.every(e=>e.programId.length>0)}):!1}async load(t){const r=t.network||F,o=Array.from(new Set(t.instructions.map(a=>a.programId)));this.logger.debug("[load] Fetching instruction descriptors",{data:{network:r,programCount:o.length,instructionCount:t.instructions.length}});const e=N(this.certificateLoader,{keyId:D.TokenMetadataKey,keyUsage:E.CoinMeta,targetDevice:t.deviceModelId}),i=Promise.all(o.map(async a=>({programId:a,either:await this.dataSource.getInstructionInfo({programId:a,network:r})}))),[s,g]=await Promise.all([e,i]);if(!s.ok||!s.value){const a=s.ok?new Error("[ContextModule] InstructionInfoContextLoader: certificate is missing"):s.error;return this.logger.warn("[load] INSTRUCTION_INFO certificate unavailable",{data:{error:a.message}}),o.map(()=>({type:p.ERROR,error:a}))}const S=s.value,n=[];for(const{programId:a,either:f}of g)f.caseOf({Left:c=>{this.logger.warn("[load] Failed to fetch instruction descriptors for program",{data:{programId:a,error:c.message}}),n.push({type:p.ERROR,error:c})},Right:c=>{const u=this.collectWantedDiscriminators(a,t.instructions);for(const[h,k]of Object.entries(c.descriptors))u!==null&&!u.has(h)||n.push(this.toInstructionInfoContext(h,k,S))}});return this.logger.debug("[load] Emitted contexts",{data:{count:n.length}}),n}collectWantedDiscriminators(t,r){const o=new Set;let e=!1;for(const i of r)i.programId===t&&(i.discriminator===void 0?e=!0:o.add(i.discriminator));return e?null:o}toInstructionInfoContext(t,r,o){const e=r.instruction_info,i=this.pickSignature(e.descriptor.signatures);if(!i){const n=new Error(`[ContextModule] InstructionInfoContextLoader: missing '${this.config.cal.mode??"prod"}' signature for (${e.program_id}, ${t})`);return this.logger.warn("[load] INSTRUCTION_INFO missing signature",{data:{programId:e.program_id,discriminator:t}}),{type:p.ERROR,error:n}}const s=[...(r.display_fields??[]).map(n=>({kind:I.DISPLAY_FIELD,data:n.descriptor})),...(r.value_flow_ports??[]).map(n=>({kind:I.VALUE_FLOW_PORT,data:n.descriptor})),...(r.hide_rules??[]).map(n=>({kind:I.HIDE_RULE,data:n.descriptor})),...(r.account_resets??[]).map(n=>({kind:I.ACCOUNT_RESET,data:n.descriptor}))],g=[];for(const[n,a]of Object.entries(r.enum_variants??{}))for(const[f,c]of Object.entries(a)){const u=Number(f);if(U.decode(u).isLeft()){this.logger.warn("[load] skipping malformed enum variant index",{data:{programId:e.program_id,enumId:n,variantIndex:f}});continue}g.push({enumId:n,variantIndex:u,descriptor:{data:c.data,signature:this.pickSignature(c.signatures)??""}})}const S={programId:e.program_id,discriminator:t,instructionInfo:{data:e.descriptor.data,signature:i},substructures:s,enumVariants:g};return{type:p.SOLANA_INSTRUCTION_INFO,payload:S,certificate:o}}pickSignature(t){const r=this.config.cal.mode??"prod";return t[r]}};C=v([T(),l(0,m(b.InstructionInfoDataSource)),l(1,m(x.Config)),l(2,m(w.PkiCertificateLoader)),l(3,m(x.ContextModuleLoggerFactory))],C);export{C as InstructionInfoContextLoader};
|
|
2
2
|
//# sourceMappingURL=InstructionInfoContextLoader.js.map
|
package/lib/esm/src/modules/solana/instruction-info/domain/InstructionInfoContextLoader.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/solana/instruction-info/domain/InstructionInfoContextLoader.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 { type ContextModuleServiceConfig } from \"@/config/model/ContextModuleConfig\";\nimport { pkiTypes } from \"@/modules/multichain/pki/di/pkiTypes\";\nimport { type PkiCertificateLoader } from \"@/modules/multichain/pki/domain/PkiCertificateLoader\";\nimport { KeyId } from \"@/modules/multichain/pki/model/KeyId\";\nimport { KeyUsage } from \"@/modules/multichain/pki/model/KeyUsage\";\nimport { type InstructionInfoDataSource } from \"@/modules/solana/instruction-info/data/InstructionInfoDataSource\";\nimport {\n type CalInstructionDescriptorDto,\n type CalSignatures,\n} from \"@/modules/solana/instruction-info/data/InstructionInfoDto\";\nimport { instructionInfoTypes } from \"@/modules/solana/instruction-info/di/instructionInfoTypes\";\nimport {\n type SolanaInstructionInfoPayload,\n type SolanaInstructionSubstructure,\n SolanaInstructionSubstructureKind,\n} from \"@/modules/solana/model/SolanaPayloads\";\nimport { type ContextLoader } from \"@/shared/domain/ContextLoader\";\nimport {\n type ClearSignContext,\n ClearSignContextType,\n} from \"@/shared/model/ClearSignContext\";\nimport { loadCertificateResult } from \"@/shared/utils/certificateResult\";\nimport { deviceModelIdCodec } from \"@/shared/utils/deviceModelIdCodec\";\n\nconst SUPPORTED_TYPES: ClearSignContextType[] = [\n ClearSignContextType.SOLANA_INSTRUCTION_INFO,\n];\n\nconst NETWORK_DEFAULT = \"solana-mainnet\";\n\n/**\n * Per-instruction reference the caller provides for the TX it wants to\n * clear-sign. Discriminator is optional: when omitted, the loader emits\n * every descriptor CAL returns for that `programId`. When present, the\n * loader filters down to descriptors whose `discriminator` matches.\n */\nexport type SolanaInstructionRef = {\n programId: string;\n discriminator?: string;\n};\n\nexport type SolanaInstructionInfoContextInput = {\n deviceModelId: DeviceModelId;\n instructions: SolanaInstructionRef[];\n network?: string;\n};\n\nconst instructionRefCodec = Codec.interface({\n programId: string,\n discriminator: optional(string),\n});\n\nconst solanaInstructionInfoInputCodec = Codec.interface({\n deviceModelId: deviceModelIdCodec,\n instructions: array(instructionRefCodec),\n network: optional(string),\n});\n\n/**\n * Emits SOLANA_INSTRUCTION_INFO contexts for the requested instructions.\n * Enum-variant emission lives in the sibling {@link EnumVariantContextLoader}.\n */\n@injectable()\nexport class InstructionInfoContextLoader\n implements ContextLoader<SolanaInstructionInfoContextInput>\n{\n private readonly logger: LoggerPublisherService;\n\n constructor(\n @inject(instructionInfoTypes.InstructionInfoDataSource)\n private readonly dataSource: InstructionInfoDataSource,\n @inject(configTypes.Config)\n private readonly config: ContextModuleServiceConfig,\n @inject(pkiTypes.PkiCertificateLoader)\n private readonly certificateLoader: PkiCertificateLoader,\n @inject(configTypes.ContextModuleLoggerFactory)\n loggerFactory: (tag: string) => LoggerPublisherService,\n ) {\n this.logger = loggerFactory(\"InstructionInfoContextLoader\");\n }\n\n public canHandle(\n input: unknown,\n expectedTypes: ClearSignContextType[],\n ): input is SolanaInstructionInfoContextInput {\n if (!SUPPORTED_TYPES.every((t) => expectedTypes.includes(t))) {\n return false;\n }\n return solanaInstructionInfoInputCodec.decode(input).caseOf({\n Left: () => false,\n Right: ({ instructions }) =>\n instructions.length > 0 &&\n instructions.every((ix) => ix.programId.length > 0),\n });\n }\n\n public async load(\n input: SolanaInstructionInfoContextInput,\n ): Promise<ClearSignContext[]> {\n const network = input.network || NETWORK_DEFAULT;\n const distinctProgramIds = Array.from(\n new Set(input.instructions.map((ix) => ix.programId)),\n );\n\n this.logger.debug(\"[load] Fetching instruction descriptors\", {\n data: {\n network,\n programCount: distinctProgramIds.length,\n instructionCount: input.instructions.length,\n },\n });\n\n // Run cert load in parallel with CAL fetches \u2014 neither depends on the\n // other. The tagged-Result wrapper lets us preserve the data fetches\n // if cert fails so we can surface a per-program ERROR rather than\n // silently dropping them.\n const certPromise = loadCertificateResult(this.certificateLoader, {\n keyId: KeyId.TokenMetadataKey,\n keyUsage: KeyUsage.CoinMeta,\n targetDevice: input.deviceModelId,\n });\n\n // One CAL call per program, fanned out in parallel.\n const dataPromise = Promise.all(\n distinctProgramIds.map(async (programId) => ({\n programId,\n either: await this.dataSource.getInstructionInfo({\n programId,\n network,\n }),\n })),\n );\n\n const [certResult, results] = await Promise.all([certPromise, dataPromise]);\n\n if (!certResult.ok || !certResult.value) {\n const error = certResult.ok\n ? new Error(\n \"[ContextModule] InstructionInfoContextLoader: certificate is missing\",\n )\n : certResult.error;\n this.logger.warn(\"[load] INSTRUCTION_INFO certificate unavailable\", {\n data: { error: error.message },\n });\n return distinctProgramIds.map(() => ({\n type: ClearSignContextType.ERROR,\n error,\n }));\n }\n const certificate = certResult.value;\n\n const contexts: ClearSignContext[] = [];\n\n for (const { programId, either } of results) {\n either.caseOf<void>({\n Left: (err) => {\n this.logger.warn(\n \"[load] Failed to fetch instruction descriptors for program\",\n { data: { programId, error: err.message } },\n );\n contexts.push({\n type: ClearSignContextType.ERROR,\n error: err,\n });\n },\n Right: (value) => {\n const wantedDiscriminators = this.collectWantedDiscriminators(\n programId,\n input.instructions,\n );\n\n for (const [discriminator, dto] of Object.entries(\n value.descriptors,\n )) {\n if (\n wantedDiscriminators !== null &&\n !wantedDiscriminators.has(discriminator)\n ) {\n continue;\n }\n contexts.push(\n this.toInstructionInfoContext(discriminator, dto, certificate),\n );\n }\n },\n });\n }\n\n this.logger.debug(\"[load] Emitted contexts\", {\n data: { count: contexts.length },\n });\n\n return contexts;\n }\n\n /**\n * Returns the set of discriminators the caller has explicitly asked\n * about for this `programId`, or `null` when the caller wants every\n * descriptor CAL has for the program.\n */\n private collectWantedDiscriminators(\n programId: string,\n instructions: SolanaInstructionRef[],\n ): Set<string> | null {\n const set = new Set<string>();\n let hasUnconstrained = false;\n for (const ix of instructions) {\n if (ix.programId !== programId) continue;\n if (ix.discriminator === undefined) {\n hasUnconstrained = true;\n } else {\n set.add(ix.discriminator);\n }\n }\n return hasUnconstrained ? null : set;\n }\n\n private toInstructionInfoContext(\n discriminator: string,\n dto: CalInstructionDescriptorDto,\n certificate: Awaited<ReturnType<PkiCertificateLoader[\"loadCertificate\"]>>,\n ): ClearSignContext {\n const info = dto.instruction_info;\n const signature = this.pickSignature(info.descriptor.signatures);\n if (!signature) {\n const error = new Error(\n `[ContextModule] InstructionInfoContextLoader: missing '${this.config.cal.mode ?? \"prod\"}' signature for (${info.program_id}, ${discriminator})`,\n );\n this.logger.warn(\"[load] INSTRUCTION_INFO missing signature\", {\n data: { programId: info.program_id, discriminator },\n });\n return { type: ClearSignContextType.ERROR, error };\n }\n\n const substructures: SolanaInstructionSubstructure[] = [\n ...(dto.display_fields ?? []).map((s) => ({\n kind: SolanaInstructionSubstructureKind.DISPLAY_FIELD,\n data: s.descriptor,\n })),\n ...(dto.value_flow_ports ?? []).map((s) => ({\n kind: SolanaInstructionSubstructureKind.VALUE_FLOW_PORT,\n data: s.descriptor,\n })),\n ...(dto.hide_rules ?? []).map((s) => ({\n kind: SolanaInstructionSubstructureKind.HIDE_RULE,\n data: s.descriptor,\n })),\n ...(dto.account_resets ?? []).map((s) => ({\n kind: SolanaInstructionSubstructureKind.ACCOUNT_RESET,\n data: s.descriptor,\n })),\n ];\n\n const payload: SolanaInstructionInfoPayload = {\n programId: info.program_id,\n discriminator,\n instructionInfo: {\n data: info.descriptor.data,\n signature,\n },\n substructures,\n };\n\n return {\n type: ClearSignContextType.SOLANA_INSTRUCTION_INFO,\n payload,\n certificate,\n };\n }\n\n private pickSignature(signatures: CalSignatures): string | undefined {\n const mode = this.config.cal.mode ?? \"prod\";\n return signatures[mode];\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,0BAE5B,OAAS,YAAAC,MAAgB,uCAEzB,OAAS,SAAAC,MAAa,uCACtB,OAAS,YAAAC,MAAgB,0CAMzB,OAAS,wBAAAC,MAA4B,4DACrC,
|
|
6
|
-
"names": ["inject", "injectable", "array", "Codec", "optional", "string", "configTypes", "pkiTypes", "KeyId", "KeyUsage", "instructionInfoTypes", "SolanaInstructionSubstructureKind", "ClearSignContextType", "loadCertificateResult", "deviceModelIdCodec", "SUPPORTED_TYPES", "ClearSignContextType", "NETWORK_DEFAULT", "instructionRefCodec", "Codec", "string", "optional", "solanaInstructionInfoInputCodec", "deviceModelIdCodec", "array", "InstructionInfoContextLoader", "dataSource", "config", "certificateLoader", "loggerFactory", "input", "expectedTypes", "t", "instructions", "ix", "network", "distinctProgramIds", "certPromise", "loadCertificateResult", "KeyId", "KeyUsage", "dataPromise", "programId", "certResult", "results", "error", "certificate", "contexts", "either", "err", "value", "wantedDiscriminators", "discriminator", "dto", "set", "hasUnconstrained", "info", "signature", "substructures", "s", "SolanaInstructionSubstructureKind", "payload", "signatures", "mode", "__decorateClass", "injectable", "__decorateParam", "inject", "instructionInfoTypes", "configTypes", "pkiTypes"]
|
|
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 { type ContextModuleServiceConfig } from \"@/config/model/ContextModuleConfig\";\nimport { pkiTypes } from \"@/modules/multichain/pki/di/pkiTypes\";\nimport { type PkiCertificateLoader } from \"@/modules/multichain/pki/domain/PkiCertificateLoader\";\nimport { KeyId } from \"@/modules/multichain/pki/model/KeyId\";\nimport { KeyUsage } from \"@/modules/multichain/pki/model/KeyUsage\";\nimport { type InstructionInfoDataSource } from \"@/modules/solana/instruction-info/data/InstructionInfoDataSource\";\nimport {\n type CalInstructionDescriptorDto,\n type CalSignatures,\n} from \"@/modules/solana/instruction-info/data/InstructionInfoDto\";\nimport { instructionInfoTypes } from \"@/modules/solana/instruction-info/di/instructionInfoTypes\";\nimport {\n type SolanaInstructionEnumVariant,\n type SolanaInstructionInfoPayload,\n type SolanaInstructionSubstructure,\n SolanaInstructionSubstructureKind,\n} from \"@/modules/solana/model/SolanaPayloads\";\nimport { type ContextLoader } from \"@/shared/domain/ContextLoader\";\nimport {\n type ClearSignContext,\n ClearSignContextType,\n} from \"@/shared/model/ClearSignContext\";\nimport { loadCertificateResult } from \"@/shared/utils/certificateResult\";\nimport { deviceModelIdCodec } from \"@/shared/utils/deviceModelIdCodec\";\nimport { u16Codec } from \"@/shared/utils/uIntCodec\";\n\nconst SUPPORTED_TYPES: ClearSignContextType[] = [\n ClearSignContextType.SOLANA_INSTRUCTION_INFO,\n];\n\nconst NETWORK_DEFAULT = \"solana-mainnet\";\n\n/**\n * Per-instruction reference the caller provides for the TX it wants to\n * clear-sign. Discriminator is optional: when omitted, the loader emits\n * every descriptor CAL returns for that `programId`. When present, the\n * loader filters down to descriptors whose `discriminator` matches.\n */\nexport type SolanaInstructionRef = {\n programId: string;\n discriminator?: string;\n};\n\nexport type SolanaInstructionInfoContextInput = {\n deviceModelId: DeviceModelId;\n instructions: SolanaInstructionRef[];\n network?: string;\n};\n\nconst instructionRefCodec = Codec.interface({\n programId: string,\n discriminator: optional(string),\n});\n\nconst solanaInstructionInfoInputCodec = Codec.interface({\n deviceModelId: deviceModelIdCodec,\n instructions: array(instructionRefCodec),\n network: optional(string),\n});\n\n/**\n * Emits SOLANA_INSTRUCTION_INFO contexts for the requested instructions.\n * Enum-variant emission lives in the sibling {@link EnumVariantContextLoader}.\n */\n@injectable()\nexport class InstructionInfoContextLoader\n implements ContextLoader<SolanaInstructionInfoContextInput>\n{\n private readonly logger: LoggerPublisherService;\n\n constructor(\n @inject(instructionInfoTypes.InstructionInfoDataSource)\n private readonly dataSource: InstructionInfoDataSource,\n @inject(configTypes.Config)\n private readonly config: ContextModuleServiceConfig,\n @inject(pkiTypes.PkiCertificateLoader)\n private readonly certificateLoader: PkiCertificateLoader,\n @inject(configTypes.ContextModuleLoggerFactory)\n loggerFactory: (tag: string) => LoggerPublisherService,\n ) {\n this.logger = loggerFactory(\"InstructionInfoContextLoader\");\n }\n\n public canHandle(\n input: unknown,\n expectedTypes: ClearSignContextType[],\n ): input is SolanaInstructionInfoContextInput {\n if (!SUPPORTED_TYPES.every((t) => expectedTypes.includes(t))) {\n return false;\n }\n return solanaInstructionInfoInputCodec.decode(input).caseOf({\n Left: () => false,\n Right: ({ instructions }) =>\n instructions.length > 0 &&\n instructions.every((ix) => ix.programId.length > 0),\n });\n }\n\n public async load(\n input: SolanaInstructionInfoContextInput,\n ): Promise<ClearSignContext[]> {\n const network = input.network || NETWORK_DEFAULT;\n const distinctProgramIds = Array.from(\n new Set(input.instructions.map((ix) => ix.programId)),\n );\n\n this.logger.debug(\"[load] Fetching instruction descriptors\", {\n data: {\n network,\n programCount: distinctProgramIds.length,\n instructionCount: input.instructions.length,\n },\n });\n\n // Run cert load in parallel with CAL fetches \u2014 neither depends on the\n // other. The tagged-Result wrapper lets us preserve the data fetches\n // if cert fails so we can surface a per-program ERROR rather than\n // silently dropping them.\n const certPromise = loadCertificateResult(this.certificateLoader, {\n keyId: KeyId.TokenMetadataKey,\n keyUsage: KeyUsage.CoinMeta,\n targetDevice: input.deviceModelId,\n });\n\n // One CAL call per program, fanned out in parallel.\n const dataPromise = Promise.all(\n distinctProgramIds.map(async (programId) => ({\n programId,\n either: await this.dataSource.getInstructionInfo({\n programId,\n network,\n }),\n })),\n );\n\n const [certResult, results] = await Promise.all([certPromise, dataPromise]);\n\n if (!certResult.ok || !certResult.value) {\n const error = certResult.ok\n ? new Error(\n \"[ContextModule] InstructionInfoContextLoader: certificate is missing\",\n )\n : certResult.error;\n this.logger.warn(\"[load] INSTRUCTION_INFO certificate unavailable\", {\n data: { error: error.message },\n });\n return distinctProgramIds.map(() => ({\n type: ClearSignContextType.ERROR,\n error,\n }));\n }\n const certificate = certResult.value;\n\n const contexts: ClearSignContext[] = [];\n\n for (const { programId, either } of results) {\n either.caseOf<void>({\n Left: (err) => {\n this.logger.warn(\n \"[load] Failed to fetch instruction descriptors for program\",\n { data: { programId, error: err.message } },\n );\n contexts.push({\n type: ClearSignContextType.ERROR,\n error: err,\n });\n },\n Right: (value) => {\n const wantedDiscriminators = this.collectWantedDiscriminators(\n programId,\n input.instructions,\n );\n\n for (const [discriminator, dto] of Object.entries(\n value.descriptors,\n )) {\n if (\n wantedDiscriminators !== null &&\n !wantedDiscriminators.has(discriminator)\n ) {\n continue;\n }\n contexts.push(\n this.toInstructionInfoContext(discriminator, dto, certificate),\n );\n }\n },\n });\n }\n\n this.logger.debug(\"[load] Emitted contexts\", {\n data: { count: contexts.length },\n });\n\n return contexts;\n }\n\n /**\n * Returns the set of discriminators the caller has explicitly asked\n * about for this `programId`, or `null` when the caller wants every\n * descriptor CAL has for the program.\n */\n private collectWantedDiscriminators(\n programId: string,\n instructions: SolanaInstructionRef[],\n ): Set<string> | null {\n const set = new Set<string>();\n let hasUnconstrained = false;\n for (const ix of instructions) {\n if (ix.programId !== programId) continue;\n if (ix.discriminator === undefined) {\n hasUnconstrained = true;\n } else {\n set.add(ix.discriminator);\n }\n }\n return hasUnconstrained ? null : set;\n }\n\n private toInstructionInfoContext(\n discriminator: string,\n dto: CalInstructionDescriptorDto,\n certificate: Awaited<ReturnType<PkiCertificateLoader[\"loadCertificate\"]>>,\n ): ClearSignContext {\n const info = dto.instruction_info;\n const signature = this.pickSignature(info.descriptor.signatures);\n if (!signature) {\n const error = new Error(\n `[ContextModule] InstructionInfoContextLoader: missing '${this.config.cal.mode ?? \"prod\"}' signature for (${info.program_id}, ${discriminator})`,\n );\n this.logger.warn(\"[load] INSTRUCTION_INFO missing signature\", {\n data: { programId: info.program_id, discriminator },\n });\n return { type: ClearSignContextType.ERROR, error };\n }\n\n const substructures: SolanaInstructionSubstructure[] = [\n ...(dto.display_fields ?? []).map((s) => ({\n kind: SolanaInstructionSubstructureKind.DISPLAY_FIELD,\n data: s.descriptor,\n })),\n ...(dto.value_flow_ports ?? []).map((s) => ({\n kind: SolanaInstructionSubstructureKind.VALUE_FLOW_PORT,\n data: s.descriptor,\n })),\n ...(dto.hide_rules ?? []).map((s) => ({\n kind: SolanaInstructionSubstructureKind.HIDE_RULE,\n data: s.descriptor,\n })),\n ...(dto.account_resets ?? []).map((s) => ({\n kind: SolanaInstructionSubstructureKind.ACCOUNT_RESET,\n data: s.descriptor,\n })),\n ];\n\n // Flatten the CAL-bundled enum variants (keyed enumId to variantIndex) into\n // a flat list. The signed TLV feeds the host's type-pool decode cache, the\n // signature is best-effort (only needed when streaming the selected\n // variant, not for decoding).\n const enumVariants: SolanaInstructionEnumVariant[] = [];\n for (const [enumId, variants] of Object.entries(dto.enum_variants ?? {})) {\n for (const [variantIndex, variant] of Object.entries(variants)) {\n // CAL keys variants by their stringified u16 index. Validate through\n // the codec so malformed keys (\"7abc\", \"-1\", \"1e10\") are skipped rather\n // than leaking NaN/out-of-range indices into the payload (matches\n // EnumVariantContextLoader).\n const index = Number(variantIndex);\n if (u16Codec.decode(index).isLeft()) {\n this.logger.warn(\"[load] skipping malformed enum variant index\", {\n data: { programId: info.program_id, enumId, variantIndex },\n });\n continue;\n }\n enumVariants.push({\n enumId,\n variantIndex: index,\n descriptor: {\n data: variant.data,\n signature: this.pickSignature(variant.signatures) ?? \"\",\n },\n });\n }\n }\n\n const payload: SolanaInstructionInfoPayload = {\n programId: info.program_id,\n discriminator,\n instructionInfo: {\n data: info.descriptor.data,\n signature,\n },\n substructures,\n enumVariants,\n };\n\n return {\n type: ClearSignContextType.SOLANA_INSTRUCTION_INFO,\n payload,\n certificate,\n };\n }\n\n private pickSignature(signatures: CalSignatures): string | undefined {\n const mode = this.config.cal.mode ?? \"prod\";\n return signatures[mode];\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["inject", "injectable", "array", "Codec", "optional", "string", "configTypes", "pkiTypes", "KeyId", "KeyUsage", "instructionInfoTypes", "SolanaInstructionSubstructureKind", "ClearSignContextType", "loadCertificateResult", "deviceModelIdCodec", "u16Codec", "SUPPORTED_TYPES", "ClearSignContextType", "NETWORK_DEFAULT", "instructionRefCodec", "Codec", "string", "optional", "solanaInstructionInfoInputCodec", "deviceModelIdCodec", "array", "InstructionInfoContextLoader", "dataSource", "config", "certificateLoader", "loggerFactory", "input", "expectedTypes", "t", "instructions", "ix", "network", "distinctProgramIds", "certPromise", "loadCertificateResult", "KeyId", "KeyUsage", "dataPromise", "programId", "certResult", "results", "error", "certificate", "contexts", "either", "err", "value", "wantedDiscriminators", "discriminator", "dto", "set", "hasUnconstrained", "info", "signature", "substructures", "s", "SolanaInstructionSubstructureKind", "enumVariants", "enumId", "variants", "variantIndex", "variant", "index", "u16Codec", "payload", "signatures", "mode", "__decorateClass", "injectable", "__decorateParam", "inject", "instructionInfoTypes", "configTypes", "pkiTypes"]
|
|
7
7
|
}
|