@seams/wallet 0.21.0 → 0.23.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.
Files changed (129) hide show
  1. package/THIRD_PARTY_NOTICES.md +5 -2
  2. package/dist/.build-inputs.sha256 +1 -1
  3. package/dist/esm/core/signingEngine/uiConfirm/ui/preact/TransactionReceipt.js +107 -43
  4. package/dist/esm/core/signingEngine/uiConfirm/ui/preact/mountConfirmationSurface.js +1 -1
  5. package/dist/esm/core/signingEngine/uiConfirm/ui/transaction-receipt.js +36 -6
  6. package/dist/esm/core/ui/messages.js +9 -14
  7. package/dist/esm/externalNear/actions.js +26 -0
  8. package/dist/esm/externalNear/boundary.js +53 -0
  9. package/dist/esm/externalNear/controller.js +187 -0
  10. package/dist/esm/externalNear/hot.js +40 -0
  11. package/dist/esm/externalNear/index.js +3 -0
  12. package/dist/esm/externalNear/intear.js +109 -0
  13. package/dist/esm/externalNear/intearPopup.js +83 -0
  14. package/dist/esm/externalNear/meteor.js +121 -0
  15. package/dist/esm/externalNear/meteorRequest.js +117 -0
  16. package/dist/esm/externalNear/types.js +17 -0
  17. package/dist/esm/externalNear/wallets.js +33 -0
  18. package/dist/esm/node_modules/borsh/lib/esm/buffer.js +67 -0
  19. package/dist/esm/node_modules/borsh/lib/esm/deserialize.js +94 -0
  20. package/dist/esm/node_modules/borsh/lib/esm/index.js +13 -0
  21. package/dist/esm/node_modules/borsh/lib/esm/serialize.js +127 -0
  22. package/dist/esm/node_modules/borsh/lib/esm/types.js +19 -0
  23. package/dist/esm/node_modules/borsh/lib/esm/utils.js +104 -0
  24. package/dist/esm/plugins/plugin-utils.js +2 -5
  25. package/dist/esm/react/components/AccountMenuButton/ProfileDropdown.js +23 -9
  26. package/dist/esm/react/components/AccountMenuButton/TransactionSettingsSection.css +376 -84
  27. package/dist/esm/react/components/AccountMenuButton/TransactionSettingsSection.js +87 -167
  28. package/dist/esm/react/components/AccountMenuButton/WalletRegionSection.js +11 -9
  29. package/dist/esm/react/components/AccountMenuButton/WalletSettingsLayout.css +76 -74
  30. package/dist/esm/react/components/AccountMenuButton/WalletSettingsLayout.js +23 -22
  31. package/dist/esm/react/components/AccountMenuButton/Web3AuthProfileButton.css +1 -5
  32. package/dist/esm/react/components/AccountMenuButton/icons/MapPinIcon.js +12 -0
  33. package/dist/esm/react/components/AccountMenuButton/index.js +2 -1
  34. package/dist/esm/react/externalNear.css +90 -0
  35. package/dist/esm/react/externalNear.js +104 -0
  36. package/dist/esm/react/styles/styles.css +452 -162
  37. package/dist/{public/sdk/ConfirmationDrawer-B7RCp58z.js → esm/sdk/ConfirmationDrawer-Ck33ZmU-.js} +1 -1
  38. package/dist/esm/sdk/adapters-CgJpSAxH.js +2 -0
  39. package/dist/esm/sdk/appearance-token-vars-BA3KQyEE.js +2 -0
  40. package/dist/esm/sdk/{export-viewer-host-CVdpEuY6.js → export-viewer-host-C9n-62Pi.js} +1 -1
  41. package/dist/esm/sdk/{exportKeypairOperation-gVCqLrDl.js → exportKeypairOperation-Du9HKB8f.js} +1 -1
  42. package/dist/esm/sdk/host-C5KYWEcs.js +1 -0
  43. package/dist/esm/sdk/{localOnly-TXaYAZj-.js → localOnly-CvFSZFUy.js} +1 -1
  44. package/dist/esm/sdk/{mountExportPrivateKeySurface-DUnKOBQr.js → mountExportPrivateKeySurface-DdJHWwqY.js} +1 -1
  45. package/dist/esm/sdk/{mountRecoveryCodeBackupSurface-MGTl6FhG.js → mountRecoveryCodeBackupSurface-D3QFgMGE.js} +1 -1
  46. package/dist/{public/sdk/pendingWalletRecoveryCodeBackup-DWeemdJv.js → esm/sdk/pendingWalletRecoveryCodeBackup-LbstFwcY.js} +1 -1
  47. package/dist/esm/sdk/{registration-DVX_lnP7.js → registration-CIxJvbTo.js} +1 -1
  48. package/dist/esm/sdk/{registration-DeCUrUR9.js → registration-CeOmKyCG.js} +1 -1
  49. package/dist/{public/sdk/runtime-auth-BAGyfscG.js → esm/sdk/runtime-auth-C0V-5znH.js} +1 -1
  50. package/dist/{public/sdk/runtime-device-link-DKkMLVor.js → esm/sdk/runtime-device-link-B1jpHQSF.js} +1 -1
  51. package/dist/{public/sdk/runtime-ecdsa-tempo-0TAVH1xg.js → esm/sdk/runtime-ecdsa-tempo-CDSZ3pxS.js} +1 -1
  52. package/dist/esm/sdk/{runtime-email-otp-DoHa8Hif.js → runtime-email-otp-DXuxFOjL.js} +1 -1
  53. package/dist/esm/sdk/{runtime-export-CAt17IFH.js → runtime-export-Ba12ChDI.js} +1 -1
  54. package/dist/esm/sdk/{runtime-near-BQX9Jk9Z.js → runtime-near-DRrdWzmr.js} +1 -1
  55. package/dist/esm/sdk/{runtime-preferences-BuKTIpv6.js → runtime-preferences-BWyhRaBA.js} +1 -1
  56. package/dist/esm/sdk/{runtime-recovery-DyxJKKNw.js → runtime-recovery-DlJlaZlf.js} +1 -1
  57. package/dist/esm/sdk/{runtime-recovery-codes-CWmt0FDB.js → runtime-recovery-codes-CuqYWqK7.js} +1 -1
  58. package/dist/esm/sdk/{shared-BVps1f3F.js → shared-B95wnpoD.js} +1 -1
  59. package/dist/esm/sdk/transaction-activity-CpDMkSPm.js +1 -0
  60. package/dist/esm/sdk/tx-confirm-ui.js +186 -71
  61. package/dist/esm/sdk/wallet-iframe-host-ecdsa.js +1 -1
  62. package/dist/esm/sdk/wallet-iframe-host-full.js +1 -1
  63. package/dist/esm/sdk/wallet-iframe-host-near.js +1 -1
  64. package/dist/esm/sdk/wallet-iframe-host-runtime.js +1 -1
  65. package/dist/esm/sdk/wallet-ui.css +1 -1
  66. package/dist/{public/sdk/walletRecoveryAdminProof-CIoLawfC.js → esm/sdk/walletRecoveryAdminProof-CtH8_lph.js} +1 -1
  67. package/dist/public/headers.manifest.json +1 -1
  68. package/dist/{esm/sdk/ConfirmationDrawer-B7RCp58z.js → public/sdk/ConfirmationDrawer-Ck33ZmU-.js} +1 -1
  69. package/dist/public/sdk/adapters-CgJpSAxH.js +2 -0
  70. package/dist/public/sdk/appearance-token-vars-BA3KQyEE.js +2 -0
  71. package/dist/public/sdk/{export-viewer-host-CVdpEuY6.js → export-viewer-host-C9n-62Pi.js} +1 -1
  72. package/dist/public/sdk/{exportKeypairOperation-gVCqLrDl.js → exportKeypairOperation-Du9HKB8f.js} +1 -1
  73. package/dist/public/sdk/host-C5KYWEcs.js +1 -0
  74. package/dist/public/sdk/{localOnly-TXaYAZj-.js → localOnly-CvFSZFUy.js} +1 -1
  75. package/dist/public/sdk/{mountExportPrivateKeySurface-DUnKOBQr.js → mountExportPrivateKeySurface-DdJHWwqY.js} +1 -1
  76. package/dist/public/sdk/{mountRecoveryCodeBackupSurface-MGTl6FhG.js → mountRecoveryCodeBackupSurface-D3QFgMGE.js} +1 -1
  77. package/dist/{esm/sdk/pendingWalletRecoveryCodeBackup-DWeemdJv.js → public/sdk/pendingWalletRecoveryCodeBackup-LbstFwcY.js} +1 -1
  78. package/dist/public/sdk/{registration-DVX_lnP7.js → registration-CIxJvbTo.js} +1 -1
  79. package/dist/public/sdk/{registration-DeCUrUR9.js → registration-CeOmKyCG.js} +1 -1
  80. package/dist/{esm/sdk/runtime-auth-BAGyfscG.js → public/sdk/runtime-auth-C0V-5znH.js} +1 -1
  81. package/dist/{esm/sdk/runtime-device-link-DKkMLVor.js → public/sdk/runtime-device-link-B1jpHQSF.js} +1 -1
  82. package/dist/{esm/sdk/runtime-ecdsa-tempo-0TAVH1xg.js → public/sdk/runtime-ecdsa-tempo-CDSZ3pxS.js} +1 -1
  83. package/dist/public/sdk/{runtime-email-otp-DoHa8Hif.js → runtime-email-otp-DXuxFOjL.js} +1 -1
  84. package/dist/public/sdk/{runtime-export-CAt17IFH.js → runtime-export-Ba12ChDI.js} +1 -1
  85. package/dist/public/sdk/{runtime-near-BQX9Jk9Z.js → runtime-near-DRrdWzmr.js} +1 -1
  86. package/dist/public/sdk/{runtime-preferences-BuKTIpv6.js → runtime-preferences-BWyhRaBA.js} +1 -1
  87. package/dist/public/sdk/{runtime-recovery-DyxJKKNw.js → runtime-recovery-DlJlaZlf.js} +1 -1
  88. package/dist/public/sdk/{runtime-recovery-codes-CWmt0FDB.js → runtime-recovery-codes-CuqYWqK7.js} +1 -1
  89. package/dist/public/sdk/{shared-BVps1f3F.js → shared-B95wnpoD.js} +1 -1
  90. package/dist/public/sdk/transaction-activity-CpDMkSPm.js +1 -0
  91. package/dist/public/sdk/tx-confirm-ui.js +186 -71
  92. package/dist/public/sdk/wallet-iframe-host-ecdsa.js +1 -1
  93. package/dist/public/sdk/wallet-iframe-host-full.js +1 -1
  94. package/dist/public/sdk/wallet-iframe-host-near.js +1 -1
  95. package/dist/public/sdk/wallet-iframe-host-runtime.js +1 -1
  96. package/dist/public/sdk/wallet-ui.css +1 -1
  97. package/dist/{esm/sdk/walletRecoveryAdminProof-CIoLawfC.js → public/sdk/walletRecoveryAdminProof-CtH8_lph.js} +1 -1
  98. package/dist/public/wallet-assets.manifest.json +50 -51
  99. package/dist/public/wallet-service/index.html +4 -4
  100. package/dist/types/wallet/src/SeamsWeb/walletIframe/host/auth-menu/controller.d.ts +2 -0
  101. package/dist/types/wallet/src/core/signingEngine/uiConfirm/ui/transaction-activity.d.ts +2 -2
  102. package/dist/types/wallet/src/core/signingEngine/uiConfirm/ui/transaction-receipt.d.ts +25 -6
  103. package/dist/types/wallet/src/core/ui/messages.d.ts +1 -1
  104. package/dist/types/wallet/src/externalNear/actions.d.ts +20 -0
  105. package/dist/types/wallet/src/externalNear/boundary.d.ts +7 -0
  106. package/dist/types/wallet/src/externalNear/controller.d.ts +23 -0
  107. package/dist/types/wallet/src/externalNear/hot.d.ts +13 -0
  108. package/dist/types/wallet/src/externalNear/index.d.ts +4 -0
  109. package/dist/types/wallet/src/externalNear/intear.d.ts +12 -0
  110. package/dist/types/wallet/src/externalNear/intearPopup.d.ts +25 -0
  111. package/dist/types/wallet/src/externalNear/meteor.d.ts +16 -0
  112. package/dist/types/wallet/src/externalNear/meteorRequest.d.ts +29 -0
  113. package/dist/types/wallet/src/externalNear/types.d.ts +67 -0
  114. package/dist/types/wallet/src/externalNear/wallets.d.ts +4 -0
  115. package/dist/types/wallet/src/plugins/plugin-utils.d.ts +1 -1
  116. package/dist/types/wallet/src/react/components/AccountMenuButton/icons/MapPinIcon.d.ts +2 -0
  117. package/dist/types/wallet/src/react/externalNear.d.ts +26 -0
  118. package/package.json +18 -1
  119. package/dist/esm/plugins/SeamsWeb/walletIframe/sanitization.js +0 -18
  120. package/dist/esm/react/components/AccountMenuButton/SegmentedControl.js +0 -77
  121. package/dist/esm/sdk/adapters-La60hveX.js +0 -2
  122. package/dist/esm/sdk/appearance-token-vars-Bu6YUWmf.js +0 -2
  123. package/dist/esm/sdk/host-Bz7mRJPZ.js +0 -1
  124. package/dist/esm/sdk/transaction-activity-D5f-Yn9t.js +0 -1
  125. package/dist/public/sdk/adapters-La60hveX.js +0 -2
  126. package/dist/public/sdk/appearance-token-vars-Bu6YUWmf.js +0 -2
  127. package/dist/public/sdk/host-Bz7mRJPZ.js +0 -1
  128. package/dist/public/sdk/transaction-activity-D5f-Yn9t.js +0 -1
  129. package/dist/types/wallet/src/react/components/AccountMenuButton/SegmentedControl.d.ts +0 -25
