@seams/wallet 0.22.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.
- package/THIRD_PARTY_NOTICES.md +5 -2
- package/dist/.build-inputs.sha256 +1 -1
- package/dist/esm/externalNear/actions.js +26 -0
- package/dist/esm/externalNear/boundary.js +53 -0
- package/dist/esm/externalNear/controller.js +187 -0
- package/dist/esm/externalNear/hot.js +40 -0
- package/dist/esm/externalNear/index.js +3 -0
- package/dist/esm/externalNear/intear.js +109 -0
- package/dist/esm/externalNear/intearPopup.js +83 -0
- package/dist/esm/externalNear/meteor.js +121 -0
- package/dist/esm/externalNear/meteorRequest.js +117 -0
- package/dist/esm/externalNear/types.js +17 -0
- package/dist/esm/externalNear/wallets.js +33 -0
- package/dist/esm/node_modules/borsh/lib/esm/buffer.js +67 -0
- package/dist/esm/node_modules/borsh/lib/esm/deserialize.js +94 -0
- package/dist/esm/node_modules/borsh/lib/esm/index.js +13 -0
- package/dist/esm/node_modules/borsh/lib/esm/serialize.js +127 -0
- package/dist/esm/node_modules/borsh/lib/esm/types.js +19 -0
- package/dist/esm/node_modules/borsh/lib/esm/utils.js +104 -0
- package/dist/esm/plugins/plugin-utils.js +2 -5
- package/dist/esm/react/externalNear.css +90 -0
- package/dist/esm/react/externalNear.js +104 -0
- package/dist/esm/sdk/host-C5KYWEcs.js +1 -0
- package/dist/esm/sdk/wallet-iframe-host-ecdsa.js +1 -1
- package/dist/esm/sdk/wallet-iframe-host-full.js +1 -1
- package/dist/esm/sdk/wallet-iframe-host-near.js +1 -1
- package/dist/esm/sdk/wallet-iframe-host-runtime.js +1 -1
- package/dist/esm/sdk/wallet-ui.css +1 -1
- package/dist/public/headers.manifest.json +1 -1
- package/dist/public/sdk/host-C5KYWEcs.js +1 -0
- package/dist/public/sdk/wallet-iframe-host-ecdsa.js +1 -1
- package/dist/public/sdk/wallet-iframe-host-full.js +1 -1
- package/dist/public/sdk/wallet-iframe-host-near.js +1 -1
- package/dist/public/sdk/wallet-iframe-host-runtime.js +1 -1
- package/dist/public/sdk/wallet-ui.css +1 -1
- package/dist/public/wallet-assets.manifest.json +4 -5
- package/dist/public/wallet-service/index.html +4 -4
- package/dist/types/wallet/src/externalNear/actions.d.ts +20 -0
- package/dist/types/wallet/src/externalNear/boundary.d.ts +7 -0
- package/dist/types/wallet/src/externalNear/controller.d.ts +23 -0
- package/dist/types/wallet/src/externalNear/hot.d.ts +13 -0
- package/dist/types/wallet/src/externalNear/index.d.ts +4 -0
- package/dist/types/wallet/src/externalNear/intear.d.ts +12 -0
- package/dist/types/wallet/src/externalNear/intearPopup.d.ts +25 -0
- package/dist/types/wallet/src/externalNear/meteor.d.ts +16 -0
- package/dist/types/wallet/src/externalNear/meteorRequest.d.ts +29 -0
- package/dist/types/wallet/src/externalNear/types.d.ts +67 -0
- package/dist/types/wallet/src/externalNear/wallets.d.ts +4 -0
- package/dist/types/wallet/src/plugins/plugin-utils.d.ts +1 -1
- package/dist/types/wallet/src/react/externalNear.d.ts +26 -0
- package/package.json +18 -1
- package/dist/esm/plugins/SeamsWeb/walletIframe/sanitization.js +0 -18
- package/dist/esm/sdk/host-DoftJdVQ.js +0 -1
- package/dist/public/sdk/host-DoftJdVQ.js +0 -1
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -5,7 +5,7 @@ distributed Seams Wallet SDK, server runtime, Wasm, strict Workers, and CLI.
|
|
|
5
5
|
Dependency license expressions come from their package metadata. Packaged
|
|
6
6
|
license and notice wording is reproduced below with normalized whitespace.
|
|
7
7
|
|
|
8
|
-
Inventory:
|
|
8
|
+
Inventory: 451 dependency records (42 npm, 409 Cargo).
|
|
9
9
|
|
|
10
10
|
## Dependency inventory
|
|
11
11
|
|
|
@@ -446,6 +446,7 @@ Inventory: 450 dependency records (41 npm, 409 Cargo).
|
|
|
446
446
|
| npm | abitype | 1.2.3 | MIT | awkweb.eth <t@wevm.dev>; jxom.eth <j@wevm.dev> |
|
|
447
447
|
| npm | asn1js | 3.0.7 | BSD-3-Clause | Ryan Hurst rmh@unmitigatedrisk.com; Yury Strozhevsky yury@strozhevsky.com |
|
|
448
448
|
| npm | base-x | 5.0.1 | MIT | Daniel Cousens |
|
|
449
|
+
| npm | borsh | 1.0.0 | Apache-2.0 | Near Inc |
|
|
449
450
|
| npm | bs58 | 6.0.0 | MIT | |
|
|
450
451
|
| npm | eventemitter3 | 5.0.1 | MIT | Arnout Kazemier |
|
|
451
452
|
| npm | idb | 8.0.3 | ISC | Jake Archibald |
|
|
@@ -2767,7 +2768,7 @@ Applies to:
|
|
|
2767
2768
|
|
|
2768
2769
|
### 23f18e03dc49df91
|
|
2769
2770
|
|
|
2770
|
-
Packaged as: LICENSE, LICENSE-MIT
|
|
2771
|
+
Packaged as: LICENSE, LICENSE-MIT, LICENSE-MIT.txt
|
|
2771
2772
|
|
|
2772
2773
|
Applies to:
|
|
2773
2774
|
|
|
@@ -2837,6 +2838,7 @@ Applies to:
|
|
|
2837
2838
|
- cargo:zmij@1.0.21
|
|
2838
2839
|
- cargo:zmij@1.0.23
|
|
2839
2840
|
- npm:@near-js/types@2.4.0
|
|
2841
|
+
- npm:borsh@1.0.0
|
|
2840
2842
|
|
|
2841
2843
|
Permission is hereby granted, free of charge, to any
|
|
2842
2844
|
person obtaining a copy of this software and associated
|
|
@@ -7353,6 +7355,7 @@ Applies to:
|
|
|
7353
7355
|
- cargo:borsh@1.6.1
|
|
7354
7356
|
- cargo:borsh@1.8.0
|
|
7355
7357
|
- cargo:borsh@1.8.1
|
|
7358
|
+
- npm:borsh@1.0.0
|
|
7356
7359
|
|
|
7357
7360
|
Apache License
|
|
7358
7361
|
Version 2.0, January 2004
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
1338f3f6f35ca0a9209fea6d25a3fa10bac28bba32ba928546abfac7a06b84a3
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ActionType, toActionArgsWasm } from "../shared-ts/src/near/actions.js";
|
|
2
|
+
//#region src/externalNear/actions.ts
|
|
3
|
+
/** Wallet Selector's JSON action format, shared by HOT and Intear. */
|
|
4
|
+
function externalNearAction(action) {
|
|
5
|
+
const normalized = toActionArgsWasm(action);
|
|
6
|
+
switch (normalized.action_type) {
|
|
7
|
+
case ActionType.Transfer: return {
|
|
8
|
+
type: "Transfer",
|
|
9
|
+
params: { deposit: normalized.deposit }
|
|
10
|
+
};
|
|
11
|
+
case ActionType.FunctionCall: return {
|
|
12
|
+
type: "FunctionCall",
|
|
13
|
+
params: {
|
|
14
|
+
methodName: normalized.method_name,
|
|
15
|
+
args: JSON.parse(normalized.args),
|
|
16
|
+
gas: normalized.gas,
|
|
17
|
+
deposit: normalized.deposit
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
default: throw new Error("Unsupported external NEAR action.");
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
export { externalNearAction };
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=actions.js.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { isPlainObject } from "../shared-ts/src/utils/validation.js";
|
|
2
|
+
import { isValidAccountId } from "../shared-ts/src/utils/near.js";
|
|
3
|
+
import { ActionType, toActionArgsWasm } from "../shared-ts/src/near/actions.js";
|
|
4
|
+
import bs58 from "bs58";
|
|
5
|
+
//#region src/externalNear/boundary.ts
|
|
6
|
+
function parseAccounts(raw) {
|
|
7
|
+
if (!Array.isArray(raw) || raw.length === 0) throw new Error("The wallet has no connected accounts.");
|
|
8
|
+
const accounts = [];
|
|
9
|
+
for (const account of raw) {
|
|
10
|
+
if (!isValidAccountId(account)) throw new Error("The wallet returned an invalid account.");
|
|
11
|
+
accounts.push(account);
|
|
12
|
+
}
|
|
13
|
+
return Object.freeze(accounts);
|
|
14
|
+
}
|
|
15
|
+
function validateTransaction(input) {
|
|
16
|
+
if (!isValidAccountId(input.receiverId) || input.actions.length === 0) throw new Error("Provide a receiver and at least one action.");
|
|
17
|
+
for (const action of input.actions) {
|
|
18
|
+
const normalized = toActionArgsWasm(action);
|
|
19
|
+
if (normalized.action_type !== ActionType.FunctionCall && normalized.action_type !== ActionType.Transfer) throw new Error("External NEAR supports function calls and transfers.");
|
|
20
|
+
if (!/^\d+$/.test(normalized.deposit)) throw new Error("Deposit must be an unsigned integer.");
|
|
21
|
+
if (normalized.action_type === ActionType.FunctionCall && (!/^\d+$/.test(normalized.gas) || BigInt(normalized.gas) === 0n || !normalized.method_name)) throw new Error("Provide a method and positive gas amount.");
|
|
22
|
+
}
|
|
23
|
+
return structuredClone(input);
|
|
24
|
+
}
|
|
25
|
+
function parseSubmission(raw, connection) {
|
|
26
|
+
if (!Array.isArray(raw) || raw.length !== 1) throw new Error("Missing transaction outcome.");
|
|
27
|
+
const outcome = raw[0];
|
|
28
|
+
if (!isPlainObject(outcome) || !isPlainObject(outcome.transaction)) throw new Error("Invalid transaction outcome.");
|
|
29
|
+
const { hash, signer_id } = outcome.transaction;
|
|
30
|
+
if (typeof hash !== "string" || bs58.decode(hash).length !== 32 || signer_id !== connection.accountId) throw new Error("Transaction identity does not match the request.");
|
|
31
|
+
return Object.freeze({
|
|
32
|
+
hash,
|
|
33
|
+
accountId: connection.accountId,
|
|
34
|
+
network: connection.network
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function executionStatus(raw, submission) {
|
|
38
|
+
if (!isPlainObject(raw) || !isPlainObject(raw.result)) return "unresolved";
|
|
39
|
+
const result = raw.result;
|
|
40
|
+
if (!isPlainObject(result.transaction) || result.transaction.hash !== submission.hash || result.transaction.signer_id !== submission.accountId || result.final_execution_status !== "FINAL") return "unresolved";
|
|
41
|
+
if (!isPlainObject(result.status)) return "unresolved";
|
|
42
|
+
if ("Failure" in result.status) return "failed";
|
|
43
|
+
if (!("SuccessValue" in result.status) || !Array.isArray(result.receipts_outcome)) return "unresolved";
|
|
44
|
+
for (const receipt of result.receipts_outcome) {
|
|
45
|
+
if (!isPlainObject(receipt) || !isPlainObject(receipt.outcome) || !isPlainObject(receipt.outcome.status)) return "unresolved";
|
|
46
|
+
if ("Failure" in receipt.outcome.status) return "failed";
|
|
47
|
+
}
|
|
48
|
+
return "confirmed";
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
export { executionStatus, parseAccounts, parseSubmission, validateTransaction };
|
|
52
|
+
|
|
53
|
+
//# sourceMappingURL=boundary.js.map
|
|
@@ -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,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
|