@asseteragmbh/wallet-connector 0.2.0 → 0.2.2

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/dist/index.cjs CHANGED
@@ -4,6 +4,11 @@
4
4
  var PROTOCOL_VERSION = 1;
5
5
  var CONNECT_PATH = "/connect";
6
6
  var ONBOARD_PATH = "/onboard";
7
+ var MANAGE_PATH = "/manage";
8
+ var SIGN_IN_HINTS = ["assetera-marketplace"];
9
+ function isSignInHint(value) {
10
+ return SIGN_IN_HINTS.includes(value);
11
+ }
7
12
  var MESSAGE_PREFIX = "assetera-wallet:";
8
13
  var SUPPORTED_METHODS = [
9
14
  "eth_requestAccounts",
@@ -48,6 +53,42 @@ function isResponseMessage(value) {
48
53
  function isEventMessage(value) {
49
54
  return hasType(value, "event") && (value.event === "accountsChanged" || value.event === "chainChanged" || value.event === "disconnect");
50
55
  }
56
+ var WALLET_PROTECTIONS = [
57
+ "unknown",
58
+ "add-factor",
59
+ "save-codes",
60
+ "create-codes",
61
+ "protected"
62
+ ];
63
+ function isCount(value) {
64
+ return typeof value === "number" && Number.isInteger(value) && value >= 0;
65
+ }
66
+ function isSecurityMessage(value) {
67
+ if (!hasType(value, "security")) return false;
68
+ const data = value.data;
69
+ return isRecord(data) && typeof data.protection === "string" && WALLET_PROTECTIONS.includes(data.protection) && isCount(data.passkeys) && isCount(data.authenticatorApps);
70
+ }
71
+ var MAX_ACCOUNT_NAMES = 50;
72
+ function hasControlCharacter(value) {
73
+ for (const character of value) {
74
+ const code = character.codePointAt(0) ?? 0;
75
+ if (code <= 31 || code >= 127 && code <= 159) return true;
76
+ }
77
+ return false;
78
+ }
79
+ function isAccountName(value) {
80
+ if (typeof value !== "string") return false;
81
+ if (hasControlCharacter(value)) return false;
82
+ const trimmed = value.trim();
83
+ return trimmed.length >= 1 && trimmed.length <= 64;
84
+ }
85
+ function isAccountsMessage(value) {
86
+ if (!hasType(value, "accounts")) return false;
87
+ const data = value.data;
88
+ return Array.isArray(data) && data.length <= MAX_ACCOUNT_NAMES && data.every(
89
+ (entry) => isRecord(entry) && typeof entry.address === "string" && EVM_ADDRESS_RE.test(entry.address) && isAccountName(entry.name)
90
+ );
91
+ }
51
92
  function isRequestMessage(value) {
52
93
  return hasType(value, "request") && typeof value.id === "string" && typeof value.method === "string";
53
94
  }
@@ -65,10 +106,11 @@ function isOnboardCancelledMessage(value) {
65
106
  function isCaip2EvmChain(value) {
66
107
  return /^eip155:\d{1,32}$/.test(value);
67
108
  }
68
- function connectUrl(walletUrl, partnerOrigin) {
109
+ function connectUrl(walletUrl, partnerOrigin, signIn) {
69
110
  const url = new URL(CONNECT_PATH, walletUrl);
70
111
  url.searchParams.set("origin", partnerOrigin);
71
112
  url.searchParams.set("v", String(PROTOCOL_VERSION));
113
+ if (signIn !== void 0) url.searchParams.set("signIn", signIn);
72
114
  return url.toString();
73
115
  }
74
116
  function onboardUrl(walletUrl, partnerOrigin, options) {
@@ -78,6 +120,15 @@ function onboardUrl(walletUrl, partnerOrigin, options) {
78
120
  url.searchParams.set("chain", options.chain);
79
121
  if (options.locale !== void 0) url.searchParams.set("lang", options.locale);
80
122
  if (options.add) url.searchParams.set("add", "1");
123
+ if (options.signIn !== void 0) url.searchParams.set("signIn", options.signIn);
124
+ return url.toString();
125
+ }
126
+ function manageUrl(walletUrl, partnerOrigin, options = {}) {
127
+ const url = new URL(MANAGE_PATH, walletUrl);
128
+ url.searchParams.set("origin", partnerOrigin);
129
+ url.searchParams.set("v", String(PROTOCOL_VERSION));
130
+ if (options.locale !== void 0) url.searchParams.set("lang", options.locale);
131
+ if (options.signIn !== void 0) url.searchParams.set("signIn", options.signIn);
81
132
  return url.toString();
82
133
  }
83
134
 
@@ -123,6 +174,8 @@ function createAsseteraWalletProvider(options) {
123
174
  let accounts = [];
124
175
  let chainIdHex = null;
125
176
  let walletDisconnected = false;
177
+ let lastSecurity;
178
+ let lastAccountNameList;
126
179
  const pending = /* @__PURE__ */ new Map();
127
180
  const listeners = /* @__PURE__ */ new Map();
128
181
  let closeTimer = null;
@@ -197,7 +250,7 @@ function createAsseteraWalletProvider(options) {
197
250
  }
198
251
  if (typeof window === "undefined") return null;
199
252
  const opened = window.open(
200
- connectUrl(options.walletUrl, window.location.origin),
253
+ connectUrl(options.walletUrl, window.location.origin, options.signIn),
201
254
  POPUP_NAME,
202
255
  popupFeatures
203
256
  );
@@ -248,6 +301,21 @@ function createAsseteraWalletProvider(options) {
248
301
  walletDisconnected = true;
249
302
  emit("disconnect", new ProviderRpcError(message.data.code, message.data.message, message.data.data));
250
303
  }
304
+ function handleSecurityMessage(message) {
305
+ lastSecurity = {
306
+ protection: message.data.protection,
307
+ passkeys: message.data.passkeys,
308
+ authenticatorApps: message.data.authenticatorApps
309
+ };
310
+ emit("assetera_security", lastSecurity);
311
+ }
312
+ function copyAccountNames2(accounts2) {
313
+ return accounts2.map((account) => ({ address: account.address, name: account.name }));
314
+ }
315
+ function handleAccountsMessage(message) {
316
+ lastAccountNameList = copyAccountNames2(message.data);
317
+ emit("assetera_accounts", copyAccountNames2(lastAccountNameList));
318
+ }
251
319
  function onWindowMessage(event) {
252
320
  if (event.origin !== walletOrigin) return;
253
321
  if (popup === null || event.source !== popup) return;
@@ -260,6 +328,14 @@ function createAsseteraWalletProvider(options) {
260
328
  handleResponse(data);
261
329
  return;
262
330
  }
331
+ if (isSecurityMessage(data)) {
332
+ handleSecurityMessage(data);
333
+ return;
334
+ }
335
+ if (isAccountsMessage(data)) {
336
+ handleAccountsMessage(data);
337
+ return;
338
+ }
263
339
  if (isEventMessage(data)) handleEventMessage(data);
264
340
  }
265
341
  if (typeof window !== "undefined") window.addEventListener("message", onWindowMessage);
@@ -332,7 +408,26 @@ function createAsseteraWalletProvider(options) {
332
408
  function removeListener(event, listener) {
333
409
  removeEventListener(event, listener);
334
410
  }
335
- return { request, on, removeListener, disconnect };
411
+ function lastSecurityStatus() {
412
+ return lastSecurity;
413
+ }
414
+ function lastAccountNames() {
415
+ return lastAccountNameList === void 0 ? void 0 : copyAccountNames2(lastAccountNameList);
416
+ }
417
+ return { request, on, removeListener, disconnect, lastSecurityStatus, lastAccountNames };
418
+ }
419
+
420
+ // src/popup.ts
421
+ var POPUP_WIDTH = 440;
422
+ var POPUP_HEIGHT = 760;
423
+ function defaultPopupFeatures(win) {
424
+ const screenX = Number.isFinite(win.screenX) ? win.screenX : 0;
425
+ const screenY = Number.isFinite(win.screenY) ? win.screenY : 0;
426
+ const outerWidth = Number.isFinite(win.outerWidth) ? win.outerWidth : POPUP_WIDTH;
427
+ const outerHeight = Number.isFinite(win.outerHeight) ? win.outerHeight : POPUP_HEIGHT;
428
+ const left = Math.max(0, Math.round(screenX + (outerWidth - POPUP_WIDTH) / 2));
429
+ const top = Math.max(0, Math.round(screenY + (outerHeight - POPUP_HEIGHT) / 2));
430
+ return `popup,width=${POPUP_WIDTH},height=${POPUP_HEIGHT},left=${left},top=${top}`;
336
431
  }
337
432
 
338
433
  // src/onboarding.ts
@@ -345,8 +440,6 @@ var OnboardingError = class extends Error {
345
440
  }
346
441
  };
347
442
  var POPUP_NAME2 = "assetera-wallet-onboarding";
348
- var POPUP_WIDTH = 440;
349
- var POPUP_HEIGHT = 760;
350
443
  var CLOSE_POLL_MS2 = 500;
351
444
  var OVERLAY_Z_INDEX = 2147483e3;
352
445
  var DEFAULT_LABELS = {
@@ -356,15 +449,6 @@ var DEFAULT_LABELS = {
356
449
  cancel: "Cancel",
357
450
  blocked: "Your browser blocked the Assetera Wallet window. Allow pop-ups for this site and try again."
358
451
  };
359
- function defaultPopupFeatures(win) {
360
- const screenX = Number.isFinite(win.screenX) ? win.screenX : 0;
361
- const screenY = Number.isFinite(win.screenY) ? win.screenY : 0;
362
- const outerWidth = Number.isFinite(win.outerWidth) ? win.outerWidth : POPUP_WIDTH;
363
- const outerHeight = Number.isFinite(win.outerHeight) ? win.outerHeight : POPUP_HEIGHT;
364
- const left = Math.max(0, Math.round(screenX + (outerWidth - POPUP_WIDTH) / 2));
365
- const top = Math.max(0, Math.round(screenY + (outerHeight - POPUP_HEIGHT) / 2));
366
- return `popup,width=${POPUP_WIDTH},height=${POPUP_HEIGHT},left=${left},top=${top}`;
367
- }
368
452
  function resolveLabels(options) {
369
453
  const overrides = typeof options.overlay === "object" && options.overlay !== null ? options.overlay : {};
370
454
  const partnerName = options.partnerName;
@@ -387,6 +471,9 @@ function cancelMessage(reason) {
387
471
  if (reason === "refused") return "The Assetera Wallet refused the onboarding.";
388
472
  return "The Assetera Wallet onboarding failed.";
389
473
  }
474
+ function copyAccountNames(accounts) {
475
+ return accounts.map((account) => ({ address: account.address, name: account.name }));
476
+ }
390
477
  function showOverlay(win, labels, handlers) {
391
478
  const doc = win.document;
392
479
  const root = doc.createElement("div");
@@ -487,7 +574,8 @@ function openAsseteraOnboarding(options) {
487
574
  onboardUrl(options.walletUrl, targetWindow.location.origin, {
488
575
  chain: options.chain,
489
576
  ...options.locale === void 0 ? {} : { locale: options.locale },
490
- ...options.addWallet ? { add: true } : {}
577
+ ...options.addWallet ? { add: true } : {},
578
+ ...options.signIn === void 0 ? {} : { signIn: options.signIn }
491
579
  }),
492
580
  POPUP_NAME2,
493
581
  options.popupFeatures ?? defaultPopupFeatures(targetWindow)
@@ -554,6 +642,7 @@ function openAsseteraOnboarding(options) {
554
642
  });
555
643
  }
556
644
  let address;
645
+ let lastAccountNameList;
557
646
  function onMessage(event) {
558
647
  if (event.origin !== walletOrigin) return;
559
648
  if (event.source !== activePopup) return;
@@ -567,7 +656,8 @@ function openAsseteraOnboarding(options) {
567
656
  chain: data.chain,
568
657
  created: data.created ?? true,
569
658
  signMessage,
570
- close
659
+ close,
660
+ lastAccounts: () => lastAccountNameList === void 0 ? void 0 : copyAccountNames(lastAccountNameList)
571
661
  });
572
662
  return;
573
663
  }
@@ -576,6 +666,11 @@ function openAsseteraOnboarding(options) {
576
666
  }
577
667
  return;
578
668
  }
669
+ if (isAccountsMessage(data)) {
670
+ lastAccountNameList = copyAccountNames(data.data);
671
+ options.onAccounts?.(copyAccountNames(lastAccountNameList));
672
+ return;
673
+ }
579
674
  if (isResponseMessage(data)) {
580
675
  const entry = pending.get(data.id);
581
676
  if (!entry) return;
@@ -614,16 +709,114 @@ function openAsseteraOnboarding(options) {
614
709
  });
615
710
  }
616
711
 
712
+ // src/manage.ts
713
+ var ManagerError = class extends Error {
714
+ code;
715
+ constructor(code, message) {
716
+ super(message);
717
+ this.name = "ManagerError";
718
+ this.code = code;
719
+ }
720
+ };
721
+ var POPUP_NAME3 = "assetera-wallet-manage";
722
+ var CLOSE_POLL_MS3 = 500;
723
+ function openAsseteraWalletManager(options) {
724
+ const requestedWindow = options.window ?? (typeof window === "undefined" ? void 0 : window);
725
+ if (!requestedWindow) {
726
+ throw new ManagerError("invalid_options", "The Assetera Wallet manager can only run in a browser.");
727
+ }
728
+ const targetWindow = requestedWindow;
729
+ let walletOrigin;
730
+ try {
731
+ walletOrigin = new URL(options.walletUrl).origin;
732
+ } catch {
733
+ throw new ManagerError("invalid_options", `"${options.walletUrl}" is not a valid wallet URL.`);
734
+ }
735
+ const popup = targetWindow.open(
736
+ manageUrl(options.walletUrl, targetWindow.location.origin, {
737
+ ...options.locale === void 0 ? {} : { locale: options.locale },
738
+ ...options.signIn === void 0 ? {} : { signIn: options.signIn }
739
+ }),
740
+ POPUP_NAME3,
741
+ options.popupFeatures ?? defaultPopupFeatures(targetWindow)
742
+ );
743
+ if (!popup) {
744
+ throw new ManagerError(
745
+ "popup_blocked",
746
+ "Your browser blocked the Assetera Wallet window. Allow pop-ups for this site and try again."
747
+ );
748
+ }
749
+ const activePopup = popup;
750
+ let finished = false;
751
+ let lastSecurity;
752
+ let lastAccounts;
753
+ let closeTimer = null;
754
+ let resolveClosed;
755
+ const closed = new Promise((resolve) => {
756
+ resolveClosed = resolve;
757
+ });
758
+ function cleanup() {
759
+ if (finished) return;
760
+ finished = true;
761
+ targetWindow.removeEventListener("message", onMessage);
762
+ if (closeTimer !== null) {
763
+ clearInterval(closeTimer);
764
+ closeTimer = null;
765
+ }
766
+ resolveClosed();
767
+ }
768
+ function close() {
769
+ cleanup();
770
+ if (!activePopup.closed) activePopup.close();
771
+ }
772
+ function copyAccountNames2(accounts) {
773
+ return accounts.map((account) => ({ address: account.address, name: account.name }));
774
+ }
775
+ function onMessage(event) {
776
+ if (event.origin !== walletOrigin) return;
777
+ if (event.source !== activePopup) return;
778
+ const data = event.data;
779
+ if (isSecurityMessage(data)) {
780
+ const status = {
781
+ protection: data.data.protection,
782
+ passkeys: data.data.passkeys,
783
+ authenticatorApps: data.data.authenticatorApps
784
+ };
785
+ lastSecurity = status;
786
+ options.onSecurity?.(status);
787
+ return;
788
+ }
789
+ if (isAccountsMessage(data)) {
790
+ lastAccounts = copyAccountNames2(data.data);
791
+ options.onAccounts?.(copyAccountNames2(lastAccounts));
792
+ }
793
+ }
794
+ targetWindow.addEventListener("message", onMessage);
795
+ closeTimer = setInterval(() => {
796
+ if (activePopup.closed) cleanup();
797
+ }, CLOSE_POLL_MS3);
798
+ return {
799
+ close,
800
+ closed,
801
+ lastSecurity: () => lastSecurity,
802
+ lastAccounts: () => lastAccounts === void 0 ? void 0 : copyAccountNames2(lastAccounts)
803
+ };
804
+ }
805
+
617
806
  exports.CONNECT_PATH = CONNECT_PATH;
807
+ exports.MANAGE_PATH = MANAGE_PATH;
618
808
  exports.MESSAGE_PREFIX = MESSAGE_PREFIX;
809
+ exports.ManagerError = ManagerError;
619
810
  exports.ONBOARD_PATH = ONBOARD_PATH;
620
811
  exports.OnboardingError = OnboardingError;
621
812
  exports.PROTOCOL_VERSION = PROTOCOL_VERSION;
622
813
  exports.ProviderRpcError = ProviderRpcError;
623
814
  exports.RpcErrorCode = RpcErrorCode;
815
+ exports.SIGN_IN_HINTS = SIGN_IN_HINTS;
624
816
  exports.SUPPORTED_METHODS = SUPPORTED_METHODS;
625
817
  exports.connectUrl = connectUrl;
626
818
  exports.createAsseteraWalletProvider = createAsseteraWalletProvider;
819
+ exports.isAccountsMessage = isAccountsMessage;
627
820
  exports.isCaip2EvmChain = isCaip2EvmChain;
628
821
  exports.isDisconnectMessage = isDisconnectMessage;
629
822
  exports.isEventMessage = isEventMessage;
@@ -632,8 +825,12 @@ exports.isOnboardedMessage = isOnboardedMessage;
632
825
  exports.isReadyMessage = isReadyMessage;
633
826
  exports.isRequestMessage = isRequestMessage;
634
827
  exports.isResponseMessage = isResponseMessage;
828
+ exports.isSecurityMessage = isSecurityMessage;
829
+ exports.isSignInHint = isSignInHint;
635
830
  exports.isSupportedMethod = isSupportedMethod;
831
+ exports.manageUrl = manageUrl;
636
832
  exports.onboardUrl = onboardUrl;
637
833
  exports.openAsseteraOnboarding = openAsseteraOnboarding;
834
+ exports.openAsseteraWalletManager = openAsseteraWalletManager;
638
835
  //# sourceMappingURL=index.cjs.map
639
836
  //# sourceMappingURL=index.cjs.map