@@ -0,0 +1,187 @@
1
+ import { createNearWalletProvider, nearWalletName } from "./wallets.js";
2
+ import { connectedNear } from "./types.js";
3
+ import { executionStatus, parseAccounts, parseSubmission, validateTransaction } from "./boundary.js";
4
+ //#region src/externalNear/controller.ts
5
+ function failure(code, message) {
6
+ return {
7
+ ok: false,
8
+ code,
9
+ message
10
+ };
11
+ }
12
+ var ExternalNearController = class {
13
+ snapshot;
14
+ enabledWallets;
15
+ network;
16
+ rpcUrl;
17
+ listeners = /* @__PURE__ */ new Set();
18
+ generation = 0;
19
+ provider = null;
20
+ pending = false;
21
+ constructor(config) {
22
+ if (config.network !== "mainnet" && config.network !== "testnet") throw new Error("Use mainnet or testnet for the NEAR network.");
23
+ const url = new URL(config.rpcUrl);
24
+ if (url.protocol !== "https:" && url.protocol !== "http:") throw new Error("Use an HTTP NEAR RPC URL.");
25
+ this.network = config.network;
26
+ this.rpcUrl = config.rpcUrl;
27
+ this.enabledWallets = normalizeWallets(config.wallets);
28
+ this.snapshot = Object.freeze({
29
+ connection: { kind: "disconnected" },
30
+ busy: false,
31
+ wallets: this.enabledWallets
32
+ });
33
+ }
34
+ setWallets(wallets) {
35
+ this.enabledWallets = normalizeWallets(wallets);
36
+ const connection = this.snapshot.connection;
37
+ if (connection.kind !== "disconnected" && !this.enabledWallets.includes(connection.wallet)) {
38
+ this.disconnect();
39
+ return;
40
+ }
41
+ this.publish(connection);
42
+ }
43
+ getSnapshot = () => this.snapshot;
44
+ subscribe = (listener) => {
45
+ this.listeners.add(listener);
46
+ return this.unsubscribe.bind(this, listener);
47
+ };
48
+ unsubscribe(listener) {
49
+ this.listeners.delete(listener);
50
+ }
51
+ publish(connection) {
52
+ this.snapshot = Object.freeze({
53
+ connection,
54
+ busy: this.pending,
55
+ wallets: this.enabledWallets
56
+ });
57
+ for (const listener of this.listeners) listener();
58
+ }
59
+ async connect(wallet) {
60
+ if (!this.enabledWallets.includes(wallet)) return failure("unavailable", "This wallet is disabled for this application.");
61
+ if (this.pending) return failure("pending_request", "Complete the pending wallet request.");
62
+ if (wallet === "hot" && this.network !== "mainnet") return failure("unavailable", "HOT Wallet is available on mainnet only.");
63
+ this.disconnect();
64
+ this.pending = true;
65
+ const generation = ++this.generation;
66
+ this.publish({
67
+ kind: "connecting",
68
+ wallet
69
+ });
70
+ try {
71
+ if (generation !== this.generation) return failure("connection_changed", "Wallet selection changed.");
72
+ const provider = await createNearWalletProvider(wallet, this.rpcUrl);
73
+ if (generation !== this.generation) {
74
+ provider.dispose();
75
+ return failure("connection_changed", "Wallet selection changed.");
76
+ }
77
+ this.provider = provider;
78
+ const accounts = parseAccounts(await provider.connect(this.network));
79
+ if (generation !== this.generation) return failure("connection_changed", "Wallet selection changed.");
80
+ const accountId = accounts[0];
81
+ if (!accountId) return failure("unavailable", "No wallet account is available.");
82
+ const connection = connectedNear(wallet, accountId, this.network, generation);
83
+ this.publish(connection);
84
+ return {
85
+ ok: true,
86
+ value: connection
87
+ };
88
+ } catch {
89
+ if (generation !== this.generation) return failure("connection_changed", "Wallet selection changed.");
90
+ this.disconnect();
91
+ return failure("unavailable", `${nearWalletName(wallet)} did not connect. Check the wallet and network, then try again.`);
92
+ } finally {
93
+ if (generation === this.generation) {
94
+ this.pending = false;
95
+ this.publish(this.snapshot.connection);
96
+ }
97
+ }
98
+ }
99
+ disconnect() {
100
+ ++this.generation;
101
+ const provider = this.provider;
102
+ this.provider = null;
103
+ this.pending = false;
104
+ provider?.dispose();
105
+ this.publish({ kind: "disconnected" });
106
+ }
107
+ async sendTransaction(connection, input) {
108
+ if (this.pending) return failure("pending_request", "Complete the pending wallet request.");
109
+ if (connection !== this.snapshot.connection || !this.provider) return failure("connection_changed", "Reconnect the wallet before signing.");
110
+ const provider = this.provider;
111
+ const generation = this.generation;
112
+ let transaction;
113
+ try {
114
+ transaction = validateTransaction(input);
115
+ } catch {
116
+ return failure("invalid_input", "Provide a valid transfer or function call.");
117
+ }
118
+ this.pending = true;
119
+ this.publish(connection);
120
+ let dispatched = false;
121
+ try {
122
+ const accounts = parseAccounts(await provider.accounts(this.network));
123
+ if (connection !== this.snapshot.connection || !accounts.includes(connection.accountId)) return failure("connection_changed", "Wallet account changed. Reconnect before signing.");
124
+ dispatched = true;
125
+ return {
126
+ ok: true,
127
+ value: parseSubmission(await provider.send(connection, transaction), connection)
128
+ };
129
+ } catch {
130
+ return dispatched ? failure("submission_unknown", "Submission is unknown. Check wallet activity before sending again.") : failure("unavailable", "Wallet account is unavailable.");
131
+ } finally {
132
+ if (generation === this.generation) {
133
+ this.pending = false;
134
+ this.publish(this.snapshot.connection);
135
+ }
136
+ }
137
+ }
138
+ async checkOutcome(submission) {
139
+ if (submission.network !== this.network) return {
140
+ kind: "unresolved",
141
+ submission
142
+ };
143
+ try {
144
+ const response = await fetch(this.rpcUrl, {
145
+ method: "POST",
146
+ headers: { "Content-Type": "application/json" },
147
+ signal: AbortSignal.timeout(15e3),
148
+ body: JSON.stringify({
149
+ jsonrpc: "2.0",
150
+ id: "external-near",
151
+ method: "tx",
152
+ params: {
153
+ tx_hash: submission.hash,
154
+ sender_account_id: submission.accountId,
155
+ wait_until: "FINAL"
156
+ }
157
+ })
158
+ });
159
+ if (!response.ok) return {
160
+ kind: "unresolved",
161
+ submission
162
+ };
163
+ return {
164
+ kind: executionStatus(await response.json(), submission),
165
+ submission
166
+ };
167
+ } catch {
168
+ return {
169
+ kind: "unresolved",
170
+ submission
171
+ };
172
+ }
173
+ }
174
+ };
175
+ function normalizeWallets(wallets) {
176
+ if (!Array.isArray(wallets)) throw new Error("Provide an explicit wallet list.");
177
+ const enabled = [];
178
+ for (const wallet of wallets) {
179
+ nearWalletName(wallet);
180
+ if (!enabled.includes(wallet)) enabled.push(wallet);
181
+ }
182
+ return Object.freeze(enabled);
183
+ }
184
+ //#endregion
185
+ export { ExternalNearController };
186
+
187
+ //# sourceMappingURL=controller.js.map
@@ -0,0 +1,40 @@
1
+ import { isObject } from "../shared-ts/src/utils/validation.js";
2
+ import { externalNearAction } from "./actions.js";
3
+ //#region src/externalNear/hot.ts
4
+ /** Direct extension API documented by hot-dao/hot-sdk-js. No iframe fallback. */
5
+ var HotExtensionProvider = class {
6
+ lifecycle = "active";
7
+ transport = Reflect.get(window, "hotExtension");
8
+ async request(method, params) {
9
+ if (this.lifecycle !== "active" || !isObject(this.transport) || Reflect.get(window, "hotExtension") !== this.transport) throw new Error("Install HOT Wallet or reconnect after the extension changes.");
10
+ const request = this.transport.request;
11
+ if (typeof request !== "function") throw new Error("HOT extension API is unavailable.");
12
+ return Reflect.apply(request, this.transport, [method, params]);
13
+ }
14
+ async connect(network) {
15
+ if (network !== "mainnet") throw new Error("HOT testnet support is not qualified.");
16
+ const response = await this.request("near:signIn", {});
17
+ if (!isObject(response)) throw new Error("Invalid HOT account response.");
18
+ return [response.accountId];
19
+ }
20
+ async accounts(network) {
21
+ return this.connect(network);
22
+ }
23
+ async send(connection, transaction) {
24
+ if (connection.network !== "mainnet") throw new Error("HOT requires mainnet.");
25
+ const response = await this.request("near:signAndSendTransactions", { transactions: [{
26
+ signerId: connection.accountId,
27
+ receiverId: transaction.receiverId,
28
+ actions: transaction.actions.map(externalNearAction)
29
+ }] });
30
+ if (!isObject(response)) throw new Error("Invalid HOT transaction response.");
31
+ return response.transactions;
32
+ }
33
+ dispose() {
34
+ this.lifecycle = "disposed";
35
+ }
36
+ };
37
+ //#endregion
38
+ export { HotExtensionProvider };
39
+
40
+ //# sourceMappingURL=hot.js.map
@@ -0,0 +1,3 @@
1
+ import { nearWalletName } from "./wallets.js";
2
+ import { ExternalNearController } from "./controller.js";
3
+ export { ExternalNearController, nearWalletName };
@@ -0,0 +1,109 @@
1
+ import { isPlainObject } from "../shared-ts/src/utils/validation.js";
2
+ import { base58Encode } from "../shared-ts/src/utils/base58.js";
3
+ import { isValidAccountId } from "../shared-ts/src/utils/near.js";
4
+ import { externalNearAction } from "./actions.js";
5
+ import { IntearPopup } from "./intearPopup.js";
6
+ //#region src/externalNear/intear.ts
7
+ /** The ephemeral key authenticates requests only. It is never installed on chain. */
8
+ async function authenticate(key, payload) {
9
+ const nonce = Date.now();
10
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(`${nonce}|${payload}`));
11
+ return {
12
+ nonce,
13
+ signature: `ed25519:${base58Encode(await crypto.subtle.sign("Ed25519", key.privateKey, digest))}`
14
+ };
15
+ }
16
+ var IntearWalletProvider = class {
17
+ lifecycle = "active";
18
+ popup = null;
19
+ session = null;
20
+ async connect(network) {
21
+ if (this.lifecycle !== "active") throw new Error("Intear connection disposed.");
22
+ const popup = new IntearPopup("/connect", "connected");
23
+ this.popup = popup;
24
+ const response = popup.result;
25
+ response.catch(ignorePopupFailure);
26
+ try {
27
+ const key = await crypto.subtle.generateKey({ name: "Ed25519" }, false, ["sign", "verify"]);
28
+ const publicKey = `ed25519:${base58Encode(await crypto.subtle.exportKey("raw", key.publicKey))}`;
29
+ const message = "{}";
30
+ const proof = await authenticate(key, message);
31
+ popup.send({
32
+ type: "signIn",
33
+ data: {
34
+ publicKey,
35
+ networkId: network,
36
+ message,
37
+ nonce: proof.nonce,
38
+ signature: proof.signature,
39
+ version: "V3",
40
+ actualOrigin: location.origin
41
+ }
42
+ });
43
+ const result = await response;
44
+ if (!isPlainObject(result) || !isValidAccountId(result.accountId) || result.functionCallKeyAdded !== false || result.useBridge !== false || result.walletUrl !== "https://wallet.intear.tech") throw new Error("Unsupported Intear connection response.");
45
+ this.session = {
46
+ accountId: result.accountId,
47
+ network,
48
+ key,
49
+ publicKey
50
+ };
51
+ return [result.accountId];
52
+ } catch (error) {
53
+ popup.fail(/* @__PURE__ */ new Error("Intear connection failed."));
54
+ throw error;
55
+ } finally {
56
+ if (this.popup === popup) this.popup = null;
57
+ }
58
+ }
59
+ async accounts(network) {
60
+ return this.session?.network === network ? [this.session.accountId] : [];
61
+ }
62
+ async send(connection, transaction) {
63
+ const session = this.session;
64
+ if (!session || session.network !== connection.network || session.accountId !== connection.accountId) throw new Error("Reconnect Intear.");
65
+ if (this.lifecycle !== "active") throw new Error("Intear connection disposed.");
66
+ const popup = new IntearPopup("/send-transactions", "sent");
67
+ this.popup = popup;
68
+ const response = popup.result;
69
+ response.catch(ignorePopupFailure);
70
+ try {
71
+ const transactions = JSON.stringify([{
72
+ signerId: session.accountId,
73
+ receiverId: transaction.receiverId,
74
+ actions: transaction.actions.map(externalNearAction)
75
+ }]);
76
+ const proof = await authenticate(session.key, transactions);
77
+ popup.send({
78
+ type: "signAndSendTransactions",
79
+ data: {
80
+ accountId: session.accountId,
81
+ publicKey: session.publicKey,
82
+ nonce: proof.nonce,
83
+ signature: proof.signature,
84
+ transactions,
85
+ mode: "Send"
86
+ }
87
+ });
88
+ const result = await response;
89
+ if (!isPlainObject(result)) throw new Error("Invalid Intear outcome.");
90
+ return result.outcomes;
91
+ } catch (error) {
92
+ popup.fail(/* @__PURE__ */ new Error("Intear transaction outcome is unknown."));
93
+ throw error;
94
+ } finally {
95
+ if (this.popup === popup) this.popup = null;
96
+ }
97
+ }
98
+ dispose() {
99
+ this.lifecycle = "disposed";
100
+ this.popup?.fail(/* @__PURE__ */ new Error("Intear disconnected. Check wallet activity for pending submissions."));
101
+ this.popup = null;
102
+ this.session = null;
103
+ }
104
+ };
105
+ function ignorePopupFailure() {}
106
+ //#endregion
107
+ export { IntearWalletProvider };
108
+
109
+ //# sourceMappingURL=intear.js.map
@@ -0,0 +1,83 @@
1
+ import { isPlainObject } from "../shared-ts/src/utils/validation.js";
2
+ //#region src/externalNear/intearPopup.ts
3
+ const INTEAR_ORIGIN = "https://wallet.intear.tech";
4
+ /** One popup per request; origin and WindowProxy together bind every response. */
5
+ var IntearPopup = class {
6
+ popup;
7
+ sent = false;
8
+ settled = false;
9
+ ready = false;
10
+ payload = null;
11
+ resolveResult = ignoreResult;
12
+ rejectResult = ignoreError;
13
+ interval;
14
+ timeout;
15
+ result;
16
+ constructor(path, successType) {
17
+ this.successType = successType;
18
+ this.popup = window.open(`${INTEAR_ORIGIN}${path}`, "_blank", "popup,width=420,height=720");
19
+ this.result = new Promise(this.captureResolvers);
20
+ window.addEventListener("message", this.receive);
21
+ this.interval = setInterval(this.checkClosed, 250);
22
+ this.timeout = setTimeout(this.expire, 12e4);
23
+ if (!this.popup) this.fail(/* @__PURE__ */ new Error("Allow popups to connect Intear Wallet."));
24
+ }
25
+ captureResolvers = (resolve, reject) => {
26
+ this.resolveResult = resolve;
27
+ this.rejectResult = reject;
28
+ };
29
+ send(payload) {
30
+ this.payload = payload;
31
+ this.dispatch();
32
+ }
33
+ dispatch() {
34
+ if (!this.settled && this.ready && !this.sent && this.payload && this.popup) {
35
+ this.sent = true;
36
+ this.popup.postMessage(this.payload, INTEAR_ORIGIN);
37
+ }
38
+ }
39
+ receive = (event) => {
40
+ if (this.settled || event.origin !== "https://wallet.intear.tech" || event.source !== this.popup || !isPlainObject(event.data)) return;
41
+ if (event.data.type === "ready") {
42
+ this.ready = true;
43
+ this.dispatch();
44
+ return;
45
+ }
46
+ if (!this.sent) return;
47
+ if (event.data.type === "error") {
48
+ this.fail(/* @__PURE__ */ new Error("Intear declined the request."));
49
+ return;
50
+ }
51
+ if (event.data.type === this.successType) {
52
+ this.cleanup();
53
+ this.resolveResult(event.data);
54
+ }
55
+ };
56
+ checkClosed = () => {
57
+ if (this.popup?.closed) this.fail(/* @__PURE__ */ new Error("Intear popup closed."));
58
+ };
59
+ expire = () => {
60
+ this.fail(/* @__PURE__ */ new Error("Intear request timed out."));
61
+ };
62
+ fail(error) {
63
+ if (!this.settled) {
64
+ this.cleanup();
65
+ this.rejectResult(error);
66
+ }
67
+ }
68
+ cleanup() {
69
+ this.settled = true;
70
+ clearInterval(this.interval);
71
+ clearTimeout(this.timeout);
72
+ window.removeEventListener("message", this.receive);
73
+ this.popup?.close();
74
+ this.popup = null;
75
+ this.payload = null;
76
+ }
77
+ };
78
+ function ignoreResult(_value) {}
79
+ function ignoreError(_error) {}
80
+ //#endregion
81
+ export { INTEAR_ORIGIN, IntearPopup };
82
+
83
+ //# sourceMappingURL=intearPopup.js.map
@@ -0,0 +1,121 @@
1
+ import { isPlainObject } from "../shared-ts/src/utils/validation.js";
2
+ import { base64Encode } from "../shared-ts/src/utils/base64.js";
3
+ import { base58Decode } from "../shared-ts/src/utils/base58.js";
4
+ import { isValidAccountId } from "../shared-ts/src/utils/near.js";
5
+ import { ActionType, toActionArgsWasm } from "../shared-ts/src/near/actions.js";
6
+ import { serialize } from "../node_modules/borsh/lib/esm/index.js";
7
+ import { MeteorRequest, meteorTransportFromWindow } from "./meteorRequest.js";
8
+ //#region src/externalNear/meteor.ts
9
+ const transactionSchema = { struct: {
10
+ signerId: "string",
11
+ publicKey: { struct: {
12
+ keyType: "u8",
13
+ data: { array: {
14
+ type: "u8",
15
+ len: 32
16
+ } }
17
+ } },
18
+ nonce: "u64",
19
+ receiverId: "string",
20
+ blockHash: { array: {
21
+ type: "u8",
22
+ len: 32
23
+ } },
24
+ actions: { array: { type: { enum: [
25
+ { struct: { createAccount: { struct: {} } } },
26
+ { struct: { deployContract: { struct: { code: { array: { type: "u8" } } } } } },
27
+ { struct: { functionCall: { struct: {
28
+ methodName: "string",
29
+ args: { array: { type: "u8" } },
30
+ gas: "u64",
31
+ deposit: "u128"
32
+ } } } },
33
+ { struct: { transfer: { struct: { deposit: "u128" } } } }
34
+ ] } } }
35
+ } };
36
+ function meteorAction(action) {
37
+ const value = toActionArgsWasm(action);
38
+ switch (value.action_type) {
39
+ case ActionType.Transfer: return { transfer: { deposit: BigInt(value.deposit) } };
40
+ case ActionType.FunctionCall: return { functionCall: {
41
+ methodName: value.method_name,
42
+ args: new TextEncoder().encode(value.args),
43
+ gas: BigInt(value.gas),
44
+ deposit: BigInt(value.deposit)
45
+ } };
46
+ default: throw new Error("Unsupported external NEAR action.");
47
+ }
48
+ }
49
+ var MeteorExtensionProvider = class {
50
+ lifecycle = "active";
51
+ transport = meteorTransportFromWindow();
52
+ request = null;
53
+ account = null;
54
+ constructor(rpcUrl) {
55
+ this.rpcUrl = rpcUrl;
56
+ }
57
+ async execute(network, action, inputs) {
58
+ if (this.lifecycle !== "active") throw new Error("Meteor connection disposed.");
59
+ const request = new MeteorRequest(this.transport, network, action, inputs);
60
+ this.request = request;
61
+ try {
62
+ return await request.result;
63
+ } finally {
64
+ if (this.request === request) this.request = null;
65
+ }
66
+ }
67
+ async connect(network) {
68
+ const response = await this.execute(network, "login", { methods: [] });
69
+ if (!isPlainObject(response) || !isValidAccountId(response.accountId)) throw new Error("Invalid Meteor account.");
70
+ this.account = {
71
+ accountId: response.accountId,
72
+ network
73
+ };
74
+ return [response.accountId];
75
+ }
76
+ async accounts(network) {
77
+ return this.account?.network === network ? [this.account.accountId] : [];
78
+ }
79
+ async send(connection, transaction) {
80
+ if (this.account?.accountId !== connection.accountId || this.account.network !== connection.network) throw new Error("Meteor account changed.");
81
+ const response = await fetch(this.rpcUrl, {
82
+ method: "POST",
83
+ headers: { "Content-Type": "application/json" },
84
+ signal: AbortSignal.timeout(15e3),
85
+ body: JSON.stringify({
86
+ jsonrpc: "2.0",
87
+ id: "meteor-block",
88
+ method: "block",
89
+ params: { finality: "final" }
90
+ })
91
+ });
92
+ const block = await response.json();
93
+ if (!response.ok || !isPlainObject(block) || !isPlainObject(block.result) || !isPlainObject(block.result.header) || typeof block.result.header.hash !== "string") throw new Error("Cannot read NEAR block.");
94
+ const blockHash = base58Decode(block.result.header.hash);
95
+ if (blockHash.length !== 32) throw new Error("Invalid NEAR block hash.");
96
+ const encoded = serialize(transactionSchema, {
97
+ signerId: connection.accountId,
98
+ publicKey: {
99
+ keyType: 0,
100
+ data: new Uint8Array(32)
101
+ },
102
+ nonce: 0n,
103
+ receiverId: transaction.receiverId,
104
+ blockHash,
105
+ actions: transaction.actions.map(meteorAction)
106
+ });
107
+ const outcome = await this.execute(connection.network, "sign", { transactions: base64Encode(encoded) });
108
+ if (!isPlainObject(outcome)) throw new Error("Invalid Meteor response.");
109
+ return outcome.executionOutcomes;
110
+ }
111
+ dispose() {
112
+ this.lifecycle = "disposed";
113
+ this.request?.cancel();
114
+ this.request = null;
115
+ this.account = null;
116
+ }
117
+ };
118
+ //#endregion
119
+ export { MeteorExtensionProvider };
120
+
121
+ //# sourceMappingURL=meteor.js.map
@@ -0,0 +1,117 @@
1
+ import { isFunction, isPlainObject } from "../shared-ts/src/utils/validation.js";
2
+ //#region src/externalNear/meteorRequest.ts
3
+ function meteorTransportFromWindow() {
4
+ const raw = Reflect.get(window, "meteorComV2");
5
+ if (!isPlainObject(raw) || !isFunction(raw.sendMessageDataAndRespond)) throw new Error("Meteor extension is not ready.");
6
+ const method = raw.sendMessageDataAndRespond;
7
+ return { sendMessageDataAndRespond: callMeteor.bind(null, raw, method) };
8
+ }
9
+ async function callMeteor(provider, method, request) {
10
+ if (Reflect.get(window, "meteorComV2") !== provider || Reflect.get(provider, "sendMessageDataAndRespond") !== method) throw new Error("Meteor transport changed.");
11
+ return Reflect.apply(method, provider, [request]);
12
+ }
13
+ /** Heartbeats carry one request ID; they never create a second signing request. */
14
+ var MeteorRequest = class {
15
+ uid = crypto.randomUUID();
16
+ state = "initializing";
17
+ resolve = ignoreResult;
18
+ reject = ignoreError;
19
+ timer;
20
+ deadline;
21
+ result;
22
+ constructor(transport, network, actionType, inputs) {
23
+ this.transport = transport;
24
+ this.network = network;
25
+ this.actionType = actionType;
26
+ this.inputs = inputs;
27
+ this.result = new Promise(this.capture);
28
+ this.timer = setInterval(this.poll, 500);
29
+ this.deadline = setTimeout(this.expire, 12e4);
30
+ this.poll();
31
+ }
32
+ capture = (resolve, reject) => {
33
+ this.resolve = resolve;
34
+ this.reject = reject;
35
+ };
36
+ poll = async () => {
37
+ if (this.state === "settled") return;
38
+ const envelope = {
39
+ uid: this.uid,
40
+ network: this.network,
41
+ actionType: this.actionType,
42
+ status: this.state,
43
+ endTags: []
44
+ };
45
+ const request = this.state === "initializing" ? {
46
+ uid: this.uid,
47
+ network: this.network,
48
+ actionType: this.actionType,
49
+ status: this.state,
50
+ endTags: [],
51
+ inputs: this.inputs
52
+ } : envelope;
53
+ try {
54
+ const response = await this.transport.sendMessageDataAndRespond(request);
55
+ this.receive(response);
56
+ } catch {
57
+ this.cancel();
58
+ }
59
+ };
60
+ receive(response) {
61
+ if (this.state === "settled" || response == null) return;
62
+ if (!isPlainObject(response) || response.uid !== this.uid) return;
63
+ switch (response.status) {
64
+ case "attempting_reconnect": {
65
+ const needsInputs = this.state !== "initializing";
66
+ this.state = "initializing";
67
+ if (needsInputs) this.poll();
68
+ return;
69
+ }
70
+ case "connected":
71
+ this.state = "connected";
72
+ return;
73
+ case "initializing": return;
74
+ case "closed_success":
75
+ this.finish();
76
+ this.acknowledge("closed_success");
77
+ this.resolve(response.payload);
78
+ return;
79
+ case "closed_fail":
80
+ case "closed_window":
81
+ this.cancel();
82
+ return;
83
+ default: this.cancel();
84
+ }
85
+ }
86
+ async acknowledge(status) {
87
+ try {
88
+ await this.transport.sendMessageDataAndRespond({
89
+ uid: this.uid,
90
+ network: this.network,
91
+ actionType: this.actionType,
92
+ status,
93
+ endTags: []
94
+ });
95
+ } catch {}
96
+ }
97
+ expire = () => {
98
+ this.cancel();
99
+ };
100
+ cancel() {
101
+ if (this.state === "settled") return;
102
+ this.finish();
103
+ this.acknowledge("disconnected");
104
+ this.reject(/* @__PURE__ */ new Error("Meteor request ended without a verified outcome."));
105
+ }
106
+ finish() {
107
+ this.state = "settled";
108
+ clearInterval(this.timer);
109
+ clearTimeout(this.deadline);
110
+ }
111
+ };
112
+ function ignoreResult(_value) {}
113
+ function ignoreError(_error) {}
114
+ //#endregion
115
+ export { MeteorRequest, meteorTransportFromWindow };
116
+
117
+ //# sourceMappingURL=meteorRequest.js.map
@@ -0,0 +1,17 @@
1
+ //#region src/externalNear/types.ts
2
+ const connectionBrand = Symbol("external NEAR connection");
3
+ function connectedNear(wallet, accountId, network, generation) {
4
+ const connection = {
5
+ kind: "connected",
6
+ wallet,
7
+ accountId,
8
+ network,
9
+ generation,
10
+ [connectionBrand]: true
11
+ };
12
+ return Object.freeze(connection);
13
+ }
14
+ //#endregion
15
+ export { connectedNear };
16
+
17
+ //# sourceMappingURL=types.js.map