@dynamic-labs/midnight 4.90.0 → 4.91.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/CHANGELOG.md +17 -0
- package/package.cjs +1 -1
- package/package.js +1 -1
- package/package.json +8 -6
- package/src/MidnightWalletConnectors.cjs +6 -0
- package/src/MidnightWalletConnectors.js +6 -0
- package/src/connectors/MidnightWalletConnector.cjs +12 -0
- package/src/connectors/MidnightWalletConnector.d.ts +6 -1
- package/src/connectors/MidnightWalletConnector.js +12 -0
- package/src/index.cjs +8 -6
- package/src/index.d.ts +1 -1
- package/src/index.js +4 -2
- package/src/types.d.ts +11 -2
- package/src/utils/MidnightUiTransaction/MidnightUiTransaction.cjs +109 -0
- package/src/utils/MidnightUiTransaction/MidnightUiTransaction.d.ts +2 -2
- package/src/utils/MidnightUiTransaction/MidnightUiTransaction.js +105 -0
- package/src/waas/DynamicWaasMidnightConnectors.cjs +16 -0
- package/src/waas/DynamicWaasMidnightConnectors.d.ts +6 -0
- package/src/waas/DynamicWaasMidnightConnectors.js +12 -0
- package/src/waas/WaasMidnightWalletConnector.cjs +109 -0
- package/src/waas/WaasMidnightWalletConnector.d.ts +44 -0
- package/src/waas/WaasMidnightWalletConnector.js +105 -0
- package/src/waas/connector/DynamicWaasMidnightConnector.cjs +571 -0
- package/src/waas/connector/DynamicWaasMidnightConnector.d.ts +444 -0
- package/src/waas/connector/DynamicWaasMidnightConnector.js +567 -0
- package/src/waas/connector/buildMidnightWaasConnectedApi.cjs +48 -0
- package/src/waas/connector/buildMidnightWaasConnectedApi.d.ts +26 -0
- package/src/waas/connector/buildMidnightWaasConnectedApi.js +42 -0
- package/src/waas/connector/index.d.ts +1 -0
- package/src/waas/index.d.ts +4 -0
- package/src/waas/isMidnightWallet.cjs +15 -0
- package/src/waas/isMidnightWallet.d.ts +7 -0
- package/src/waas/isMidnightWallet.js +11 -0
- package/src/wallet/MidnightWallet.cjs +2 -0
- package/src/wallet/MidnightWallet.js +2 -0
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import { __awaiter } from '../../../_virtual/_tslib.js';
|
|
3
|
+
import { MFAAction, TokenScope } from '@dynamic-labs/sdk-api-core';
|
|
4
|
+
import { DynamicError } from '@dynamic-labs/utils';
|
|
5
|
+
import { withDynamicWaas } from '@dynamic-labs/waas';
|
|
6
|
+
import { isSameAddress } from '@dynamic-labs/wallet-connector-core';
|
|
7
|
+
import { Logger } from '@dynamic-labs/logger';
|
|
8
|
+
import { MIDNIGHT_NATIVE_SHIELDED_TOKEN_TYPE, MIDNIGHT_NATIVE_TOKEN_TYPE } from '../../constants.js';
|
|
9
|
+
import { MidnightUiTransaction } from '../../utils/MidnightUiTransaction/MidnightUiTransaction.js';
|
|
10
|
+
import { MidnightWallet } from '../../wallet/MidnightWallet.js';
|
|
11
|
+
import { WaasMidnightWalletConnector } from '../WaasMidnightWalletConnector.js';
|
|
12
|
+
import { buildMidnightWaasConnectedApi, normalizeDustBalance, toBigIntBalanceMap } from './buildMidnightWaasConnectedApi.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* WaaS (Wallet-as-a-Service) connector for Midnight blockchain.
|
|
16
|
+
* Extends the WaaS-specific Midnight base with MPC wallet functionality.
|
|
17
|
+
*
|
|
18
|
+
* Phase 1: Supports unshielded assets (NIGHT, MNTs) only.
|
|
19
|
+
*/
|
|
20
|
+
class DynamicWaasMidnightConnector extends withDynamicWaas(WaasMidnightWalletConnector) {
|
|
21
|
+
constructor(props) {
|
|
22
|
+
super('Dynamic Waas', props);
|
|
23
|
+
this.ChainWallet = MidnightWallet;
|
|
24
|
+
this.name = 'Dynamic Waas';
|
|
25
|
+
this.overrideKey = 'dynamicwaas';
|
|
26
|
+
this.isEmbeddedWallet = true;
|
|
27
|
+
this.logger = new Logger('DynamicWaasMidnightConnector');
|
|
28
|
+
this.walletUiUtils = props.walletUiUtils;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Signs a message with additional context information
|
|
32
|
+
* @param message - The message to sign (string or object with raw property)
|
|
33
|
+
* @param context - Additional context for the message signing
|
|
34
|
+
* @returns The signed message as a string
|
|
35
|
+
* @throws {Error} Method not implemented for Midnight
|
|
36
|
+
*/
|
|
37
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
38
|
+
signMessageWithContext({ message, context, }) {
|
|
39
|
+
/* eslint-enable @typescript-eslint/no-unused-vars */
|
|
40
|
+
throw new Error('Method not implemented.');
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Override setVerifiedCredentials to filter and set Midnight WaaS credentials
|
|
44
|
+
* Filters for credentials with walletName === 'dynamicwaas' and chain === 'midnight'
|
|
45
|
+
*/
|
|
46
|
+
setVerifiedCredentials(verifiedCredentials) {
|
|
47
|
+
this.verifiedCredentials = verifiedCredentials.filter((vc) => vc.walletName === 'dynamicwaas' && vc.chain === 'midnight');
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Override setEnvironmentId to ensure it's set on the logger
|
|
51
|
+
*/
|
|
52
|
+
setEnvironmentId(environmentId) {
|
|
53
|
+
super.setEnvironmentId(environmentId);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Gets the wallet client for a specific account address and sets it as active
|
|
57
|
+
* @param accountAddress - The Midnight account address
|
|
58
|
+
* @returns The WaaS wallet client instance
|
|
59
|
+
*/
|
|
60
|
+
getWalletClientByAddress(_a) {
|
|
61
|
+
return __awaiter(this, arguments, void 0, function* ({ accountAddress, }) {
|
|
62
|
+
this.setActiveAccountAddress(accountAddress);
|
|
63
|
+
return this.getWaasWalletClient();
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Gets the currently active account address
|
|
68
|
+
* @returns The active account address or undefined if not set
|
|
69
|
+
*/
|
|
70
|
+
getActiveAccountAddress() {
|
|
71
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
72
|
+
return this.activeAccountAddress;
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Sets the active account address for this connector
|
|
77
|
+
* @param accountAddress - The Midnight account address to set as active
|
|
78
|
+
*/
|
|
79
|
+
setActiveAccountAddress(accountAddress) {
|
|
80
|
+
this.activeAccountAddress = accountAddress;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Called when the user selects this wallet in the modal.
|
|
84
|
+
* Sets the active account so getAddress/getNetwork work correctly during network validation.
|
|
85
|
+
*/
|
|
86
|
+
afterWalletSelectHook(walletAddress) {
|
|
87
|
+
this.setActiveAccountAddress(walletAddress);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Requires and returns the signed session ID
|
|
91
|
+
* @returns The signed session ID
|
|
92
|
+
* @throws {DynamicError} If signed session ID is not available
|
|
93
|
+
*/
|
|
94
|
+
requireSignedSessionId() {
|
|
95
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
96
|
+
var _a;
|
|
97
|
+
const signedSessionId = yield ((_a = this.getSignedSessionId) === null || _a === void 0 ? void 0 : _a.call(this));
|
|
98
|
+
if (!signedSessionId) {
|
|
99
|
+
throw new DynamicError('Signed session ID is required');
|
|
100
|
+
}
|
|
101
|
+
return signedSessionId;
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Validates that the expected wallet address matches the active wallet
|
|
106
|
+
* Updates the active account address if it doesn't match
|
|
107
|
+
* @param expectedAddress - The expected wallet address to validate
|
|
108
|
+
* @throws {DynamicError} If signed session ID is not available or account is not found
|
|
109
|
+
*/
|
|
110
|
+
validateActiveWallet(expectedAddress) {
|
|
111
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
112
|
+
var _a;
|
|
113
|
+
const walletClient = yield this.getWaasWalletClient();
|
|
114
|
+
const signedSessionId = yield this.requireSignedSessionId();
|
|
115
|
+
const targetWallet = yield walletClient.getWallet({
|
|
116
|
+
accountAddress: expectedAddress,
|
|
117
|
+
authToken: (_a = this.getAuthToken) === null || _a === void 0 ? void 0 : _a.call(this),
|
|
118
|
+
signedSessionId,
|
|
119
|
+
});
|
|
120
|
+
if (!targetWallet) {
|
|
121
|
+
throw new DynamicError('Account not found');
|
|
122
|
+
}
|
|
123
|
+
const isWalletActive = isSameAddress(targetWallet.accountAddress, this.activeAccountAddress || '', this.connectedChain);
|
|
124
|
+
if (!isWalletActive) {
|
|
125
|
+
this.activeAccountAddress = targetWallet.accountAddress;
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* When active account is not set (e.g. primary wallet restored from session),
|
|
131
|
+
* fall back to the first Midnight verified credential so signing and getAddress work.
|
|
132
|
+
*/
|
|
133
|
+
ensureActiveAccountFromVerifiedCredentials() {
|
|
134
|
+
var _a, _b;
|
|
135
|
+
if (this.activeAccountAddress)
|
|
136
|
+
return;
|
|
137
|
+
const address = (_b = (_a = this.verifiedCredentials) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.address;
|
|
138
|
+
if (typeof address === 'string') {
|
|
139
|
+
this.setActiveAccountAddress(address);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Gets the active wallet address
|
|
144
|
+
* Falls back to verified credential address if no active account is set
|
|
145
|
+
* @returns The wallet address or empty string if not found
|
|
146
|
+
*/
|
|
147
|
+
getAddress() {
|
|
148
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
149
|
+
this.ensureActiveAccountFromVerifiedCredentials();
|
|
150
|
+
return this.activeAccountAddress || '';
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Connects to the WaaS wallet client
|
|
155
|
+
*/
|
|
156
|
+
connect() {
|
|
157
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
158
|
+
yield this.getWaasWalletClient();
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Disconnects from the wallet (no-op for WaaS)
|
|
163
|
+
*/
|
|
164
|
+
disconnect() {
|
|
165
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
166
|
+
// WaaS doesn't require explicit disconnect
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Signs a message using the wallet UI utils for user interaction
|
|
171
|
+
* @param message - The message to sign
|
|
172
|
+
* @returns The signed message as a string
|
|
173
|
+
*/
|
|
174
|
+
signMessage(message) {
|
|
175
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
176
|
+
return this.walletUiUtils.signMessage({
|
|
177
|
+
handler: () => __awaiter(this, void 0, void 0, function* () { return this.internalSignMessage(message); }),
|
|
178
|
+
message,
|
|
179
|
+
walletConnector: this,
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Internal method to sign a message with MFA token
|
|
185
|
+
* @param message - The message to sign
|
|
186
|
+
* @returns The signed message as a string
|
|
187
|
+
* @throws {DynamicError} If active account address is not set
|
|
188
|
+
* @throws {DynamicError} If signed session ID is not available
|
|
189
|
+
*/
|
|
190
|
+
internalSignMessage(message) {
|
|
191
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
192
|
+
var _a, _b;
|
|
193
|
+
this.ensureActiveAccountFromVerifiedCredentials();
|
|
194
|
+
const walletClient = yield this.getWaasWalletClient();
|
|
195
|
+
if (!this.activeAccountAddress) {
|
|
196
|
+
throw new DynamicError('Active account address is required');
|
|
197
|
+
}
|
|
198
|
+
const signedSessionId = yield this.requireSignedSessionId();
|
|
199
|
+
const mfaToken = yield ((_a = this.getMfaToken) === null || _a === void 0 ? void 0 : _a.call(this, {
|
|
200
|
+
mfaAction: MFAAction.WalletWaasSign,
|
|
201
|
+
}));
|
|
202
|
+
const password = yield this.getPasswordIfNeeded({
|
|
203
|
+
accountAddress: this.activeAccountAddress,
|
|
204
|
+
});
|
|
205
|
+
const signedMessage = yield walletClient.signMessage({
|
|
206
|
+
accountAddress: this.activeAccountAddress,
|
|
207
|
+
authToken: (_b = this.getAuthToken) === null || _b === void 0 ? void 0 : _b.call(this),
|
|
208
|
+
message,
|
|
209
|
+
mfaToken,
|
|
210
|
+
password,
|
|
211
|
+
signedSessionId,
|
|
212
|
+
});
|
|
213
|
+
return signedMessage;
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Ends the current session and clears the active account address
|
|
218
|
+
*/
|
|
219
|
+
endSession() {
|
|
220
|
+
const _super = Object.create(null, {
|
|
221
|
+
endSession: { get: () => super.endSession }
|
|
222
|
+
});
|
|
223
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
224
|
+
yield _super.endSession.call(this);
|
|
225
|
+
this.activeAccountAddress = undefined;
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Signs and finalizes (ZK proves) a Midnight transaction via MPC.
|
|
230
|
+
* Step 2 of the 3-step flow: createTransferTransaction → signTransaction → submitTransaction.
|
|
231
|
+
*
|
|
232
|
+
* @param serializedTransaction - Base64-encoded unsigned UnprovenTransaction
|
|
233
|
+
* @returns Base64-encoded FinalizedTransaction, ready for submitTransaction
|
|
234
|
+
*/
|
|
235
|
+
signTransaction(serializedTransaction) {
|
|
236
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
237
|
+
var _a, _b, _c, _d;
|
|
238
|
+
if (!this.activeAccountAddress) {
|
|
239
|
+
throw new DynamicError('Active account address is required');
|
|
240
|
+
}
|
|
241
|
+
const walletClient = yield this.getWaasWalletClient();
|
|
242
|
+
const signedSessionId = yield this.requireSignedSessionId();
|
|
243
|
+
const mfaToken = yield ((_a = this.getMfaToken) === null || _a === void 0 ? void 0 : _a.call(this, {
|
|
244
|
+
mfaAction: MFAAction.WalletWaasSign,
|
|
245
|
+
}));
|
|
246
|
+
const password = yield this.getPasswordIfNeeded({
|
|
247
|
+
accountAddress: this.activeAccountAddress,
|
|
248
|
+
});
|
|
249
|
+
// Derive the chain name (e.g. "mainnet" / "preview") — not the numeric
|
|
250
|
+
// network ID — because `chainId` is typed as a string and resolved by
|
|
251
|
+
// name on the wallet-client side. Passing a stringified numeric id
|
|
252
|
+
// (`"1"`) reaches `getNetworkIdFromChainId`, which doesn't recognize it.
|
|
253
|
+
const selectedNetwork = this.getSelectedNetwork();
|
|
254
|
+
const isTestnet = (_c = (_b = selectedNetwork === null || selectedNetwork === void 0 ? void 0 : selectedNetwork.name) === null || _b === void 0 ? void 0 : _b.toLowerCase().includes('testnet')) !== null && _c !== void 0 ? _c : false;
|
|
255
|
+
const chainId = isTestnet ? 'preview' : 'mainnet';
|
|
256
|
+
// Sign the transaction using MPC
|
|
257
|
+
const signature = yield walletClient.signTransaction({
|
|
258
|
+
authToken: (_d = this.getAuthToken) === null || _d === void 0 ? void 0 : _d.call(this),
|
|
259
|
+
chainId,
|
|
260
|
+
mfaToken,
|
|
261
|
+
password,
|
|
262
|
+
senderAddress: this.activeAccountAddress,
|
|
263
|
+
signedSessionId,
|
|
264
|
+
transaction: serializedTransaction,
|
|
265
|
+
});
|
|
266
|
+
return signature;
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Creates a UI transaction for the send balance flow
|
|
271
|
+
* @param from - The sender's address
|
|
272
|
+
* @returns A new IUITransaction instance
|
|
273
|
+
*/
|
|
274
|
+
createUiTransaction(from) {
|
|
275
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
276
|
+
yield this.validateActiveWallet(from);
|
|
277
|
+
return new MidnightUiTransaction({
|
|
278
|
+
// No on-chain dust-fee estimate is exposed yet; DUST fees are charged
|
|
279
|
+
// at submit time. TODO: surface a real estimate from the wallet client.
|
|
280
|
+
fetchDustFee: () => __awaiter(this, void 0, void 0, function* () { return BigInt(0); }),
|
|
281
|
+
from,
|
|
282
|
+
// Balances come from the connector (MPC state), reusing the cached
|
|
283
|
+
// synced wallet — never from the redcoast public-balance endpoint,
|
|
284
|
+
// which doesn't support Midnight addresses.
|
|
285
|
+
getShieldedBalance: () => __awaiter(this, void 0, void 0, function* () {
|
|
286
|
+
var _a;
|
|
287
|
+
return (_a = (yield this.getBalances()).shielded[MIDNIGHT_NATIVE_SHIELDED_TOKEN_TYPE]) !== null && _a !== void 0 ? _a : BigInt(0);
|
|
288
|
+
}),
|
|
289
|
+
getUnshieldedBalance: () => __awaiter(this, void 0, void 0, function* () {
|
|
290
|
+
var _b;
|
|
291
|
+
return (_b = (yield this.getBalances()).unshielded[MIDNIGHT_NATIVE_TOKEN_TYPE]) !== null && _b !== void 0 ? _b : BigInt(0);
|
|
292
|
+
}),
|
|
293
|
+
onSubmit: (tx) => __awaiter(this, void 0, void 0, function* () {
|
|
294
|
+
if (tx.to === undefined || tx.value === undefined) {
|
|
295
|
+
throw new DynamicError('Recipient and amount are required before submitting');
|
|
296
|
+
}
|
|
297
|
+
// 3-step Midnight transfer: build → sign (ZK prove) → submit.
|
|
298
|
+
const { serializedTransaction } = yield this.createTransferTransaction({
|
|
299
|
+
transfers: [
|
|
300
|
+
{
|
|
301
|
+
amount: tx.value.toString(),
|
|
302
|
+
recipientAddress: tx.to,
|
|
303
|
+
type: tx.activePool,
|
|
304
|
+
},
|
|
305
|
+
],
|
|
306
|
+
});
|
|
307
|
+
const finalized = yield this.signTransaction(serializedTransaction);
|
|
308
|
+
// Return the tx hash so the Send flow advances to the success view.
|
|
309
|
+
const { txHash } = yield this.submitTransaction(finalized);
|
|
310
|
+
return txHash;
|
|
311
|
+
}),
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Returns the provider.
|
|
317
|
+
* For WaaS, there is no traditional browser provider.
|
|
318
|
+
*/
|
|
319
|
+
getProvider() {
|
|
320
|
+
return undefined;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Creates a new Midnight wallet account.
|
|
324
|
+
* Midnight wallets automatically create multiple address types:
|
|
325
|
+
* - Primary: unshielded address (for public transfers)
|
|
326
|
+
* - Additional: dust address (for gas/fee management) and shielded address (for private transfers)
|
|
327
|
+
*
|
|
328
|
+
* All address types are derived from a single keygen using different BIP-44 derivation paths.
|
|
329
|
+
*
|
|
330
|
+
* @param thresholdSignatureScheme - The threshold signature scheme (default: 'TWO_OF_TWO')
|
|
331
|
+
* @param password - Optional password for the wallet
|
|
332
|
+
* @returns The created wallet account information with primary (unshielded) address
|
|
333
|
+
*/
|
|
334
|
+
createWalletAccount() {
|
|
335
|
+
const _super = Object.create(null, {
|
|
336
|
+
createWalletAccount: { get: () => super.createWalletAccount }
|
|
337
|
+
});
|
|
338
|
+
return __awaiter(this, arguments, void 0, function* ({ thresholdSignatureScheme = 'TWO_OF_TWO', password, } = {}) {
|
|
339
|
+
// Midnight automatically creates all address types (unshielded, dust, shielded)
|
|
340
|
+
// from a single keygen. The primary address is unshielded, additional addresses
|
|
341
|
+
// (dust and shielded) are returned as additionalAddresses from the server.
|
|
342
|
+
return _super.createWalletAccount.call(this, {
|
|
343
|
+
password,
|
|
344
|
+
thresholdSignatureScheme,
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
// Passes through the wallet client's shape. `dust` carries `{ balance, cap }`
|
|
349
|
+
// (wallet-SDK v4 — cap = summed dust maxCap). `dustSynced` is false while dust
|
|
350
|
+
// finishes syncing in the background (balance resolves on shielded+unshielded
|
|
351
|
+
// first). Matches the base `IDynamicWaasConnector` / `DynamicWaasMixin`.
|
|
352
|
+
getPrivateBalance() {
|
|
353
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
354
|
+
var _a, _b, _c, _d;
|
|
355
|
+
this.ensureActiveAccountFromVerifiedCredentials();
|
|
356
|
+
if (!this.activeAccountAddress) {
|
|
357
|
+
throw new DynamicError('Active account address is required');
|
|
358
|
+
}
|
|
359
|
+
const walletClient = yield this.getWaasWalletClient();
|
|
360
|
+
const signedSessionId = yield this.requireSignedSessionId();
|
|
361
|
+
const mfaToken = yield ((_a = this.getMfaToken) === null || _a === void 0 ? void 0 : _a.call(this, {
|
|
362
|
+
mfaAction: MFAAction.WalletWaasSign,
|
|
363
|
+
}));
|
|
364
|
+
const elevatedAccessToken = yield ((_b = this.getElevatedAccessToken) === null || _b === void 0 ? void 0 : _b.call(this, {
|
|
365
|
+
scope: TokenScope.Walletsign,
|
|
366
|
+
}));
|
|
367
|
+
const result = yield walletClient.getPrivateBalance({
|
|
368
|
+
accountAddress: this.activeAccountAddress,
|
|
369
|
+
authToken: (_c = this.getAuthToken) === null || _c === void 0 ? void 0 : _c.call(this),
|
|
370
|
+
elevatedAccessToken,
|
|
371
|
+
mfaToken,
|
|
372
|
+
signedSessionId,
|
|
373
|
+
});
|
|
374
|
+
// Defensive: wallet-client builds that predate the dust decouple don't
|
|
375
|
+
// return `dustSynced` — treat those as already-synced (no loading state),
|
|
376
|
+
// preserving prior behavior until the client is republished.
|
|
377
|
+
return Object.assign(Object.assign({}, result), { dustSynced: (_d = result.dustSynced) !== null && _d !== void 0 ? _d : true });
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Exposes the same `ConnectedAPI` surface injected Midnight wallets provide,
|
|
382
|
+
* adapted from WaaS MPC state. This is what lets the shared
|
|
383
|
+
* `MidnightWalletConnector` balance methods (`getShieldedBalances`,
|
|
384
|
+
* `getUnshieldedBalances`, `getDustBalance`) work for WaaS wallets — they all
|
|
385
|
+
* delegate to `getConnectedAPI()`, which throws (returns undefined) by
|
|
386
|
+
* default. Address lookups and transfers stay on the WaaS-specific paths.
|
|
387
|
+
*/
|
|
388
|
+
getConnectedAPI() {
|
|
389
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
390
|
+
return buildMidnightWaasConnectedApi(() => this.getPrivateBalance());
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Adapts the WaaS MPC `getPrivateBalance()` result to the shape
|
|
395
|
+
* `MidnightWallet.getFormattedBalances()` expects. The base
|
|
396
|
+
* `MidnightWalletConnector.getBalances()` throws unconditionally (it does not
|
|
397
|
+
* route through `getConnectedAPI`), so WaaS still overrides it directly —
|
|
398
|
+
* here in a single `getPrivateBalance()` call.
|
|
399
|
+
*
|
|
400
|
+
* Cap-tolerant: `dust.cap` is 0n on browser-wallet-client <= 1.0.16 and the
|
|
401
|
+
* real cap once the wallet-SDK-v4 change is published (no code change needed).
|
|
402
|
+
*/
|
|
403
|
+
getBalances() {
|
|
404
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
405
|
+
const { shielded, unshielded, dust, dustSynced } = yield this.getPrivateBalance();
|
|
406
|
+
return {
|
|
407
|
+
dust: normalizeDustBalance(dust),
|
|
408
|
+
// True while dust is still catching up (balance resolved on
|
|
409
|
+
// shielded+unshielded). Drives the "dust loading" UI; only WaaS sets it.
|
|
410
|
+
dustSyncing: !dustSynced,
|
|
411
|
+
shielded: toBigIntBalanceMap(shielded),
|
|
412
|
+
unshielded: toBigIntBalanceMap(unshielded),
|
|
413
|
+
};
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
registerDust() {
|
|
417
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
418
|
+
var _a, _b, _c;
|
|
419
|
+
this.ensureActiveAccountFromVerifiedCredentials();
|
|
420
|
+
if (!this.activeAccountAddress) {
|
|
421
|
+
throw new DynamicError('Active account address is required');
|
|
422
|
+
}
|
|
423
|
+
const walletClient = yield this.getWaasWalletClient();
|
|
424
|
+
const signedSessionId = yield this.requireSignedSessionId();
|
|
425
|
+
const mfaToken = yield ((_a = this.getMfaToken) === null || _a === void 0 ? void 0 : _a.call(this, {
|
|
426
|
+
mfaAction: MFAAction.WalletWaasSign,
|
|
427
|
+
}));
|
|
428
|
+
const elevatedAccessToken = yield ((_b = this.getElevatedAccessToken) === null || _b === void 0 ? void 0 : _b.call(this, {
|
|
429
|
+
scope: TokenScope.Walletsign,
|
|
430
|
+
}));
|
|
431
|
+
return walletClient.registerDust({
|
|
432
|
+
accountAddress: this.activeAccountAddress,
|
|
433
|
+
authToken: (_c = this.getAuthToken) === null || _c === void 0 ? void 0 : _c.call(this),
|
|
434
|
+
elevatedAccessToken,
|
|
435
|
+
mfaToken,
|
|
436
|
+
signedSessionId,
|
|
437
|
+
});
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Step 1 of the Midnight transfer flow.
|
|
442
|
+
* Builds an unsigned UnprovenTransaction from one or more transfer specs.
|
|
443
|
+
* Accepts any combination of unshielded / shielded transfers in a single tx.
|
|
444
|
+
* No MPC, no proof server, no broadcast.
|
|
445
|
+
*/
|
|
446
|
+
createTransferTransaction(params) {
|
|
447
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
448
|
+
var _a, _b, _c;
|
|
449
|
+
this.ensureActiveAccountFromVerifiedCredentials();
|
|
450
|
+
if (!this.activeAccountAddress) {
|
|
451
|
+
throw new DynamicError('Active account address is required');
|
|
452
|
+
}
|
|
453
|
+
const walletClient = yield this.getWaasWalletClient();
|
|
454
|
+
const signedSessionId = yield this.requireSignedSessionId();
|
|
455
|
+
const mfaToken = yield ((_a = this.getMfaToken) === null || _a === void 0 ? void 0 : _a.call(this, {
|
|
456
|
+
mfaAction: MFAAction.WalletWaasSign,
|
|
457
|
+
}));
|
|
458
|
+
const elevatedAccessToken = yield ((_b = this.getElevatedAccessToken) === null || _b === void 0 ? void 0 : _b.call(this, {
|
|
459
|
+
scope: TokenScope.Walletsign,
|
|
460
|
+
}));
|
|
461
|
+
return walletClient.createTransferTransaction({
|
|
462
|
+
accountAddress: this.activeAccountAddress,
|
|
463
|
+
authToken: (_c = this.getAuthToken) === null || _c === void 0 ? void 0 : _c.call(this),
|
|
464
|
+
elevatedAccessToken,
|
|
465
|
+
mfaToken,
|
|
466
|
+
signedSessionId,
|
|
467
|
+
transfers: params.transfers,
|
|
468
|
+
ttlMinutes: params.ttlMinutes,
|
|
469
|
+
});
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Release UTXOs reserved by an abandoned draft or signed-but-never-submitted
|
|
474
|
+
* transaction. Accepts either an unsigned (Step 1 output) or finalized
|
|
475
|
+
* (Step 2 output) serialized transaction.
|
|
476
|
+
*/
|
|
477
|
+
revertTransaction(serializedTransaction) {
|
|
478
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
479
|
+
return this.callRevertTransaction({ transaction: serializedTransaction });
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Revert every finalized-but-unsubmitted transaction the SDK knows about
|
|
484
|
+
* (`state.pending.all`). Does not cover unfinalized Step-1-only drafts —
|
|
485
|
+
* those are never registered with the pending-tx service by design. If
|
|
486
|
+
* UTXOs are still stuck after this, use `resetWalletCache()`.
|
|
487
|
+
*/
|
|
488
|
+
revertAllPending() {
|
|
489
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
490
|
+
return this.callRevertTransaction({ all: true });
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Nuclear reset. Stops the cached wallet, clears its persisted IndexedDB
|
|
495
|
+
* entry, and forces the next operation to cold-sync from chain. Midnight's
|
|
496
|
+
* officially recommended workaround when UTXOs get stuck in pending state
|
|
497
|
+
* that can't be reverted through the normal API.
|
|
498
|
+
*/
|
|
499
|
+
resetWalletCache() {
|
|
500
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
501
|
+
return this.callRevertTransaction({ resetCache: true });
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
// Internal shared glue: resolves auth + forwards to the iframe handler.
|
|
505
|
+
callRevertTransaction(args) {
|
|
506
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
507
|
+
var _a, _b, _c;
|
|
508
|
+
this.ensureActiveAccountFromVerifiedCredentials();
|
|
509
|
+
if (!this.activeAccountAddress) {
|
|
510
|
+
throw new DynamicError('Active account address is required');
|
|
511
|
+
}
|
|
512
|
+
const walletClient = yield this.getWaasWalletClient();
|
|
513
|
+
const signedSessionId = yield this.requireSignedSessionId();
|
|
514
|
+
const mfaToken = yield ((_a = this.getMfaToken) === null || _a === void 0 ? void 0 : _a.call(this, {
|
|
515
|
+
mfaAction: MFAAction.WalletWaasSign,
|
|
516
|
+
}));
|
|
517
|
+
const elevatedAccessToken = yield ((_b = this.getElevatedAccessToken) === null || _b === void 0 ? void 0 : _b.call(this, {
|
|
518
|
+
scope: TokenScope.Walletsign,
|
|
519
|
+
}));
|
|
520
|
+
return walletClient.revertTransaction({
|
|
521
|
+
accountAddress: this.activeAccountAddress,
|
|
522
|
+
all: args.all,
|
|
523
|
+
authToken: (_c = this.getAuthToken) === null || _c === void 0 ? void 0 : _c.call(this),
|
|
524
|
+
elevatedAccessToken,
|
|
525
|
+
mfaToken,
|
|
526
|
+
resetCache: args.resetCache,
|
|
527
|
+
signedSessionId,
|
|
528
|
+
transaction: args.transaction,
|
|
529
|
+
});
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Step 3 of the unshielded public transfer flow.
|
|
534
|
+
* Broadcasts a finalized transaction via the wallet's NodeClient.
|
|
535
|
+
* Optionally waits for the unshielded balance to stabilize.
|
|
536
|
+
*/
|
|
537
|
+
submitTransaction(serializedFinalizedTransaction, options) {
|
|
538
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
539
|
+
var _a, _b, _c;
|
|
540
|
+
this.ensureActiveAccountFromVerifiedCredentials();
|
|
541
|
+
if (!this.activeAccountAddress) {
|
|
542
|
+
throw new DynamicError('Active account address is required');
|
|
543
|
+
}
|
|
544
|
+
const walletClient = yield this.getWaasWalletClient();
|
|
545
|
+
const signedSessionId = yield this.requireSignedSessionId();
|
|
546
|
+
const mfaToken = yield ((_a = this.getMfaToken) === null || _a === void 0 ? void 0 : _a.call(this, {
|
|
547
|
+
mfaAction: MFAAction.WalletWaasSign,
|
|
548
|
+
}));
|
|
549
|
+
const elevatedAccessToken = yield ((_b = this.getElevatedAccessToken) === null || _b === void 0 ? void 0 : _b.call(this, {
|
|
550
|
+
scope: TokenScope.Walletsign,
|
|
551
|
+
}));
|
|
552
|
+
return walletClient.submitTransaction({
|
|
553
|
+
accountAddress: this.activeAccountAddress,
|
|
554
|
+
authToken: (_c = this.getAuthToken) === null || _c === void 0 ? void 0 : _c.call(this),
|
|
555
|
+
balanceBefore: options === null || options === void 0 ? void 0 : options.balanceBefore,
|
|
556
|
+
elevatedAccessToken,
|
|
557
|
+
mfaToken,
|
|
558
|
+
signedSessionId,
|
|
559
|
+
tokenType: options === null || options === void 0 ? void 0 : options.tokenType,
|
|
560
|
+
transaction: serializedFinalizedTransaction,
|
|
561
|
+
waitForBalance: options === null || options === void 0 ? void 0 : options.waitForBalance,
|
|
562
|
+
});
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
export { DynamicWaasMidnightConnector };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
5
|
+
|
|
6
|
+
var _tslib = require('../../../_virtual/_tslib.cjs');
|
|
7
|
+
|
|
8
|
+
const toBigIntBalanceMap = (record) => {
|
|
9
|
+
const out = {};
|
|
10
|
+
for (const [tokenType, value] of Object.entries(record)) {
|
|
11
|
+
out[tokenType] = BigInt(value);
|
|
12
|
+
}
|
|
13
|
+
return out;
|
|
14
|
+
};
|
|
15
|
+
// Normalizes the cap-tolerant `dust` field to `{ balance, cap }` bigints.
|
|
16
|
+
// cap is 0n on clients that don't yet surface it.
|
|
17
|
+
const normalizeDustBalance = (dust) => typeof dust === 'string'
|
|
18
|
+
? { balance: BigInt(dust), cap: BigInt(0) }
|
|
19
|
+
: { balance: BigInt(dust.balance), cap: BigInt(dust.cap) };
|
|
20
|
+
/**
|
|
21
|
+
* Builds a minimal `ConnectedAPI` adapter for WaaS Midnight wallets so the
|
|
22
|
+
* shared `MidnightWalletConnector` balance methods — which delegate to
|
|
23
|
+
* `getConnectedAPI()` — behave the same as they do for injected wallets.
|
|
24
|
+
*
|
|
25
|
+
* Only the balance dimensions are derivable from MPC state. Address lookups
|
|
26
|
+
* (which need coin/encryption public keys) and transfers use the
|
|
27
|
+
* WaaS-specific paths (server `additionalAddresses` and the 3-step
|
|
28
|
+
* createTransferTransaction → sign → submit flow), so they throw here.
|
|
29
|
+
*/
|
|
30
|
+
const buildMidnightWaasConnectedApi = (getPrivateBalance) => {
|
|
31
|
+
const unsupported = (method) => () => {
|
|
32
|
+
throw new Error(`${method} is not available on WaaS Midnight wallets; use the WaaS-specific flow`);
|
|
33
|
+
};
|
|
34
|
+
return {
|
|
35
|
+
getDustAddress: unsupported('getDustAddress'),
|
|
36
|
+
getDustBalance: () => _tslib.__awaiter(void 0, void 0, void 0, function* () { return normalizeDustBalance((yield getPrivateBalance()).dust); }),
|
|
37
|
+
getShieldedAddresses: unsupported('getShieldedAddresses'),
|
|
38
|
+
getShieldedBalances: () => _tslib.__awaiter(void 0, void 0, void 0, function* () { return toBigIntBalanceMap((yield getPrivateBalance()).shielded); }),
|
|
39
|
+
getUnshieldedAddress: unsupported('getUnshieldedAddress'),
|
|
40
|
+
getUnshieldedBalances: () => _tslib.__awaiter(void 0, void 0, void 0, function* () { return toBigIntBalanceMap((yield getPrivateBalance()).unshielded); }),
|
|
41
|
+
makeTransfer: unsupported('makeTransfer'),
|
|
42
|
+
submitTransaction: unsupported('submitTransaction'),
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
exports.buildMidnightWaasConnectedApi = buildMidnightWaasConnectedApi;
|
|
47
|
+
exports.normalizeDustBalance = normalizeDustBalance;
|
|
48
|
+
exports.toBigIntBalanceMap = toBigIntBalanceMap;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ConnectedAPI, TokenType } from '@midnight-ntwrk/dapp-connector-api';
|
|
2
|
+
export type WaasPrivateBalance = {
|
|
3
|
+
unshielded: Record<string, string>;
|
|
4
|
+
shielded: Record<string, string>;
|
|
5
|
+
dust: string | {
|
|
6
|
+
balance: string;
|
|
7
|
+
cap: string;
|
|
8
|
+
};
|
|
9
|
+
address: string;
|
|
10
|
+
};
|
|
11
|
+
export declare const toBigIntBalanceMap: (record: Record<string, string>) => Record<TokenType, bigint>;
|
|
12
|
+
export declare const normalizeDustBalance: (dust: WaasPrivateBalance['dust']) => {
|
|
13
|
+
balance: bigint;
|
|
14
|
+
cap: bigint;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Builds a minimal `ConnectedAPI` adapter for WaaS Midnight wallets so the
|
|
18
|
+
* shared `MidnightWalletConnector` balance methods — which delegate to
|
|
19
|
+
* `getConnectedAPI()` — behave the same as they do for injected wallets.
|
|
20
|
+
*
|
|
21
|
+
* Only the balance dimensions are derivable from MPC state. Address lookups
|
|
22
|
+
* (which need coin/encryption public keys) and transfers use the
|
|
23
|
+
* WaaS-specific paths (server `additionalAddresses` and the 3-step
|
|
24
|
+
* createTransferTransaction → sign → submit flow), so they throw here.
|
|
25
|
+
*/
|
|
26
|
+
export declare const buildMidnightWaasConnectedApi: (getPrivateBalance: () => Promise<WaasPrivateBalance>) => ConnectedAPI;
|