@notabene/javascript-sdk 1.32.0 → 2.0.0-next.10

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/src/types.ts CHANGED
@@ -1,5 +1,50 @@
1
- import { DateAndPlaceOfBirth, NationalIdentification } from 'ivms/types';
1
+ import {
2
+ Beneficiary,
3
+ DateAndPlaceOfBirth,
4
+ IVMS101,
5
+ NationalIdentification,
6
+ Originator,
7
+ } from './ivms/types';
2
8
 
9
+ export type { IVMS101 };
10
+
11
+ export type CAIP2 = `${string}:${string}`;
12
+ export type CAIP10 = `${CAIP2}:${string}`;
13
+ export type CAIP19 = `${CAIP2}/${string}:${string}`;
14
+ export type CAIP220 = string;
15
+
16
+ export type DTI = string;
17
+ export type NotabeneAsset = string;
18
+
19
+ /**
20
+ * The asset of a transaction either a Notabene asset, a CAIP-19 asset, or a DTI.
21
+ *
22
+ * @public
23
+ */
24
+
25
+ export type TransactionAsset = NotabeneAsset | CAIP19 | DTI;
26
+
27
+ export type BlockchainAddress = string;
28
+ export type TravelAddress = `ta${string}`;
29
+ export type CryptoCredential = `${string}.${string}.mastercard`;
30
+
31
+ /**
32
+ * The destination of a transaction either a blockchain address, a CAIP-19 address, or a travel address.
33
+ * @public
34
+ */
35
+ export type Destination =
36
+ | BlockchainAddress
37
+ | CAIP10
38
+ | CryptoCredential
39
+ | TravelAddress;
40
+ export type Source = BlockchainAddress | CAIP10;
41
+ export type URI = string;
42
+ export type DID = `did:${string}:${string}`;
43
+
44
+ /**
45
+ * The theme of the Notabene SDK
46
+ * @public
47
+ */
3
48
  export type Theme = {
4
49
  primaryColor: string;
5
50
  secondaryColor: string;
@@ -7,21 +52,40 @@ export type Theme = {
7
52
  logo: string;
8
53
  };
9
54
 
10
- export enum WidgetType {
11
- WITHDRAWAL = 'WITHDRAWAL',
12
- POST_DEPOSIT = 'POST_DEPOSIT',
13
- }
55
+ // export type TransactionType = 'VASP_2_VASP' | 'SELF_HOSTED';
56
+
57
+ // export type TransactionTypeAllowed =
58
+ // | 'ALL'
59
+ // | 'VASP_2_VASP_ONLY'
60
+ // | 'FIRST_PARTY_ONLY';
14
61
 
15
- export type TransactionTypeAllowed =
16
- | 'ALL'
17
- | 'VASP_2_VASP_ONLY'
18
- | 'SELF_TRANSACTION_ONLY';
62
+ // export type NonCustodialDeclarationType =
63
+ // | 'SIGNATURE'
64
+ // | 'DECLARATION'
65
+ // | 'SIGNATURE_AND_DECLARATION';
19
66
 
20
- export type NonCustodialDeclarationType =
21
- | 'SIGNATURE'
22
- | 'DECLARATION'
23
- | 'SIGNATURE_AND_DECLARATION';
67
+ /**
68
+ * The type of Agent. Either a wallet or a VASP
69
+ * @public
70
+ */
24
71
 
72
+ export enum AgentType {
73
+ PRIVATE = 'WALLET',
74
+ VASP = 'VASP',
75
+ }
76
+
77
+ /**
78
+ * Who is the agent acting on behalf of the counterparty
79
+ * @public
80
+ */
81
+ export declare interface Agent {
82
+ did: DID;
83
+ type: AgentType;
84
+ logo?: string;
85
+ url?: string;
86
+ name?: string;
87
+ verified?: boolean;
88
+ }
25
89
  export type OwnershipProofInput = {
26
90
  type?: string | null;
27
91
  proof?: string | null;
@@ -37,55 +101,177 @@ export type CountryType = {
37
101
  name: string;
38
102
  };
39
103
 
40
- export type PersonType = 'natural' | 'legal';
41
-
42
- export type OriginatorFields = {
43
- originatorAccountNumber?: string;
44
- originatorDid?: string;
45
- originatorVASPname?: string;
46
- originatorVASPdid?: string;
47
- originatorName?: string;
48
- originatorGeographicAddress?: string;
49
- originatorNationalIdentification?: NationalIdentification;
50
- originatorDateAndPlaceOfBirth?: DateAndPlaceOfBirth;
51
- originatorPersonType?: PersonType;
52
- };
53
-
54
- export type BeneficiaryFields = {
55
- beneficiaryAccountNumber?: string;
56
- beneficiaryDid?: string;
57
- beneficiaryVASPname?: string;
58
- beneficiaryVASPdid?: string;
59
- beneficiaryName?: string;
60
- beneficiaryGeographicAddress?: string;
61
- beneficiaryNationalIdentification?: NationalIdentification;
62
- beneficiaryDateAndPlaceOfBirth?: DateAndPlaceOfBirth;
63
- beneficiaryPersonType?: PersonType;
64
- };
65
-
66
- export type CAIP19 = {
67
- caip19: string;
68
- };
69
-
70
- export type CoingeckoIdAndNetwork = { coingeckoId: string; network: string };
71
-
72
- export type TransactionAsset = string | CAIP19 | CoingeckoIdAndNetwork;
73
- export type Transaction = OriginatorFields &
74
- BeneficiaryFields & {
75
- transactionAsset: TransactionAsset;
76
- transactionAmount: string;
77
- transactionBlockchainInfo?: TransactionBlockchainInfo;
78
- customAssetPrice?: {
79
- priceUSD: number;
80
- decimals: number;
81
- };
82
- isNonCustodial?: boolean;
83
- originatorEqualsBeneficiary?: boolean;
84
- beneficiaryProof?: OwnershipProofInput;
85
- originatorProof?: OwnershipProofInput;
86
- isValid?: boolean;
87
- errors?: string[];
104
+ /**
105
+ * The type of counterparty. Either a natural person or a legal person. If the customer is the same as the counterparty then the counterparty is a self.
106
+ * @public
107
+ */
108
+ export enum PersonType {
109
+ NATURAL = 'natural',
110
+ LEGAL = 'legal',
111
+ SELF = 'self', // first party
112
+ }
113
+
114
+ /**
115
+ * A VASP agent acting on behalf of the counterparty
116
+ * @public
117
+ */
118
+ export interface VASP extends Agent {
119
+ url?: string;
120
+ name?: string;
121
+ }
122
+ /**
123
+ * A wallet agent acting on behalf of the counterparty
124
+ * @public
125
+ */
126
+ export interface Wallet extends Agent {
127
+ proof: OwnershipProof;
128
+ wallet_connect_id?: string;
129
+ }
130
+
131
+ /**
132
+ * The counterparty of a transaction.
133
+ * @public
134
+ */
135
+ export interface Counterparty {
136
+ name?: string;
137
+ accountNumber?: string;
138
+ did?: string;
139
+ geographicAddress?: string;
140
+ type?: PersonType;
141
+ verified?: boolean;
142
+ }
143
+ /**
144
+ * A counterparty object representing a natural person
145
+ * @public
146
+ */
147
+ export interface NaturalPerson extends Counterparty {
148
+ type: PersonType.NATURAL;
149
+ nationalIdentification?: NationalIdentification;
150
+ dateAndPlaceOfBirth?: DateAndPlaceOfBirth;
151
+ name: string;
152
+ }
153
+
154
+ /**
155
+ * A counterparty object representing a legal person
156
+ * @public
157
+ */
158
+ export interface LegalPerson extends Counterparty {
159
+ type: PersonType.LEGAL;
160
+ name: string;
161
+ lei?: string;
162
+ url?: string;
163
+ logo?: string;
164
+ }
165
+
166
+ type OriginatorFields = {
167
+ source?: Source;
168
+ };
169
+
170
+ type BeneficiaryFields = {
171
+ destination?: Destination;
172
+ };
173
+
174
+ type DepositRequestFields = {
175
+ destinations: Destination[];
176
+ };
177
+
178
+ /**
179
+ * An abstract transaction object
180
+ * @public
181
+ */
182
+ export interface Transaction {
183
+ agent: Agent;
184
+ customer?: Counterparty;
185
+ counterparty: Counterparty;
186
+ asset: TransactionAsset;
187
+ amountDecimal: number;
188
+ // transactionBlockchainInfo?: TransactionBlockchainInfo;
189
+ customAssetPrice?: {
190
+ price: number;
191
+ currency: string;
88
192
  };
193
+ }
194
+
195
+ /**
196
+ * An object representing a withdrawal transaction
197
+ * @public
198
+ */
199
+ export interface Withdrawal extends BeneficiaryFields, Transaction {}
200
+
201
+ /**
202
+ * An object representing a deposit transaction
203
+ * @public
204
+ */
205
+ export interface Deposit extends OriginatorFields, Transaction {}
206
+
207
+ /**
208
+ * An object representing a request for a deposit
209
+ * @public
210
+ */
211
+ export interface DepositRequest extends DepositRequestFields, Transaction {}
212
+
213
+ /**
214
+ * Configuration options for Transaction components
215
+ * @public
216
+ */
217
+
218
+ export interface TransactionOptions {
219
+ microTransactionAddress?: BlockchainAddress;
220
+ proofFallbacks?: ProofTypes[];
221
+ }
222
+
223
+ /**
224
+ * The verification status of a transaction
225
+ * @public
226
+ */
227
+ export enum Status {
228
+ EMPTY = 'empty',
229
+ VERIFY = 'verify',
230
+ PENDING = 'pending',
231
+ VERIFIED = 'verified',
232
+ BANNED = 'banned',
233
+ }
234
+
235
+ type V1Asset =
236
+ | {
237
+ caip19: CAIP19;
238
+ }
239
+ | string;
240
+
241
+ /**
242
+ * Transaction payload suitable for calling Notabene v1 tx/create
243
+ * @public
244
+ */
245
+
246
+ export type V1Transaction = {
247
+ transactionAsset: V1Asset;
248
+ transactionAmount: string;
249
+ originatorEqualsBeneficiary?: boolean;
250
+ originatorVASPdid: DID;
251
+ beneficiaryVASPdid: DID;
252
+ beneficiaryProof: OwnershipProof;
253
+ originator: Originator;
254
+ beneficiary: Beneficiary;
255
+ };
256
+ /**
257
+ * The response of a transaction
258
+ * @public
259
+ */
260
+
261
+ export type TransactionResponse = {
262
+ value: Transaction;
263
+ ivms: IVMS101;
264
+ proof?: OwnershipProof;
265
+ valid: boolean;
266
+ status: Status;
267
+ errors: ValidationError[];
268
+ txCreate?: V1Transaction;
269
+ };
270
+
271
+ export type ValidationError = {
272
+ attribute: string;
273
+ message: string;
274
+ };
89
275
 
90
276
  export type FieldProps = {
91
277
  forceDisplay?: boolean; // instead of beneficiaryDetails - defaults to false
@@ -95,6 +281,7 @@ export type FieldProps = {
95
281
  export type FieldName =
96
282
  | 'counterparty'
97
283
  | 'geographicAddress'
284
+ | 'firstName'
98
285
  | 'name'
99
286
  | 'nationalIdentification'
100
287
  | 'dateAndPlaceOfBirth';
@@ -103,12 +290,193 @@ export type FieldsProps = {
103
290
  [name in FieldName]?: FieldProps;
104
291
  };
105
292
 
106
- type WalletNotSupportedFlow = {
107
- flow: 'WALLET_NOT_SUPPORTED';
108
- action: 'DECLARATION' | 'REJECT';
293
+ /**
294
+ * Component Message Types
295
+ * @internal
296
+ */
297
+ export const enum CMType {
298
+ COMPLETE = 'complete',
299
+ RESIZE = 'resize',
300
+ RESULT = 'result',
301
+ READY = 'ready',
302
+ INVALID = 'invalid',
303
+ MODAL = 'openModal',
304
+ ERROR = 'error',
305
+ CLOSE = 'closeModal',
306
+ CANCEL = 'cancel',
307
+ }
308
+
309
+ export type Completed = {
310
+ type: CMType.COMPLETE;
311
+ response: TransactionResponse;
312
+ };
313
+
314
+ export type Result = {
315
+ type: CMType.RESULT;
316
+ reqid: RequestID;
317
+ response: TransactionResponse;
318
+ };
319
+
320
+ export type Ready = {
321
+ type: CMType.READY;
322
+ };
323
+
324
+ export type ResizeRequest = {
325
+ type: CMType.RESIZE;
326
+ height: number;
327
+ };
328
+
329
+ export type Error = {
330
+ type: CMType.ERROR;
331
+ message: string;
332
+ };
333
+
334
+ export type ModalRequest = {
335
+ type: CMType.MODAL;
336
+ url: string;
337
+ reqid: RequestID;
338
+ };
339
+
340
+ export type CloseModal = {
341
+ type: CMType.CLOSE;
342
+ reqid: RequestID;
343
+ };
344
+
345
+ export type Cancel = {
346
+ type: CMType.CANCEL;
347
+ };
348
+
349
+ export type InvalidValue = {
350
+ type: CMType.INVALID;
351
+ value: any;
352
+ errors: ValidationError[];
353
+ };
354
+
355
+ /**
356
+ * Component Message
357
+ * @internal
358
+ */
359
+ export type ComponentMessage =
360
+ | Completed
361
+ | Cancel
362
+ | Error
363
+ | Result
364
+ | Ready
365
+ | ResizeRequest
366
+ | ModalRequest
367
+ | InvalidValue
368
+ | CloseModal;
369
+
370
+ export type RequestID = string;
371
+
372
+ /**
373
+ * Host Message Types
374
+ * @internal
375
+ */
376
+
377
+ export const enum HMType {
378
+ UPDATE = 'update',
379
+ REQUEST_RESPONSE = 'requestResponse',
380
+ }
381
+
382
+ export type UpdateValue = {
383
+ type: HMType.UPDATE;
384
+ value: Partial<Transaction>;
109
385
  };
110
386
 
111
- // This is going to be union type
112
- export type Fallback = WalletNotSupportedFlow;
387
+ export type RequestResponse = {
388
+ type: HMType.REQUEST_RESPONSE;
389
+ reqid: RequestID;
390
+ };
391
+
392
+ /**
393
+ * Host Messages
394
+ * @internal
395
+ */
396
+
397
+ export type HostMessage = UpdateValue | RequestResponse;
398
+
399
+ export interface CallbackOptions {
400
+ callback?: URI;
401
+ redirectUri?: URI;
402
+ }
403
+
404
+ /**
405
+ * Status of the proof
406
+ * @public
407
+ */
408
+ export enum ProofStatus {
409
+ PENDING = 'pending', // Verification is pending
410
+ FAILED = 'rejected', // Rejected
411
+ FLAGGED = 'flagged', // Flagged for manual review
412
+ VERIFIED = 'verified', // Verified
413
+ }
414
+
415
+ /**
416
+ * The type of Proofs supported
417
+ * @public
418
+ **/
419
+ export enum ProofTypes {
420
+ SelfDeclaration = 'self-declaration',
421
+ PersonalSignEIP191 = 'eip-191',
422
+ PersonalSignEIP712 = 'eip-712',
423
+ PersonalSignBIP137 = 'bip-137',
424
+ PersonalSignXPUB = 'xpub',
425
+ MicroTransfer = 'microtransfer',
426
+ Screenshot = 'screenshot',
427
+ }
428
+ /**
429
+ * Ownership Proof
430
+ * @public
431
+ */
432
+ export interface OwnershipProof {
433
+ type: ProofTypes;
434
+ status: ProofStatus;
435
+ did: DID;
436
+ address: CAIP10;
437
+ }
438
+
439
+ /**
440
+ * Ownership Proof using Message Signature
441
+ * @public
442
+ */
443
+ export interface SignatureProof extends OwnershipProof {
444
+ type:
445
+ | ProofTypes.PersonalSignEIP191
446
+ | ProofTypes.PersonalSignEIP712
447
+ | ProofTypes.PersonalSignBIP137
448
+ | ProofTypes.PersonalSignXPUB;
449
+ proof: string;
450
+ attestation: string;
451
+ wallet_provider: string;
452
+ }
113
453
 
114
- export type OptInFeature = 'REUSE_ADDRESS_OWNERSHIP_PROOF';
454
+ /**
455
+ * Ownership Proof using Self Declaration
456
+ * @public
457
+ */
458
+ export interface DeclarationProof extends OwnershipProof {
459
+ type: ProofTypes.SelfDeclaration;
460
+ attestation: string;
461
+ confirmed: boolean;
462
+ }
463
+
464
+ /**
465
+ * Ownership Proof using Micro Transfer
466
+ * @public
467
+ */
468
+ export interface MicroTransferProof extends OwnershipProof {
469
+ type: ProofTypes.MicroTransfer;
470
+ txhash: string;
471
+ chain: CAIP2;
472
+ amount: number;
473
+ }
474
+
475
+ /**
476
+ * Ownership Proof using Screenshot
477
+ * @public
478
+ */
479
+ export interface ScreenshotProof extends OwnershipProof {
480
+ type: ProofTypes.Screenshot;
481
+ url: string;
482
+ }
@@ -0,0 +1,54 @@
1
+ import { ComponentMessage, HostMessage } from '../types';
2
+
3
+ export type MessageCallback = (message: ComponentMessage) => void;
4
+
5
+ export class MessageEventManager {
6
+ private listeners: Map<string, Set<MessageCallback>> = new Map();
7
+ private port?: MessagePort;
8
+
9
+ constructor() {
10
+ this.handleMessage = this.handleMessage.bind(this);
11
+ }
12
+
13
+ setPort(port: MessagePort): void {
14
+ this.port = port;
15
+ this.port.onmessage = this.handleMessage;
16
+ this.port.start();
17
+ }
18
+
19
+ on(messageType: string, callback: MessageCallback): void {
20
+ if (!this.listeners.has(messageType)) {
21
+ this.listeners.set(messageType, new Set());
22
+ }
23
+ this.listeners.get(messageType)!.add(callback);
24
+ }
25
+
26
+ off(messageType: string, callback: MessageCallback): void {
27
+ const callbacks = this.listeners.get(messageType);
28
+ if (callbacks) {
29
+ callbacks.delete(callback);
30
+ if (callbacks.size === 0) {
31
+ this.listeners.delete(messageType);
32
+ }
33
+ }
34
+ }
35
+
36
+ private handleMessage(event: MessageEvent): void {
37
+ console.log('received message', event.data);
38
+ const message = event.data;
39
+ if (typeof message === 'object' && message !== null && 'type' in message) {
40
+ const messageType = message.type as string;
41
+ const callbacks = this.listeners.get(messageType);
42
+ if (callbacks) {
43
+ callbacks.forEach((callback) => callback(message));
44
+ }
45
+ }
46
+ }
47
+
48
+ // Method to send messages through the port
49
+ send(message: HostMessage): void {
50
+ if (this.port) {
51
+ this.port.postMessage(message);
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,95 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { HMType, UpdateValue } from '../../types';
3
+ import { MessageEventManager } from '../MessageEventManager';
4
+
5
+ describe('MessageEventManager', () => {
6
+ let messageEventManager: MessageEventManager;
7
+ let mockPort: MessagePort;
8
+
9
+ beforeEach(() => {
10
+ mockPort = {
11
+ onmessage: null,
12
+ postMessage: vi.fn(),
13
+ start: vi.fn(),
14
+ } as unknown as MessagePort;
15
+
16
+ messageEventManager = new MessageEventManager();
17
+ });
18
+
19
+ describe('setPort', () => {
20
+ beforeEach(() => {
21
+ messageEventManager.setPort(mockPort);
22
+ });
23
+
24
+ it('should start the port when initialized', () => {
25
+ expect(mockPort.start).toHaveBeenCalled();
26
+ });
27
+
28
+ it('should subscribe and receive messages of a specific type', () => {
29
+ const readyHandler = vi.fn();
30
+ messageEventManager.on('ready', readyHandler);
31
+
32
+ const readyMessage = { type: 'ready' };
33
+ mockPort.onmessage!({ data: readyMessage } as MessageEvent);
34
+
35
+ expect(readyHandler).toHaveBeenCalledWith(readyMessage);
36
+ });
37
+
38
+ it('should not call handlers for different message types', () => {
39
+ const updateHandler = vi.fn();
40
+ const errorHandler = vi.fn();
41
+ messageEventManager.on('update', updateHandler);
42
+ messageEventManager.on('error', errorHandler);
43
+
44
+ const errorMessage = { type: 'error', message: 'Something went wrong' };
45
+ mockPort.onmessage!({ data: errorMessage } as MessageEvent);
46
+
47
+ expect(updateHandler).not.toHaveBeenCalled();
48
+ expect(errorHandler).toHaveBeenCalledWith(errorMessage);
49
+ });
50
+
51
+ it('should unsubscribe handlers', () => {
52
+ const updateHandler = vi.fn();
53
+ messageEventManager.on('update', updateHandler);
54
+ messageEventManager.off('update', updateHandler);
55
+
56
+ const updateMessage = { type: 'update', data: { foo: 'bar' } };
57
+ mockPort.onmessage!({ data: updateMessage } as MessageEvent);
58
+
59
+ expect(updateHandler).not.toHaveBeenCalled();
60
+ });
61
+
62
+ it('should allow multiple subscribers for the same message type', () => {
63
+ const updateHandler1 = vi.fn();
64
+ const updateHandler2 = vi.fn();
65
+ messageEventManager.on('update', updateHandler1);
66
+ messageEventManager.on('update', updateHandler2);
67
+
68
+ const updateMessage = { type: 'update', data: { foo: 'bar' } };
69
+ mockPort.onmessage!({ data: updateMessage } as MessageEvent);
70
+
71
+ expect(updateHandler1).toHaveBeenCalledWith(updateMessage);
72
+ expect(updateHandler2).toHaveBeenCalledWith(updateMessage);
73
+ });
74
+
75
+ it('should send messages through the port', () => {
76
+ const message: UpdateValue = {
77
+ type: HMType.UPDATE,
78
+ value: { asset: 'USDC' },
79
+ };
80
+ messageEventManager.send(message);
81
+
82
+ expect(mockPort.postMessage).toHaveBeenCalledWith(message);
83
+ });
84
+
85
+ it('should ignore messages without a type', () => {
86
+ const handler = vi.fn();
87
+ messageEventManager.on('update', handler);
88
+
89
+ const invalidMessage = { data: 'no type field' };
90
+ mockPort.onmessage!({ data: invalidMessage } as MessageEvent);
91
+
92
+ expect(handler).not.toHaveBeenCalled();
93
+ });
94
+ });
95
+ });
@@ -0,0 +1,26 @@
1
+ import fc from 'fast-check';
2
+ import type { CAIP10, CAIP19, CAIP2, CAIP220 } from '../types';
3
+ import {
4
+ CAIP10_MATCHER,
5
+ CAIP19_MATCHER,
6
+ CAIP220_MATCHER,
7
+ CAIP2_MATCHER,
8
+ } from './caip';
9
+
10
+ const LOCALE = new RegExp(/^[a-z]{2}(-[A-Z]{2})?$/);
11
+
12
+ // Arbitraries
13
+ export const arbitraryCAIP10 = (): fc.Arbitrary<CAIP10> =>
14
+ fc.stringMatching(CAIP10_MATCHER) as fc.Arbitrary<CAIP10>;
15
+
16
+ export const arbitraryCAIP2 = (): fc.Arbitrary<CAIP2> =>
17
+ fc.stringMatching(CAIP2_MATCHER) as fc.Arbitrary<CAIP2>;
18
+
19
+ export const arbitraryCAIP19 = (): fc.Arbitrary<CAIP19> =>
20
+ fc.stringMatching(CAIP19_MATCHER) as fc.Arbitrary<CAIP19>;
21
+
22
+ export const arbitraryCAIP220 = (): fc.Arbitrary<CAIP220> =>
23
+ fc.stringMatching(CAIP220_MATCHER) as fc.Arbitrary<CAIP220>;
24
+
25
+ export const arbitraryLocale = (): fc.Arbitrary<string> =>
26
+ fc.stringMatching(LOCALE) as fc.Arbitrary<string>;