Hi team,
While working on a Mainnet deploy from a CLI-based script (Node v22, @midnight-ntwrk/midnight-js-* SDK), I hit a series of TypeError failures caused by Iterator Helpers assumptions in the SDK. Sharing the full list of affected sites and the workaround I applied, in case it’s useful to the SDK team or other developers who run into the same issues.
Environment
-
Node: v22.x
-
SDK packages affected:
-
@midnight-ntwrk/wallet-sdk-shieldedv3.0.0 -
@midnight-ntwrk/wallet-sdk-unshielded-walletv3.0.0 -
@midnight-ntwrk/wallet-sdk-facadev4.0.0 -
@midnight-ntwrk/wallet-sdk-dust-walletv4.0.0 -
@midnight-ntwrk/wallet-sdk-capabilities(balancer submodule) -
@midnight-ntwrk/compact-runtime
-
-
Pattern: SDK chains
.entries()/.keys()/.values()(returning WASM-backedMapiterators) with subsequent.filter()/.map()/.every()/.some()/.find()/.forEach()/.toArray()etc., assuming TC39 Iterator Helpers behavior. Under Node v22, these chains throwTypeError: X.entries(...).filter is not a function.
Root cause (my understanding)
The SDK appears to be written against a JS runtime where Iterator Helpers (Stage 4 proposal) are available on the objects returned by WASM bindings. In Node v22, the Iterator Helpers proposal is implemented but the iterators returned from WASM-bound Map instances do not inherit from Iterator.prototype, so the helper methods (.filter, .map, etc.) are not available on them.
Wrapping each call site with Array.from(...) converts the iterator to a plain array, which has the equivalent methods as Array.prototype methods — resolving the failures without any functional change.
Full list of affected sites (12 total across 8 files)
I identified these by running a regex scan across node_modules/@midnight-ntwrk/ for the pattern:
regex
\.(entries|values|keys)\(\s*\)\s*(\n\s*)?\.(filter|map|flatMap|reduce|every|some|find|toArray|forEach)\b
| # | File | Line | Method chain |
|---|---|---|---|
| 1 | wallet-sdk-shielded/dist/v1/CoreWallet.js |
-– | MapIterator.map() |
| 2 | wallet-sdk-shielded/dist/v1/TransactionOps.js |
~52 | tx.imbalances(0).entries().filter(...) (getGuaranteedImbalances) |
| 3 | wallet-sdk-shielded/dist/v1/TransactionOps.js |
-– | tx.imbalances(segment).entries().filter(...) (getFallibleImbalances) |
| 4 | wallet-sdk-shielded/dist/v1/TransactionImbalances.js |
-– | imbalances.guaranteed.entries().every(...) |
| 5 | wallet-sdk-shielded/dist/v1/TransactionImbalances.js |
-– | segmentImbalances.entries().every(...) |
| 6 | wallet-sdk-unshielded-wallet/dist/v1/TransactionOps.js |
~31 | transaction.intents?.keys(...).toArray() (getSegments) |
| 7 | wallet-sdk-unshielded-wallet/dist/v1/TransactionOps.js |
~64 | transaction.imbalances(...).entries().filter(...) |
| 8 | wallet-sdk-facade/dist/transaction.js |
-– | iterator chain (2 sites) |
| 9 | wallet-sdk-facade/dist/transaction.js |
-– | (same file, second site) |
| 10 | wallet-sdk-capabilities/dist/balancer/Imbalances.js |
~35-50 | merge/apply iterator chains |
| 11 | wallet-sdk-dust-wallet/dist/v1/Transacting.js |
~177-180 | transaction.imbalances(0, totalFee).entries().find(...) (feeImbalance) |
| 12 | compact-runtime/dist/contract-dependencies.js |
~168 | stateMap.keys().forEach(...) (compactMapADTDependencies) |
Example patch (one of 12)
Before (wallet-sdk-shielded/dist/v1/TransactionOps.js):
js
getGuaranteedImbalances: (tx) => {
const rawGuaranteedImbalances = tx
.imbalances(0)
.entries()
.filter(([token]) => token.tag === 'shielded')
.map(([token, value]) => {
return [token.raw, value];
});
return Imbalances.fromEntries(rawGuaranteedImbalances);
},
After:
js
getGuaranteedImbalances: (tx) => {
const rawGuaranteedImbalances = Array.from(tx.imbalances(0).entries())
.filter(([token]) => token.tag === 'shielded')
.map(([token, value]) => [token.raw, value]);
return Imbalances.fromEntries(rawGuaranteedImbalances);
},
All 12 sites follow the same pattern: wrap the iterator-returning call with Array.from(...).
Result after applying all 12 patches
-
Wallet sync

-
Provider configuration

-
Proof generation

-
TX construction

-
TX submission to node
(got past every SDK failure, reached the actual substrate node)
(The deploy itself then failed at the substrate level with 1016 Immediately Dropped, which I believe is a separate issue unrelated to the SDK — filed in another topic re: mNIGHT/bridge requirements.)
Suggested fixes for the SDK
-
Short-term: wrap all
.entries()/.values()/.keys()calls from WASM-returned Maps withArray.from(...)in the SDK source. This is minimally invasive and cross-runtime compatible. -
Long-term: either (a) document the required Node version / polyfill, or (b) update the WASM bindings layer to return objects that inherit from
Iterator.prototypeso Iterator Helpers work natively. -
Testing: add a CI job running the SDK against Node v22 (current LTS is v22.x).
Persisting the workaround
Since these edits live in node_modules/, they’re wiped by npm install. For production use, the fix should be persisted with patch-package:
bash
npx patch-package @midnight-ntwrk/wallet-sdk-shielded
# ... repeat for each of the 8 affected packages
Plus a postinstall script:
json
"scripts": {
"postinstall": "patch-package"
}
Happy to contribute
If it would help the SDK team, I can:
-
Share the full
.origvs patched diff for all 12 sites as a gist -
Open a PR against the relevant SDK repos (if public)
-
Provide the full deploy log reproducing each failure in sequence
Let me know what format is most useful.
Thanks!
Takuya Ogura ECOSUS CO., LTD. (Thailand) Running GYOTAK food-traceability on Midnight