@ledgerhq/device-contacts-kit 0.3.1 → 0.4.0
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/README.md +22 -13
- package/lib/cjs/api/index.js +1 -1
- package/lib/cjs/api/index.js.map +3 -3
- package/lib/cjs/api/model/ContactsVersionRequirements.js +1 -1
- package/lib/cjs/api/model/ContactsVersionRequirements.js.map +3 -3
- package/lib/cjs/api/model/ContactsVersionRequirements.test.js +1 -1
- package/lib/cjs/api/model/ContactsVersionRequirements.test.js.map +3 -3
- package/lib/cjs/api/model/ProvideContact.js +2 -0
- package/lib/cjs/api/model/ProvideContact.js.map +7 -0
- package/lib/cjs/internal/app-binder/command/ProvideContactCommand.js +2 -0
- package/lib/cjs/internal/app-binder/command/ProvideContactCommand.js.map +7 -0
- package/lib/cjs/internal/app-binder/command/ProvideContactCommand.test.js +2 -0
- package/lib/cjs/internal/app-binder/command/ProvideContactCommand.test.js.map +7 -0
- package/lib/cjs/internal/app-binder/contactsVersionGuards.js +1 -1
- package/lib/cjs/internal/app-binder/contactsVersionGuards.js.map +2 -2
- package/lib/cjs/internal/app-binder/contactsVersionGuards.test.js +1 -1
- package/lib/cjs/internal/app-binder/contactsVersionGuards.test.js.map +2 -2
- package/lib/cjs/internal/app-binder/model/contactsConstants.js +1 -1
- package/lib/cjs/internal/app-binder/model/contactsConstants.js.map +3 -3
- package/lib/cjs/internal/app-binder/services/provideContact.js +2 -0
- package/lib/cjs/internal/app-binder/services/provideContact.js.map +7 -0
- package/lib/cjs/internal/app-binder/services/provideContact.test.js +2 -0
- package/lib/cjs/internal/app-binder/services/provideContact.test.js.map +7 -0
- package/lib/cjs/internal/app-binder/task/SendRegisterIdentityTask.js.map +2 -2
- package/lib/cjs/internal/app-binder/task/SendRegisterIdentityTask.test.js.map +1 -1
- package/lib/cjs/internal/app-binder/task/SendRenameContactTask.js +1 -1
- package/lib/cjs/internal/app-binder/task/SendRenameContactTask.js.map +2 -2
- package/lib/cjs/internal/app-binder/task/SendRenameContactTask.test.js +1 -1
- package/lib/cjs/internal/app-binder/task/SendRenameContactTask.test.js.map +3 -3
- package/lib/cjs/package.json +2 -2
- package/lib/esm/api/index.js +1 -1
- package/lib/esm/api/index.js.map +3 -3
- package/lib/esm/api/model/ContactsVersionRequirements.js +1 -1
- package/lib/esm/api/model/ContactsVersionRequirements.js.map +3 -3
- package/lib/esm/api/model/ContactsVersionRequirements.test.js +1 -1
- package/lib/esm/api/model/ContactsVersionRequirements.test.js.map +3 -3
- package/lib/esm/api/model/ProvideContact.js +1 -0
- package/lib/esm/api/model/ProvideContact.js.map +7 -0
- package/lib/esm/internal/app-binder/command/ProvideContactCommand.js +2 -0
- package/lib/esm/internal/app-binder/command/ProvideContactCommand.js.map +7 -0
- package/lib/esm/internal/app-binder/command/ProvideContactCommand.test.js +2 -0
- package/lib/esm/internal/app-binder/command/ProvideContactCommand.test.js.map +7 -0
- package/lib/esm/internal/app-binder/contactsVersionGuards.js +1 -1
- package/lib/esm/internal/app-binder/contactsVersionGuards.js.map +2 -2
- package/lib/esm/internal/app-binder/contactsVersionGuards.test.js +1 -1
- package/lib/esm/internal/app-binder/contactsVersionGuards.test.js.map +2 -2
- package/lib/esm/internal/app-binder/model/contactsConstants.js +1 -1
- package/lib/esm/internal/app-binder/model/contactsConstants.js.map +3 -3
- package/lib/esm/internal/app-binder/services/provideContact.js +2 -0
- package/lib/esm/internal/app-binder/services/provideContact.js.map +7 -0
- package/lib/esm/internal/app-binder/services/provideContact.test.js +2 -0
- package/lib/esm/internal/app-binder/services/provideContact.test.js.map +7 -0
- package/lib/esm/internal/app-binder/task/SendRegisterIdentityTask.js.map +2 -2
- package/lib/esm/internal/app-binder/task/SendRegisterIdentityTask.test.js.map +1 -1
- package/lib/esm/internal/app-binder/task/SendRenameContactTask.js +1 -1
- package/lib/esm/internal/app-binder/task/SendRenameContactTask.js.map +3 -3
- package/lib/esm/internal/app-binder/task/SendRenameContactTask.test.js +1 -1
- package/lib/esm/internal/app-binder/task/SendRenameContactTask.test.js.map +3 -3
- package/lib/esm/package.json +2 -2
- package/lib/types/api/index.d.ts +4 -1
- package/lib/types/api/index.d.ts.map +1 -1
- package/lib/types/api/model/ContactsVersionRequirements.d.ts +7 -25
- package/lib/types/api/model/ContactsVersionRequirements.d.ts.map +1 -1
- package/lib/types/api/model/ProvideContact.d.ts +20 -0
- package/lib/types/api/model/ProvideContact.d.ts.map +1 -0
- package/lib/types/internal/app-binder/command/ProvideContactCommand.d.ts +18 -0
- package/lib/types/internal/app-binder/command/ProvideContactCommand.d.ts.map +1 -0
- package/lib/types/internal/app-binder/command/ProvideContactCommand.test.d.ts +2 -0
- package/lib/types/internal/app-binder/command/ProvideContactCommand.test.d.ts.map +1 -0
- package/lib/types/internal/app-binder/model/contactsConstants.d.ts +2 -0
- package/lib/types/internal/app-binder/model/contactsConstants.d.ts.map +1 -1
- package/lib/types/internal/app-binder/services/provideContact.d.ts +15 -0
- package/lib/types/internal/app-binder/services/provideContact.d.ts.map +1 -0
- package/lib/types/internal/app-binder/services/provideContact.test.d.ts +2 -0
- package/lib/types/internal/app-binder/services/provideContact.test.d.ts.map +1 -0
- package/lib/types/internal/app-binder/task/SendRegisterIdentityTask.d.ts.map +1 -1
- package/lib/types/internal/app-binder/task/SendRenameContactTask.d.ts.map +1 -1
- package/lib/types/tsconfig.prod.tsbuildinfo +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -94,7 +94,8 @@ consume them without duplicating the values:
|
|
|
94
94
|
```ts
|
|
95
95
|
import {
|
|
96
96
|
CONTACTS_VERSION_REQUIREMENTS,
|
|
97
|
-
|
|
97
|
+
ETHEREUM_APP_NAME,
|
|
98
|
+
isVersionAtLeast,
|
|
98
99
|
resolveContactsVersionRequirements,
|
|
99
100
|
} from "@ledgerhq/device-contacts-kit";
|
|
100
101
|
|
|
@@ -102,17 +103,25 @@ import {
|
|
|
102
103
|
const requirement = resolveContactsVersionRequirements(deviceModelId);
|
|
103
104
|
// { supported: true, minOsVersion, minAppVersion: { Ethereum } } | { supported: false }
|
|
104
105
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
106
|
+
if (requirement.supported) {
|
|
107
|
+
// OS-owned operations, e.g. renaming a contact from the dashboard.
|
|
108
|
+
const osReady = isVersionAtLeast(osVersion, requirement.minOsVersion);
|
|
109
|
+
|
|
110
|
+
// App-owned operations, e.g. registering an external address.
|
|
111
|
+
const minAppVersion = requirement.minAppVersion[ETHEREUM_APP_NAME];
|
|
112
|
+
const appReady =
|
|
113
|
+
minAppVersion !== undefined && isVersionAtLeast(appVersion, minAppVersion);
|
|
114
|
+
}
|
|
112
115
|
```
|
|
113
116
|
|
|
114
|
-
|
|
115
|
-
e.g. renaming a contact from the dashboard) and
|
|
116
|
-
by the embedded app, keyed by app name — v1
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
The two axes are checked independently, never as one combined verdict: `minOsVersion` gates
|
|
118
|
+
OS-owned operations (served by the device OS, e.g. renaming a contact from the dashboard) and
|
|
119
|
+
`minAppVersion` gates app-owned operations (served by the embedded app, keyed by app name — v1
|
|
120
|
+
ships Ethereum only). A device can satisfy one and not the other, and each Contacts operation
|
|
121
|
+
depends on exactly one of them, so collapsing them into a single "is Contacts supported" answer
|
|
122
|
+
would reject devices that can serve the operation the host actually wants.
|
|
123
|
+
|
|
124
|
+
`isVersionAtLeast` and the raw `CONTACTS_VERSION_REQUIREMENTS` table are pure and
|
|
125
|
+
dependency-light, so a host such as Ledger Wallet can import either to compose its own
|
|
126
|
+
app-readiness checks. Version comparison ignores prerelease and build tags, so a device on a
|
|
127
|
+
release candidate of a minimum (`1.7.0-rc2` against a `1.7.0` minimum) counts as meeting it.
|
package/lib/cjs/api/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var o=Object.defineProperty;var i=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var E=Object.prototype.hasOwnProperty;var y=(r,e)=>{for(var n in e)o(r,n,{get:e[n],enumerable:!0})},u=(r,e,n,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let d of A(e))!E.call(r,d)&&d!==n&&o(r,d,{get:()=>e[d],enumerable:!(s=i(e,d))||s.enumerable});return r};var x=r=>u(o({},"__esModule",{value:!0}),r);var l={};y(l,{CONTACTS_VERSION_REQUIREMENTS:()=>t.CONTACTS_VERSION_REQUIREMENTS,ContactsManagerBuilder:()=>a.ContactsManagerBuilder,ETHEREUM_APP_NAME:()=>t.ETHEREUM_APP_NAME,buildProvideContactPayload:()=>p.buildProvideContactPayload,isVersionAtLeast:()=>t.isVersionAtLeast,resolveContactsVersionRequirements:()=>t.resolveContactsVersionRequirements,sendProvideContactPayload:()=>p.sendProvideContactPayload});module.exports=x(l);var a=require("./ContactsManagerBuilder"),t=require("./model/ContactsVersionRequirements"),p=require("../internal/app-binder/services/provideContact");0&&(module.exports={CONTACTS_VERSION_REQUIREMENTS,ContactsManagerBuilder,ETHEREUM_APP_NAME,buildProvideContactPayload,isVersionAtLeast,resolveContactsVersionRequirements,sendProvideContactPayload});
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/lib/cjs/api/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/api/index.ts"],
|
|
4
|
-
"sourcesContent": ["export {\n type EditExternalAddressIdentifierDAError,\n type EditExternalAddressIdentifierDAInput,\n type EditExternalAddressIdentifierDAIntermediateValue,\n type EditExternalAddressIdentifierDAOutput,\n type EditExternalAddressIdentifierDARequiredInteraction,\n type EditExternalAddressIdentifierDAReturnType,\n type EditExternalAddressIdentifierDAState,\n} from \"@api/app-binder/EditExternalAddressIdentifierDeviceActionTypes\";\nexport {\n type EditExternalAddressScopeDAError,\n type EditExternalAddressScopeDAInput,\n type EditExternalAddressScopeDAIntermediateValue,\n type EditExternalAddressScopeDAOutput,\n type EditExternalAddressScopeDARequiredInteraction,\n type EditExternalAddressScopeDAReturnType,\n type EditExternalAddressScopeDAState,\n} from \"@api/app-binder/EditExternalAddressScopeDeviceActionTypes\";\nexport {\n type RegisterExternalAddressDAError,\n type RegisterExternalAddressDAInput,\n type RegisterExternalAddressDAIntermediateValue,\n type RegisterExternalAddressDAOutput,\n type RegisterExternalAddressDARequiredInteraction,\n type RegisterExternalAddressDAReturnType,\n type RegisterExternalAddressDAState,\n} from \"@api/app-binder/RegisterExternalAddressDeviceActionTypes\";\nexport {\n type RenameContactDAError,\n type RenameContactDAInput,\n type RenameContactDAIntermediateValue,\n type RenameContactDAOutput,\n type RenameContactDARequiredInteraction,\n type RenameContactDAReturnType,\n type RenameContactDAState,\n} from \"@api/app-binder/RenameContactDeviceActionTypes\";\nexport { type ContactsManager } from \"@api/ContactsManager\";\nexport { ContactsManagerBuilder } from \"@api/ContactsManagerBuilder\";\nexport {\n CONTACTS_VERSION_REQUIREMENTS,\n type ContactsModelRequirement,\n type ContactsModelSupport,\n type ContactsModelUnsupported,\n type
|
|
5
|
-
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,
|
|
6
|
-
"names": ["api_exports", "__export", "__toCommonJS", "import_ContactsManagerBuilder", "import_ContactsVersionRequirements"]
|
|
4
|
+
"sourcesContent": ["export {\n type EditExternalAddressIdentifierDAError,\n type EditExternalAddressIdentifierDAInput,\n type EditExternalAddressIdentifierDAIntermediateValue,\n type EditExternalAddressIdentifierDAOutput,\n type EditExternalAddressIdentifierDARequiredInteraction,\n type EditExternalAddressIdentifierDAReturnType,\n type EditExternalAddressIdentifierDAState,\n} from \"@api/app-binder/EditExternalAddressIdentifierDeviceActionTypes\";\nexport {\n type EditExternalAddressScopeDAError,\n type EditExternalAddressScopeDAInput,\n type EditExternalAddressScopeDAIntermediateValue,\n type EditExternalAddressScopeDAOutput,\n type EditExternalAddressScopeDARequiredInteraction,\n type EditExternalAddressScopeDAReturnType,\n type EditExternalAddressScopeDAState,\n} from \"@api/app-binder/EditExternalAddressScopeDeviceActionTypes\";\nexport {\n type RegisterExternalAddressDAError,\n type RegisterExternalAddressDAInput,\n type RegisterExternalAddressDAIntermediateValue,\n type RegisterExternalAddressDAOutput,\n type RegisterExternalAddressDARequiredInteraction,\n type RegisterExternalAddressDAReturnType,\n type RegisterExternalAddressDAState,\n} from \"@api/app-binder/RegisterExternalAddressDeviceActionTypes\";\nexport {\n type RenameContactDAError,\n type RenameContactDAInput,\n type RenameContactDAIntermediateValue,\n type RenameContactDAOutput,\n type RenameContactDARequiredInteraction,\n type RenameContactDAReturnType,\n type RenameContactDAState,\n} from \"@api/app-binder/RenameContactDeviceActionTypes\";\nexport { type ContactsManager } from \"@api/ContactsManager\";\nexport { ContactsManagerBuilder } from \"@api/ContactsManagerBuilder\";\nexport {\n CONTACTS_VERSION_REQUIREMENTS,\n type ContactsModelRequirement,\n type ContactsModelSupport,\n type ContactsModelUnsupported,\n type ContactsVersionRequirements,\n ETHEREUM_APP_NAME,\n isVersionAtLeast,\n resolveContactsVersionRequirements,\n} from \"@api/model/ContactsVersionRequirements\";\nexport {\n type EditExternalAddressIdentifierInput,\n type EditExternalAddressIdentifierOutput,\n} from \"@api/model/EditExternalAddressIdentifier\";\nexport {\n type EditExternalAddressScopeInput,\n type EditExternalAddressScopeOutput,\n} from \"@api/model/EditExternalAddressScope\";\nexport { type ProvideContactInput } from \"@api/model/ProvideContact\";\nexport {\n type ExistingContactGroup,\n type RegisterExternalAddressInput,\n type RegisterExternalAddressMode,\n type RegisterExternalAddressOutput,\n} from \"@api/model/RegisterExternalAddress\";\nexport {\n type RenameContactInput,\n type RenameContactOutput,\n} from \"@api/model/RenameContact\";\nexport { type ContactsErrorCodes } from \"@internal/app-binder/model/contactsErrors\";\nexport {\n buildProvideContactPayload,\n sendProvideContactPayload,\n type SendProvideContactPayloadArgs,\n} from \"@internal/app-binder/services/provideContact\";\n"],
|
|
5
|
+
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,4ZAAAE,EAAAF,GAqCA,IAAAG,EAAuC,uCACvCC,EASO,kDAqBPC,EAIO",
|
|
6
|
+
"names": ["api_exports", "__export", "__toCommonJS", "import_ContactsManagerBuilder", "import_ContactsVersionRequirements", "import_provideContact"]
|
|
7
7
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var u=Object.defineProperty;var l=Object.getOwnPropertyDescriptor;var N=Object.getOwnPropertyNames;var _=Object.prototype.hasOwnProperty;var O=(e,o)=>{for(var n in o)u(e,n,{get:o[n],enumerable:!0})},a=(e,o,n,p)=>{if(o&&typeof o=="object"||typeof o=="function")for(let i of N(o))!_.call(e,i)&&i!==n&&u(e,i,{get:()=>o[i],enumerable:!(p=l(o,i))||p.enumerable});return e};var m=e=>a(u({},"__esModule",{value:!0}),e);var y={};O(y,{CONTACTS_VERSION_REQUIREMENTS:()=>c,ETHEREUM_APP_NAME:()=>s,isVersionAtLeast:()=>C,resolveContactsVersionRequirements:()=>I});module.exports=m(y);var r=require("@ledgerhq/device-management-kit"),t=require("semver");const s="Ethereum",E={supported:!1},V="1.11.0",R="1.7.0",S="1.2.0",M="2.8.0",A="1.7.0",d="1.23.0",c={[r.DeviceModelId.NANO_S]:E,[r.DeviceModelId.NANO_SP]:{supported:!0,minOsVersion:A,minAppVersion:{[s]:d}},[r.DeviceModelId.NANO_X]:{supported:!0,minOsVersion:M,minAppVersion:{[s]:d}},[r.DeviceModelId.STAX]:{supported:!0,minOsVersion:V,minAppVersion:{[s]:d}},[r.DeviceModelId.FLEX]:{supported:!0,minOsVersion:R,minAppVersion:{[s]:d}},[r.DeviceModelId.APEX]:{supported:!0,minOsVersion:S,minAppVersion:{[s]:d}}};function I(e){return c[e]}function C(e,o){const n=(0,t.valid)((0,t.coerce)(e)),p=(0,t.valid)((0,t.coerce)(o));return n===null||p===null?!1:(0,t.gte)(n,p)}0&&(module.exports={CONTACTS_VERSION_REQUIREMENTS,ETHEREUM_APP_NAME,isVersionAtLeast,resolveContactsVersionRequirements});
|
|
2
2
|
//# sourceMappingURL=ContactsVersionRequirements.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/api/model/ContactsVersionRequirements.ts"],
|
|
4
|
-
"sourcesContent": ["import { DeviceModelId } from \"@ledgerhq/device-management-kit\";\nimport { coerce, gte, valid } from \"semver\";\n\n/**\n * Version requirements for a device model on which Contacts (Address Book)\n * operations are supported.\n *\n * Contacts has two independent version axes:\n * - `minOsVersion` gates OS-owned operations (e.g. renaming a contact from the\n * device dashboard), which are served by the device OS.\n * - `minAppVersion` gates app-owned operations (registering / editing external\n * addresses and Ledger accounts), which are served by the embedded app. It is\n * keyed by app name because the required version differs per app; v1 ships\n * Ethereum only.\n */\nexport type ContactsModelSupport = {\n readonly supported: true;\n readonly minOsVersion: string;\n readonly minAppVersion: Readonly<Record<string, string>>;\n};\n\n/** Marks a device model on which Contacts is not supported at all. */\nexport type ContactsModelUnsupported = {\n readonly supported: false;\n};\n\nexport type ContactsModelRequirement =\n | ContactsModelSupport\n | ContactsModelUnsupported;\n\n/**\n * The full, static Contacts version-requirement table, keyed by device model.\n * Serializable plain data with no runtime dependencies, so hosts (e.g. Ledger\n * Wallet) can import and consume it directly when composing their own\n * app-readiness checks, without duplicating the values.\n */\nexport type ContactsVersionRequirements = Readonly<\n Record<DeviceModelId, ContactsModelRequirement>\n>;\n\n/** The name of the Ethereum embedded app (the only app supported in v1). */\nexport const ETHEREUM_APP_NAME = \"Ethereum\";\n\nconst UNSUPPORTED: ContactsModelUnsupported = { supported: false };\n\
|
|
5
|
-
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,mCAAAE,EAAA,sBAAAC,EAAA,
|
|
6
|
-
"names": ["ContactsVersionRequirements_exports", "__export", "CONTACTS_VERSION_REQUIREMENTS", "ETHEREUM_APP_NAME", "
|
|
4
|
+
"sourcesContent": ["import { DeviceModelId } from \"@ledgerhq/device-management-kit\";\nimport { coerce, gte, valid } from \"semver\";\n\n/**\n * Version requirements for a device model on which Contacts (Address Book)\n * operations are supported.\n *\n * Contacts has two independent version axes:\n * - `minOsVersion` gates OS-owned operations (e.g. renaming a contact from the\n * device dashboard), which are served by the device OS.\n * - `minAppVersion` gates app-owned operations (registering / editing external\n * addresses and Ledger accounts), which are served by the embedded app. It is\n * keyed by app name because the required version differs per app; v1 ships\n * Ethereum only.\n */\nexport type ContactsModelSupport = {\n readonly supported: true;\n readonly minOsVersion: string;\n readonly minAppVersion: Readonly<Record<string, string>>;\n};\n\n/** Marks a device model on which Contacts is not supported at all. */\nexport type ContactsModelUnsupported = {\n readonly supported: false;\n};\n\nexport type ContactsModelRequirement =\n | ContactsModelSupport\n | ContactsModelUnsupported;\n\n/**\n * The full, static Contacts version-requirement table, keyed by device model.\n * Serializable plain data with no runtime dependencies, so hosts (e.g. Ledger\n * Wallet) can import and consume it directly when composing their own\n * app-readiness checks, without duplicating the values.\n */\nexport type ContactsVersionRequirements = Readonly<\n Record<DeviceModelId, ContactsModelRequirement>\n>;\n\n/** The name of the Ethereum embedded app (the only app supported in v1). */\nexport const ETHEREUM_APP_NAME = \"Ethereum\";\n\nconst UNSUPPORTED: ContactsModelUnsupported = { supported: false };\n\nconst MIN_OS_VERSION_STAX = \"1.11.0\";\nconst MIN_OS_VERSION_FLEX = \"1.7.0\";\nconst MIN_OS_VERSION_APEX = \"1.2.0\";\nconst MIN_OS_VERSION_NANO_X = \"2.8.0\";\nconst MIN_OS_VERSION_NANO_SP = \"1.7.0\";\n\nconst MIN_ETHEREUM_APP_VERSION = \"1.23.0\";\n\n/**\n * Contacts APDUs are supported on the touchscreen device models (Stax, Flex,\n * Apex) and on Nano X / Nano SP. Nano S does not support Contacts at all.\n */\nexport const CONTACTS_VERSION_REQUIREMENTS: ContactsVersionRequirements = {\n [DeviceModelId.NANO_S]: UNSUPPORTED,\n [DeviceModelId.NANO_SP]: {\n supported: true,\n minOsVersion: MIN_OS_VERSION_NANO_SP,\n minAppVersion: { [ETHEREUM_APP_NAME]: MIN_ETHEREUM_APP_VERSION },\n },\n [DeviceModelId.NANO_X]: {\n supported: true,\n minOsVersion: MIN_OS_VERSION_NANO_X,\n minAppVersion: { [ETHEREUM_APP_NAME]: MIN_ETHEREUM_APP_VERSION },\n },\n [DeviceModelId.STAX]: {\n supported: true,\n minOsVersion: MIN_OS_VERSION_STAX,\n minAppVersion: { [ETHEREUM_APP_NAME]: MIN_ETHEREUM_APP_VERSION },\n },\n [DeviceModelId.FLEX]: {\n supported: true,\n minOsVersion: MIN_OS_VERSION_FLEX,\n minAppVersion: { [ETHEREUM_APP_NAME]: MIN_ETHEREUM_APP_VERSION },\n },\n [DeviceModelId.APEX]: {\n supported: true,\n minOsVersion: MIN_OS_VERSION_APEX,\n minAppVersion: { [ETHEREUM_APP_NAME]: MIN_ETHEREUM_APP_VERSION },\n },\n};\n\n/**\n * Resolve the Contacts version requirements for a device model \u2014 i.e. the\n * minimum supported OS and app versions, or that the model is unsupported.\n */\nexport function resolveContactsVersionRequirements(\n deviceModelId: DeviceModelId,\n): ContactsModelRequirement {\n return CONTACTS_VERSION_REQUIREMENTS[deviceModelId];\n}\n\n/**\n * Whether `actual` is greater than or equal to `minimum`, comparing as semver.\n * Tolerant of non-strict version strings and prerelease/build tags (coerces\n * both sides, which drops them) so a release candidate such as `1.7.0-rc2`\n * counts as meeting a `1.7.0` minimum. Returns `false` (i.e. requirement not\n * met) when either version cannot be parsed, so an unknown version never\n * passes a check.\n */\nexport function isVersionAtLeast(actual: string, minimum: string): boolean {\n const a = valid(coerce(actual));\n const b = valid(coerce(minimum));\n if (a === null || b === null) return false;\n return gte(a, b);\n}\n"],
|
|
5
|
+
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,mCAAAE,EAAA,sBAAAC,EAAA,qBAAAC,EAAA,uCAAAC,IAAA,eAAAC,EAAAN,GAAA,IAAAO,EAA8B,2CAC9BC,EAAmC,kBAwC5B,MAAML,EAAoB,WAE3BM,EAAwC,CAAE,UAAW,EAAM,EAE3DC,EAAsB,SACtBC,EAAsB,QACtBC,EAAsB,QACtBC,EAAwB,QACxBC,EAAyB,QAEzBC,EAA2B,SAMpBb,EAA6D,CACxE,CAAC,gBAAc,MAAM,EAAGO,EACxB,CAAC,gBAAc,OAAO,EAAG,CACvB,UAAW,GACX,aAAcK,EACd,cAAe,CAAE,CAACX,CAAiB,EAAGY,CAAyB,CACjE,EACA,CAAC,gBAAc,MAAM,EAAG,CACtB,UAAW,GACX,aAAcF,EACd,cAAe,CAAE,CAACV,CAAiB,EAAGY,CAAyB,CACjE,EACA,CAAC,gBAAc,IAAI,EAAG,CACpB,UAAW,GACX,aAAcL,EACd,cAAe,CAAE,CAACP,CAAiB,EAAGY,CAAyB,CACjE,EACA,CAAC,gBAAc,IAAI,EAAG,CACpB,UAAW,GACX,aAAcJ,EACd,cAAe,CAAE,CAACR,CAAiB,EAAGY,CAAyB,CACjE,EACA,CAAC,gBAAc,IAAI,EAAG,CACpB,UAAW,GACX,aAAcH,EACd,cAAe,CAAE,CAACT,CAAiB,EAAGY,CAAyB,CACjE,CACF,EAMO,SAASV,EACdW,EAC0B,CAC1B,OAAOd,EAA8Bc,CAAa,CACpD,CAUO,SAASZ,EAAiBa,EAAgBC,EAA0B,CACzE,MAAMC,KAAI,YAAM,UAAOF,CAAM,CAAC,EACxBG,KAAI,YAAM,UAAOF,CAAO,CAAC,EAC/B,OAAIC,IAAM,MAAQC,IAAM,KAAa,MAC9B,OAAID,EAAGC,CAAC,CACjB",
|
|
6
|
+
"names": ["ContactsVersionRequirements_exports", "__export", "CONTACTS_VERSION_REQUIREMENTS", "ETHEREUM_APP_NAME", "isVersionAtLeast", "resolveContactsVersionRequirements", "__toCommonJS", "import_device_management_kit", "import_semver", "UNSUPPORTED", "MIN_OS_VERSION_STAX", "MIN_OS_VERSION_FLEX", "MIN_OS_VERSION_APEX", "MIN_OS_VERSION_NANO_X", "MIN_OS_VERSION_NANO_SP", "MIN_ETHEREUM_APP_VERSION", "deviceModelId", "actual", "minimum", "a", "b"]
|
|
7
7
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var t=require("@ledgerhq/device-management-kit"),e=require("./ContactsVersionRequirements");describe("ContactsVersionRequirements",()=>{describe("resolveContactsVersionRequirements",()=>{it("marks every device model, supported or not",()=>{for(const s of Object.values(t.DeviceModelId))expect(e.CONTACTS_VERSION_REQUIREMENTS[s]).toBeDefined()}),it("reports Nano S as unsupported",()=>{expect((0,e.resolveContactsVersionRequirements)(t.DeviceModelId.NANO_S)).toEqual({supported:!1})}),it("pins the minimum OS and app versions of every supported model",()=>{const s={[e.ETHEREUM_APP_NAME]:"1.23.0"};expect(e.CONTACTS_VERSION_REQUIREMENTS).toEqual({[t.DeviceModelId.NANO_S]:{supported:!1},[t.DeviceModelId.NANO_SP]:{supported:!0,minOsVersion:"1.7.0",minAppVersion:s},[t.DeviceModelId.NANO_X]:{supported:!0,minOsVersion:"2.8.0",minAppVersion:s},[t.DeviceModelId.STAX]:{supported:!0,minOsVersion:"1.11.0",minAppVersion:s},[t.DeviceModelId.FLEX]:{supported:!0,minOsVersion:"1.7.0",minAppVersion:s},[t.DeviceModelId.APEX]:{supported:!0,minOsVersion:"1.2.0",minAppVersion:s}})})}),describe("isVersionAtLeast",()=>{it("compares versions as semver, inclusive of equality",()=>{expect((0,e.isVersionAtLeast)("1.2.0","1.2.0")).toBe(!0),expect((0,e.isVersionAtLeast)("1.3.0","1.2.0")).toBe(!0),expect((0,e.isVersionAtLeast)("1.1.9","1.2.0")).toBe(!1)}),it("coerces non-strict version strings",()=>{expect((0,e.isVersionAtLeast)("1.2","1.2.0")).toBe(!0)}),it("returns false when a version cannot be parsed",()=>{expect((0,e.isVersionAtLeast)("not-a-version","1.2.0")).toBe(!1),expect((0,e.isVersionAtLeast)("1.2.0","not-a-version")).toBe(!1)}),it("ignores prerelease and build tags when comparing to the minimum",()=>{expect((0,e.isVersionAtLeast)("1.7.0-rc2","1.7.0")).toBe(!0),expect((0,e.isVersionAtLeast)("1.23.0-dev","1.23.0")).toBe(!0)}),it("still fails a minimum strictly above the tagged version's core",()=>{expect((0,e.isVersionAtLeast)("1.6.9-rc5","1.7.0")).toBe(!1)})})});
|
|
2
2
|
//# sourceMappingURL=ContactsVersionRequirements.test.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/api/model/ContactsVersionRequirements.test.ts"],
|
|
4
|
-
"sourcesContent": ["import { DeviceModelId } from \"@ledgerhq/device-management-kit\";\n\nimport {\n CONTACTS_VERSION_REQUIREMENTS,\n
|
|
5
|
-
"mappings": "aAAA,IAAAA,EAA8B,2CAE9BC,
|
|
6
|
-
"names": ["import_device_management_kit", "import_ContactsVersionRequirements", "
|
|
4
|
+
"sourcesContent": ["import { DeviceModelId } from \"@ledgerhq/device-management-kit\";\n\nimport {\n CONTACTS_VERSION_REQUIREMENTS,\n ETHEREUM_APP_NAME,\n isVersionAtLeast,\n resolveContactsVersionRequirements,\n} from \"./ContactsVersionRequirements\";\n\ndescribe(\"ContactsVersionRequirements\", () => {\n describe(\"resolveContactsVersionRequirements\", () => {\n it(\"marks every device model, supported or not\", () => {\n for (const modelId of Object.values(DeviceModelId)) {\n expect(CONTACTS_VERSION_REQUIREMENTS[modelId]).toBeDefined();\n }\n });\n\n it(\"reports Nano S as unsupported\", () => {\n expect(resolveContactsVersionRequirements(DeviceModelId.NANO_S)).toEqual({\n supported: false,\n });\n });\n\n // Pins the minimums themselves, not just their shape: each OS version is\n // the release whose changelog introduces the Address Book feature, so a\n // change here has to be a deliberate one, reviewed against that release.\n it(\"pins the minimum OS and app versions of every supported model\", () => {\n const ethereum = { [ETHEREUM_APP_NAME]: \"1.23.0\" };\n\n expect(CONTACTS_VERSION_REQUIREMENTS).toEqual({\n [DeviceModelId.NANO_S]: { supported: false },\n [DeviceModelId.NANO_SP]: {\n supported: true,\n minOsVersion: \"1.7.0\",\n minAppVersion: ethereum,\n },\n [DeviceModelId.NANO_X]: {\n supported: true,\n minOsVersion: \"2.8.0\",\n minAppVersion: ethereum,\n },\n [DeviceModelId.STAX]: {\n supported: true,\n minOsVersion: \"1.11.0\",\n minAppVersion: ethereum,\n },\n [DeviceModelId.FLEX]: {\n supported: true,\n minOsVersion: \"1.7.0\",\n minAppVersion: ethereum,\n },\n [DeviceModelId.APEX]: {\n supported: true,\n minOsVersion: \"1.2.0\",\n minAppVersion: ethereum,\n },\n });\n });\n });\n\n describe(\"isVersionAtLeast\", () => {\n it(\"compares versions as semver, inclusive of equality\", () => {\n expect(isVersionAtLeast(\"1.2.0\", \"1.2.0\")).toBe(true);\n expect(isVersionAtLeast(\"1.3.0\", \"1.2.0\")).toBe(true);\n expect(isVersionAtLeast(\"1.1.9\", \"1.2.0\")).toBe(false);\n });\n\n it(\"coerces non-strict version strings\", () => {\n expect(isVersionAtLeast(\"1.2\", \"1.2.0\")).toBe(true);\n });\n\n it(\"returns false when a version cannot be parsed\", () => {\n expect(isVersionAtLeast(\"not-a-version\", \"1.2.0\")).toBe(false);\n expect(isVersionAtLeast(\"1.2.0\", \"not-a-version\")).toBe(false);\n });\n\n it(\"ignores prerelease and build tags when comparing to the minimum\", () => {\n expect(isVersionAtLeast(\"1.7.0-rc2\", \"1.7.0\")).toBe(true);\n expect(isVersionAtLeast(\"1.23.0-dev\", \"1.23.0\")).toBe(true);\n });\n\n it(\"still fails a minimum strictly above the tagged version's core\", () => {\n expect(isVersionAtLeast(\"1.6.9-rc5\", \"1.7.0\")).toBe(false);\n });\n });\n});\n"],
|
|
5
|
+
"mappings": "aAAA,IAAAA,EAA8B,2CAE9BC,EAKO,yCAEP,SAAS,8BAA+B,IAAM,CAC5C,SAAS,qCAAsC,IAAM,CACnD,GAAG,6CAA8C,IAAM,CACrD,UAAWC,KAAW,OAAO,OAAO,eAAa,EAC/C,OAAO,gCAA8BA,CAAO,CAAC,EAAE,YAAY,CAE/D,CAAC,EAED,GAAG,gCAAiC,IAAM,CACxC,UAAO,sCAAmC,gBAAc,MAAM,CAAC,EAAE,QAAQ,CACvE,UAAW,EACb,CAAC,CACH,CAAC,EAKD,GAAG,gEAAiE,IAAM,CACxE,MAAMC,EAAW,CAAE,CAAC,mBAAiB,EAAG,QAAS,EAEjD,OAAO,+BAA6B,EAAE,QAAQ,CAC5C,CAAC,gBAAc,MAAM,EAAG,CAAE,UAAW,EAAM,EAC3C,CAAC,gBAAc,OAAO,EAAG,CACvB,UAAW,GACX,aAAc,QACd,cAAeA,CACjB,EACA,CAAC,gBAAc,MAAM,EAAG,CACtB,UAAW,GACX,aAAc,QACd,cAAeA,CACjB,EACA,CAAC,gBAAc,IAAI,EAAG,CACpB,UAAW,GACX,aAAc,SACd,cAAeA,CACjB,EACA,CAAC,gBAAc,IAAI,EAAG,CACpB,UAAW,GACX,aAAc,QACd,cAAeA,CACjB,EACA,CAAC,gBAAc,IAAI,EAAG,CACpB,UAAW,GACX,aAAc,QACd,cAAeA,CACjB,CACF,CAAC,CACH,CAAC,CACH,CAAC,EAED,SAAS,mBAAoB,IAAM,CACjC,GAAG,qDAAsD,IAAM,CAC7D,UAAO,oBAAiB,QAAS,OAAO,CAAC,EAAE,KAAK,EAAI,EACpD,UAAO,oBAAiB,QAAS,OAAO,CAAC,EAAE,KAAK,EAAI,EACpD,UAAO,oBAAiB,QAAS,OAAO,CAAC,EAAE,KAAK,EAAK,CACvD,CAAC,EAED,GAAG,qCAAsC,IAAM,CAC7C,UAAO,oBAAiB,MAAO,OAAO,CAAC,EAAE,KAAK,EAAI,CACpD,CAAC,EAED,GAAG,gDAAiD,IAAM,CACxD,UAAO,oBAAiB,gBAAiB,OAAO,CAAC,EAAE,KAAK,EAAK,EAC7D,UAAO,oBAAiB,QAAS,eAAe,CAAC,EAAE,KAAK,EAAK,CAC/D,CAAC,EAED,GAAG,kEAAmE,IAAM,CAC1E,UAAO,oBAAiB,YAAa,OAAO,CAAC,EAAE,KAAK,EAAI,EACxD,UAAO,oBAAiB,aAAc,QAAQ,CAAC,EAAE,KAAK,EAAI,CAC5D,CAAC,EAED,GAAG,iEAAkE,IAAM,CACzE,UAAO,oBAAiB,YAAa,OAAO,CAAC,EAAE,KAAK,EAAK,CAC3D,CAAC,CACH,CAAC,CACH,CAAC",
|
|
6
|
+
"names": ["import_device_management_kit", "import_ContactsVersionRequirements", "modelId", "ethereum"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var o=Object.defineProperty;var i=Object.getOwnPropertyDescriptor;var y=Object.getOwnPropertyNames;var d=Object.prototype.hasOwnProperty;var l=(n,r,t,e)=>{if(r&&typeof r=="object"||typeof r=="function")for(let a of y(r))!d.call(n,a)&&a!==t&&o(n,a,{get:()=>r[a],enumerable:!(e=i(r,a))||e.enumerable});return n};var c=n=>l(o({},"__esModule",{value:!0}),n);var g={};module.exports=c(g);
|
|
2
|
+
//# sourceMappingURL=ProvideContact.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/api/model/ProvideContact.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Everything the device needs to accept a registered external contact as a\n * decoration for an upcoming signing flow: the name-level material shared by\n * the whole contact group, plus the address-level material of the one matched\n * address.\n */\nexport type ProvideContactInput = {\n readonly contactName: string;\n readonly scope: string;\n /** Raw bytes, chain-dependent: a 20-byte address for Ethereum. */\n readonly identifier: Uint8Array;\n readonly groupHandle: Uint8Array;\n readonly hmacProof: Uint8Array;\n readonly hmacRest: Uint8Array;\n /** Lowercase family name, e.g. `\"ethereum\"`. */\n readonly blockchainFamily: string;\n /** Mandatory for Ethereum, omitted for every other family. */\n readonly chainId?: bigint;\n};\n"],
|
|
5
|
+
"mappings": "+WAAA,IAAAA,EAAA,kBAAAC,EAAAD",
|
|
6
|
+
"names": ["ProvideContact_exports", "__toCommonJS"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var m=Object.defineProperty;var i=Object.getOwnPropertyDescriptor;var c=Object.getOwnPropertyNames;var l=Object.prototype.hasOwnProperty;var u=(r,o)=>{for(var a in o)m(r,a,{get:o[a],enumerable:!0})},A=(r,o,a,s)=>{if(o&&typeof o=="object"||typeof o=="function")for(let t of c(o))!l.call(r,t)&&t!==a&&m(r,t,{get:()=>o[t],enumerable:!(s=i(o,t))||s.enumerable});return r};var y=r=>A(m({},"__esModule",{value:!0}),r);var R={};u(R,{ProvideContactCommand:()=>P});module.exports=y(R);var n=require("@ledgerhq/device-management-kit"),C=require("@ledgerhq/signer-utils"),p=require("purify-ts"),e=require("../../app-binder/model/contactsConstants"),d=require("../../app-binder/model/contactsErrors");class P{name="provideContact";args;errorHelper=new C.CommandErrorHelper(d.CONTACTS_APP_ERRORS,d.contactsCommandErrorFactory);constructor(o){this.args=o}getApdu(){return new n.ApduBuilder({cla:e.CONTACTS_APDU_CLA,ins:e.CONTACTS_APDU_INS,p1:e.SUB_CMD_PROVIDE_CONTACT,p2:this.args.p2}).addBufferToData(this.args.data).build()}parseResponse(o){return p.Maybe.fromNullable(this.errorHelper.getError(o)).orDefaultLazy(()=>(0,n.CommandResultFactory)({data:void 0}))}}0&&(module.exports={ProvideContactCommand});
|
|
2
|
+
//# sourceMappingURL=ProvideContactCommand.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/internal/app-binder/command/ProvideContactCommand.ts"],
|
|
4
|
+
"sourcesContent": ["// Provide Contact \u2014 sub-command P1=0x20 of the address-book APDU (CLA 0xB0 /\n// INS 0x10). Sent before a signing APDU so the device renders a registered\n// contact name in place of the raw recipient address. Caller\n// (sendProvideContactPayload) assembles the TLV, prepends the 2-byte BE total\n// length and slices into <=255B chunks; this command frames one chunk. Every\n// chunk answers with SW=0x9000 and no data \u2014 the outcome is a device-side\n// effect, not a returned value.\n// Protocol: Address Book Final Specifications \u2014 Provide Contact.\nimport {\n type Apdu,\n ApduBuilder,\n type ApduResponse,\n type Command,\n type CommandResult,\n CommandResultFactory,\n} from \"@ledgerhq/device-management-kit\";\nimport { CommandErrorHelper } from \"@ledgerhq/signer-utils\";\nimport { Maybe } from \"purify-ts\";\n\nimport {\n CONTACTS_APDU_CLA,\n CONTACTS_APDU_INS,\n SUB_CMD_PROVIDE_CONTACT,\n} from \"@internal/app-binder/model/contactsConstants\";\nimport {\n CONTACTS_APP_ERRORS,\n contactsCommandErrorFactory,\n type ContactsErrorCodes,\n} from \"@internal/app-binder/model/contactsErrors\";\n\nexport type ProvideContactCommandArgs = {\n /** One framed chunk built by sendProvideContactPayload. */\n readonly data: Uint8Array;\n /** 0x00 for the first/only chunk, 0x80 for continuation chunks. */\n readonly p2: number;\n};\n\nexport type ProvideContactCommandResponse = void;\n\nexport class ProvideContactCommand\n implements\n Command<\n ProvideContactCommandResponse,\n ProvideContactCommandArgs,\n ContactsErrorCodes\n >\n{\n readonly name = \"provideContact\";\n readonly args: ProvideContactCommandArgs;\n private readonly errorHelper = new CommandErrorHelper<\n ProvideContactCommandResponse,\n ContactsErrorCodes\n >(CONTACTS_APP_ERRORS, contactsCommandErrorFactory);\n\n constructor(args: ProvideContactCommandArgs) {\n this.args = args;\n }\n\n getApdu(): Apdu {\n return new ApduBuilder({\n cla: CONTACTS_APDU_CLA,\n ins: CONTACTS_APDU_INS,\n p1: SUB_CMD_PROVIDE_CONTACT,\n p2: this.args.p2,\n })\n .addBufferToData(this.args.data)\n .build();\n }\n\n parseResponse(\n response: ApduResponse,\n ): CommandResult<ProvideContactCommandResponse, ContactsErrorCodes> {\n return Maybe.fromNullable(\n this.errorHelper.getError(response),\n ).orDefaultLazy(() =>\n CommandResultFactory<ProvideContactCommandResponse, ContactsErrorCodes>({\n data: undefined,\n }),\n );\n }\n}\n"],
|
|
5
|
+
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,2BAAAE,IAAA,eAAAC,EAAAH,GAQA,IAAAI,EAOO,2CACPC,EAAmC,kCACnCC,EAAsB,qBAEtBC,EAIO,wDACPC,EAIO,qDAWA,MAAMN,CAOb,CACW,KAAO,iBACP,KACQ,YAAc,IAAI,qBAGjC,sBAAqB,6BAA2B,EAElD,YAAYO,EAAiC,CAC3C,KAAK,KAAOA,CACd,CAEA,SAAgB,CACd,OAAO,IAAI,cAAY,CACrB,IAAK,oBACL,IAAK,oBACL,GAAI,0BACJ,GAAI,KAAK,KAAK,EAChB,CAAC,EACE,gBAAgB,KAAK,KAAK,IAAI,EAC9B,MAAM,CACX,CAEA,cACEC,EACkE,CAClE,OAAO,QAAM,aACX,KAAK,YAAY,SAASA,CAAQ,CACpC,EAAE,cAAc,OACd,wBAAwE,CACtE,KAAM,MACR,CAAC,CACH,CACF,CACF",
|
|
6
|
+
"names": ["ProvideContactCommand_exports", "__export", "ProvideContactCommand", "__toCommonJS", "import_device_management_kit", "import_signer_utils", "import_purify_ts", "import_contactsConstants", "import_contactsErrors", "args", "response"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var r=require("@ledgerhq/device-management-kit"),a=require("../../app-binder/model/contactsErrors"),e=require("./ProvideContactCommand");describe("ProvideContactCommand",()=>{describe("getApdu",()=>{it("wraps the framed chunk under B0 10 20 with P2=0x00 for the first/only chunk",()=>{const t=Uint8Array.from([0,3,170,187,204]),o=new e.ProvideContactCommand({data:t,p2:0}).getApdu();expect(o.getRawApdu()).toStrictEqual(Uint8Array.from([176,16,32,0,5,0,3,170,187,204]))}),it("uses P2=0x80 for continuation chunks",()=>{const t=Uint8Array.from([170,187]),o=new e.ProvideContactCommand({data:t,p2:128}).getApdu();expect(o.getRawApdu()).toStrictEqual(Uint8Array.from([176,16,32,128,2,170,187]))})}),describe("parseResponse",()=>{it("succeeds with no data on SW=0x9000",()=>{const t=new e.ProvideContactCommand({data:new Uint8Array,p2:0}).parseResponse({data:Buffer.from([]),statusCode:Buffer.from([144,0])});expect(t).toStrictEqual({status:r.CommandResultStatus.Success,data:void 0})}),it("maps SW=0x6982 to a seed-mismatch ContactsCommandError",()=>{const t=new e.ProvideContactCommand({data:new Uint8Array,p2:0}).parseResponse({data:Buffer.from([]),statusCode:Buffer.from([105,130])});expect(t.status).toBe(r.CommandResultStatus.Error),t.status===r.CommandResultStatus.Error&&(expect(t.error).toBeInstanceOf(a.ContactsCommandError),expect(t.error.errorCode).toBe(a.CONTACT_SEED_MISMATCH_ERROR_CODE))}),it("maps SW=0x6a80 to a ContactsCommandError",()=>{const t=new e.ProvideContactCommand({data:new Uint8Array,p2:0}).parseResponse({data:Buffer.from([]),statusCode:Buffer.from([106,128])});expect(t.status).toBe(r.CommandResultStatus.Error),t.status===r.CommandResultStatus.Error&&expect(t.error.errorCode).toBe("6a80")})})});
|
|
2
|
+
//# sourceMappingURL=ProvideContactCommand.test.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/internal/app-binder/command/ProvideContactCommand.test.ts"],
|
|
4
|
+
"sourcesContent": ["import { CommandResultStatus } from \"@ledgerhq/device-management-kit\";\n\nimport {\n CONTACT_SEED_MISMATCH_ERROR_CODE,\n ContactsCommandError,\n} from \"@internal/app-binder/model/contactsErrors\";\n\nimport { ProvideContactCommand } from \"./ProvideContactCommand\";\n\ndescribe(\"ProvideContactCommand\", () => {\n describe(\"getApdu\", () => {\n it(\"wraps the framed chunk under B0 10 20 with P2=0x00 for the first/only chunk\", () => {\n const data = Uint8Array.from([0x00, 0x03, 0xaa, 0xbb, 0xcc]);\n\n const apdu = new ProvideContactCommand({ data, p2: 0x00 }).getApdu();\n\n expect(apdu.getRawApdu()).toStrictEqual(\n Uint8Array.from([\n 0xb0, 0x10, 0x20, 0x00, 0x05, 0x00, 0x03, 0xaa, 0xbb, 0xcc,\n ]),\n );\n });\n\n it(\"uses P2=0x80 for continuation chunks\", () => {\n const data = Uint8Array.from([0xaa, 0xbb]);\n\n const apdu = new ProvideContactCommand({ data, p2: 0x80 }).getApdu();\n\n expect(apdu.getRawApdu()).toStrictEqual(\n Uint8Array.from([0xb0, 0x10, 0x20, 0x80, 0x02, 0xaa, 0xbb]),\n );\n });\n });\n\n describe(\"parseResponse\", () => {\n it(\"succeeds with no data on SW=0x9000\", () => {\n const result = new ProvideContactCommand({\n data: new Uint8Array(),\n p2: 0x00,\n }).parseResponse({\n data: Buffer.from([]),\n statusCode: Buffer.from([0x90, 0x00]),\n });\n\n expect(result).toStrictEqual({\n status: CommandResultStatus.Success,\n data: undefined,\n });\n });\n\n it(\"maps SW=0x6982 to a seed-mismatch ContactsCommandError\", () => {\n const result = new ProvideContactCommand({\n data: new Uint8Array(),\n p2: 0x00,\n }).parseResponse({\n data: Buffer.from([]),\n statusCode: Buffer.from([0x69, 0x82]),\n });\n\n expect(result.status).toBe(CommandResultStatus.Error);\n if (result.status === CommandResultStatus.Error) {\n expect(result.error).toBeInstanceOf(ContactsCommandError);\n expect((result.error as ContactsCommandError).errorCode).toBe(\n CONTACT_SEED_MISMATCH_ERROR_CODE,\n );\n }\n });\n\n it(\"maps SW=0x6a80 to a ContactsCommandError\", () => {\n const result = new ProvideContactCommand({\n data: new Uint8Array(),\n p2: 0x00,\n }).parseResponse({\n data: Buffer.from([]),\n statusCode: Buffer.from([0x6a, 0x80]),\n });\n\n expect(result.status).toBe(CommandResultStatus.Error);\n if (result.status === CommandResultStatus.Error) {\n expect((result.error as ContactsCommandError).errorCode).toBe(\"6a80\");\n }\n });\n });\n});\n"],
|
|
5
|
+
"mappings": "aAAA,IAAAA,EAAoC,2CAEpCC,EAGO,qDAEPC,EAAsC,mCAEtC,SAAS,wBAAyB,IAAM,CACtC,SAAS,UAAW,IAAM,CACxB,GAAG,8EAA+E,IAAM,CACtF,MAAMC,EAAO,WAAW,KAAK,CAAC,EAAM,EAAM,IAAM,IAAM,GAAI,CAAC,EAErDC,EAAO,IAAI,wBAAsB,CAAE,KAAAD,EAAM,GAAI,CAAK,CAAC,EAAE,QAAQ,EAEnE,OAAOC,EAAK,WAAW,CAAC,EAAE,cACxB,WAAW,KAAK,CACd,IAAM,GAAM,GAAM,EAAM,EAAM,EAAM,EAAM,IAAM,IAAM,GACxD,CAAC,CACH,CACF,CAAC,EAED,GAAG,uCAAwC,IAAM,CAC/C,MAAMD,EAAO,WAAW,KAAK,CAAC,IAAM,GAAI,CAAC,EAEnCC,EAAO,IAAI,wBAAsB,CAAE,KAAAD,EAAM,GAAI,GAAK,CAAC,EAAE,QAAQ,EAEnE,OAAOC,EAAK,WAAW,CAAC,EAAE,cACxB,WAAW,KAAK,CAAC,IAAM,GAAM,GAAM,IAAM,EAAM,IAAM,GAAI,CAAC,CAC5D,CACF,CAAC,CACH,CAAC,EAED,SAAS,gBAAiB,IAAM,CAC9B,GAAG,qCAAsC,IAAM,CAC7C,MAAMC,EAAS,IAAI,wBAAsB,CACvC,KAAM,IAAI,WACV,GAAI,CACN,CAAC,EAAE,cAAc,CACf,KAAM,OAAO,KAAK,CAAC,CAAC,EACpB,WAAY,OAAO,KAAK,CAAC,IAAM,CAAI,CAAC,CACtC,CAAC,EAED,OAAOA,CAAM,EAAE,cAAc,CAC3B,OAAQ,sBAAoB,QAC5B,KAAM,MACR,CAAC,CACH,CAAC,EAED,GAAG,yDAA0D,IAAM,CACjE,MAAMA,EAAS,IAAI,wBAAsB,CACvC,KAAM,IAAI,WACV,GAAI,CACN,CAAC,EAAE,cAAc,CACf,KAAM,OAAO,KAAK,CAAC,CAAC,EACpB,WAAY,OAAO,KAAK,CAAC,IAAM,GAAI,CAAC,CACtC,CAAC,EAED,OAAOA,EAAO,MAAM,EAAE,KAAK,sBAAoB,KAAK,EAChDA,EAAO,SAAW,sBAAoB,QACxC,OAAOA,EAAO,KAAK,EAAE,eAAe,sBAAoB,EACxD,OAAQA,EAAO,MAA+B,SAAS,EAAE,KACvD,kCACF,EAEJ,CAAC,EAED,GAAG,2CAA4C,IAAM,CACnD,MAAMA,EAAS,IAAI,wBAAsB,CACvC,KAAM,IAAI,WACV,GAAI,CACN,CAAC,EAAE,cAAc,CACf,KAAM,OAAO,KAAK,CAAC,CAAC,EACpB,WAAY,OAAO,KAAK,CAAC,IAAM,GAAI,CAAC,CACtC,CAAC,EAED,OAAOA,EAAO,MAAM,EAAE,KAAK,sBAAoB,KAAK,EAChDA,EAAO,SAAW,sBAAoB,OACxC,OAAQA,EAAO,MAA+B,SAAS,EAAE,KAAK,MAAM,CAExE,CAAC,CACH,CAAC,CACH,CAAC",
|
|
6
|
+
"names": ["import_device_management_kit", "import_contactsErrors", "import_ProvideContactCommand", "data", "apdu", "result"]
|
|
7
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var s=Object.defineProperty;var u=Object.getOwnPropertyDescriptor;var d=Object.getOwnPropertyNames;var l=Object.prototype.hasOwnProperty;var f=(n,e)=>{for(var t in e)s(n,t,{get:e[t],enumerable:!0})},m=(n,e,t,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let
|
|
1
|
+
"use strict";var s=Object.defineProperty;var u=Object.getOwnPropertyDescriptor;var d=Object.getOwnPropertyNames;var l=Object.prototype.hasOwnProperty;var f=(n,e)=>{for(var t in e)s(n,t,{get:e[t],enumerable:!0})},m=(n,e,t,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of d(e))!l.call(n,r)&&r!==t&&s(n,r,{get:()=>e[r],enumerable:!(o=u(e,r))||o.enumerable});return n};var S=n=>m(s({},"__esModule",{value:!0}),n);var C={};f(C,{isContactsAppVersionSupportedForSession:()=>A,isContactsOsSupportedForSession:()=>v});module.exports=S(C);var p=require("@ledgerhq/device-management-kit"),i=require("../../api/model/ContactsVersionRequirements"),c=require("./ContactsApplicationResolver");function a(n){return n.supported}function v(n){const e=n.getDeviceSessionState(),t=(0,i.resolveContactsVersionRequirements)(e.deviceModelId);if(!a(t))return!1;const o="firmwareVersion"in e?e.firmwareVersion?.os:void 0;return o===void 0?!1:(0,i.isVersionAtLeast)(o,t.minOsVersion)}function A(n,e){const t=n.getDeviceSessionState(),o=(0,i.resolveContactsVersionRequirements)(t.deviceModelId);if(!a(o))return!1;const r=o.minAppVersion[e.name];return r===void 0?!1:new p.ApplicationChecker(t,{version:e.version},new c.ContactsApplicationResolver).withMinVersionInclusiveAcceptingPrerelease(r).check()}0&&(module.exports={isContactsAppVersionSupportedForSession,isContactsOsSupportedForSession});
|
|
2
2
|
//# sourceMappingURL=contactsVersionGuards.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/internal/app-binder/contactsVersionGuards.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n ApplicationChecker,\n type InternalApi,\n} from \"@ledgerhq/device-management-kit\";\n\nimport {\n type ContactsModelRequirement,\n type ContactsModelSupport,\n isVersionAtLeast,\n resolveContactsVersionRequirements,\n} from \"@api/model/ContactsVersionRequirements\";\n\nimport { ContactsApplicationResolver } from \"./ContactsApplicationResolver\";\n\n/** The running app's identity, read freshly from the device. */\nexport type RunningApp = {\n readonly name: string;\n readonly version: string;\n};\n\n/** Narrows a resolved requirement to the case where Contacts is supported at all on this device model. */\nfunction isContactsModelSupported(\n requirement: ContactsModelRequirement,\n): requirement is ContactsModelSupport {\n return requirement.supported;\n}\n\n/**\n * OS-only Contacts support check, for internal consumption by dashboard\n * `DeviceAction`s (e.g. Rename Contact, EDIT CONTACT NAME). Dashboard operations\n * are served by the device OS, not the embedded app, so only two dimensions\n * apply:\n * - the device model must be supported;\n * - the device OS must meet its minimum version.\n *\n * The running-app dimension is intentionally skipped: on the dashboard there is\n * no embedded app to gate on (the running \"app\" is BOLOS).\n *\n * @param internalApi - the DeviceAction's internal API for the current session.\n */\nexport function isContactsOsSupportedForSession(\n internalApi: InternalApi,\n): boolean {\n const deviceState = internalApi.getDeviceSessionState();\n const requirement = resolveContactsVersionRequirements(\n deviceState.deviceModelId,\n );\n if (!isContactsModelSupported(requirement)) return false;\n\n const osVersion =\n \"firmwareVersion\" in deviceState\n ? deviceState.firmwareVersion?.os\n : undefined;\n if (osVersion === undefined) return false;\n\n return isVersionAtLeast(osVersion, requirement.minOsVersion);\n}\n\n/**\n * Session-aware Contacts support check, for internal consumption by app-owned\n * Contacts `DeviceAction`s (registering / editing external addresses). These\n * operations are served by the embedded app, not the device OS, so only two\n * dimensions apply:\n * - the device model must be supported;\n * - the running app must be known to Contacts and meet its minimum version.\n *\n * The app `name` and `version` are supplied by the caller so they can come from\n * a fresh `WaitForAppAndVersion` result rather than the (potentially stale)\n * device session state; the model comes from the session state, which does\n * not change under the caller's feet the way the running app can.\n *\n * @param internalApi - the DeviceAction's internal API for the current session.\n * @param app - the running app's fresh name and version.\n */\nexport function isContactsAppVersionSupportedForSession(\n internalApi: InternalApi,\n app: RunningApp,\n): boolean {\n const deviceState = internalApi.getDeviceSessionState();\n const requirement = resolveContactsVersionRequirements(\n deviceState.deviceModelId,\n );\n if (!isContactsModelSupported(requirement)) return false;\n\n const minAppVersion = requirement.minAppVersion[app.name];\n if (minAppVersion === undefined) return false;\n\n return new ApplicationChecker(\n deviceState,\n { version: app.version },\n new ContactsApplicationResolver(),\n )\n .
|
|
5
|
-
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,6CAAAE,EAAA,oCAAAC,IAAA,eAAAC,EAAAJ,GAAA,IAAAK,EAGO,2CAEPC,EAKO,kDAEPC,EAA4C,yCAS5C,SAASC,EACPC,EACqC,CACrC,OAAOA,EAAY,SACrB,CAeO,SAASN,EACdO,EACS,CACT,MAAMC,EAAcD,EAAY,sBAAsB,EAChDD,KAAc,sCAClBE,EAAY,aACd,EACA,GAAI,CAACH,EAAyBC,CAAW,EAAG,MAAO,GAEnD,MAAMG,EACJ,oBAAqBD,EACjBA,EAAY,iBAAiB,GAC7B,OACN,OAAIC,IAAc,OAAkB,MAE7B,oBAAiBA,EAAWH,EAAY,YAAY,CAC7D,CAkBO,SAASP,EACdQ,EACAG,EACS,CACT,MAAMF,EAAcD,EAAY,sBAAsB,EAChDD,KAAc,sCAClBE,EAAY,aACd,EACA,GAAI,CAACH,EAAyBC,CAAW,EAAG,MAAO,GAEnD,MAAMK,EAAgBL,EAAY,cAAcI,EAAI,IAAI,EACxD,OAAIC,IAAkB,OAAkB,GAEjC,IAAI,qBACTH,EACA,CAAE,QAASE,EAAI,OAAQ,EACvB,IAAI,6BACN,EACG,
|
|
4
|
+
"sourcesContent": ["import {\n ApplicationChecker,\n type InternalApi,\n} from \"@ledgerhq/device-management-kit\";\n\nimport {\n type ContactsModelRequirement,\n type ContactsModelSupport,\n isVersionAtLeast,\n resolveContactsVersionRequirements,\n} from \"@api/model/ContactsVersionRequirements\";\n\nimport { ContactsApplicationResolver } from \"./ContactsApplicationResolver\";\n\n/** The running app's identity, read freshly from the device. */\nexport type RunningApp = {\n readonly name: string;\n readonly version: string;\n};\n\n/** Narrows a resolved requirement to the case where Contacts is supported at all on this device model. */\nfunction isContactsModelSupported(\n requirement: ContactsModelRequirement,\n): requirement is ContactsModelSupport {\n return requirement.supported;\n}\n\n/**\n * OS-only Contacts support check, for internal consumption by dashboard\n * `DeviceAction`s (e.g. Rename Contact, EDIT CONTACT NAME). Dashboard operations\n * are served by the device OS, not the embedded app, so only two dimensions\n * apply:\n * - the device model must be supported;\n * - the device OS must meet its minimum version.\n *\n * The running-app dimension is intentionally skipped: on the dashboard there is\n * no embedded app to gate on (the running \"app\" is BOLOS).\n *\n * @param internalApi - the DeviceAction's internal API for the current session.\n */\nexport function isContactsOsSupportedForSession(\n internalApi: InternalApi,\n): boolean {\n const deviceState = internalApi.getDeviceSessionState();\n const requirement = resolveContactsVersionRequirements(\n deviceState.deviceModelId,\n );\n if (!isContactsModelSupported(requirement)) return false;\n\n const osVersion =\n \"firmwareVersion\" in deviceState\n ? deviceState.firmwareVersion?.os\n : undefined;\n if (osVersion === undefined) return false;\n\n return isVersionAtLeast(osVersion, requirement.minOsVersion);\n}\n\n/**\n * Session-aware Contacts support check, for internal consumption by app-owned\n * Contacts `DeviceAction`s (registering / editing external addresses). These\n * operations are served by the embedded app, not the device OS, so only two\n * dimensions apply:\n * - the device model must be supported;\n * - the running app must be known to Contacts and meet its minimum version.\n *\n * The app `name` and `version` are supplied by the caller so they can come from\n * a fresh `WaitForAppAndVersion` result rather than the (potentially stale)\n * device session state; the model comes from the session state, which does\n * not change under the caller's feet the way the running app can.\n *\n * @param internalApi - the DeviceAction's internal API for the current session.\n * @param app - the running app's fresh name and version.\n */\nexport function isContactsAppVersionSupportedForSession(\n internalApi: InternalApi,\n app: RunningApp,\n): boolean {\n const deviceState = internalApi.getDeviceSessionState();\n const requirement = resolveContactsVersionRequirements(\n deviceState.deviceModelId,\n );\n if (!isContactsModelSupported(requirement)) return false;\n\n const minAppVersion = requirement.minAppVersion[app.name];\n if (minAppVersion === undefined) return false;\n\n return new ApplicationChecker(\n deviceState,\n { version: app.version },\n new ContactsApplicationResolver(),\n )\n .withMinVersionInclusiveAcceptingPrerelease(minAppVersion)\n .check();\n}\n"],
|
|
5
|
+
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,6CAAAE,EAAA,oCAAAC,IAAA,eAAAC,EAAAJ,GAAA,IAAAK,EAGO,2CAEPC,EAKO,kDAEPC,EAA4C,yCAS5C,SAASC,EACPC,EACqC,CACrC,OAAOA,EAAY,SACrB,CAeO,SAASN,EACdO,EACS,CACT,MAAMC,EAAcD,EAAY,sBAAsB,EAChDD,KAAc,sCAClBE,EAAY,aACd,EACA,GAAI,CAACH,EAAyBC,CAAW,EAAG,MAAO,GAEnD,MAAMG,EACJ,oBAAqBD,EACjBA,EAAY,iBAAiB,GAC7B,OACN,OAAIC,IAAc,OAAkB,MAE7B,oBAAiBA,EAAWH,EAAY,YAAY,CAC7D,CAkBO,SAASP,EACdQ,EACAG,EACS,CACT,MAAMF,EAAcD,EAAY,sBAAsB,EAChDD,KAAc,sCAClBE,EAAY,aACd,EACA,GAAI,CAACH,EAAyBC,CAAW,EAAG,MAAO,GAEnD,MAAMK,EAAgBL,EAAY,cAAcI,EAAI,IAAI,EACxD,OAAIC,IAAkB,OAAkB,GAEjC,IAAI,qBACTH,EACA,CAAE,QAASE,EAAI,OAAQ,EACvB,IAAI,6BACN,EACG,2CAA2CC,CAAa,EACxD,MAAM,CACX",
|
|
6
6
|
"names": ["contactsVersionGuards_exports", "__export", "isContactsAppVersionSupportedForSession", "isContactsOsSupportedForSession", "__toCommonJS", "import_device_management_kit", "import_ContactsVersionRequirements", "import_ContactsApplicationResolver", "isContactsModelSupported", "requirement", "internalApi", "deviceState", "osVersion", "app", "minAppVersion"]
|
|
7
7
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var n=require("@ledgerhq/device-management-kit"),i=require("../../api/model/ContactsVersionRequirements"),t=require("./contactsVersionGuards");const c=(()=>{const e=(0,i.resolveContactsVersionRequirements)(n.DeviceModelId.FLEX);if(!e.supported)throw new Error("Flex must be supported");return e})(),u=c.minOsVersion,d=(()=>{const e=c.minAppVersion[i.ETHEREUM_APP_NAME];if(e===void 0)throw new Error("Ethereum min version required");return e})(),a="0.0.1";function o({osVersion:e=u,modelId:p=n.DeviceModelId.FLEX}={}){return{sessionStateType:n.DeviceSessionStateType.ReadyWithoutSecureChannel,deviceStatus:n.DeviceStatus.CONNECTED,installedApps:[],currentApp:{name:i.ETHEREUM_APP_NAME,version:"0.0.0"},deviceModelId:p,firmwareVersion:{mcu:"1.0.0",bootloader:"1.0.0",os:e},isSecureConnectionAllowed:!1}}function s(e){return{getDeviceSessionState:()=>e}}function r(e=i.ETHEREUM_APP_NAME,p=d){return{name:e,version:p}}describe("isContactsAppVersionSupportedForSession",()=>{it("returns true when the model and app version meet the minimums",()=>{const e=s(o());expect((0,t.isContactsAppVersionSupportedForSession)(e,r())).toBe(!0)}),it("returns true regardless of OS version, since app-owned operations are not gated by it",()=>{const e=s(o({osVersion:a}));expect((0,t.isContactsAppVersionSupportedForSession)(e,r())).toBe(!0)}),it("returns false on an unsupported device model",()=>{const e=s(o({modelId:n.DeviceModelId.NANO_S}));expect((0,t.isContactsAppVersionSupportedForSession)(e,r())).toBe(!1)}),it("returns false when the app version is below the minimum",()=>{const e=s(o());expect((0,t.isContactsAppVersionSupportedForSession)(e,r(i.ETHEREUM_APP_NAME,a))).toBe(!1)}),it("returns false when the running app is unknown to Contacts",()=>{const e=s(o());expect((0,t.isContactsAppVersionSupportedForSession)(e,r("Bitcoin"))).toBe(!1)}),it("returns true for a prerelease build of the minimum app version",()=>{const e=s(o());expect((0,t.isContactsAppVersionSupportedForSession)(e,r(i.ETHEREUM_APP_NAME,`${d}-rc2`))).toBe(!0)}),it("returns false when the device session has no running app",()=>{const e=s({sessionStateType:n.DeviceSessionStateType.Connected,deviceStatus:n.DeviceStatus.CONNECTED,deviceModelId:n.DeviceModelId.FLEX});expect((0,t.isContactsAppVersionSupportedForSession)(e,r())).toBe(!1)})});describe("isContactsOsSupportedForSession",()=>{it("returns true when the model and OS version meet the minimums",()=>{const e=s(o());expect((0,t.isContactsOsSupportedForSession)(e)).toBe(!0)}),it("returns false on an unsupported device model",()=>{const e=s(o({modelId:n.DeviceModelId.NANO_S}));expect((0,t.isContactsOsSupportedForSession)(e)).toBe(!1)}),it("returns false when the OS version is below the minimum",()=>{const e=s(o({osVersion:a}));expect((0,t.isContactsOsSupportedForSession)(e)).toBe(!1)}),it("returns true for a release candidate of the minimum OS version",()=>{const e=s(o({osVersion:`${u}-rc2`}));expect((0,t.isContactsOsSupportedForSession)(e)).toBe(!0)}),it("returns false for a release candidate below the minimum OS version",()=>{const e=s(o({osVersion:`${a}-rc2`}));expect((0,t.isContactsOsSupportedForSession)(e)).toBe(!1)}),it("returns false when the device session has no firmware version",()=>{const e=s({sessionStateType:n.DeviceSessionStateType.Connected,deviceStatus:n.DeviceStatus.CONNECTED,deviceModelId:n.DeviceModelId.FLEX});expect((0,t.isContactsOsSupportedForSession)(e)).toBe(!1)})});
|
|
2
2
|
//# sourceMappingURL=contactsVersionGuards.test.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/internal/app-binder/contactsVersionGuards.test.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n DeviceModelId,\n DeviceSessionStateType,\n DeviceStatus,\n type InternalApi,\n} from \"@ledgerhq/device-management-kit\";\n\nimport {\n ETHEREUM_APP_NAME,\n resolveContactsVersionRequirements,\n} from \"@api/model/ContactsVersionRequirements\";\n\nimport {\n isContactsAppVersionSupportedForSession,\n isContactsOsSupportedForSession,\n type RunningApp,\n} from \"./contactsVersionGuards\";\n\nconst flexSupport = (() => {\n const requirement = resolveContactsVersionRequirements(DeviceModelId.FLEX);\n if (!requirement.supported) throw new Error(\"Flex must be supported\");\n return requirement;\n})();\n\nconst MIN_OS_VERSION = flexSupport.minOsVersion;\nconst MIN_APP_VERSION = (() => {\n const version = flexSupport.minAppVersion[ETHEREUM_APP_NAME];\n if (version === undefined) throw new Error(\"Ethereum min version required\");\n return version;\n})();\nconst BELOW_ANY_VERSION = \"0.0.1\";\n\ntype ReadyStateOptions = {\n osVersion?: string;\n modelId?: DeviceModelId;\n};\n\nfunction createReadyState({\n osVersion = MIN_OS_VERSION,\n modelId = DeviceModelId.FLEX,\n}: ReadyStateOptions = {}) {\n return {\n sessionStateType: DeviceSessionStateType.ReadyWithoutSecureChannel,\n deviceStatus: DeviceStatus.CONNECTED,\n installedApps: [],\n currentApp: { name: ETHEREUM_APP_NAME, version: \"0.0.0\" },\n deviceModelId: modelId,\n firmwareVersion: { mcu: \"1.0.0\", bootloader: \"1.0.0\", os: osVersion },\n isSecureConnectionAllowed: false,\n };\n}\n\nfunction createInternalApi(deviceState: object): InternalApi {\n return {\n getDeviceSessionState: () => deviceState,\n } as unknown as InternalApi;\n}\n\n// The running app identity now comes from the caller (a fresh\n// WaitForAppAndVersion result), not the device session state.\nfunction runningApp(\n name = ETHEREUM_APP_NAME,\n version = MIN_APP_VERSION,\n): RunningApp {\n return { name, version };\n}\n\ndescribe(\"isContactsAppVersionSupportedForSession\", () => {\n it(\"returns true when the model and app version meet the minimums\", () => {\n const api = createInternalApi(createReadyState());\n expect(isContactsAppVersionSupportedForSession(api, runningApp())).toBe(\n true,\n );\n });\n\n it(\"returns true regardless of OS version, since app-owned operations are not gated by it\", () => {\n const api = createInternalApi(\n createReadyState({ osVersion: BELOW_ANY_VERSION }),\n );\n expect(isContactsAppVersionSupportedForSession(api, runningApp())).toBe(\n true,\n );\n });\n\n it(\"returns false on an unsupported device model\", () => {\n const api = createInternalApi(\n createReadyState({ modelId: DeviceModelId.
|
|
5
|
-
"mappings": "aAAA,IAAAA,EAKO,2CAEPC,EAGO,kDAEPC,EAIO,mCAEP,MAAMC,GAAe,IAAM,CACzB,MAAMC,KAAc,sCAAmC,gBAAc,IAAI,EACzE,GAAI,CAACA,EAAY,UAAW,MAAM,IAAI,MAAM,wBAAwB,EACpE,OAAOA,CACT,GAAG,EAEGC,EAAiBF,EAAY,aAC7BG,GAAmB,IAAM,CAC7B,MAAMC,EAAUJ,EAAY,cAAc,mBAAiB,EAC3D,GAAII,IAAY,OAAW,MAAM,IAAI,MAAM,+BAA+B,EAC1E,OAAOA,CACT,GAAG,EACGC,EAAoB,QAO1B,SAASC,EAAiB,CACxB,UAAAC,EAAYL,EACZ,QAAAM,EAAU,gBAAc,IAC1B,EAAuB,CAAC,EAAG,CACzB,MAAO,CACL,iBAAkB,yBAAuB,0BACzC,aAAc,eAAa,UAC3B,cAAe,CAAC,EAChB,WAAY,CAAE,KAAM,oBAAmB,QAAS,OAAQ,EACxD,cAAeA,EACf,gBAAiB,CAAE,IAAK,QAAS,WAAY,QAAS,GAAID,CAAU,EACpE,0BAA2B,EAC7B,CACF,CAEA,SAASE,EAAkBC,EAAkC,CAC3D,MAAO,CACL,sBAAuB,IAAMA,CAC/B,CACF,CAIA,SAASC,EACPC,EAAO,oBACPR,EAAUD,EACE,CACZ,MAAO,CAAE,KAAAS,EAAM,QAAAR,CAAQ,CACzB,CAEA,SAAS,0CAA2C,IAAM,CACxD,GAAG,gEAAiE,IAAM,CACxE,MAAMS,EAAMJ,EAAkBH,EAAiB,CAAC,EAChD,UAAO,2CAAwCO,EAAKF,EAAW,CAAC,CAAC,EAAE,KACjE,EACF,CACF,CAAC,EAED,GAAG,wFAAyF,IAAM,CAChG,MAAME,EAAMJ,EACVH,EAAiB,CAAE,UAAWD,CAAkB,CAAC,CACnD,EACA,UAAO,2CAAwCQ,EAAKF,EAAW,CAAC,CAAC,EAAE,KACjE,EACF,CACF,CAAC,EAED,GAAG,+CAAgD,IAAM,CACvD,MAAME,EAAMJ,EACVH,EAAiB,CAAE,QAAS,gBAAc,MAAO,CAAC,CACpD,EACA,UAAO,2CAAwCO,EAAKF,EAAW,CAAC,CAAC,EAAE,KACjE,EACF,CACF,CAAC,EAED,GAAG,0DAA2D,IAAM,CAClE,MAAME,EAAMJ,EAAkBH,EAAiB,CAAC,EAChD,UACE,2CACEO,EACAF,EAAW,oBAAmBN,CAAiB,CACjD,CACF,EAAE,KAAK,EAAK,CACd,CAAC,EAED,GAAG,4DAA6D,IAAM,CACpE,MAAMQ,EAAMJ,EAAkBH,EAAiB,CAAC,EAChD,UACE,2CAAwCO,EAAKF,EAAW,SAAS,CAAC,CACpE,EAAE,KAAK,EAAK,CACd,CAAC,EAED,GAAG,2DAA4D,IAAM,CACnE,
|
|
4
|
+
"sourcesContent": ["import {\n DeviceModelId,\n DeviceSessionStateType,\n DeviceStatus,\n type InternalApi,\n} from \"@ledgerhq/device-management-kit\";\n\nimport {\n ETHEREUM_APP_NAME,\n resolveContactsVersionRequirements,\n} from \"@api/model/ContactsVersionRequirements\";\n\nimport {\n isContactsAppVersionSupportedForSession,\n isContactsOsSupportedForSession,\n type RunningApp,\n} from \"./contactsVersionGuards\";\n\nconst flexSupport = (() => {\n const requirement = resolveContactsVersionRequirements(DeviceModelId.FLEX);\n if (!requirement.supported) throw new Error(\"Flex must be supported\");\n return requirement;\n})();\n\nconst MIN_OS_VERSION = flexSupport.minOsVersion;\nconst MIN_APP_VERSION = (() => {\n const version = flexSupport.minAppVersion[ETHEREUM_APP_NAME];\n if (version === undefined) throw new Error(\"Ethereum min version required\");\n return version;\n})();\nconst BELOW_ANY_VERSION = \"0.0.1\";\n\ntype ReadyStateOptions = {\n osVersion?: string;\n modelId?: DeviceModelId;\n};\n\nfunction createReadyState({\n osVersion = MIN_OS_VERSION,\n modelId = DeviceModelId.FLEX,\n}: ReadyStateOptions = {}) {\n return {\n sessionStateType: DeviceSessionStateType.ReadyWithoutSecureChannel,\n deviceStatus: DeviceStatus.CONNECTED,\n installedApps: [],\n currentApp: { name: ETHEREUM_APP_NAME, version: \"0.0.0\" },\n deviceModelId: modelId,\n firmwareVersion: { mcu: \"1.0.0\", bootloader: \"1.0.0\", os: osVersion },\n isSecureConnectionAllowed: false,\n };\n}\n\nfunction createInternalApi(deviceState: object): InternalApi {\n return {\n getDeviceSessionState: () => deviceState,\n } as unknown as InternalApi;\n}\n\n// The running app identity now comes from the caller (a fresh\n// WaitForAppAndVersion result), not the device session state.\nfunction runningApp(\n name = ETHEREUM_APP_NAME,\n version = MIN_APP_VERSION,\n): RunningApp {\n return { name, version };\n}\n\ndescribe(\"isContactsAppVersionSupportedForSession\", () => {\n it(\"returns true when the model and app version meet the minimums\", () => {\n const api = createInternalApi(createReadyState());\n expect(isContactsAppVersionSupportedForSession(api, runningApp())).toBe(\n true,\n );\n });\n\n it(\"returns true regardless of OS version, since app-owned operations are not gated by it\", () => {\n const api = createInternalApi(\n createReadyState({ osVersion: BELOW_ANY_VERSION }),\n );\n expect(isContactsAppVersionSupportedForSession(api, runningApp())).toBe(\n true,\n );\n });\n\n it(\"returns false on an unsupported device model\", () => {\n const api = createInternalApi(\n createReadyState({ modelId: DeviceModelId.NANO_S }),\n );\n expect(isContactsAppVersionSupportedForSession(api, runningApp())).toBe(\n false,\n );\n });\n\n it(\"returns false when the app version is below the minimum\", () => {\n const api = createInternalApi(createReadyState());\n expect(\n isContactsAppVersionSupportedForSession(\n api,\n runningApp(ETHEREUM_APP_NAME, BELOW_ANY_VERSION),\n ),\n ).toBe(false);\n });\n\n it(\"returns false when the running app is unknown to Contacts\", () => {\n const api = createInternalApi(createReadyState());\n expect(\n isContactsAppVersionSupportedForSession(api, runningApp(\"Bitcoin\")),\n ).toBe(false);\n });\n\n it(\"returns true for a prerelease build of the minimum app version\", () => {\n const api = createInternalApi(createReadyState());\n expect(\n isContactsAppVersionSupportedForSession(\n api,\n runningApp(ETHEREUM_APP_NAME, `${MIN_APP_VERSION}-rc2`),\n ),\n ).toBe(true);\n });\n\n it(\"returns false when the device session has no running app\", () => {\n const api = createInternalApi({\n sessionStateType: DeviceSessionStateType.Connected,\n deviceStatus: DeviceStatus.CONNECTED,\n deviceModelId: DeviceModelId.FLEX,\n });\n expect(isContactsAppVersionSupportedForSession(api, runningApp())).toBe(\n false,\n );\n });\n});\n\ndescribe(\"isContactsOsSupportedForSession\", () => {\n it(\"returns true when the model and OS version meet the minimums\", () => {\n const api = createInternalApi(createReadyState());\n expect(isContactsOsSupportedForSession(api)).toBe(true);\n });\n\n it(\"returns false on an unsupported device model\", () => {\n const api = createInternalApi(\n createReadyState({ modelId: DeviceModelId.NANO_S }),\n );\n expect(isContactsOsSupportedForSession(api)).toBe(false);\n });\n\n it(\"returns false when the OS version is below the minimum\", () => {\n const api = createInternalApi(\n createReadyState({ osVersion: BELOW_ANY_VERSION }),\n );\n expect(isContactsOsSupportedForSession(api)).toBe(false);\n });\n\n it(\"returns true for a release candidate of the minimum OS version\", () => {\n const api = createInternalApi(\n createReadyState({ osVersion: `${MIN_OS_VERSION}-rc2` }),\n );\n expect(isContactsOsSupportedForSession(api)).toBe(true);\n });\n\n it(\"returns false for a release candidate below the minimum OS version\", () => {\n const api = createInternalApi(\n createReadyState({ osVersion: `${BELOW_ANY_VERSION}-rc2` }),\n );\n expect(isContactsOsSupportedForSession(api)).toBe(false);\n });\n\n it(\"returns false when the device session has no firmware version\", () => {\n const api = createInternalApi({\n sessionStateType: DeviceSessionStateType.Connected,\n deviceStatus: DeviceStatus.CONNECTED,\n deviceModelId: DeviceModelId.FLEX,\n });\n expect(isContactsOsSupportedForSession(api)).toBe(false);\n });\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["import_device_management_kit", "import_ContactsVersionRequirements", "import_contactsVersionGuards", "flexSupport", "requirement", "MIN_OS_VERSION", "MIN_APP_VERSION", "version", "BELOW_ANY_VERSION", "createReadyState", "osVersion", "modelId", "createInternalApi", "deviceState", "runningApp", "name", "api"]
|
|
7
7
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var C=Object.defineProperty;var E=Object.getOwnPropertyDescriptor;var T=Object.getOwnPropertyNames;var e=Object.prototype.hasOwnProperty;var n=(_,o)=>{for(var x in o)C(_,x,{get:o[x],enumerable:!0})},r=(_,o,x,A)=>{if(o&&typeof o=="object"||typeof o=="function")for(let t of T(o))!e.call(_,t)&&t!==x&&C(_,t,{get:()=>o[t],enumerable:!(A=E(o,t))||A.enumerable});return _};var c=_=>r(C({},"__esModule",{value:!0}),_);var H={};n(H,{BLOCKCHAIN_FAMILY_BY_NAME:()=>a,CONTACTS_APDU_CLA:()=>s,CONTACTS_APDU_INS:()=>N,GROUP_HANDLE_BYTES:()=>B,HMAC_NAME_BYTES:()=>Y,HMAC_PROOF_BYTES:()=>P,HMAC_REST_BYTES:()=>U,RENAME_CONTACT_APDU_CLA:()=>M,RENAME_CONTACT_APDU_INS:()=>O,RENAME_CONTACT_P1:()=>R,SUB_CMD_EDIT_IDENTIFIER:()=>D,SUB_CMD_EDIT_SCOPE:()=>I,SUB_CMD_PROVIDE_CONTACT:()=>S,SUB_CMD_REGISTER_IDENTITY:()=>p});module.exports=c(H);const s=176,N=16,p=1,S=32,D=3,I=4,M=224,O=46,R=0,B=64,P=32,U=32,Y=32,a={bitcoin:0,ethereum:1,solana:2,polkadot:3,cosmos:4,cardano:5};0&&(module.exports={BLOCKCHAIN_FAMILY_BY_NAME,CONTACTS_APDU_CLA,CONTACTS_APDU_INS,GROUP_HANDLE_BYTES,HMAC_NAME_BYTES,HMAC_PROOF_BYTES,HMAC_REST_BYTES,RENAME_CONTACT_APDU_CLA,RENAME_CONTACT_APDU_INS,RENAME_CONTACT_P1,SUB_CMD_EDIT_IDENTIFIER,SUB_CMD_EDIT_SCOPE,SUB_CMD_PROVIDE_CONTACT,SUB_CMD_REGISTER_IDENTITY});
|
|
2
2
|
//# sourceMappingURL=contactsConstants.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/internal/app-binder/model/contactsConstants.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Protocol constants shared by the Contacts (Address Book) operations.\n *\n * Wire format and values follow the BOLOS SDK Address Book specification\n * (ledger-secure-sdk/app_features/address_book). Op-specific sub-command bytes\n * and response field sizes live here; TLV tags and struct-type bytes live in\n * {@link ./../services/contactsTlvSerializer}.\n */\n\n/** APDU class/instruction shared by every Address Book sub-command. */\nexport const CONTACTS_APDU_CLA = 0xb0;\nexport const CONTACTS_APDU_INS = 0x10;\n\n/** P1 sub-command selector for REGISTER IDENTITY (register external address). */\nexport const SUB_CMD_REGISTER_IDENTITY = 0x01;\n\n/**\n * P1 sub-command selector for EDIT IDENTIFIER \u2014 an address-book app sub-command\n * (CLA 0xB0 / INS 0x10) served by the coin app, never OS-dispatched.\n */\nexport const SUB_CMD_EDIT_IDENTIFIER = 0x03;\n\n/**\n * P1 sub-command selector for EDIT SCOPE \u2014 an address-book app sub-command\n * (CLA 0xB0 / INS 0x10) served by the coin app, never OS-dispatched. Changes an\n * entry's scope while keeping its contact name and identifier.\n */\nexport const SUB_CMD_EDIT_SCOPE = 0x04;\n\n/**\n * EDIT CONTACT NAME (rename) is a blockchain-agnostic OS/dashboard command with\n * its own CLA/INS \u2014 NOT a sub-command of the address-book app APDU (0xB0/0x10).\n * The OS serves it directly, so it must run on the dashboard. P1 is a fixed\n * 0x00 (there is no sub-command selector for this command); P2 still carries the\n * chunk-continuation flag handled by {@link ./../services/sendFramedContactsPayload}.\n */\nexport const RENAME_CONTACT_APDU_CLA = 0xe0;\nexport const RENAME_CONTACT_APDU_INS = 0x2e;\nexport const RENAME_CONTACT_P1 = 0x00;\n\n/** Sizes (bytes) of the fields in the REGISTER IDENTITY final-chunk response. */\nexport const GROUP_HANDLE_BYTES = 64;\nexport const HMAC_PROOF_BYTES = 32;\nexport const HMAC_REST_BYTES = 32;\n\n/**\n * Size (bytes) of the rotated `hmac_name` returned on the EDIT CONTACT NAME\n * final-chunk response \u2014 the replacement group-level name proof.\n */\nexport const HMAC_NAME_BYTES = 32;\n\n/**\n * Blockchain-family byte encoded in the `BLOCKCHAIN_FAMILY` TLV, keyed by a\n * lowercase family name. v1 ships Ethereum only; the remaining families are\n * listed for completeness and will be enabled as their chains are supported.\n */\nexport const BLOCKCHAIN_FAMILY_BY_NAME: Readonly<Record<string, number>> = {\n bitcoin: 0x00,\n ethereum: 0x01,\n solana: 0x02,\n polkadot: 0x03,\n cosmos: 0x04,\n cardano: 0x05,\n};\n"],
|
|
5
|
-
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,+BAAAE,EAAA,sBAAAC,EAAA,sBAAAC,EAAA,uBAAAC,EAAA,oBAAAC,EAAA,qBAAAC,EAAA,oBAAAC,EAAA,4BAAAC,EAAA,4BAAAC,EAAA,sBAAAC,EAAA,4BAAAC,EAAA,uBAAAC,EAAA,8BAAAC,IAAA,eAAAC,
|
|
6
|
-
"names": ["contactsConstants_exports", "__export", "BLOCKCHAIN_FAMILY_BY_NAME", "CONTACTS_APDU_CLA", "CONTACTS_APDU_INS", "GROUP_HANDLE_BYTES", "HMAC_NAME_BYTES", "HMAC_PROOF_BYTES", "HMAC_REST_BYTES", "RENAME_CONTACT_APDU_CLA", "RENAME_CONTACT_APDU_INS", "RENAME_CONTACT_P1", "SUB_CMD_EDIT_IDENTIFIER", "SUB_CMD_EDIT_SCOPE", "SUB_CMD_REGISTER_IDENTITY", "__toCommonJS"]
|
|
4
|
+
"sourcesContent": ["/**\n * Protocol constants shared by the Contacts (Address Book) operations.\n *\n * Wire format and values follow the BOLOS SDK Address Book specification\n * (ledger-secure-sdk/app_features/address_book). Op-specific sub-command bytes\n * and response field sizes live here; TLV tags and struct-type bytes live in\n * {@link ./../services/contactsTlvSerializer}.\n */\n\n/** APDU class/instruction shared by every Address Book sub-command. */\nexport const CONTACTS_APDU_CLA = 0xb0;\nexport const CONTACTS_APDU_INS = 0x10;\n\n/** P1 sub-command selector for REGISTER IDENTITY (register external address). */\nexport const SUB_CMD_REGISTER_IDENTITY = 0x01;\n\n/** P1 sub-command selector for PROVIDE CONTACT (pre-signing decoration). */\nexport const SUB_CMD_PROVIDE_CONTACT = 0x20;\n\n/**\n * P1 sub-command selector for EDIT IDENTIFIER \u2014 an address-book app sub-command\n * (CLA 0xB0 / INS 0x10) served by the coin app, never OS-dispatched.\n */\nexport const SUB_CMD_EDIT_IDENTIFIER = 0x03;\n\n/**\n * P1 sub-command selector for EDIT SCOPE \u2014 an address-book app sub-command\n * (CLA 0xB0 / INS 0x10) served by the coin app, never OS-dispatched. Changes an\n * entry's scope while keeping its contact name and identifier.\n */\nexport const SUB_CMD_EDIT_SCOPE = 0x04;\n\n/**\n * EDIT CONTACT NAME (rename) is a blockchain-agnostic OS/dashboard command with\n * its own CLA/INS \u2014 NOT a sub-command of the address-book app APDU (0xB0/0x10).\n * The OS serves it directly, so it must run on the dashboard. P1 is a fixed\n * 0x00 (there is no sub-command selector for this command); P2 still carries the\n * chunk-continuation flag handled by {@link ./../services/sendFramedContactsPayload}.\n */\nexport const RENAME_CONTACT_APDU_CLA = 0xe0;\nexport const RENAME_CONTACT_APDU_INS = 0x2e;\nexport const RENAME_CONTACT_P1 = 0x00;\n\n/** Sizes (bytes) of the fields in the REGISTER IDENTITY final-chunk response. */\nexport const GROUP_HANDLE_BYTES = 64;\nexport const HMAC_PROOF_BYTES = 32;\nexport const HMAC_REST_BYTES = 32;\n\n/**\n * Size (bytes) of the rotated `hmac_name` returned on the EDIT CONTACT NAME\n * final-chunk response \u2014 the replacement group-level name proof.\n */\nexport const HMAC_NAME_BYTES = 32;\n\n/**\n * Blockchain-family byte encoded in the `BLOCKCHAIN_FAMILY` TLV, keyed by a\n * lowercase family name. v1 ships Ethereum only; the remaining families are\n * listed for completeness and will be enabled as their chains are supported.\n */\nexport const BLOCKCHAIN_FAMILY_BY_NAME: Readonly<Record<string, number>> = {\n bitcoin: 0x00,\n ethereum: 0x01,\n solana: 0x02,\n polkadot: 0x03,\n cosmos: 0x04,\n cardano: 0x05,\n};\n"],
|
|
5
|
+
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,+BAAAE,EAAA,sBAAAC,EAAA,sBAAAC,EAAA,uBAAAC,EAAA,oBAAAC,EAAA,qBAAAC,EAAA,oBAAAC,EAAA,4BAAAC,EAAA,4BAAAC,EAAA,sBAAAC,EAAA,4BAAAC,EAAA,uBAAAC,EAAA,4BAAAC,EAAA,8BAAAC,IAAA,eAAAC,EAAAhB,GAUO,MAAMG,EAAoB,IACpBC,EAAoB,GAGpBW,EAA4B,EAG5BD,EAA0B,GAM1BF,EAA0B,EAO1BC,EAAqB,EASrBJ,EAA0B,IAC1BC,EAA0B,GAC1BC,EAAoB,EAGpBN,EAAqB,GACrBE,EAAmB,GACnBC,EAAkB,GAMlBF,EAAkB,GAOlBJ,EAA8D,CACzE,QAAS,EACT,SAAU,EACV,OAAQ,EACR,SAAU,EACV,OAAQ,EACR,QAAS,CACX",
|
|
6
|
+
"names": ["contactsConstants_exports", "__export", "BLOCKCHAIN_FAMILY_BY_NAME", "CONTACTS_APDU_CLA", "CONTACTS_APDU_INS", "GROUP_HANDLE_BYTES", "HMAC_NAME_BYTES", "HMAC_PROOF_BYTES", "HMAC_REST_BYTES", "RENAME_CONTACT_APDU_CLA", "RENAME_CONTACT_APDU_INS", "RENAME_CONTACT_P1", "SUB_CMD_EDIT_IDENTIFIER", "SUB_CMD_EDIT_SCOPE", "SUB_CMD_PROVIDE_CONTACT", "SUB_CMD_REGISTER_IDENTITY", "__toCommonJS"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var C=Object.defineProperty;var l=Object.getOwnPropertyDescriptor;var T=Object.getOwnPropertyNames;var s=Object.prototype.hasOwnProperty;var A=(e,a)=>{for(var r in a)C(e,r,{get:a[r],enumerable:!0})},P=(e,a,r,n)=>{if(a&&typeof a=="object"||typeof a=="function")for(let t of T(a))!s.call(e,t)&&t!==r&&C(e,t,{get:()=>a[t],enumerable:!(n=l(a,t))||n.enumerable});return e};var y=e=>P(C({},"__esModule",{value:!0}),e);var f={};A(f,{buildProvideContactPayload:()=>_,sendProvideContactPayload:()=>I});module.exports=y(f);var i=require("@ledgerhq/device-management-kit"),c=require("../../app-binder/command/ProvideContactCommand"),d=require("../../app-binder/model/contactsConstants"),o=require("../../app-binder/services/contactsTlvSerializer"),m=require("../../app-binder/services/sendFramedContactsPayload");function _(e){const a=d.BLOCKCHAIN_FAMILY_BY_NAME[e.blockchainFamily.toLowerCase()];if(a===void 0)throw new Error(`Unsupported blockchain family: ${e.blockchainFamily}`);const r=new i.ByteArrayBuilder;return(0,o.encodeTlvUInt8)(r,o.CONTACTS_TLV_TAG.STRUCT_TYPE,o.STRUCT_TYPE_PROVIDE_CONTACT),(0,o.encodeTlvUInt8)(r,o.CONTACTS_TLV_TAG.STRUCT_VERSION,o.STRUCT_VERSION_VALUE),(0,o.encodeTlvAscii)(r,o.CONTACTS_TLV_TAG.CONTACT_NAME,e.contactName),(0,o.encodeTlvAscii)(r,o.CONTACTS_TLV_TAG.SCOPE,e.scope),(0,o.encodeTlvBuffer)(r,o.CONTACTS_TLV_TAG.ACCOUNT_IDENTIFIER,e.identifier),(0,o.encodeTlvBuffer)(r,o.CONTACTS_TLV_TAG.GROUP_HANDLE,e.groupHandle),e.chainId!==void 0&&(0,o.encodeTlvChainId)(r,o.CONTACTS_TLV_TAG.CHAIN_ID,e.chainId),(0,o.encodeTlvUInt8)(r,o.CONTACTS_TLV_TAG.BLOCKCHAIN_FAMILY,a),(0,o.encodeTlvBuffer)(r,o.CONTACTS_TLV_TAG.HMAC_PROOF,e.hmacProof),(0,o.encodeTlvBuffer)(r,o.CONTACTS_TLV_TAG.HMAC_REST,e.hmacRest),r.build()}function I(e,{payload:a,logger:r}){return(0,m.sendFramedContactsPayload)(e,{payload:a,p1:d.SUB_CMD_PROVIDE_CONTACT,makeCommand:(n,t)=>new c.ProvideContactCommand({data:n,p2:t}),logger:r,commandTag:"provideContact"})}0&&(module.exports={buildProvideContactPayload,sendProvideContactPayload});
|
|
2
|
+
//# sourceMappingURL=provideContact.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/internal/app-binder/services/provideContact.ts"],
|
|
4
|
+
"sourcesContent": ["// TLV assembly and transport for op 0x20 (Provide Contact), dispatched via the\n// chunked-framing scheme shared with the other address-book ops.\n//\n// Reference: Address Book Final Specifications \u2014 Provide Contact. Tag order:\n// STRUCT_TYPE, STRUCT_VERSION, CONTACT_NAME, SCOPE, ACCOUNT_IDENTIFIER,\n// GROUP_HANDLE, CHAIN_ID (Ethereum only), BLOCKCHAIN_FAMILY, HMAC_PROOF,\n// HMAC_REST. No DERIVATION_PATH \u2014 that tag is Ledger-Account only.\n//\n// Device replies SW=0x9000 with empty data on success \u2014 no fields to extract.\nimport {\n ByteArrayBuilder,\n type CommandResult,\n type InternalApi,\n type LoggerPublisherService,\n} from \"@ledgerhq/device-management-kit\";\n\nimport { type ProvideContactInput } from \"@api/model/ProvideContact\";\nimport { ProvideContactCommand } from \"@internal/app-binder/command/ProvideContactCommand\";\nimport {\n BLOCKCHAIN_FAMILY_BY_NAME,\n SUB_CMD_PROVIDE_CONTACT,\n} from \"@internal/app-binder/model/contactsConstants\";\nimport { type ContactsErrorCodes } from \"@internal/app-binder/model/contactsErrors\";\nimport {\n CONTACTS_TLV_TAG,\n encodeTlvAscii,\n encodeTlvBuffer,\n encodeTlvChainId,\n encodeTlvUInt8,\n STRUCT_TYPE_PROVIDE_CONTACT,\n STRUCT_VERSION_VALUE,\n} from \"@internal/app-binder/services/contactsTlvSerializer\";\nimport { sendFramedContactsPayload } from \"@internal/app-binder/services/sendFramedContactsPayload\";\n\n/**\n * Encode a matched contact as the PROVIDE CONTACT TLV payload, without the\n * chunk framing. Pure, so callers can build it while matching a recipient\n * against the address book, with no device involved.\n */\nexport function buildProvideContactPayload(\n input: ProvideContactInput,\n): Uint8Array {\n const family =\n BLOCKCHAIN_FAMILY_BY_NAME[input.blockchainFamily.toLowerCase()];\n if (family === undefined) {\n throw new Error(`Unsupported blockchain family: ${input.blockchainFamily}`);\n }\n\n const builder = new ByteArrayBuilder();\n encodeTlvUInt8(\n builder,\n CONTACTS_TLV_TAG.STRUCT_TYPE,\n STRUCT_TYPE_PROVIDE_CONTACT,\n );\n encodeTlvUInt8(\n builder,\n CONTACTS_TLV_TAG.STRUCT_VERSION,\n STRUCT_VERSION_VALUE,\n );\n encodeTlvAscii(builder, CONTACTS_TLV_TAG.CONTACT_NAME, input.contactName);\n encodeTlvAscii(builder, CONTACTS_TLV_TAG.SCOPE, input.scope);\n encodeTlvBuffer(\n builder,\n CONTACTS_TLV_TAG.ACCOUNT_IDENTIFIER,\n input.identifier,\n );\n encodeTlvBuffer(builder, CONTACTS_TLV_TAG.GROUP_HANDLE, input.groupHandle);\n if (input.chainId !== undefined) {\n encodeTlvChainId(builder, CONTACTS_TLV_TAG.CHAIN_ID, input.chainId);\n }\n encodeTlvUInt8(builder, CONTACTS_TLV_TAG.BLOCKCHAIN_FAMILY, family);\n encodeTlvBuffer(builder, CONTACTS_TLV_TAG.HMAC_PROOF, input.hmacProof);\n encodeTlvBuffer(builder, CONTACTS_TLV_TAG.HMAC_REST, input.hmacRest);\n\n return builder.build();\n}\n\nexport type SendProvideContactPayloadArgs = {\n readonly payload: Uint8Array;\n readonly logger?: LoggerPublisherService;\n};\n\nexport function sendProvideContactPayload(\n api: InternalApi,\n { payload, logger }: SendProvideContactPayloadArgs,\n): Promise<CommandResult<void, ContactsErrorCodes>> {\n return sendFramedContactsPayload(api, {\n payload,\n p1: SUB_CMD_PROVIDE_CONTACT,\n makeCommand: (chunk, p2) => new ProvideContactCommand({ data: chunk, p2 }),\n logger,\n commandTag: \"provideContact\",\n }) as Promise<CommandResult<void, ContactsErrorCodes>>;\n}\n"],
|
|
5
|
+
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,gCAAAE,EAAA,8BAAAC,IAAA,eAAAC,EAAAJ,GASA,IAAAK,EAKO,2CAGPC,EAAsC,8DACtCC,EAGO,wDAEPC,EAQO,+DACPC,EAA0C,mEAOnC,SAASP,EACdQ,EACY,CACZ,MAAMC,EACJ,4BAA0BD,EAAM,iBAAiB,YAAY,CAAC,EAChE,GAAIC,IAAW,OACb,MAAM,IAAI,MAAM,kCAAkCD,EAAM,gBAAgB,EAAE,EAG5E,MAAME,EAAU,IAAI,mBACpB,2BACEA,EACA,mBAAiB,YACjB,6BACF,KACA,kBACEA,EACA,mBAAiB,eACjB,sBACF,KACA,kBAAeA,EAAS,mBAAiB,aAAcF,EAAM,WAAW,KACxE,kBAAeE,EAAS,mBAAiB,MAAOF,EAAM,KAAK,KAC3D,mBACEE,EACA,mBAAiB,mBACjBF,EAAM,UACR,KACA,mBAAgBE,EAAS,mBAAiB,aAAcF,EAAM,WAAW,EACrEA,EAAM,UAAY,WACpB,oBAAiBE,EAAS,mBAAiB,SAAUF,EAAM,OAAO,KAEpE,kBAAeE,EAAS,mBAAiB,kBAAmBD,CAAM,KAClE,mBAAgBC,EAAS,mBAAiB,WAAYF,EAAM,SAAS,KACrE,mBAAgBE,EAAS,mBAAiB,UAAWF,EAAM,QAAQ,EAE5DE,EAAQ,MAAM,CACvB,CAOO,SAAST,EACdU,EACA,CAAE,QAAAC,EAAS,OAAAC,CAAO,EACgC,CAClD,SAAO,6BAA0BF,EAAK,CACpC,QAAAC,EACA,GAAI,0BACJ,YAAa,CAACE,EAAOC,IAAO,IAAI,wBAAsB,CAAE,KAAMD,EAAO,GAAAC,CAAG,CAAC,EACzE,OAAAF,EACA,WAAY,gBACd,CAAC,CACH",
|
|
6
|
+
"names": ["provideContact_exports", "__export", "buildProvideContactPayload", "sendProvideContactPayload", "__toCommonJS", "import_device_management_kit", "import_ProvideContactCommand", "import_contactsConstants", "import_contactsTlvSerializer", "import_sendFramedContactsPayload", "input", "family", "builder", "api", "payload", "logger", "chunk", "p2"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var o=require("@ledgerhq/device-management-kit"),r=require("../../app-binder/command/ProvideContactCommand"),i=require("../../app-binder/device-action/__test-utils__/makeInternalApi"),d=require("../../app-binder/model/contactsErrors"),t=require("./provideContact");const m=new Uint8Array(20).fill(17),p=new Uint8Array(64).fill(34),f=new Uint8Array(32).fill(51),h=new Uint8Array(32).fill(68),n={contactName:"Alice",scope:"Ethereum",identifier:m,groupHandle:p,hmacProof:f,hmacRest:h,blockchainFamily:"ethereum",chainId:1n},C="010133",u="020101",y="81f005416c696365",T="81f108457468657265756d",x="81f214"+"11".repeat(20),s="81f640"+"22".repeat(64),E="230101",P="510101",_="2920"+"33".repeat(32),I="81f720"+"44".repeat(32);describe("buildProvideContactPayload",()=>{it("encodes every tag in the order the spec lists",()=>{const e=(0,t.buildProvideContactPayload)(n);expect((0,o.bufferToHexaString)(e,!1)).toBe(C+u+y+T+x+s+E+P+_+I)}),it("fits an Ethereum contact in a single 255-byte chunk",()=>{expect((0,t.buildProvideContactPayload)(n).length).toBeLessThanOrEqual(253)}),it("omits CHAIN_ID for a family that does not carry one",()=>{const e=(0,t.buildProvideContactPayload)({...n,blockchainFamily:"bitcoin",chainId:void 0});expect((0,o.bufferToHexaString)(e,!1)).toContain(s+"510100")}),it("encodes a multi-byte chain id big-endian with no leading zeroes",()=>{const e=(0,t.buildProvideContactPayload)({...n,chainId:8453n});expect((0,o.bufferToHexaString)(e,!1)).toContain("23022105")}),it("accepts the family name case-insensitively",()=>{expect((0,t.buildProvideContactPayload)({...n,blockchainFamily:"Ethereum"})).toStrictEqual((0,t.buildProvideContactPayload)(n))}),it("throws on an unknown blockchain family",()=>{expect(()=>(0,t.buildProvideContactPayload)({...n,blockchainFamily:"dogecoin"})).toThrow("Unsupported blockchain family: dogecoin")})});describe("sendProvideContactPayload",()=>{it("sends one chunk under P2=0x00, prefixed with the 2-byte BE total length",async()=>{const e=(0,i.makeDeviceActionInternalApiMock)();e.sendCommand.mockResolvedValue((0,o.CommandResultFactory)({data:void 0}));const a=(0,t.buildProvideContactPayload)(n),l=await(0,t.sendProvideContactPayload)(e,{payload:a});expect(e.sendCommand).toHaveBeenCalledTimes(1);const c=e.sendCommand.mock.calls[0][0];expect(c).toBeInstanceOf(r.ProvideContactCommand),expect(c.args.p2).toBe(0),expect(c.args.data.slice(0,2)).toStrictEqual(Uint8Array.from([a.length>>8&255,a.length&255])),expect(c.args.data.slice(2)).toStrictEqual(a),expect(l.status).toBe(o.CommandResultStatus.Success)}),it("propagates a device rejection instead of throwing",async()=>{const e=(0,i.makeDeviceActionInternalApiMock)();e.sendCommand.mockResolvedValue((0,o.CommandResultFactory)({error:new d.ContactsCommandError({errorCode:"6982",message:"wrong seed"})}));const a=await(0,t.sendProvideContactPayload)(e,{payload:(0,t.buildProvideContactPayload)(n)});expect(a.status).toBe(o.CommandResultStatus.Error)})});
|
|
2
|
+
//# sourceMappingURL=provideContact.test.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/internal/app-binder/services/provideContact.test.ts"],
|
|
4
|
+
"sourcesContent": ["import {\n bufferToHexaString,\n CommandResultFactory,\n CommandResultStatus,\n} from \"@ledgerhq/device-management-kit\";\n\nimport { type ProvideContactInput } from \"@api/model/ProvideContact\";\nimport { ProvideContactCommand } from \"@internal/app-binder/command/ProvideContactCommand\";\nimport { makeDeviceActionInternalApiMock } from \"@internal/app-binder/device-action/__test-utils__/makeInternalApi\";\nimport { ContactsCommandError } from \"@internal/app-binder/model/contactsErrors\";\n\nimport {\n buildProvideContactPayload,\n sendProvideContactPayload,\n} from \"./provideContact\";\n\nconst IDENTIFIER = new Uint8Array(20).fill(0x11);\nconst GROUP_HANDLE = new Uint8Array(64).fill(0x22);\nconst HMAC_PROOF = new Uint8Array(32).fill(0x33);\nconst HMAC_REST = new Uint8Array(32).fill(0x44);\n\nconst INPUT: ProvideContactInput = {\n contactName: \"Alice\",\n scope: \"Ethereum\",\n identifier: IDENTIFIER,\n groupHandle: GROUP_HANDLE,\n hmacProof: HMAC_PROOF,\n hmacRest: HMAC_REST,\n blockchainFamily: \"ethereum\",\n chainId: 1n,\n};\n\n// Tags >= 0x80 use the DER long form (0x81 then the tag), hence the \"81\" prefix\n// on CONTACT_NAME (0xf0), SCOPE (0xf1), ACCOUNT_IDENTIFIER (0xf2),\n// GROUP_HANDLE (0xf6) and HMAC_REST (0xf7).\nconst STRUCT_TYPE_TLV = \"010133\";\nconst STRUCT_VERSION_TLV = \"020101\";\nconst CONTACT_NAME_TLV = \"81f005\" + \"416c696365\"; // \"Alice\"\nconst SCOPE_TLV = \"81f108\" + \"457468657265756d\"; // \"Ethereum\"\nconst IDENTIFIER_TLV = \"81f214\" + \"11\".repeat(20);\nconst GROUP_HANDLE_TLV = \"81f640\" + \"22\".repeat(64);\nconst CHAIN_ID_TLV = \"230101\";\nconst FAMILY_TLV = \"510101\";\nconst HMAC_PROOF_TLV = \"2920\" + \"33\".repeat(32);\nconst HMAC_REST_TLV = \"81f720\" + \"44\".repeat(32);\n\ndescribe(\"buildProvideContactPayload\", () => {\n it(\"encodes every tag in the order the spec lists\", () => {\n const payload = buildProvideContactPayload(INPUT);\n\n expect(bufferToHexaString(payload, false)).toBe(\n STRUCT_TYPE_TLV +\n STRUCT_VERSION_TLV +\n CONTACT_NAME_TLV +\n SCOPE_TLV +\n IDENTIFIER_TLV +\n GROUP_HANDLE_TLV +\n CHAIN_ID_TLV +\n FAMILY_TLV +\n HMAC_PROOF_TLV +\n HMAC_REST_TLV,\n );\n });\n\n it(\"fits an Ethereum contact in a single 255-byte chunk\", () => {\n expect(buildProvideContactPayload(INPUT).length).toBeLessThanOrEqual(253);\n });\n\n it(\"omits CHAIN_ID for a family that does not carry one\", () => {\n const payload = buildProvideContactPayload({\n ...INPUT,\n blockchainFamily: \"bitcoin\",\n chainId: undefined,\n });\n\n expect(bufferToHexaString(payload, false)).toContain(\n GROUP_HANDLE_TLV + \"510100\",\n );\n });\n\n it(\"encodes a multi-byte chain id big-endian with no leading zeroes\", () => {\n const payload = buildProvideContactPayload({ ...INPUT, chainId: 8453n });\n\n expect(bufferToHexaString(payload, false)).toContain(\"2302\" + \"2105\");\n });\n\n it(\"accepts the family name case-insensitively\", () => {\n expect(\n buildProvideContactPayload({ ...INPUT, blockchainFamily: \"Ethereum\" }),\n ).toStrictEqual(buildProvideContactPayload(INPUT));\n });\n\n it(\"throws on an unknown blockchain family\", () => {\n expect(() =>\n buildProvideContactPayload({ ...INPUT, blockchainFamily: \"dogecoin\" }),\n ).toThrow(\"Unsupported blockchain family: dogecoin\");\n });\n});\n\ndescribe(\"sendProvideContactPayload\", () => {\n it(\"sends one chunk under P2=0x00, prefixed with the 2-byte BE total length\", async () => {\n const api = makeDeviceActionInternalApiMock();\n api.sendCommand.mockResolvedValue(\n CommandResultFactory({ data: undefined }),\n );\n const payload = buildProvideContactPayload(INPUT);\n\n const result = await sendProvideContactPayload(api, { payload });\n\n expect(api.sendCommand).toHaveBeenCalledTimes(1);\n const command = api.sendCommand.mock.calls[0]![0] as ProvideContactCommand;\n expect(command).toBeInstanceOf(ProvideContactCommand);\n expect(command.args.p2).toBe(0x00);\n expect(command.args.data.slice(0, 2)).toStrictEqual(\n Uint8Array.from([(payload.length >> 8) & 0xff, payload.length & 0xff]),\n );\n expect(command.args.data.slice(2)).toStrictEqual(payload);\n expect(result.status).toBe(CommandResultStatus.Success);\n });\n\n it(\"propagates a device rejection instead of throwing\", async () => {\n const api = makeDeviceActionInternalApiMock();\n api.sendCommand.mockResolvedValue(\n CommandResultFactory({\n error: new ContactsCommandError({\n errorCode: \"6982\",\n message: \"wrong seed\",\n }),\n }),\n );\n\n const result = await sendProvideContactPayload(api, {\n payload: buildProvideContactPayload(INPUT),\n });\n\n expect(result.status).toBe(CommandResultStatus.Error);\n });\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["import_device_management_kit", "import_ProvideContactCommand", "import_makeInternalApi", "import_contactsErrors", "import_provideContact", "IDENTIFIER", "GROUP_HANDLE", "HMAC_PROOF", "HMAC_REST", "INPUT", "STRUCT_TYPE_TLV", "STRUCT_VERSION_TLV", "CONTACT_NAME_TLV", "SCOPE_TLV", "IDENTIFIER_TLV", "GROUP_HANDLE_TLV", "CHAIN_ID_TLV", "FAMILY_TLV", "HMAC_PROOF_TLV", "HMAC_REST_TLV", "payload", "api", "result", "command"]
|
|
7
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/internal/app-binder/task/SendRegisterIdentityTask.ts"],
|
|
4
|
-
"sourcesContent": ["// Builds the TLV payload for op 1 (Register Identity / Register External\n// Address) and dispatches it via the chunked-framing scheme shared with the\n// other address-book ops (2-byte BE total length + <=255B chunks). The device\n// returns the 129-byte register response on the final chunk.\n//\n// Reference: Address Book Final Specifications \u2014 Register Identity. Tag order:\n// STRUCT_TYPE, STRUCT_VERSION, CONTACT_NAME, SCOPE, ACCOUNT_IDENTIFIER,\n// CHAIN_ID (Ethereum only), BLOCKCHAIN_FAMILY, then optional
|
|
5
|
-
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,8BAAAE,IAAA,eAAAC,EAAAH,
|
|
4
|
+
"sourcesContent": ["// Builds the TLV payload for op 1 (Register Identity / Register External\n// Address) and dispatches it via the chunked-framing scheme shared with the\n// other address-book ops (2-byte BE total length + <=255B chunks). The device\n// returns the 129-byte register response on the final chunk.\n//\n// Reference: Address Book Final Specifications \u2014 Register Identity. Tag order:\n// STRUCT_TYPE, STRUCT_VERSION, CONTACT_NAME, SCOPE, ACCOUNT_IDENTIFIER,\n// CHAIN_ID (Ethereum only), BLOCKCHAIN_FAMILY, then optional GROUP_HANDLE +\n// HMAC_PROOF when extending an existing group.\n//\n// No DERIVATION_PATH. The tag's status changed three times in the BOLOS SDK,\n// and the address-book TLV parser is compiled into the app (app_features/), so\n// the SDK revision the app was *built* against decides \u2014 not the app version,\n// which reads 1.23.0-dev either way:\n// - before 2026-08-07: tag 0x69 present and MANDATORY, omitting it -> 0x6a80\n// - 8e7e7a4f (2026-08-07): made optional, both forms accepted\n// - a0bb21f5 (2026-08-10): removed, sending it -> 0x6a80 (unknown tag)\n// Omitting it is therefore correct for any app built from 2026-08-07 onward,\n// and wrong for one built before. Both ends verified on hardware: a Flex\n// running a pre-08-07 build of app-ethereum a79f9f8f rejects the payload\n// without the tag in 9ms and no review screen; Speculos running a post-08-10\n// build of the same commit rejects it *with* the tag, the same way.\n// The OS derives the HMAC key from a fixed internal path.\nimport {\n ByteArrayBuilder,\n type CommandResult,\n DmkResultFactory,\n type InternalApi,\n InvalidStatusWordError,\n isSuccessCommandResult,\n type LoggerPublisherService,\n} from \"@ledgerhq/device-management-kit\";\n\nimport { type ExistingContactGroup } from \"@api/model/RegisterExternalAddress\";\nimport { RegisterIdentityCommand } from \"@internal/app-binder/command/RegisterIdentityCommand\";\nimport {\n BLOCKCHAIN_FAMILY_BY_NAME,\n SUB_CMD_REGISTER_IDENTITY,\n} from \"@internal/app-binder/model/contactsConstants\";\nimport { type ContactsErrorCodes } from \"@internal/app-binder/model/contactsErrors\";\nimport {\n CONTACTS_TLV_TAG,\n encodeTlvAscii,\n encodeTlvBuffer,\n encodeTlvChainId,\n encodeTlvUInt8,\n STRUCT_TYPE_REGISTER_IDENTITY,\n STRUCT_VERSION_VALUE,\n} from \"@internal/app-binder/services/contactsTlvSerializer\";\nimport { sendFramedContactsPayload } from \"@internal/app-binder/services/sendFramedContactsPayload\";\n\nexport type RegisterIdentityProofs = {\n readonly groupHandle: Uint8Array;\n readonly hmacProof: Uint8Array;\n readonly hmacRest: Uint8Array;\n};\n\nexport type SendRegisterIdentityTaskArgs = {\n readonly contactName: string;\n readonly scope: string;\n readonly identifier: Uint8Array;\n readonly blockchainFamily: string;\n readonly chainId?: bigint;\n readonly existingContactGroup?: ExistingContactGroup;\n readonly logger?: LoggerPublisherService;\n};\n\nexport class SendRegisterIdentityTask {\n constructor(\n private readonly api: InternalApi,\n private readonly args: SendRegisterIdentityTaskArgs,\n ) {}\n\n async run(): Promise<\n CommandResult<RegisterIdentityProofs, ContactsErrorCodes>\n > {\n const payload = this.buildPayload(this.args);\n\n const result = (await sendFramedContactsPayload(this.api, {\n payload,\n p1: SUB_CMD_REGISTER_IDENTITY,\n makeCommand: (chunk, p2) =>\n new RegisterIdentityCommand({ data: chunk, p2 }),\n logger: this.args.logger,\n commandTag: \"SendRegisterIdentityTask\",\n })) as CommandResult<\n {\n readonly groupHandle?: Uint8Array;\n readonly hmacProof?: Uint8Array;\n readonly hmacRest?: Uint8Array;\n },\n ContactsErrorCodes\n >;\n\n if (!isSuccessCommandResult(result)) {\n return result;\n }\n\n const { groupHandle, hmacProof, hmacRest } = result.data;\n if (!groupHandle || !hmacProof || !hmacRest) {\n return DmkResultFactory({\n error: new InvalidStatusWordError(\n \"RegisterIdentity final-chunk response was incomplete\",\n ),\n });\n }\n return DmkResultFactory({ data: { groupHandle, hmacProof, hmacRest } });\n }\n\n private buildPayload(args: SendRegisterIdentityTaskArgs): Uint8Array {\n const family =\n BLOCKCHAIN_FAMILY_BY_NAME[args.blockchainFamily.toLowerCase()];\n if (family === undefined) {\n throw new Error(\n `Unsupported blockchain family: ${args.blockchainFamily}`,\n );\n }\n\n const builder = new ByteArrayBuilder();\n encodeTlvUInt8(\n builder,\n CONTACTS_TLV_TAG.STRUCT_TYPE,\n STRUCT_TYPE_REGISTER_IDENTITY,\n );\n encodeTlvUInt8(\n builder,\n CONTACTS_TLV_TAG.STRUCT_VERSION,\n STRUCT_VERSION_VALUE,\n );\n encodeTlvAscii(builder, CONTACTS_TLV_TAG.CONTACT_NAME, args.contactName);\n encodeTlvAscii(builder, CONTACTS_TLV_TAG.SCOPE, args.scope);\n encodeTlvBuffer(\n builder,\n CONTACTS_TLV_TAG.ACCOUNT_IDENTIFIER,\n args.identifier,\n );\n if (args.chainId !== undefined) {\n encodeTlvChainId(builder, CONTACTS_TLV_TAG.CHAIN_ID, args.chainId);\n }\n encodeTlvUInt8(builder, CONTACTS_TLV_TAG.BLOCKCHAIN_FAMILY, family);\n\n if (args.existingContactGroup) {\n encodeTlvBuffer(\n builder,\n CONTACTS_TLV_TAG.GROUP_HANDLE,\n args.existingContactGroup.groupHandle,\n );\n encodeTlvBuffer(\n builder,\n CONTACTS_TLV_TAG.HMAC_PROOF,\n args.existingContactGroup.hmacProof,\n );\n }\n\n return builder.build();\n }\n}\n"],
|
|
5
|
+
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,8BAAAE,IAAA,eAAAC,EAAAH,GAuBA,IAAAI,EAQO,2CAGPC,EAAwC,gEACxCC,EAGO,wDAEPC,EAQO,+DACPC,EAA0C,mEAkBnC,MAAMN,CAAyB,CACpC,YACmBO,EACAC,EACjB,CAFiB,SAAAD,EACA,UAAAC,CAChB,CAEH,MAAM,KAEJ,CACA,MAAMC,EAAU,KAAK,aAAa,KAAK,IAAI,EAErCC,EAAU,QAAM,6BAA0B,KAAK,IAAK,CACxD,QAAAD,EACA,GAAI,4BACJ,YAAa,CAACE,EAAOC,IACnB,IAAI,0BAAwB,CAAE,KAAMD,EAAO,GAAAC,CAAG,CAAC,EACjD,OAAQ,KAAK,KAAK,OAClB,WAAY,0BACd,CAAC,EASD,GAAI,IAAC,0BAAuBF,CAAM,EAChC,OAAOA,EAGT,KAAM,CAAE,YAAAG,EAAa,UAAAC,EAAW,SAAAC,CAAS,EAAIL,EAAO,KACpD,MAAI,CAACG,GAAe,CAACC,GAAa,CAACC,KAC1B,oBAAiB,CACtB,MAAO,IAAI,yBACT,sDACF,CACF,CAAC,KAEI,oBAAiB,CAAE,KAAM,CAAE,YAAAF,EAAa,UAAAC,EAAW,SAAAC,CAAS,CAAE,CAAC,CACxE,CAEQ,aAAaP,EAAgD,CACnE,MAAMQ,EACJ,4BAA0BR,EAAK,iBAAiB,YAAY,CAAC,EAC/D,GAAIQ,IAAW,OACb,MAAM,IAAI,MACR,kCAAkCR,EAAK,gBAAgB,EACzD,EAGF,MAAMS,EAAU,IAAI,mBACpB,2BACEA,EACA,mBAAiB,YACjB,+BACF,KACA,kBACEA,EACA,mBAAiB,eACjB,sBACF,KACA,kBAAeA,EAAS,mBAAiB,aAAcT,EAAK,WAAW,KACvE,kBAAeS,EAAS,mBAAiB,MAAOT,EAAK,KAAK,KAC1D,mBACES,EACA,mBAAiB,mBACjBT,EAAK,UACP,EACIA,EAAK,UAAY,WACnB,oBAAiBS,EAAS,mBAAiB,SAAUT,EAAK,OAAO,KAEnE,kBAAeS,EAAS,mBAAiB,kBAAmBD,CAAM,EAE9DR,EAAK,0BACP,mBACES,EACA,mBAAiB,aACjBT,EAAK,qBAAqB,WAC5B,KACA,mBACES,EACA,mBAAiB,WACjBT,EAAK,qBAAqB,SAC5B,GAGKS,EAAQ,MAAM,CACvB,CACF",
|
|
6
6
|
"names": ["SendRegisterIdentityTask_exports", "__export", "SendRegisterIdentityTask", "__toCommonJS", "import_device_management_kit", "import_RegisterIdentityCommand", "import_contactsConstants", "import_contactsTlvSerializer", "import_sendFramedContactsPayload", "api", "args", "payload", "result", "chunk", "p2", "groupHandle", "hmacProof", "hmacRest", "family", "builder"]
|
|
7
7
|
}
|