@dynamic-labs/midnight 4.91.0 → 4.91.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -0
- package/package.cjs +1 -1
- package/package.js +1 -1
- package/package.json +8 -8
- package/src/connectors/MidnightWalletConnector.cjs +29 -0
- package/src/connectors/MidnightWalletConnector.d.ts +36 -0
- package/src/connectors/MidnightWalletConnector.js +29 -0
- package/src/utils/MidnightUiTransaction/MidnightUiTransaction.cjs +80 -40
- package/src/utils/MidnightUiTransaction/MidnightUiTransaction.d.ts +14 -5
- package/src/utils/MidnightUiTransaction/MidnightUiTransaction.js +81 -41
- package/src/waas/connector/DynamicWaasMidnightConnector.cjs +46 -20
- package/src/waas/connector/DynamicWaasMidnightConnector.d.ts +37 -5
- package/src/waas/connector/DynamicWaasMidnightConnector.js +47 -21
- package/src/wallet/MidnightWallet.cjs +34 -4
- package/src/wallet/MidnightWallet.d.ts +15 -0
- package/src/wallet/MidnightWallet.js +34 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,47 @@
|
|
|
1
1
|
|
|
2
|
+
### [4.91.2](https://github.com/dynamic-labs/dynamic-auth/compare/v4.91.1...v4.91.2) (2026-06-26)
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
### ⚠ BREAKING CHANGES
|
|
6
|
+
|
|
7
|
+
* **waas:** require expectedAddresses on migrateFromFireblocks (#11730)
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **midnight:** support shielded/unshielded transfers and dust states in the embedded Send flow ([#11731](https://github.com/dynamic-labs/dynamic-auth/issues/11731)) ([663d171](https://github.com/dynamic-labs/dynamic-auth/commit/663d171a9628c634586831ee35c234e9331e3f80))
|
|
12
|
+
* **react-native-extension:** instrument all embedded webview failure paths ([#11737](https://github.com/dynamic-labs/dynamic-auth/issues/11737)) ([172998b](https://github.com/dynamic-labs/dynamic-auth/commit/172998b56e9bee04fbb8da71e83ce8885bc378ff))
|
|
13
|
+
* **waas:** migrateFromFireblocks connector method + demo panel (browser-wallet-client 1.0.46) ([#11702](https://github.com/dynamic-labs/dynamic-auth/issues/11702)) ([6bfc844](https://github.com/dynamic-labs/dynamic-auth/commit/6bfc8443fbaedabe1e7bee0ed78742cbbe5d0084))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* add coverage for scenario with passkey re-login and smart account ([#11726](https://github.com/dynamic-labs/dynamic-auth/issues/11726)) ([5364a72](https://github.com/dynamic-labs/dynamic-auth/commit/5364a72a750d835b9b2da602a91e8b5d7d305db7))
|
|
19
|
+
* remediate high-severity dependency vulnerabilities ([#11721](https://github.com/dynamic-labs/dynamic-auth/issues/11721)) ([cdb9306](https://github.com/dynamic-labs/dynamic-auth/commit/cdb93061365a20b8f73a1f8bea8352332e282c1e))
|
|
20
|
+
* replace node-fetch v2 with native fetch to fix Node 22 stream failure ([#11732](https://github.com/dynamic-labs/dynamic-auth/issues/11732)) ([b98f801](https://github.com/dynamic-labs/dynamic-auth/commit/b98f801e307b52dbd7e4b241a9a4a2d34d89118e))
|
|
21
|
+
* **zerodev-extension:** derive kernel 7702 vs 4337 per-wallet, not from global dashboard flag ([#11735](https://github.com/dynamic-labs/dynamic-auth/issues/11735)) ([72cf6a7](https://github.com/dynamic-labs/dynamic-auth/commit/72cf6a70a4857a4ea469faa555c485dbad8d0217))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
* **waas:** require expectedAddresses on migrateFromFireblocks ([#11730](https://github.com/dynamic-labs/dynamic-auth/issues/11730)) ([c94824e](https://github.com/dynamic-labs/dynamic-auth/commit/c94824ebe11e0433a6ffbd2a5a9dd4283aeed31e))
|
|
25
|
+
|
|
26
|
+
### [4.91.1](https://github.com/dynamic-labs/dynamic-auth/compare/v4.91.0...v4.91.1) (2026-06-24)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### Features
|
|
30
|
+
|
|
31
|
+
* **aleo:** update sendBalance with individual and marketplace logic ([#11709](https://github.com/dynamic-labs/dynamic-auth/issues/11709)) ([f8dfc48](https://github.com/dynamic-labs/dynamic-auth/commit/f8dfc488ae607fbbbcc3432a3ef69f746f0b7df0))
|
|
32
|
+
* **sdk-react-core:** surface shielded address in Midnight deposit view ([#11698](https://github.com/dynamic-labs/dynamic-auth/issues/11698)) ([79c5402](https://github.com/dynamic-labs/dynamic-auth/commit/79c54029b1d8f722cf9f1d8b4c11977898442ac0))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
### Bug Fixes
|
|
36
|
+
|
|
37
|
+
* display actual error message for WaaS export failures instead of generic fallback ([#11498](https://github.com/dynamic-labs/dynamic-auth/issues/11498)) ([373c931](https://github.com/dynamic-labs/dynamic-auth/commit/373c931eb96c88e484c557ee8f2934660d028f97))
|
|
38
|
+
* **wallet-book:** guard replaceKey and remove stale override ([#11682](https://github.com/dynamic-labs/dynamic-auth/issues/11682)) ([13947d9](https://github.com/dynamic-labs/dynamic-auth/commit/13947d972ed1f8227712bfc01af9cc2be616a78a))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
### Performance Improvements
|
|
42
|
+
|
|
43
|
+
* **e2e:** prebuild Expo web bundle for RN e2e tests ([#11674](https://github.com/dynamic-labs/dynamic-auth/issues/11674)) ([16e002d](https://github.com/dynamic-labs/dynamic-auth/commit/16e002d59b2035e3f2c116879a78418a959da8bc))
|
|
44
|
+
|
|
2
45
|
## [4.91.0](https://github.com/dynamic-labs/dynamic-auth/compare/v4.90.0...v4.91.0) (2026-06-23)
|
|
3
46
|
|
|
4
47
|
|
package/package.cjs
CHANGED
package/package.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dynamic-labs/midnight",
|
|
3
|
-
"version": "4.91.
|
|
3
|
+
"version": "4.91.2",
|
|
4
4
|
"description": "A React SDK for implementing wallet web3 authentication and authorization to your website.",
|
|
5
5
|
"author": "Dynamic Labs, Inc.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,15 +18,15 @@
|
|
|
18
18
|
},
|
|
19
19
|
"homepage": "https://www.dynamic.xyz/",
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@dynamic-labs/assert-package-version": "4.91.
|
|
21
|
+
"@dynamic-labs/assert-package-version": "4.91.2",
|
|
22
22
|
"@dynamic-labs/sdk-api-core": "0.0.1046",
|
|
23
23
|
"@midnight-ntwrk/dapp-connector-api": "4.0.1",
|
|
24
|
-
"@dynamic-labs/logger": "4.91.
|
|
25
|
-
"@dynamic-labs/types": "4.91.
|
|
26
|
-
"@dynamic-labs/utils": "4.91.
|
|
27
|
-
"@dynamic-labs/waas": "4.91.
|
|
28
|
-
"@dynamic-labs/wallet-book": "4.91.
|
|
29
|
-
"@dynamic-labs/wallet-connector-core": "4.91.
|
|
24
|
+
"@dynamic-labs/logger": "4.91.2",
|
|
25
|
+
"@dynamic-labs/types": "4.91.2",
|
|
26
|
+
"@dynamic-labs/utils": "4.91.2",
|
|
27
|
+
"@dynamic-labs/waas": "4.91.2",
|
|
28
|
+
"@dynamic-labs/wallet-book": "4.91.2",
|
|
29
|
+
"@dynamic-labs/wallet-connector-core": "4.91.2"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {}
|
|
32
32
|
}
|
|
@@ -74,6 +74,35 @@ class MidnightWalletConnector extends walletConnectorCore.WalletConnectorBase {
|
|
|
74
74
|
throw new Error('signTransaction is not supported by this connector');
|
|
75
75
|
});
|
|
76
76
|
}
|
|
77
|
+
createTransferTransaction(
|
|
78
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
79
|
+
params) {
|
|
80
|
+
return _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
81
|
+
throw new Error('createTransferTransaction is not supported by this connector');
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
registerDust() {
|
|
85
|
+
return _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
86
|
+
throw new Error('registerDust is not supported by this connector');
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
revertTransaction(
|
|
90
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
91
|
+
serializedTransaction) {
|
|
92
|
+
return _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
93
|
+
throw new Error('revertTransaction is not supported by this connector');
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
revertAllPending() {
|
|
97
|
+
return _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
98
|
+
throw new Error('revertAllPending is not supported by this connector');
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
resetWalletCache() {
|
|
102
|
+
return _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
103
|
+
throw new Error('resetWalletCache is not supported by this connector');
|
|
104
|
+
});
|
|
105
|
+
}
|
|
77
106
|
getShieldedBalances() {
|
|
78
107
|
return _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
79
108
|
const api = yield this.getConnectedAPI();
|
|
@@ -31,6 +31,42 @@ export declare abstract class MidnightWalletConnector extends WalletConnectorBas
|
|
|
31
31
|
}>;
|
|
32
32
|
}): Promise<string>;
|
|
33
33
|
signTransaction(serializedTransaction: string): Promise<string>;
|
|
34
|
+
createTransferTransaction(params: {
|
|
35
|
+
transfers: Array<{
|
|
36
|
+
type: 'unshielded' | 'shielded';
|
|
37
|
+
recipientAddress: string;
|
|
38
|
+
amount: string;
|
|
39
|
+
tokenType?: string;
|
|
40
|
+
}>;
|
|
41
|
+
ttlMinutes?: number;
|
|
42
|
+
}): Promise<{
|
|
43
|
+
serializedTransaction: string;
|
|
44
|
+
}>;
|
|
45
|
+
registerDust(): Promise<{
|
|
46
|
+
dustBalance: string;
|
|
47
|
+
txId?: string;
|
|
48
|
+
status: 'registered' | 'already_registered' | 'already_has_dust' | 'no_utxos';
|
|
49
|
+
message: string;
|
|
50
|
+
registeredCount: number;
|
|
51
|
+
}>;
|
|
52
|
+
revertTransaction(serializedTransaction: string): Promise<{
|
|
53
|
+
reverted: boolean;
|
|
54
|
+
count: number;
|
|
55
|
+
cacheCleared: boolean;
|
|
56
|
+
message: string;
|
|
57
|
+
}>;
|
|
58
|
+
revertAllPending(): Promise<{
|
|
59
|
+
reverted: boolean;
|
|
60
|
+
count: number;
|
|
61
|
+
cacheCleared: boolean;
|
|
62
|
+
message: string;
|
|
63
|
+
}>;
|
|
64
|
+
resetWalletCache(): Promise<{
|
|
65
|
+
reverted: boolean;
|
|
66
|
+
count: number;
|
|
67
|
+
cacheCleared: boolean;
|
|
68
|
+
message: string;
|
|
69
|
+
}>;
|
|
34
70
|
getShieldedBalances(): Promise<Record<TokenType, bigint>>;
|
|
35
71
|
getUnshieldedBalances(): Promise<Record<TokenType, bigint>>;
|
|
36
72
|
getDustBalance(): Promise<{
|
|
@@ -70,6 +70,35 @@ class MidnightWalletConnector extends WalletConnectorBase {
|
|
|
70
70
|
throw new Error('signTransaction is not supported by this connector');
|
|
71
71
|
});
|
|
72
72
|
}
|
|
73
|
+
createTransferTransaction(
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
75
|
+
params) {
|
|
76
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
77
|
+
throw new Error('createTransferTransaction is not supported by this connector');
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
registerDust() {
|
|
81
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
82
|
+
throw new Error('registerDust is not supported by this connector');
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
revertTransaction(
|
|
86
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
87
|
+
serializedTransaction) {
|
|
88
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
89
|
+
throw new Error('revertTransaction is not supported by this connector');
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
revertAllPending() {
|
|
93
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
94
|
+
throw new Error('revertAllPending is not supported by this connector');
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
resetWalletCache() {
|
|
98
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
99
|
+
throw new Error('resetWalletCache is not supported by this connector');
|
|
100
|
+
});
|
|
101
|
+
}
|
|
73
102
|
getShieldedBalances() {
|
|
74
103
|
return __awaiter(this, void 0, void 0, function* () {
|
|
75
104
|
const api = yield this.getConnectedAPI();
|
|
@@ -8,6 +8,10 @@ var constants = require('../../constants.cjs');
|
|
|
8
8
|
var formatMidnightAtomicBalance = require('../formatMidnightAtomicBalance/formatMidnightAtomicBalance.cjs');
|
|
9
9
|
var parseMidnightAtomicAmount = require('../parseMidnightAtomicAmount/parseMidnightAtomicAmount.cjs');
|
|
10
10
|
|
|
11
|
+
// Shielded tokens aren't NIGHT and have no decimals metadata, so they're shown
|
|
12
|
+
// at their raw amount (decimals 0); native NIGHT keeps 6-decimal formatting.
|
|
13
|
+
const SHIELDED_TOKEN_DECIMALS = 0;
|
|
14
|
+
const shortTokenLabel = (address) => `TOKEN: ${address.slice(0, 8)}...${address.slice(-8)}`;
|
|
11
15
|
class MidnightUiTransaction {
|
|
12
16
|
constructor(opts) {
|
|
13
17
|
this.to = undefined;
|
|
@@ -17,68 +21,104 @@ class MidnightUiTransaction {
|
|
|
17
21
|
this.chain = 'MIDNIGHT';
|
|
18
22
|
this.fee = { gas: undefined };
|
|
19
23
|
this.feeTokenSymbol = 'DUST';
|
|
24
|
+
// Pool defaults to unshielded; it is derived from the selected token (or the
|
|
25
|
+
// transaction-mode toggle), never inferred from a hard-coded token hash.
|
|
20
26
|
this.pool = 'unshielded';
|
|
27
|
+
this.selectedTransactionMode = 'unshielded';
|
|
28
|
+
this.selectedTokenAddress = constants.MIDNIGHT_NATIVE_TOKEN_TYPE;
|
|
21
29
|
this.from = opts.from;
|
|
30
|
+
this.balances = opts.balances;
|
|
31
|
+
this.shieldedAddresses = new Set(Object.keys(opts.balances.shielded));
|
|
22
32
|
this.onSubmitFn = opts.onSubmit;
|
|
23
|
-
this.getUnshieldedBalanceFn = opts.getUnshieldedBalance;
|
|
24
|
-
this.getShieldedBalanceFn = opts.getShieldedBalance;
|
|
25
33
|
this.fetchDustFeeFn = opts.fetchDustFee;
|
|
26
34
|
}
|
|
27
35
|
get activePool() {
|
|
28
36
|
return this.pool;
|
|
29
37
|
}
|
|
38
|
+
get transactionModes() {
|
|
39
|
+
return [
|
|
40
|
+
{ id: 'unshielded', label: 'Unshielded' },
|
|
41
|
+
{ id: 'shielded', label: 'Shielded' },
|
|
42
|
+
];
|
|
43
|
+
}
|
|
44
|
+
get selectedTokenDecimals() {
|
|
45
|
+
return this.selectedTokenAddress === constants.MIDNIGHT_NATIVE_TOKEN_TYPE
|
|
46
|
+
? constants.MIDNIGHT_NATIVE_UNSHIELDED_DECIMALS
|
|
47
|
+
: SHIELDED_TOKEN_DECIMALS;
|
|
48
|
+
}
|
|
49
|
+
// Pool a token belongs to — used by the send form to filter the picker.
|
|
50
|
+
getTokenPool(contractAddress) {
|
|
51
|
+
return this.shieldedAddresses.has(contractAddress)
|
|
52
|
+
? 'shielded'
|
|
53
|
+
: 'unshielded';
|
|
54
|
+
}
|
|
55
|
+
setTransactionMode(modeId) {
|
|
56
|
+
this.selectedTransactionMode = modeId;
|
|
57
|
+
this.pool = modeId === 'shielded' ? 'shielded' : 'unshielded';
|
|
58
|
+
this.to = undefined;
|
|
59
|
+
this.value = undefined;
|
|
60
|
+
}
|
|
30
61
|
setSelectedToken(contractAddress) {
|
|
31
|
-
this.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
: 'unshielded';
|
|
62
|
+
this.selectedTokenAddress = contractAddress;
|
|
63
|
+
this.pool = this.getTokenPool(contractAddress);
|
|
64
|
+
this.selectedTransactionMode = this.pool;
|
|
35
65
|
this.to = undefined;
|
|
36
66
|
this.value = undefined;
|
|
37
67
|
}
|
|
38
68
|
getSendableTokenBalances() {
|
|
39
69
|
return _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
]);
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
var _a;
|
|
71
|
+
const toDisplay = (raw, decimals) => Number(formatMidnightAtomicBalance.formatMidnightAtomicBalance(raw, decimals));
|
|
72
|
+
// Native unshielded NIGHT — always shown, even at a zero balance.
|
|
73
|
+
const unshieldedNightRaw = (_a = this.balances.unshielded[constants.MIDNIGHT_NATIVE_TOKEN_TYPE]) !== null && _a !== void 0 ? _a : BigInt(0);
|
|
74
|
+
const nightEntry = {
|
|
75
|
+
address: constants.MIDNIGHT_NATIVE_TOKEN_TYPE,
|
|
76
|
+
balance: toDisplay(unshieldedNightRaw, constants.MIDNIGHT_NATIVE_UNSHIELDED_DECIMALS),
|
|
77
|
+
decimals: constants.MIDNIGHT_NATIVE_UNSHIELDED_DECIMALS,
|
|
78
|
+
isNative: true,
|
|
79
|
+
logoURI: '',
|
|
80
|
+
name: 'NIGHT',
|
|
81
|
+
rawBalance: Number(unshieldedNightRaw),
|
|
82
|
+
rawBalanceString: unshieldedNightRaw.toString(),
|
|
83
|
+
symbol: 'NIGHT',
|
|
84
|
+
};
|
|
85
|
+
// Non-NIGHT tokens (other unshielded + every shielded token), shown at raw
|
|
86
|
+
// amount. `isNative: true` is deliberate — like Aleo, every Midnight token
|
|
87
|
+
// uses the `transaction.parse`/`getBalance` path; the form's non-native
|
|
88
|
+
// branch (`parseNonNativeToken`) is EVM-only and doesn't apply here.
|
|
89
|
+
const tokenEntry = (address, raw) => ({
|
|
90
|
+
address,
|
|
91
|
+
balance: toDisplay(raw, SHIELDED_TOKEN_DECIMALS),
|
|
92
|
+
decimals: SHIELDED_TOKEN_DECIMALS,
|
|
93
|
+
isNative: true,
|
|
94
|
+
logoURI: '',
|
|
95
|
+
name: shortTokenLabel(address),
|
|
96
|
+
rawBalance: Number(raw),
|
|
97
|
+
rawBalanceString: raw.toString(),
|
|
98
|
+
symbol: '',
|
|
99
|
+
});
|
|
100
|
+
const otherUnshielded = Object.entries(this.balances.unshielded)
|
|
101
|
+
.filter(([address, raw]) => address !== constants.MIDNIGHT_NATIVE_TOKEN_TYPE && raw > BigInt(0))
|
|
102
|
+
.map(([address, raw]) => tokenEntry(address, raw));
|
|
103
|
+
const shielded = Object.entries(this.balances.shielded)
|
|
104
|
+
.filter(([, raw]) => raw > BigInt(0))
|
|
105
|
+
.map(([address, raw]) => tokenEntry(address, raw));
|
|
106
|
+
return [nightEntry, ...otherUnshielded, ...shielded];
|
|
69
107
|
});
|
|
70
108
|
}
|
|
71
109
|
parse(input) {
|
|
72
|
-
return parseMidnightAtomicAmount.parseMidnightAtomicAmount(input,
|
|
110
|
+
return parseMidnightAtomicAmount.parseMidnightAtomicAmount(input, this.selectedTokenDecimals);
|
|
73
111
|
}
|
|
74
112
|
format(value) {
|
|
75
|
-
return formatMidnightAtomicBalance.formatMidnightAtomicBalance(value,
|
|
113
|
+
return formatMidnightAtomicBalance.formatMidnightAtomicBalance(value, this.selectedTokenDecimals);
|
|
76
114
|
}
|
|
77
115
|
getBalance() {
|
|
78
116
|
return _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
117
|
+
var _a;
|
|
118
|
+
const map = this.pool === 'shielded'
|
|
119
|
+
? this.balances.shielded
|
|
120
|
+
: this.balances.unshielded;
|
|
121
|
+
return (_a = map[this.selectedTokenAddress]) !== null && _a !== void 0 ? _a : BigInt(0);
|
|
82
122
|
});
|
|
83
123
|
}
|
|
84
124
|
fetchFee() {
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import type { TokenBalance } from '@dynamic-labs/sdk-api-core';
|
|
2
|
-
import type { IUITransaction } from '@dynamic-labs/types';
|
|
2
|
+
import type { IUITransaction, TransactionModeOption } from '@dynamic-labs/types';
|
|
3
3
|
type MidnightPool = 'unshielded' | 'shielded';
|
|
4
|
+
export type MidnightBalanceMaps = {
|
|
5
|
+
shielded: Record<string, bigint>;
|
|
6
|
+
unshielded: Record<string, bigint>;
|
|
7
|
+
};
|
|
4
8
|
type MidnightUiTransactionOpts = {
|
|
5
9
|
from: string;
|
|
6
10
|
onSubmit: (tx: MidnightUiTransaction) => Promise<string>;
|
|
7
|
-
|
|
8
|
-
getShieldedBalance: () => Promise<bigint>;
|
|
11
|
+
balances: MidnightBalanceMaps;
|
|
9
12
|
fetchDustFee: () => Promise<bigint>;
|
|
10
13
|
};
|
|
11
14
|
export declare class MidnightUiTransaction implements IUITransaction {
|
|
@@ -20,12 +23,18 @@ export declare class MidnightUiTransaction implements IUITransaction {
|
|
|
20
23
|
};
|
|
21
24
|
feeTokenSymbol: string;
|
|
22
25
|
private pool;
|
|
26
|
+
selectedTransactionMode: string;
|
|
27
|
+
selectedTokenAddress: string;
|
|
28
|
+
private readonly balances;
|
|
29
|
+
private readonly shieldedAddresses;
|
|
23
30
|
private readonly onSubmitFn;
|
|
24
|
-
private readonly getUnshieldedBalanceFn;
|
|
25
|
-
private readonly getShieldedBalanceFn;
|
|
26
31
|
private readonly fetchDustFeeFn;
|
|
27
32
|
constructor(opts: MidnightUiTransactionOpts);
|
|
28
33
|
get activePool(): MidnightPool;
|
|
34
|
+
get transactionModes(): TransactionModeOption[];
|
|
35
|
+
private get selectedTokenDecimals();
|
|
36
|
+
getTokenPool(contractAddress: string): MidnightPool;
|
|
37
|
+
setTransactionMode(modeId: string): void;
|
|
29
38
|
setSelectedToken(contractAddress: string): void;
|
|
30
39
|
getSendableTokenBalances(): Promise<TokenBalance[]>;
|
|
31
40
|
parse(input: string): bigint;
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
import { __awaiter } from '../../../_virtual/_tslib.js';
|
|
3
|
-
import {
|
|
3
|
+
import { MIDNIGHT_NATIVE_TOKEN_TYPE, MIDNIGHT_NATIVE_UNSHIELDED_DECIMALS } from '../../constants.js';
|
|
4
4
|
import { formatMidnightAtomicBalance } from '../formatMidnightAtomicBalance/formatMidnightAtomicBalance.js';
|
|
5
5
|
import { parseMidnightAtomicAmount } from '../parseMidnightAtomicAmount/parseMidnightAtomicAmount.js';
|
|
6
6
|
|
|
7
|
+
// Shielded tokens aren't NIGHT and have no decimals metadata, so they're shown
|
|
8
|
+
// at their raw amount (decimals 0); native NIGHT keeps 6-decimal formatting.
|
|
9
|
+
const SHIELDED_TOKEN_DECIMALS = 0;
|
|
10
|
+
const shortTokenLabel = (address) => `TOKEN: ${address.slice(0, 8)}...${address.slice(-8)}`;
|
|
7
11
|
class MidnightUiTransaction {
|
|
8
12
|
constructor(opts) {
|
|
9
13
|
this.to = undefined;
|
|
@@ -13,68 +17,104 @@ class MidnightUiTransaction {
|
|
|
13
17
|
this.chain = 'MIDNIGHT';
|
|
14
18
|
this.fee = { gas: undefined };
|
|
15
19
|
this.feeTokenSymbol = 'DUST';
|
|
20
|
+
// Pool defaults to unshielded; it is derived from the selected token (or the
|
|
21
|
+
// transaction-mode toggle), never inferred from a hard-coded token hash.
|
|
16
22
|
this.pool = 'unshielded';
|
|
23
|
+
this.selectedTransactionMode = 'unshielded';
|
|
24
|
+
this.selectedTokenAddress = MIDNIGHT_NATIVE_TOKEN_TYPE;
|
|
17
25
|
this.from = opts.from;
|
|
26
|
+
this.balances = opts.balances;
|
|
27
|
+
this.shieldedAddresses = new Set(Object.keys(opts.balances.shielded));
|
|
18
28
|
this.onSubmitFn = opts.onSubmit;
|
|
19
|
-
this.getUnshieldedBalanceFn = opts.getUnshieldedBalance;
|
|
20
|
-
this.getShieldedBalanceFn = opts.getShieldedBalance;
|
|
21
29
|
this.fetchDustFeeFn = opts.fetchDustFee;
|
|
22
30
|
}
|
|
23
31
|
get activePool() {
|
|
24
32
|
return this.pool;
|
|
25
33
|
}
|
|
34
|
+
get transactionModes() {
|
|
35
|
+
return [
|
|
36
|
+
{ id: 'unshielded', label: 'Unshielded' },
|
|
37
|
+
{ id: 'shielded', label: 'Shielded' },
|
|
38
|
+
];
|
|
39
|
+
}
|
|
40
|
+
get selectedTokenDecimals() {
|
|
41
|
+
return this.selectedTokenAddress === MIDNIGHT_NATIVE_TOKEN_TYPE
|
|
42
|
+
? MIDNIGHT_NATIVE_UNSHIELDED_DECIMALS
|
|
43
|
+
: SHIELDED_TOKEN_DECIMALS;
|
|
44
|
+
}
|
|
45
|
+
// Pool a token belongs to — used by the send form to filter the picker.
|
|
46
|
+
getTokenPool(contractAddress) {
|
|
47
|
+
return this.shieldedAddresses.has(contractAddress)
|
|
48
|
+
? 'shielded'
|
|
49
|
+
: 'unshielded';
|
|
50
|
+
}
|
|
51
|
+
setTransactionMode(modeId) {
|
|
52
|
+
this.selectedTransactionMode = modeId;
|
|
53
|
+
this.pool = modeId === 'shielded' ? 'shielded' : 'unshielded';
|
|
54
|
+
this.to = undefined;
|
|
55
|
+
this.value = undefined;
|
|
56
|
+
}
|
|
26
57
|
setSelectedToken(contractAddress) {
|
|
27
|
-
this.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
: 'unshielded';
|
|
58
|
+
this.selectedTokenAddress = contractAddress;
|
|
59
|
+
this.pool = this.getTokenPool(contractAddress);
|
|
60
|
+
this.selectedTransactionMode = this.pool;
|
|
31
61
|
this.to = undefined;
|
|
32
62
|
this.value = undefined;
|
|
33
63
|
}
|
|
34
64
|
getSendableTokenBalances() {
|
|
35
65
|
return __awaiter(this, void 0, void 0, function* () {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
]);
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
66
|
+
var _a;
|
|
67
|
+
const toDisplay = (raw, decimals) => Number(formatMidnightAtomicBalance(raw, decimals));
|
|
68
|
+
// Native unshielded NIGHT — always shown, even at a zero balance.
|
|
69
|
+
const unshieldedNightRaw = (_a = this.balances.unshielded[MIDNIGHT_NATIVE_TOKEN_TYPE]) !== null && _a !== void 0 ? _a : BigInt(0);
|
|
70
|
+
const nightEntry = {
|
|
71
|
+
address: MIDNIGHT_NATIVE_TOKEN_TYPE,
|
|
72
|
+
balance: toDisplay(unshieldedNightRaw, MIDNIGHT_NATIVE_UNSHIELDED_DECIMALS),
|
|
73
|
+
decimals: MIDNIGHT_NATIVE_UNSHIELDED_DECIMALS,
|
|
74
|
+
isNative: true,
|
|
75
|
+
logoURI: '',
|
|
76
|
+
name: 'NIGHT',
|
|
77
|
+
rawBalance: Number(unshieldedNightRaw),
|
|
78
|
+
rawBalanceString: unshieldedNightRaw.toString(),
|
|
79
|
+
symbol: 'NIGHT',
|
|
80
|
+
};
|
|
81
|
+
// Non-NIGHT tokens (other unshielded + every shielded token), shown at raw
|
|
82
|
+
// amount. `isNative: true` is deliberate — like Aleo, every Midnight token
|
|
83
|
+
// uses the `transaction.parse`/`getBalance` path; the form's non-native
|
|
84
|
+
// branch (`parseNonNativeToken`) is EVM-only and doesn't apply here.
|
|
85
|
+
const tokenEntry = (address, raw) => ({
|
|
86
|
+
address,
|
|
87
|
+
balance: toDisplay(raw, SHIELDED_TOKEN_DECIMALS),
|
|
88
|
+
decimals: SHIELDED_TOKEN_DECIMALS,
|
|
89
|
+
isNative: true,
|
|
90
|
+
logoURI: '',
|
|
91
|
+
name: shortTokenLabel(address),
|
|
92
|
+
rawBalance: Number(raw),
|
|
93
|
+
rawBalanceString: raw.toString(),
|
|
94
|
+
symbol: '',
|
|
95
|
+
});
|
|
96
|
+
const otherUnshielded = Object.entries(this.balances.unshielded)
|
|
97
|
+
.filter(([address, raw]) => address !== MIDNIGHT_NATIVE_TOKEN_TYPE && raw > BigInt(0))
|
|
98
|
+
.map(([address, raw]) => tokenEntry(address, raw));
|
|
99
|
+
const shielded = Object.entries(this.balances.shielded)
|
|
100
|
+
.filter(([, raw]) => raw > BigInt(0))
|
|
101
|
+
.map(([address, raw]) => tokenEntry(address, raw));
|
|
102
|
+
return [nightEntry, ...otherUnshielded, ...shielded];
|
|
65
103
|
});
|
|
66
104
|
}
|
|
67
105
|
parse(input) {
|
|
68
|
-
return parseMidnightAtomicAmount(input,
|
|
106
|
+
return parseMidnightAtomicAmount(input, this.selectedTokenDecimals);
|
|
69
107
|
}
|
|
70
108
|
format(value) {
|
|
71
|
-
return formatMidnightAtomicBalance(value,
|
|
109
|
+
return formatMidnightAtomicBalance(value, this.selectedTokenDecimals);
|
|
72
110
|
}
|
|
73
111
|
getBalance() {
|
|
74
112
|
return __awaiter(this, void 0, void 0, function* () {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
113
|
+
var _a;
|
|
114
|
+
const map = this.pool === 'shielded'
|
|
115
|
+
? this.balances.shielded
|
|
116
|
+
: this.balances.unshielded;
|
|
117
|
+
return (_a = map[this.selectedTokenAddress]) !== null && _a !== void 0 ? _a : BigInt(0);
|
|
78
118
|
});
|
|
79
119
|
}
|
|
80
120
|
fetchFee() {
|
|
@@ -18,8 +18,6 @@ var buildMidnightWaasConnectedApi = require('./buildMidnightWaasConnectedApi.cjs
|
|
|
18
18
|
/**
|
|
19
19
|
* WaaS (Wallet-as-a-Service) connector for Midnight blockchain.
|
|
20
20
|
* Extends the WaaS-specific Midnight base with MPC wallet functionality.
|
|
21
|
-
*
|
|
22
|
-
* Phase 1: Supports unshielded assets (NIGHT, MNTs) only.
|
|
23
21
|
*/
|
|
24
22
|
class DynamicWaasMidnightConnector extends waas.withDynamicWaas(WaasMidnightWalletConnector.WaasMidnightWalletConnector) {
|
|
25
23
|
constructor(props) {
|
|
@@ -89,6 +87,8 @@ class DynamicWaasMidnightConnector extends waas.withDynamicWaas(WaasMidnightWall
|
|
|
89
87
|
*/
|
|
90
88
|
afterWalletSelectHook(walletAddress) {
|
|
91
89
|
this.setActiveAccountAddress(walletAddress);
|
|
90
|
+
// Different wallet → its balances are unrelated; drop any cached snapshot.
|
|
91
|
+
this.clearSendBalanceCache();
|
|
92
92
|
}
|
|
93
93
|
/**
|
|
94
94
|
* Requires and returns the signed session ID
|
|
@@ -167,7 +167,9 @@ class DynamicWaasMidnightConnector extends waas.withDynamicWaas(WaasMidnightWall
|
|
|
167
167
|
*/
|
|
168
168
|
disconnect() {
|
|
169
169
|
return _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
170
|
-
// WaaS doesn't require explicit disconnect
|
|
170
|
+
// WaaS doesn't require explicit disconnect, but drop the balance snapshot
|
|
171
|
+
// so a shielded balance isn't retained after the wallet is disconnected.
|
|
172
|
+
this.clearSendBalanceCache();
|
|
171
173
|
});
|
|
172
174
|
}
|
|
173
175
|
/**
|
|
@@ -270,47 +272,54 @@ class DynamicWaasMidnightConnector extends waas.withDynamicWaas(WaasMidnightWall
|
|
|
270
272
|
return signature;
|
|
271
273
|
});
|
|
272
274
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
* @returns A new IUITransaction instance
|
|
277
|
-
*/
|
|
275
|
+
clearSendBalanceCache() {
|
|
276
|
+
this.sendBalanceCache = undefined;
|
|
277
|
+
}
|
|
278
278
|
createUiTransaction(from) {
|
|
279
279
|
return _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
280
280
|
yield this.validateActiveWallet(from);
|
|
281
|
+
// Balances from the connector (MPC state) — never the redcoast
|
|
282
|
+
// public-balance endpoint, which rejects Midnight addresses. Reused from the
|
|
283
|
+
// short-lived cache across recreations (see field doc).
|
|
284
|
+
const now = Date.now();
|
|
285
|
+
if (!this.sendBalanceCache ||
|
|
286
|
+
now - this.sendBalanceCache.at >
|
|
287
|
+
DynamicWaasMidnightConnector.SEND_BALANCE_TTL_MS) {
|
|
288
|
+
const { shielded, unshielded } = yield this.getBalances();
|
|
289
|
+
this.sendBalanceCache = { at: now, maps: { shielded, unshielded } };
|
|
290
|
+
}
|
|
291
|
+
const balances = this.sendBalanceCache.maps;
|
|
281
292
|
return new MidnightUiTransaction.MidnightUiTransaction({
|
|
293
|
+
balances,
|
|
282
294
|
// No on-chain dust-fee estimate is exposed yet; DUST fees are charged
|
|
283
295
|
// at submit time. TODO: surface a real estimate from the wallet client.
|
|
284
296
|
fetchDustFee: () => _tslib.__awaiter(this, void 0, void 0, function* () { return BigInt(0); }),
|
|
285
297
|
from,
|
|
286
|
-
// Balances come from the connector (MPC state), reusing the cached
|
|
287
|
-
// synced wallet — never from the redcoast public-balance endpoint,
|
|
288
|
-
// which doesn't support Midnight addresses.
|
|
289
|
-
getShieldedBalance: () => _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
290
|
-
var _a;
|
|
291
|
-
return (_a = (yield this.getBalances()).shielded[constants.MIDNIGHT_NATIVE_SHIELDED_TOKEN_TYPE]) !== null && _a !== void 0 ? _a : BigInt(0);
|
|
292
|
-
}),
|
|
293
|
-
getUnshieldedBalance: () => _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
294
|
-
var _b;
|
|
295
|
-
return (_b = (yield this.getBalances()).unshielded[constants.MIDNIGHT_NATIVE_TOKEN_TYPE]) !== null && _b !== void 0 ? _b : BigInt(0);
|
|
296
|
-
}),
|
|
297
298
|
onSubmit: (tx) => _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
298
299
|
if (tx.to === undefined || tx.value === undefined) {
|
|
299
300
|
throw new utils.DynamicError('Recipient and amount are required before submitting');
|
|
300
301
|
}
|
|
302
|
+
// Native NIGHT needs no tokenType; any other (shielded) token is passed
|
|
303
|
+
// by its ledger token type so the correct balance is debited.
|
|
304
|
+
const tokenType = tx.selectedTokenAddress === constants.MIDNIGHT_NATIVE_TOKEN_TYPE
|
|
305
|
+
? undefined
|
|
306
|
+
: tx.selectedTokenAddress;
|
|
301
307
|
// 3-step Midnight transfer: build → sign (ZK prove) → submit.
|
|
302
308
|
const { serializedTransaction } = yield this.createTransferTransaction({
|
|
303
309
|
transfers: [
|
|
304
310
|
{
|
|
305
311
|
amount: tx.value.toString(),
|
|
306
312
|
recipientAddress: tx.to,
|
|
313
|
+
tokenType,
|
|
307
314
|
type: tx.activePool,
|
|
308
315
|
},
|
|
309
316
|
],
|
|
310
317
|
});
|
|
311
318
|
const finalized = yield this.signTransaction(serializedTransaction);
|
|
312
|
-
// Return the tx hash so the Send flow advances to the success view.
|
|
313
319
|
const { txHash } = yield this.submitTransaction(finalized);
|
|
320
|
+
// Balances changed — drop the snapshot so the next send fetches fresh.
|
|
321
|
+
this.clearSendBalanceCache();
|
|
322
|
+
// Return the tx hash so the Send flow advances to the success view.
|
|
314
323
|
return txHash;
|
|
315
324
|
}),
|
|
316
325
|
});
|
|
@@ -441,6 +450,21 @@ class DynamicWaasMidnightConnector extends waas.withDynamicWaas(WaasMidnightWall
|
|
|
441
450
|
});
|
|
442
451
|
});
|
|
443
452
|
}
|
|
453
|
+
/**
|
|
454
|
+
* One-call transfer: runs the full build → sign (ZK prove) → submit flow and
|
|
455
|
+
* returns the broadcast tx hash. This is what backs `wallet.sendBalance` for
|
|
456
|
+
* embedded Midnight wallets; use the individual steps
|
|
457
|
+
* (`createTransferTransaction` / `signTransaction` / `submitTransaction`) when
|
|
458
|
+
* you need to inspect or persist the transaction between stages.
|
|
459
|
+
*/
|
|
460
|
+
createTransfer(params) {
|
|
461
|
+
return _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
462
|
+
const { serializedTransaction } = yield this.createTransferTransaction(params);
|
|
463
|
+
const finalizedTransaction = yield this.signTransaction(serializedTransaction);
|
|
464
|
+
const { txHash } = yield this.submitTransaction(finalizedTransaction);
|
|
465
|
+
return txHash;
|
|
466
|
+
});
|
|
467
|
+
}
|
|
444
468
|
/**
|
|
445
469
|
* Step 1 of the Midnight transfer flow.
|
|
446
470
|
* Builds an unsigned UnprovenTransaction from one or more transfer specs.
|
|
@@ -567,5 +591,7 @@ class DynamicWaasMidnightConnector extends waas.withDynamicWaas(WaasMidnightWall
|
|
|
567
591
|
});
|
|
568
592
|
}
|
|
569
593
|
}
|
|
594
|
+
// Max age of the Send-flow balance snapshot (see `sendBalanceCache`).
|
|
595
|
+
DynamicWaasMidnightConnector.SEND_BALANCE_TTL_MS = 30000;
|
|
570
596
|
|
|
571
597
|
exports.DynamicWaasMidnightConnector = DynamicWaasMidnightConnector;
|
|
@@ -80,6 +80,15 @@ declare const DynamicWaasMidnightConnector_base: (abstract new (...args: any[])
|
|
|
80
80
|
legacyWalletId?: string | undefined;
|
|
81
81
|
password?: string | undefined;
|
|
82
82
|
}): Promise<void>;
|
|
83
|
+
migrateFromFireblocks({ deviceId, jwt, walletPassword, ncwStorage, expectedAddresses, coinTypes, password, }: {
|
|
84
|
+
deviceId: string;
|
|
85
|
+
jwt: string;
|
|
86
|
+
walletPassword: string;
|
|
87
|
+
ncwStorage: Record<string, string>;
|
|
88
|
+
expectedAddresses: Record<string, string>;
|
|
89
|
+
coinTypes?: Record<string, number> | undefined;
|
|
90
|
+
password?: string | undefined;
|
|
91
|
+
}): Promise<import("@dynamic-labs-wallet/browser-wallet-client").MigrateFromFireblocksResponse>;
|
|
83
92
|
exportPrivateKey({ accountAddress, displayContainer, password, }?: {
|
|
84
93
|
accountAddress?: string | undefined;
|
|
85
94
|
displayContainer?: HTMLIFrameElement | undefined;
|
|
@@ -98,6 +107,10 @@ declare const DynamicWaasMidnightConnector_base: (abstract new (...args: any[])
|
|
|
98
107
|
googleDriveAccessToken?: string | undefined;
|
|
99
108
|
}): Promise<void>;
|
|
100
109
|
exportClientKeysharesFromGoogleDrive({ accountAddress, password, }: {
|
|
110
|
+
/**
|
|
111
|
+
* When active account is not set (e.g. primary wallet restored from session),
|
|
112
|
+
* fall back to the first Midnight verified credential so signing and getAddress work.
|
|
113
|
+
*/
|
|
101
114
|
accountAddress: string;
|
|
102
115
|
password?: string | undefined;
|
|
103
116
|
}): Promise<void>;
|
|
@@ -119,6 +132,9 @@ declare const DynamicWaasMidnightConnector_base: (abstract new (...args: any[])
|
|
|
119
132
|
thresholdSignatureScheme: string;
|
|
120
133
|
password?: string | undefined;
|
|
121
134
|
}): Promise<void>;
|
|
135
|
+
/**
|
|
136
|
+
* Connects to the WaaS wallet client
|
|
137
|
+
*/
|
|
122
138
|
revokeDelegation({ accountAddress, password, }: {
|
|
123
139
|
accountAddress: string;
|
|
124
140
|
password?: string | undefined;
|
|
@@ -130,9 +146,7 @@ declare const DynamicWaasMidnightConnector_base: (abstract new (...args: any[])
|
|
|
130
146
|
}): Promise<void>;
|
|
131
147
|
setPassword({ accountAddress, newPassword, }: {
|
|
132
148
|
accountAddress: string;
|
|
133
|
-
newPassword: string;
|
|
134
|
-
* Disconnects from the wallet (no-op for WaaS)
|
|
135
|
-
*/
|
|
149
|
+
newPassword: string;
|
|
136
150
|
}): Promise<void>;
|
|
137
151
|
signRawMessage({ accountAddress, message, password, }: {
|
|
138
152
|
accountAddress: string;
|
|
@@ -180,8 +194,6 @@ declare const DynamicWaasMidnightConnector_base: (abstract new (...args: any[])
|
|
|
180
194
|
/**
|
|
181
195
|
* WaaS (Wallet-as-a-Service) connector for Midnight blockchain.
|
|
182
196
|
* Extends the WaaS-specific Midnight base with MPC wallet functionality.
|
|
183
|
-
*
|
|
184
|
-
* Phase 1: Supports unshielded assets (NIGHT, MNTs) only.
|
|
185
197
|
*/
|
|
186
198
|
export declare class DynamicWaasMidnightConnector extends DynamicWaasMidnightConnector_base implements IDynamicWaasConnector {
|
|
187
199
|
ChainWallet: typeof MidnightWallet;
|
|
@@ -189,6 +201,7 @@ export declare class DynamicWaasMidnightConnector extends DynamicWaasMidnightCon
|
|
|
189
201
|
overrideKey: string;
|
|
190
202
|
isEmbeddedWallet: boolean;
|
|
191
203
|
logger: Logger;
|
|
204
|
+
private static readonly SEND_BALANCE_TTL_MS;
|
|
192
205
|
activeAccountAddress: string | undefined;
|
|
193
206
|
verifiedCredentials: JwtVerifiedCredential[] | undefined;
|
|
194
207
|
protected walletUiUtils: WalletUiUtils<InternalWalletConnector>;
|
|
@@ -301,6 +314,8 @@ export declare class DynamicWaasMidnightConnector extends DynamicWaasMidnightCon
|
|
|
301
314
|
* @param from - The sender's address
|
|
302
315
|
* @returns A new IUITransaction instance
|
|
303
316
|
*/
|
|
317
|
+
private sendBalanceCache;
|
|
318
|
+
private clearSendBalanceCache;
|
|
304
319
|
createUiTransaction(from: string): Promise<IUITransaction>;
|
|
305
320
|
/**
|
|
306
321
|
* Returns the provider.
|
|
@@ -373,6 +388,23 @@ export declare class DynamicWaasMidnightConnector extends DynamicWaasMidnightCon
|
|
|
373
388
|
message: string;
|
|
374
389
|
registeredCount: number;
|
|
375
390
|
}>;
|
|
391
|
+
/**
|
|
392
|
+
* One-call transfer: runs the full build → sign (ZK prove) → submit flow and
|
|
393
|
+
* returns the broadcast tx hash. This is what backs `wallet.sendBalance` for
|
|
394
|
+
* embedded Midnight wallets; use the individual steps
|
|
395
|
+
* (`createTransferTransaction` / `signTransaction` / `submitTransaction`) when
|
|
396
|
+
* you need to inspect or persist the transaction between stages.
|
|
397
|
+
*/
|
|
398
|
+
createTransfer(params: {
|
|
399
|
+
transfers: Array<{
|
|
400
|
+
type: 'unshielded' | 'shielded';
|
|
401
|
+
recipientAddress: string;
|
|
402
|
+
amount: string;
|
|
403
|
+
tokenType?: string;
|
|
404
|
+
}>;
|
|
405
|
+
/** Optional TTL in minutes. Defaults to 5. */
|
|
406
|
+
ttlMinutes?: number;
|
|
407
|
+
}): Promise<string>;
|
|
376
408
|
/**
|
|
377
409
|
* Step 1 of the Midnight transfer flow.
|
|
378
410
|
* Builds an unsigned UnprovenTransaction from one or more transfer specs.
|
|
@@ -5,7 +5,7 @@ import { DynamicError } from '@dynamic-labs/utils';
|
|
|
5
5
|
import { withDynamicWaas } from '@dynamic-labs/waas';
|
|
6
6
|
import { isSameAddress } from '@dynamic-labs/wallet-connector-core';
|
|
7
7
|
import { Logger } from '@dynamic-labs/logger';
|
|
8
|
-
import {
|
|
8
|
+
import { MIDNIGHT_NATIVE_TOKEN_TYPE } from '../../constants.js';
|
|
9
9
|
import { MidnightUiTransaction } from '../../utils/MidnightUiTransaction/MidnightUiTransaction.js';
|
|
10
10
|
import { MidnightWallet } from '../../wallet/MidnightWallet.js';
|
|
11
11
|
import { WaasMidnightWalletConnector } from '../WaasMidnightWalletConnector.js';
|
|
@@ -14,8 +14,6 @@ import { buildMidnightWaasConnectedApi, normalizeDustBalance, toBigIntBalanceMap
|
|
|
14
14
|
/**
|
|
15
15
|
* WaaS (Wallet-as-a-Service) connector for Midnight blockchain.
|
|
16
16
|
* Extends the WaaS-specific Midnight base with MPC wallet functionality.
|
|
17
|
-
*
|
|
18
|
-
* Phase 1: Supports unshielded assets (NIGHT, MNTs) only.
|
|
19
17
|
*/
|
|
20
18
|
class DynamicWaasMidnightConnector extends withDynamicWaas(WaasMidnightWalletConnector) {
|
|
21
19
|
constructor(props) {
|
|
@@ -85,6 +83,8 @@ class DynamicWaasMidnightConnector extends withDynamicWaas(WaasMidnightWalletCon
|
|
|
85
83
|
*/
|
|
86
84
|
afterWalletSelectHook(walletAddress) {
|
|
87
85
|
this.setActiveAccountAddress(walletAddress);
|
|
86
|
+
// Different wallet → its balances are unrelated; drop any cached snapshot.
|
|
87
|
+
this.clearSendBalanceCache();
|
|
88
88
|
}
|
|
89
89
|
/**
|
|
90
90
|
* Requires and returns the signed session ID
|
|
@@ -163,7 +163,9 @@ class DynamicWaasMidnightConnector extends withDynamicWaas(WaasMidnightWalletCon
|
|
|
163
163
|
*/
|
|
164
164
|
disconnect() {
|
|
165
165
|
return __awaiter(this, void 0, void 0, function* () {
|
|
166
|
-
// WaaS doesn't require explicit disconnect
|
|
166
|
+
// WaaS doesn't require explicit disconnect, but drop the balance snapshot
|
|
167
|
+
// so a shielded balance isn't retained after the wallet is disconnected.
|
|
168
|
+
this.clearSendBalanceCache();
|
|
167
169
|
});
|
|
168
170
|
}
|
|
169
171
|
/**
|
|
@@ -266,47 +268,54 @@ class DynamicWaasMidnightConnector extends withDynamicWaas(WaasMidnightWalletCon
|
|
|
266
268
|
return signature;
|
|
267
269
|
});
|
|
268
270
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
* @returns A new IUITransaction instance
|
|
273
|
-
*/
|
|
271
|
+
clearSendBalanceCache() {
|
|
272
|
+
this.sendBalanceCache = undefined;
|
|
273
|
+
}
|
|
274
274
|
createUiTransaction(from) {
|
|
275
275
|
return __awaiter(this, void 0, void 0, function* () {
|
|
276
276
|
yield this.validateActiveWallet(from);
|
|
277
|
+
// Balances from the connector (MPC state) — never the redcoast
|
|
278
|
+
// public-balance endpoint, which rejects Midnight addresses. Reused from the
|
|
279
|
+
// short-lived cache across recreations (see field doc).
|
|
280
|
+
const now = Date.now();
|
|
281
|
+
if (!this.sendBalanceCache ||
|
|
282
|
+
now - this.sendBalanceCache.at >
|
|
283
|
+
DynamicWaasMidnightConnector.SEND_BALANCE_TTL_MS) {
|
|
284
|
+
const { shielded, unshielded } = yield this.getBalances();
|
|
285
|
+
this.sendBalanceCache = { at: now, maps: { shielded, unshielded } };
|
|
286
|
+
}
|
|
287
|
+
const balances = this.sendBalanceCache.maps;
|
|
277
288
|
return new MidnightUiTransaction({
|
|
289
|
+
balances,
|
|
278
290
|
// No on-chain dust-fee estimate is exposed yet; DUST fees are charged
|
|
279
291
|
// at submit time. TODO: surface a real estimate from the wallet client.
|
|
280
292
|
fetchDustFee: () => __awaiter(this, void 0, void 0, function* () { return BigInt(0); }),
|
|
281
293
|
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
294
|
onSubmit: (tx) => __awaiter(this, void 0, void 0, function* () {
|
|
294
295
|
if (tx.to === undefined || tx.value === undefined) {
|
|
295
296
|
throw new DynamicError('Recipient and amount are required before submitting');
|
|
296
297
|
}
|
|
298
|
+
// Native NIGHT needs no tokenType; any other (shielded) token is passed
|
|
299
|
+
// by its ledger token type so the correct balance is debited.
|
|
300
|
+
const tokenType = tx.selectedTokenAddress === MIDNIGHT_NATIVE_TOKEN_TYPE
|
|
301
|
+
? undefined
|
|
302
|
+
: tx.selectedTokenAddress;
|
|
297
303
|
// 3-step Midnight transfer: build → sign (ZK prove) → submit.
|
|
298
304
|
const { serializedTransaction } = yield this.createTransferTransaction({
|
|
299
305
|
transfers: [
|
|
300
306
|
{
|
|
301
307
|
amount: tx.value.toString(),
|
|
302
308
|
recipientAddress: tx.to,
|
|
309
|
+
tokenType,
|
|
303
310
|
type: tx.activePool,
|
|
304
311
|
},
|
|
305
312
|
],
|
|
306
313
|
});
|
|
307
314
|
const finalized = yield this.signTransaction(serializedTransaction);
|
|
308
|
-
// Return the tx hash so the Send flow advances to the success view.
|
|
309
315
|
const { txHash } = yield this.submitTransaction(finalized);
|
|
316
|
+
// Balances changed — drop the snapshot so the next send fetches fresh.
|
|
317
|
+
this.clearSendBalanceCache();
|
|
318
|
+
// Return the tx hash so the Send flow advances to the success view.
|
|
310
319
|
return txHash;
|
|
311
320
|
}),
|
|
312
321
|
});
|
|
@@ -437,6 +446,21 @@ class DynamicWaasMidnightConnector extends withDynamicWaas(WaasMidnightWalletCon
|
|
|
437
446
|
});
|
|
438
447
|
});
|
|
439
448
|
}
|
|
449
|
+
/**
|
|
450
|
+
* One-call transfer: runs the full build → sign (ZK prove) → submit flow and
|
|
451
|
+
* returns the broadcast tx hash. This is what backs `wallet.sendBalance` for
|
|
452
|
+
* embedded Midnight wallets; use the individual steps
|
|
453
|
+
* (`createTransferTransaction` / `signTransaction` / `submitTransaction`) when
|
|
454
|
+
* you need to inspect or persist the transaction between stages.
|
|
455
|
+
*/
|
|
456
|
+
createTransfer(params) {
|
|
457
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
458
|
+
const { serializedTransaction } = yield this.createTransferTransaction(params);
|
|
459
|
+
const finalizedTransaction = yield this.signTransaction(serializedTransaction);
|
|
460
|
+
const { txHash } = yield this.submitTransaction(finalizedTransaction);
|
|
461
|
+
return txHash;
|
|
462
|
+
});
|
|
463
|
+
}
|
|
440
464
|
/**
|
|
441
465
|
* Step 1 of the Midnight transfer flow.
|
|
442
466
|
* Builds an unsigned UnprovenTransaction from one or more transfer specs.
|
|
@@ -563,5 +587,7 @@ class DynamicWaasMidnightConnector extends withDynamicWaas(WaasMidnightWalletCon
|
|
|
563
587
|
});
|
|
564
588
|
}
|
|
565
589
|
}
|
|
590
|
+
// Max age of the Send-flow balance snapshot (see `sendBalanceCache`).
|
|
591
|
+
DynamicWaasMidnightConnector.SEND_BALANCE_TTL_MS = 30000;
|
|
566
592
|
|
|
567
593
|
export { DynamicWaasMidnightConnector };
|
|
@@ -69,24 +69,54 @@ class MidnightWallet extends walletConnectorCore.Wallet {
|
|
|
69
69
|
getFormattedBalances() {
|
|
70
70
|
return _tslib.__awaiter(this, void 0, void 0, function* () {
|
|
71
71
|
const raw = yield this._connector.getBalances();
|
|
72
|
-
const shieldedRaw = raw.shielded[constants.MIDNIGHT_NATIVE_SHIELDED_TOKEN_TYPE];
|
|
73
72
|
const unshieldedRaw = raw.unshielded[constants.MIDNIGHT_NATIVE_TOKEN_TYPE];
|
|
74
73
|
const dustEmpty = raw.dust.balance === BigInt(0) && raw.dust.cap === BigInt(0);
|
|
74
|
+
// The shielded pool holds non-NIGHT token types only, so surface a count of
|
|
75
|
+
// distinct tokens with a positive balance rather than a (non-existent)
|
|
76
|
+
// shielded NIGHT amount.
|
|
77
|
+
const shieldedTokenCount = Object.values(raw.shielded).filter((amount) => amount > BigInt(0)).length;
|
|
75
78
|
return {
|
|
76
79
|
dustBalance: dustEmpty
|
|
77
80
|
? undefined
|
|
78
81
|
: formatMidnightConnectorDustBalance.formatMidnightConnectorDustBalance(raw.dust.balance, raw.dust.cap),
|
|
79
82
|
// Passthrough (undefined for injected/external wallets, which omit it).
|
|
80
83
|
dustSyncing: raw.dustSyncing,
|
|
81
|
-
|
|
82
|
-
? formatMidnightNativeUnshieldedBalance.formatMidnightNativeUnshieldedBalance(shieldedRaw)
|
|
83
|
-
: undefined,
|
|
84
|
+
shieldedTokenCount,
|
|
84
85
|
unshieldedBalance: unshieldedRaw !== undefined
|
|
85
86
|
? formatMidnightNativeUnshieldedBalance.formatMidnightNativeUnshieldedBalance(unshieldedRaw)
|
|
86
87
|
: undefined,
|
|
87
88
|
};
|
|
88
89
|
});
|
|
89
90
|
}
|
|
91
|
+
// Exclusive for embedded Midnight wallets
|
|
92
|
+
/** Builds an unsigned transaction from one or more transfer specs (step 1). */
|
|
93
|
+
createTransferTransaction(...args) {
|
|
94
|
+
return this._connector.createTransferTransaction(...args);
|
|
95
|
+
}
|
|
96
|
+
/** Signs and ZK-proves an unsigned transaction via MPC (step 2). */
|
|
97
|
+
signTransaction(...args) {
|
|
98
|
+
return this._connector.signTransaction(...args);
|
|
99
|
+
}
|
|
100
|
+
/** Broadcasts a finalized transaction (step 3). */
|
|
101
|
+
submitTransaction(...args) {
|
|
102
|
+
return this._connector.submitTransaction(...args);
|
|
103
|
+
}
|
|
104
|
+
/** Registers the wallet's NIGHT for DUST generation. */
|
|
105
|
+
registerDust(...args) {
|
|
106
|
+
return this._connector.registerDust(...args);
|
|
107
|
+
}
|
|
108
|
+
/** Releases the reserved inputs of a specific built/signed transaction. */
|
|
109
|
+
revertTransaction(...args) {
|
|
110
|
+
return this._connector.revertTransaction(...args);
|
|
111
|
+
}
|
|
112
|
+
/** Releases every finalized-but-unsubmitted transaction's reserved inputs. */
|
|
113
|
+
revertAllPending(...args) {
|
|
114
|
+
return this._connector.revertAllPending(...args);
|
|
115
|
+
}
|
|
116
|
+
/** Clears the wallet's persisted state and forces a cold re-sync. */
|
|
117
|
+
resetWalletCache(...args) {
|
|
118
|
+
return this._connector.resetWalletCache(...args);
|
|
119
|
+
}
|
|
90
120
|
}
|
|
91
121
|
|
|
92
122
|
exports.MidnightWallet = MidnightWallet;
|
|
@@ -20,6 +20,7 @@ export declare class MidnightWallet extends Wallet<MidnightWalletConnector> {
|
|
|
20
20
|
cap: bigint;
|
|
21
21
|
balance: bigint;
|
|
22
22
|
};
|
|
23
|
+
dustSyncing?: boolean;
|
|
23
24
|
}>;
|
|
24
25
|
getShieldedBalance(): Promise<string | undefined>;
|
|
25
26
|
getUnshieldedBalance(): Promise<string | undefined>;
|
|
@@ -28,4 +29,18 @@ export declare class MidnightWallet extends Wallet<MidnightWalletConnector> {
|
|
|
28
29
|
cap: string;
|
|
29
30
|
}>;
|
|
30
31
|
getFormattedBalances(): Promise<MidnightFormattedBalances>;
|
|
32
|
+
/** Builds an unsigned transaction from one or more transfer specs (step 1). */
|
|
33
|
+
createTransferTransaction(...args: Parameters<MidnightWalletConnector['createTransferTransaction']>): ReturnType<MidnightWalletConnector['createTransferTransaction']>;
|
|
34
|
+
/** Signs and ZK-proves an unsigned transaction via MPC (step 2). */
|
|
35
|
+
signTransaction(...args: Parameters<MidnightWalletConnector['signTransaction']>): ReturnType<MidnightWalletConnector['signTransaction']>;
|
|
36
|
+
/** Broadcasts a finalized transaction (step 3). */
|
|
37
|
+
submitTransaction(...args: Parameters<MidnightWalletConnector['submitTransaction']>): ReturnType<MidnightWalletConnector['submitTransaction']>;
|
|
38
|
+
/** Registers the wallet's NIGHT for DUST generation. */
|
|
39
|
+
registerDust(...args: Parameters<MidnightWalletConnector['registerDust']>): ReturnType<MidnightWalletConnector['registerDust']>;
|
|
40
|
+
/** Releases the reserved inputs of a specific built/signed transaction. */
|
|
41
|
+
revertTransaction(...args: Parameters<MidnightWalletConnector['revertTransaction']>): ReturnType<MidnightWalletConnector['revertTransaction']>;
|
|
42
|
+
/** Releases every finalized-but-unsubmitted transaction's reserved inputs. */
|
|
43
|
+
revertAllPending(...args: Parameters<MidnightWalletConnector['revertAllPending']>): ReturnType<MidnightWalletConnector['revertAllPending']>;
|
|
44
|
+
/** Clears the wallet's persisted state and forces a cold re-sync. */
|
|
45
|
+
resetWalletCache(...args: Parameters<MidnightWalletConnector['resetWalletCache']>): ReturnType<MidnightWalletConnector['resetWalletCache']>;
|
|
31
46
|
}
|
|
@@ -65,24 +65,54 @@ class MidnightWallet extends Wallet {
|
|
|
65
65
|
getFormattedBalances() {
|
|
66
66
|
return __awaiter(this, void 0, void 0, function* () {
|
|
67
67
|
const raw = yield this._connector.getBalances();
|
|
68
|
-
const shieldedRaw = raw.shielded[MIDNIGHT_NATIVE_SHIELDED_TOKEN_TYPE];
|
|
69
68
|
const unshieldedRaw = raw.unshielded[MIDNIGHT_NATIVE_TOKEN_TYPE];
|
|
70
69
|
const dustEmpty = raw.dust.balance === BigInt(0) && raw.dust.cap === BigInt(0);
|
|
70
|
+
// The shielded pool holds non-NIGHT token types only, so surface a count of
|
|
71
|
+
// distinct tokens with a positive balance rather than a (non-existent)
|
|
72
|
+
// shielded NIGHT amount.
|
|
73
|
+
const shieldedTokenCount = Object.values(raw.shielded).filter((amount) => amount > BigInt(0)).length;
|
|
71
74
|
return {
|
|
72
75
|
dustBalance: dustEmpty
|
|
73
76
|
? undefined
|
|
74
77
|
: formatMidnightConnectorDustBalance(raw.dust.balance, raw.dust.cap),
|
|
75
78
|
// Passthrough (undefined for injected/external wallets, which omit it).
|
|
76
79
|
dustSyncing: raw.dustSyncing,
|
|
77
|
-
|
|
78
|
-
? formatMidnightNativeUnshieldedBalance(shieldedRaw)
|
|
79
|
-
: undefined,
|
|
80
|
+
shieldedTokenCount,
|
|
80
81
|
unshieldedBalance: unshieldedRaw !== undefined
|
|
81
82
|
? formatMidnightNativeUnshieldedBalance(unshieldedRaw)
|
|
82
83
|
: undefined,
|
|
83
84
|
};
|
|
84
85
|
});
|
|
85
86
|
}
|
|
87
|
+
// Exclusive for embedded Midnight wallets
|
|
88
|
+
/** Builds an unsigned transaction from one or more transfer specs (step 1). */
|
|
89
|
+
createTransferTransaction(...args) {
|
|
90
|
+
return this._connector.createTransferTransaction(...args);
|
|
91
|
+
}
|
|
92
|
+
/** Signs and ZK-proves an unsigned transaction via MPC (step 2). */
|
|
93
|
+
signTransaction(...args) {
|
|
94
|
+
return this._connector.signTransaction(...args);
|
|
95
|
+
}
|
|
96
|
+
/** Broadcasts a finalized transaction (step 3). */
|
|
97
|
+
submitTransaction(...args) {
|
|
98
|
+
return this._connector.submitTransaction(...args);
|
|
99
|
+
}
|
|
100
|
+
/** Registers the wallet's NIGHT for DUST generation. */
|
|
101
|
+
registerDust(...args) {
|
|
102
|
+
return this._connector.registerDust(...args);
|
|
103
|
+
}
|
|
104
|
+
/** Releases the reserved inputs of a specific built/signed transaction. */
|
|
105
|
+
revertTransaction(...args) {
|
|
106
|
+
return this._connector.revertTransaction(...args);
|
|
107
|
+
}
|
|
108
|
+
/** Releases every finalized-but-unsubmitted transaction's reserved inputs. */
|
|
109
|
+
revertAllPending(...args) {
|
|
110
|
+
return this._connector.revertAllPending(...args);
|
|
111
|
+
}
|
|
112
|
+
/** Clears the wallet's persisted state and forces a cold re-sync. */
|
|
113
|
+
resetWalletCache(...args) {
|
|
114
|
+
return this._connector.resetWalletCache(...args);
|
|
115
|
+
}
|
|
86
116
|
}
|
|
87
117
|
|
|
88
118
|
export { MidnightWallet };
|