@metamask/assets-controller 9.0.2 → 10.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/CHANGELOG.md +44 -1
  2. package/dist/AssetsController.cjs +357 -105
  3. package/dist/AssetsController.cjs.map +1 -1
  4. package/dist/AssetsController.d.cts +11 -8
  5. package/dist/AssetsController.d.cts.map +1 -1
  6. package/dist/AssetsController.d.mts +11 -8
  7. package/dist/AssetsController.d.mts.map +1 -1
  8. package/dist/AssetsController.mjs +357 -105
  9. package/dist/AssetsController.mjs.map +1 -1
  10. package/dist/data-sources/AccountsApiDataSource.cjs +15 -2
  11. package/dist/data-sources/AccountsApiDataSource.cjs.map +1 -1
  12. package/dist/data-sources/AccountsApiDataSource.d.cts +8 -0
  13. package/dist/data-sources/AccountsApiDataSource.d.cts.map +1 -1
  14. package/dist/data-sources/AccountsApiDataSource.d.mts +8 -0
  15. package/dist/data-sources/AccountsApiDataSource.d.mts.map +1 -1
  16. package/dist/data-sources/AccountsApiDataSource.mjs +15 -2
  17. package/dist/data-sources/AccountsApiDataSource.mjs.map +1 -1
  18. package/dist/data-sources/BackendWebsocketDataSource.cjs +236 -149
  19. package/dist/data-sources/BackendWebsocketDataSource.cjs.map +1 -1
  20. package/dist/data-sources/BackendWebsocketDataSource.d.cts +15 -0
  21. package/dist/data-sources/BackendWebsocketDataSource.d.cts.map +1 -1
  22. package/dist/data-sources/BackendWebsocketDataSource.d.mts +15 -0
  23. package/dist/data-sources/BackendWebsocketDataSource.d.mts.map +1 -1
  24. package/dist/data-sources/BackendWebsocketDataSource.mjs +236 -146
  25. package/dist/data-sources/BackendWebsocketDataSource.mjs.map +1 -1
  26. package/dist/data-sources/PriceDataSource.cjs +82 -22
  27. package/dist/data-sources/PriceDataSource.cjs.map +1 -1
  28. package/dist/data-sources/PriceDataSource.d.cts +13 -0
  29. package/dist/data-sources/PriceDataSource.d.cts.map +1 -1
  30. package/dist/data-sources/PriceDataSource.d.mts +13 -0
  31. package/dist/data-sources/PriceDataSource.d.mts.map +1 -1
  32. package/dist/data-sources/PriceDataSource.mjs +83 -23
  33. package/dist/data-sources/PriceDataSource.mjs.map +1 -1
  34. package/dist/data-sources/RpcDataSource.cjs +29 -9
  35. package/dist/data-sources/RpcDataSource.cjs.map +1 -1
  36. package/dist/data-sources/RpcDataSource.d.cts +5 -0
  37. package/dist/data-sources/RpcDataSource.d.cts.map +1 -1
  38. package/dist/data-sources/RpcDataSource.d.mts +5 -0
  39. package/dist/data-sources/RpcDataSource.d.mts.map +1 -1
  40. package/dist/data-sources/RpcDataSource.mjs +29 -9
  41. package/dist/data-sources/RpcDataSource.mjs.map +1 -1
  42. package/dist/data-sources/SnapDataSource.cjs +2 -2
  43. package/dist/data-sources/SnapDataSource.cjs.map +1 -1
  44. package/dist/data-sources/SnapDataSource.mjs +2 -2
  45. package/dist/data-sources/SnapDataSource.mjs.map +1 -1
  46. package/dist/index.cjs +3 -1
  47. package/dist/index.cjs.map +1 -1
  48. package/dist/index.d.cts +2 -2
  49. package/dist/index.d.cts.map +1 -1
  50. package/dist/index.d.mts +2 -2
  51. package/dist/index.d.mts.map +1 -1
  52. package/dist/index.mjs +1 -1
  53. package/dist/index.mjs.map +1 -1
  54. package/dist/middlewares/DetectionMiddleware.cjs +64 -23
  55. package/dist/middlewares/DetectionMiddleware.cjs.map +1 -1
  56. package/dist/middlewares/DetectionMiddleware.d.cts +15 -13
  57. package/dist/middlewares/DetectionMiddleware.d.cts.map +1 -1
  58. package/dist/middlewares/DetectionMiddleware.d.mts +15 -13
  59. package/dist/middlewares/DetectionMiddleware.d.mts.map +1 -1
  60. package/dist/middlewares/DetectionMiddleware.mjs +64 -23
  61. package/dist/middlewares/DetectionMiddleware.mjs.map +1 -1
  62. package/dist/middlewares/ParallelMiddleware.cjs +7 -0
  63. package/dist/middlewares/ParallelMiddleware.cjs.map +1 -1
  64. package/dist/middlewares/ParallelMiddleware.d.cts.map +1 -1
  65. package/dist/middlewares/ParallelMiddleware.d.mts.map +1 -1
  66. package/dist/middlewares/ParallelMiddleware.mjs +7 -0
  67. package/dist/middlewares/ParallelMiddleware.mjs.map +1 -1
  68. package/dist/selectors/balance.cjs +191 -8
  69. package/dist/selectors/balance.cjs.map +1 -1
  70. package/dist/selectors/balance.d.cts +80 -0
  71. package/dist/selectors/balance.d.cts.map +1 -1
  72. package/dist/selectors/balance.d.mts +80 -0
  73. package/dist/selectors/balance.d.mts.map +1 -1
  74. package/dist/selectors/balance.mjs +186 -7
  75. package/dist/selectors/balance.mjs.map +1 -1
  76. package/dist/types.cjs.map +1 -1
  77. package/dist/types.d.cts +20 -4
  78. package/dist/types.d.cts.map +1 -1
  79. package/dist/types.d.mts +20 -4
  80. package/dist/types.d.mts.map +1 -1
  81. package/dist/types.mjs.map +1 -1
  82. package/dist/utils/dedupingBatchFetcher.cjs +168 -0
  83. package/dist/utils/dedupingBatchFetcher.cjs.map +1 -0
  84. package/dist/utils/dedupingBatchFetcher.d.cts +72 -0
  85. package/dist/utils/dedupingBatchFetcher.d.cts.map +1 -0
  86. package/dist/utils/dedupingBatchFetcher.d.mts +72 -0
  87. package/dist/utils/dedupingBatchFetcher.d.mts.map +1 -0
  88. package/dist/utils/dedupingBatchFetcher.mjs +164 -0
  89. package/dist/utils/dedupingBatchFetcher.mjs.map +1 -0
  90. package/dist/utils/processAccountActivityBalanceUpdates.cjs +54 -0
  91. package/dist/utils/processAccountActivityBalanceUpdates.cjs.map +1 -0
  92. package/dist/utils/processAccountActivityBalanceUpdates.d.cts +13 -0
  93. package/dist/utils/processAccountActivityBalanceUpdates.d.cts.map +1 -0
  94. package/dist/utils/processAccountActivityBalanceUpdates.d.mts +13 -0
  95. package/dist/utils/processAccountActivityBalanceUpdates.d.mts.map +1 -0
  96. package/dist/utils/processAccountActivityBalanceUpdates.mjs +47 -0
  97. package/dist/utils/processAccountActivityBalanceUpdates.mjs.map +1 -0
  98. package/package.json +7 -7
@@ -115,12 +115,12 @@ export class SnapDataSource extends AbstractDataSource {
115
115
  return {};
116
116
  }
117
117
  if (!request?.accountsWithSupportedChains?.length) {
118
- return { assetsBalance: {}, assetsInfo: {}, updateMode: 'full' };
118
+ return { assetsBalance: {}, assetsInfo: {}, updateMode: 'merge' };
119
119
  }
120
120
  const results = {
121
121
  assetsBalance: {},
122
122
  assetsInfo: {},
123
- updateMode: 'full',
123
+ updateMode: 'merge',
124
124
  };
125
125
  // Fetch balances for each account using its snap ID from metadata
126
126
  for (const { account } of request.accountsWithSupportedChains) {
@@ -1 +1 @@
1
- {"version":3,"file":"SnapDataSource.mjs","sourceRoot":"","sources":["../../src/data-sources/SnapDataSource.ts"],"names":[],"mappings":";;;;;;;;;;;;AACA,OAAO,EAAE,aAAa,EAAE,sCAAsC;AAc9D,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,8BAA8B;AACpE,OAAO,EAAE,kBAAkB,EAAE,wBAAwB;AAIrD,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,sBAAkB;AAS9D,OAAO,EAAE,kBAAkB,EAAE,iCAA6B;AAiC1D,MAAM,GAAG,GAAG,kBAAkB,CAAC,aAAa,EAAE,gBAAgB,CAAC,CAAC;AAEhE,+EAA+E;AAC/E,YAAY;AACZ,+EAA+E;AAE/E,MAAM,CAAC,MAAM,qBAAqB,GAAG,gBAAgB,CAAC;AAEtD,qDAAqD;AACrD,MAAM,CAAC,MAAM,kBAAkB,GAAG,mBAAmB,CAAC;AAEtD,wEAAwE;AACxE,MAAM,CAAC,MAAM,iBAAiB,GAAG,kBAAkB,CAAC;AAEpD,+EAA+E;AAC/E,uBAAuB;AACvB,+EAA+E;AAE/E;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAC/B,UAAiC;IAEjC,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CACpC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,KAAK,cAAc,CAAC,QAAQ,CACpB,CAAC;IAE1C,OAAO,MAAM,CAAC,CAAC,CAAE,MAAM,CAAC,KAAmB,CAAC,CAAC,CAAC,IAAI,CAAC;AACrD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,uBAAuB,CAAC,OAAe;IACrD,MAAM,MAAM,GAAG,kBAAkB,CAAC,OAAwB,CAAC,CAAC;IAC5D,OAAO,MAAM,CAAC,OAAO,CAAC;AACxB,CAAC;AAkBD,MAAM,gBAAgB,GAAwB;IAC5C,YAAY,EAAE,EAAE;IAChB,WAAW,EAAE,EAAE;CAChB,CAAC;AAwCF,+EAA+E;AAC/E,mBAAmB;AACnB,+EAA+E;AAE/E;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,OAAO,cAAe,SAAQ,kBAGnC;IAmBC,YAAY,OAA8B;QACxC,KAAK,CAAC,qBAAqB,EAAE;YAC3B,GAAG,gBAAgB;YACnB,GAAG,OAAO,CAAC,KAAK;SACjB,CAAC,CAAC;;QAtBI,4CAAsC;QAEtC,wDAIC;QAEV,yEAAyE;QAChE,iEAEC;QAED,mEAA8C;QAEvD,6EAA6E;QACpE,6CAAkD,IAAI,GAAG,EAAE,EAAC;QAQnE,uBAAA,IAAI,6BAAc,OAAO,CAAC,SAAS,MAAA,CAAC;QACpC,uBAAA,IAAI,yCAA0B,OAAO,CAAC,qBAAqB,MAAA,CAAC;QAE5D,yCAAyC;QACzC,uBAAA,IAAI,kDAAmC,uBAAA,IAAI,4EAA2B,CAAC,IAAI,CACzE,IAAI,CACoD,MAAA,CAAC;QAC3D,uBAAA,IAAI,oDACF,uBAAA,IAAI,uEAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,MAAA,CAAC;QAExC,uBAAA,IAAI,oEAAmB,MAAvB,IAAI,CAAqB,CAAC;QAE1B,uEAAuE;QACvE,uBAAA,IAAI,uEAAsB,MAA1B,IAAI,CAAwB,CAAC;IAC/B,CAAC;IAoMD,+EAA+E;IAC/E,QAAQ;IACR,+EAA+E;IAE/E,KAAK,CAAC,KAAK,CAAC,OAAoB;;QAC9B,kCAAkC;QAClC,gFAAgF;QAChF,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;YAC/B,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,IAAI,CAAC,OAAO,EAAE,2BAA2B,EAAE,MAAM,EAAE,CAAC;YAClD,OAAO,EAAE,aAAa,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;QACnE,CAAC;QAED,MAAM,OAAO,GAAiB;YAC5B,aAAa,EAAE,EAAE;YACjB,UAAU,EAAE,EAAE;YACd,UAAU,EAAE,MAAM;SACnB,CAAC;QAEF,kEAAkE;QAClE,KAAK,MAAM,EAAE,OAAO,EAAE,IAAI,OAAO,CAAC,2BAA2B,EAAE,CAAC;YAC9D,0DAA0D;YAC1D,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;YACzC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,SAAS;YACX,CAAC;YAED,uEAAuE;YACvE,MAAM,2BAA2B,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CACvD,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,KAAK,MAAM,CACxD,CAAC;YACF,IAAI,CAAC,2BAA2B,EAAE,CAAC;gBACjC,SAAS;YACX,CAAC;YAED,MAAM,SAAS,GAAG,OAAO,CAAC,EAAE,CAAC;YAC7B,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,uBAAA,IAAI,mEAAkB,MAAtB,IAAI,EAAmB,MAAM,CAAC,CAAC;gBAE9C,kDAAkD;gBAClD,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;gBAEhE,qCAAqC;gBACrC,IAAI,CAAC,aAAa,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACjD,SAAS;gBACX,CAAC;gBAED,iDAAiD;gBACjD,MAAM,QAAQ,GACZ,MAAM,MAAM,CAAC,kBAAkB,CAC7B,SAAS,EACT,aAAgC,CACjC,CAAC;gBAEJ,oDAAoD;gBACpD,IAAI,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;oBACtE,KAAK,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;wBAC1D,MAAA,OAAO,CAAC,aAAa,EAAC,SAAS,SAAT,SAAS,IAAM,EAAE,EAAC;wBACxC,MAAM,eAAe,GAAG,OAAO,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;wBACzD,IAAI,eAAe,EAAE,CAAC;4BACnB,eAA2C,CAAC,OAAO,CAAC,GAAG;gCACtD,MAAM,EAAE,OAAO,CAAC,MAAM;6BACvB,CAAC;wBACJ,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,oDAAoD;YACtD,CAAC;QACH,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,+EAA+E;IAC/E,aAAa;IACb,+EAA+E;IAE/E;;;;;;;;;;OAUG;IACH,IAAI,gBAAgB;QAClB,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;;YAC7B,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;YAE5B,6CAA6C;YAC7C,MAAM,eAAe,GAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAC1D,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,CAC1C,CAAC;YAEF,2DAA2D;YAC3D,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACjC,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC;YACvB,CAAC;YAED,IAAI,yBAAyB,GAAc,EAAE,CAAC;YAE9C,IAAI,CAAC;gBACH,6BAA6B;gBAC7B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC;oBAChC,GAAG,OAAO;oBACV,QAAQ,EAAE,eAAe;iBAC1B,CAAC,CAAC;gBAEH,8BAA8B;gBAC9B,IAAI,QAAQ,CAAC,aAAa,EAAE,CAAC;oBAC3B,MAAA,OAAO,CAAC,QAAQ,EAAC,aAAa,QAAb,aAAa,GAAK,EAAE,EAAC;oBACtC,KAAK,MAAM,CAAC,SAAS,EAAE,eAAe,CAAC,IAAI,MAAM,CAAC,OAAO,CACvD,QAAQ,CAAC,aAAa,CACvB,EAAE,CAAC;wBACF,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,SAAS,CAAC,GAAG;4BAC1C,GAAG,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,SAAS,CAAC;4BAC5C,GAAG,eAAe;yBACnB,CAAC;oBACJ,CAAC;gBACH,CAAC;gBACD,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;oBACxB,OAAO,CAAC,QAAQ,CAAC,UAAU,GAAG;wBAC5B,GAAG,OAAO,CAAC,QAAQ,CAAC,UAAU;wBAC9B,GAAG,QAAQ,CAAC,UAAU;qBACvB,CAAC;gBACJ,CAAC;gBACD,IAAI,QAAQ,CAAC,WAAW,EAAE,CAAC;oBACzB,OAAO,CAAC,QAAQ,CAAC,WAAW,GAAG;wBAC7B,GAAG,OAAO,CAAC,QAAQ,CAAC,WAAW;wBAC/B,GAAG,QAAQ,CAAC,WAAW;qBACxB,CAAC;gBACJ,CAAC;gBAED,yDAAyD;gBACzD,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC;gBACjE,yBAAyB,GAAG,eAAe,CAAC,MAAM,CAChD,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CACxC,CAAC;YACJ,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,GAAG,CAAC,yBAAyB,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;gBAC1C,yBAAyB,GAAG,EAAE,CAAC;YACjC,CAAC;YAED,sCAAsC;YACtC,IAAI,WAAW,GAAG,OAAO,CAAC;YAC1B,IAAI,yBAAyB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACzC,MAAM,eAAe,GAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAC7C,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,yBAAyB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAC1D,CAAC;gBACF,WAAW,GAAG;oBACZ,GAAG,OAAO;oBACV,OAAO,EAAE;wBACP,GAAG,OAAO;wBACV,QAAQ,EAAE,eAAe;qBAC1B;iBACF,CAAC;YACJ,CAAC;YAED,uBAAuB;YACvB,OAAO,IAAI,CAAC,WAAW,CAAC,CAAC;QAC3B,CAAC,CAAC;IACJ,CAAC;IAED,+EAA+E;IAC/E,4CAA4C;IAC5C,+EAA+E;IAE/E,KAAK,CAAC,SAAS,CAAC,mBAAwC;QACtD,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,GAAG,mBAAmB,CAAC;QAElE,8CAA8C;QAC9C,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC;YACvB,OAAO;QACT,CAAC;QAED,sCAAsC;QACtC,MAAM,eAAe,GAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAC1D,uBAAA,IAAI,yEAAwB,MAA5B,IAAI,EAAyB,OAAO,CAAC,CACtC,CAAC;QAEF,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjC,OAAO;QACT,CAAC;QAED,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,QAAQ,GAAG,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;YAC9D,IAAI,QAAQ,EAAE,CAAC;gBACb,QAAQ,CAAC,MAAM,GAAG,eAAe,CAAC;gBAClC,uDAAuD;gBACvD,IAAI,CAAC,KAAK,CAAC;oBACT,GAAG,OAAO;oBACV,QAAQ,EAAE,eAAe;iBAC1B,CAAC;qBACC,IAAI,CAAC,KAAK,EAAE,aAAa,EAAE,EAAE;oBAC5B,IAAI,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBAC9D,MAAM,QAAQ,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC;oBAC/C,CAAC;oBACD,OAAO,aAAa,CAAC;gBACvB,CAAC,CAAC;qBACD,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;oBACf,GAAG,CAAC,kCAAkC,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAC;gBACrE,CAAC,CAAC,CAAC;gBACL,OAAO;YACT,CAAC;QACH,CAAC;QAED,MAAM,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC;QAEvC,gFAAgF;QAChF,iEAAiE;QACjE,sEAAsE;QAEtE,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,cAAc,EAAE;YAC3C,OAAO,EAAE,GAAG,EAAE;gBACZ,iDAAiD;YACnD,CAAC;YACD,MAAM,EAAE,eAAe;YACvB,cAAc,EAAE,mBAAmB,CAAC,cAAc;SACnD,CAAC,CAAC;QAEH,wCAAwC;QACxC,IAAI,CAAC;YACH,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC;gBACrC,GAAG,OAAO;gBACV,QAAQ,EAAE,eAAe;aAC1B,CAAC,CAAC;YAEH,MAAM,YAAY,GAAG,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;YAClE,IACE,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC;gBACzD,YAAY,EACZ,CAAC;gBACD,MAAM,YAAY,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC;YACnD,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,sBAAsB,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAC;QACzD,CAAC;IACH,CAAC;IAqCD,+EAA+E;IAC/E,UAAU;IACV,+EAA+E;IAE/E,OAAO;QACL,8DAA8D;QAC9D,MAAM,SAAS,GAAG,uBAAA,IAAI,iCAAkB,CAAC;QAEzC,uCAAuC;QACvC,IAAI,CAAC;YACH,SAAS,CAAC,WAAW,CACnB,2CAA2C,EAC3C,uBAAA,IAAI,sDAAgC,CACrC,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,gDAAgD,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QACnE,CAAC;QAED,sCAAsC;QACtC,IAAI,CAAC;YACH,SAAS,CAAC,WAAW,CACnB,kCAAkC,EAClC,uBAAA,IAAI,wDAAkC,CACvC,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,+CAA+C,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAClE,CAAC;QAED,gCAAgC;QAChC,KAAK,MAAM,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;YACxD,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;gBAC1C,wBAAwB;YAC1B,CAAC,CAAC,CAAC;QACL,CAAC;QAED,6BAA6B;QAC7B,uBAAA,IAAI,0CAAoB,CAAC,KAAK,EAAE,CAAC;IACnC,CAAC;CACF;;IA1fG,2FAA2F;IAC3F,8DAA8D;IAC9D,MAAM,SAAS,GAAG,uBAAA,IAAI,iCAAkB,CAAC;IACzC,SAAS,CAAC,SAAS,CACjB,2CAA2C,EAC3C,uBAAA,IAAI,sDAAgC,CACrC,CAAC;IACF,SAAS,CAAC,SAAS,CACjB,kCAAkC,EAClC,uBAAA,IAAI,wDAAkC,CACvC,CAAC;IACF,wDAAwD;IACxD,SAAS,CAAC,SAAS,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACvD,uBAAA,IAAI,uEAAsB,MAA1B,IAAI,CAAwB,CAAC;IAC/B,CAAC,CAAC,CAAC;AACL,CAAC,iGASC,OAA2C;IAE3C,4DAA4D;IAC5D,IAAI,aAAqE,CAAC;IAE1E,KAAK,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnE,IAAI,aAA8D,CAAC;QAEnE,KAAK,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACxD,IAAI,OAAgB,CAAC;YACrB,IAAI,CAAC;gBACH,OAAO,GAAG,uBAAuB,CAAC,OAAO,CAAC,CAAC;YAC7C,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,GAAG,CAAC,8CAA8C,EAAE;oBAClD,OAAO;oBACP,KAAK;iBACN,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YACD,IAAI,uBAAA,IAAI,yEAAwB,MAA5B,IAAI,EAAyB,OAAO,CAAC,EAAE,CAAC;gBAC1C,aAAa,KAAb,aAAa,GAAK,EAAE,EAAC;gBACrB,aAAa,CAAC,OAAwB,CAAC,GAAG;oBACxC,MAAM,EAAE,OAAO,CAAC,MAAM;iBACvB,CAAC;YACJ,CAAC;QACH,CAAC;QAED,IAAI,aAAa,EAAE,CAAC;YAClB,aAAa,KAAb,aAAa,GAAK,EAAE,EAAC;YACrB,aAAa,CAAC,SAAS,CAAC,GAAG,aAAa,CAAC;QAC3C,CAAC;IACH,CAAC;IAED,8CAA8C;IAC9C,IAAI,aAAa,EAAE,CAAC;QAClB,MAAM,QAAQ,GAAiB,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;QACtE,KAAK,MAAM,YAAY,IAAI,IAAI,CAAC,mBAAmB,CAAC,MAAM,EAAE,EAAE,CAAC;YAC7D,YAAY,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;AACH,CAAC,2FAQuB,OAAgB;IACtC,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AACnD,CAAC;IAaC,IAAI,CAAC;QACH,8DAA8D;QAC9D,OAAQ,uBAAA,IAAI,iCAAmB,CAAC,IAAI,CAClC,iCAAiC,CACxB,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;QAC3C,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC,mFASC,MAAc;IAEd,IAAI,CAAC;QACH,8DAA8D;QAC9D,OAAQ,uBAAA,IAAI,iCAAmB,CAAC,IAAI,CAClC,qCAAqC,EACrC,MAAM,CACqC,CAAC;IAChD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,oCAAoC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;QAC7D,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;IAkBC,IAAI,CAAC;QACH,MAAM,aAAa,GAAG,uBAAA,IAAI,mEAAkB,MAAtB,IAAI,CAAoB,CAAC;QAC/C,MAAM,WAAW,GAA4B,EAAE,CAAC;QAChD,MAAM,eAAe,GAAc,EAAE,CAAC;QAEtC,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;YACjC,MAAM,WAAW,GAAG,uBAAA,IAAI,qEAAoB,MAAxB,IAAI,EAAqB,IAAI,CAAC,EAAE,CAAC,CAAC;YACtD,8DAA8D;YAC9D,IAAI,CAAC,WAAW,EAAE,CAAC,kBAAkB,CAAC,EAAE,CAAC;gBACvC,SAAS;YACX,CAAC;YAED,0EAA0E;YAC1E,8CAA8C;YAC9C,MAAM,gBAAgB,GAAG,WAAW,CAAC,iBAAiB,CAAC,CAAC;YACxD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;YAErD,+EAA+E;YAC/E,IAAI,QAAQ,EAAE,CAAC;gBACb,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;oBAC/B,IAAI,CAAC,CAAC,OAAO,IAAI,WAAW,CAAC,EAAE,CAAC;wBAC9B,WAAW,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC;wBAC/B,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;oBAChC,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,6BAA6B;QAC7B,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,WAAW,CAAC;QAErC,2BAA2B;QAC3B,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAC9C,IAAI,CAAC,kBAAkB,CAAC,eAAe,EAAE,CAAC,aAAa,EAAE,EAAE;gBACzD,uBAAA,IAAI,6CAAuB,MAA3B,IAAI,EAAwB,IAAI,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,QAAQ,CAAC,CAAC;YACvE,CAAC,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,kEAAkE;QACpE,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,+BAA+B,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAChD,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,EAAE,CAAC;QAC5B,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAC9C,IAAI,CAAC,kBAAkB,CAAC,EAAE,EAAE,CAAC,aAAa,EAAE,EAAE;gBAC5C,uBAAA,IAAI,6CAAuB,MAA3B,IAAI,EAAwB,IAAI,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,QAAQ,CAAC,CAAC;YACvE,CAAC,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,kEAAkE;QACpE,CAAC;IACH,CAAC;AACH,CAAC,+EAiQiB,MAAc;IAC9B,MAAM,YAAY,GAAG,uBAAA,IAAI,0CAAoB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1D,IAAI,YAAY,EAAE,CAAC;QACjB,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,aAAa,CAAC;QAC/B,IAAI,EAAE,KAAK,EAAE,OAAuB,EAAiB,EAAE,CACrD,MACE,uBAAA,IAAI,iCAGL,CAAC,IAAI,CAAC,8BAA8B,EAAE;YACrC,MAAM,EAAE,MAAgB;YACxB,MAAM,EAAE,UAAU;YAClB,OAAO,EAAE,WAAW,CAAC,gBAAgB;YACrC,OAAO;SACR,CAAC;KACL,CAAC,CAAC;IAEH,uBAAA,IAAI,0CAAoB,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,OAAO,MAAM,CAAC;AAChB,CAAC;AA0CH,+EAA+E;AAC/E,UAAU;AACV,+EAA+E;AAE/E,MAAM,UAAU,oBAAoB,CAClC,OAA8B;IAE9B,OAAO,IAAI,cAAc,CAAC,OAAO,CAAC,CAAC;AACrC,CAAC","sourcesContent":["import type { Balance, CaipAssetType } from '@metamask/keyring-api';\nimport { KeyringClient } from '@metamask/keyring-snap-client';\nimport type {\n Caveat,\n GetPermissions,\n PermissionConstraint,\n PermissionControllerStateChange,\n SubjectPermissions,\n} from '@metamask/permission-controller';\nimport type {\n SnapControllerGetRunnableSnapsAction,\n SnapControllerHandleRequestAction,\n SnapControllerSnapInstalledEvent,\n} from '@metamask/snaps-controllers';\nimport type { Snap, SnapId } from '@metamask/snaps-sdk';\nimport { HandlerType, SnapCaveatType } from '@metamask/snaps-utils';\nimport { parseCaipAssetType } from '@metamask/utils';\nimport type { Json, JsonRpcRequest } from '@metamask/utils';\n\nimport type { AssetsControllerMessenger } from '../AssetsController';\nimport { projectLogger, createModuleLogger } from '../logger';\nimport type {\n AssetBalance,\n ChainId,\n Caip19AssetId,\n DataRequest,\n DataResponse,\n Middleware,\n} from '../types';\nimport { AbstractDataSource } from './AbstractDataSource';\nimport type {\n DataSourceState,\n SubscriptionRequest,\n} from './AbstractDataSource';\n\n// ============================================================================\n// SNAP KEYRING EVENT TYPES\n// ============================================================================\n\n/**\n * Payload for AccountsController:accountBalancesUpdated event.\n * Re-published from SnapKeyring:accountBalancesUpdated.\n */\nexport type AccountBalancesUpdatedEventPayload = {\n balances: {\n [accountId: string]: {\n [assetId: string]: {\n amount: string;\n unit: string;\n };\n };\n };\n};\n\n/**\n * Event from AccountsController when snap balances are updated.\n */\nexport type AccountsControllerAccountBalancesUpdatedEvent = {\n type: 'AccountsController:accountBalancesUpdated';\n payload: [AccountBalancesUpdatedEventPayload];\n};\n\nconst log = createModuleLogger(projectLogger, 'SnapDataSource');\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nexport const SNAP_DATA_SOURCE_NAME = 'SnapDataSource';\n\n/** The permission name for snap keyring endowment */\nexport const KEYRING_PERMISSION = 'endowment:keyring';\n\n/** The permission name for snap assets endowment (contains chainIds) */\nexport const ASSETS_PERMISSION = 'endowment:assets';\n\n// ============================================================================\n// PERMISSION UTILITIES\n// ============================================================================\n\n/**\n * Getter function to get the chainIds caveat from a permission.\n *\n * This does basic validation of the caveat, but does not validate the type or\n * value of the namespaces object itself, as this is handled by the\n * `PermissionsController` when the permission is requested.\n *\n * @param permission - The permission to get the `chainIds` caveat from.\n * @returns An array of `chainIds` that the snap supports, or null if none.\n */\nexport function getChainIdsCaveat(\n permission?: PermissionConstraint,\n): ChainId[] | null {\n if (!permission?.caveats) {\n return null;\n }\n\n const caveat = permission.caveats.find(\n (permCaveat) => permCaveat.type === SnapCaveatType.ChainIds,\n ) as Caveat<string, string[]> | undefined;\n\n return caveat ? (caveat.value as ChainId[]) : null;\n}\n\n/**\n * Extracts the CAIP-2 chain ID from a CAIP-19 asset ID.\n * e.g., \"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501\" -> \"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp\"\n * Uses @metamask/utils parseCaipAssetType for CAIP parsing.\n *\n * @param assetId - The CAIP-19 asset ID to extract chain from.\n * @returns The CAIP-2 chain ID portion of the asset ID.\n */\nexport function extractChainFromAssetId(assetId: string): ChainId {\n const parsed = parseCaipAssetType(assetId as CaipAssetType);\n return parsed.chainId;\n}\n\n// ============================================================================\n// STATE\n// ============================================================================\n\n/**\n * State for the SnapDataSource.\n * Uses dynamic snap discovery - chains are populated from PermissionController.\n */\nexport type SnapDataSourceState = {\n /**\n * Mapping of chain IDs to snap IDs that support them.\n * Used to filter which accounts to process for a given chain request.\n */\n chainToSnap: Record<ChainId, string>;\n} & DataSourceState;\n\nconst defaultSnapState: SnapDataSourceState = {\n activeChains: [],\n chainToSnap: {},\n};\n\n// ============================================================================\n// MESSENGER TYPES\n// ============================================================================\n\n/**\n * Allowed events that SnapDataSource can subscribe to.\n */\nexport type SnapDataSourceAllowedEvents =\n | AccountsControllerAccountBalancesUpdatedEvent\n | PermissionControllerStateChange\n | SnapControllerSnapInstalledEvent;\n\nexport type SnapDataSourceAllowedActions =\n | SnapControllerGetRunnableSnapsAction\n | SnapControllerHandleRequestAction\n | GetPermissions;\n\n// ============================================================================\n// OPTIONS\n// ============================================================================\n\nexport type SnapDataSourceOptions = {\n /** The AssetsController messenger (shared by all data sources). */\n messenger: AssetsControllerMessenger;\n /** Called when this data source's active chains change. Pass dataSourceName so the controller knows the source. */\n onActiveChainsUpdated: (\n dataSourceName: string,\n chains: ChainId[],\n previousChains: ChainId[],\n ) => void;\n /** Configured networks to support (defaults to all snap networks) */\n configuredNetworks?: ChainId[];\n /** Default polling interval in ms for subscriptions */\n pollInterval?: number;\n /** Initial state */\n state?: Partial<SnapDataSourceState>;\n};\n\n// ============================================================================\n// SNAP DATA SOURCE\n// ============================================================================\n\n/**\n * Unified Snap data source that routes requests to the appropriate wallet snap\n * based on the chain ID prefix.\n *\n * @example\n * ```typescript\n * const snapDataSource = new SnapDataSource({\n * messenger,\n * onActiveChainsUpdated: (chains) => { /* ... *\\/ },\n * });\n *\n * // Fetch will automatically route to the correct snap\n * await snapDataSource.fetch({\n * chainIds: ['solana:mainnet', 'bip122:000000000019d6689c085ae165831e93'],\n * accountIds: ['account1'],\n * });\n * ```\n */\nexport class SnapDataSource extends AbstractDataSource<\n typeof SNAP_DATA_SOURCE_NAME,\n SnapDataSourceState\n> {\n readonly #messenger: AssetsControllerMessenger;\n\n readonly #onActiveChainsUpdated: (\n dataSourceName: string,\n chains: ChainId[],\n previousChains: ChainId[],\n ) => void;\n\n /** Bound handler for snap keyring balance updates, stored for cleanup */\n readonly #handleSnapBalancesUpdatedBound: (\n payload: AccountBalancesUpdatedEventPayload,\n ) => void;\n\n readonly #handlePermissionStateChangeBound: () => void;\n\n /** Cache of KeyringClient instances per snap ID to avoid re-instantiation */\n readonly #keyringClientCache: Map<string, KeyringClient> = new Map();\n\n constructor(options: SnapDataSourceOptions) {\n super(SNAP_DATA_SOURCE_NAME, {\n ...defaultSnapState,\n ...options.state,\n });\n\n this.#messenger = options.messenger;\n this.#onActiveChainsUpdated = options.onActiveChainsUpdated;\n\n // Bind handlers for cleanup in destroy()\n this.#handleSnapBalancesUpdatedBound = this.#handleSnapBalancesUpdated.bind(\n this,\n ) as (payload: AccountBalancesUpdatedEventPayload) => void;\n this.#handlePermissionStateChangeBound =\n this.#discoverKeyringSnaps.bind(this);\n\n this.#subscribeToEvents();\n\n // Discover keyring-capable snaps and populate activeChains dynamically\n this.#discoverKeyringSnaps();\n }\n\n /**\n * Subscribe to all events needed by SnapDataSource.\n * Groups snap keyring events and permission change events.\n */\n #subscribeToEvents(): void {\n // Subscribe to snap keyring events and permission changes (not in AssetsControllerEvents).\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const messenger = this.#messenger as any;\n messenger.subscribe(\n 'AccountsController:accountBalancesUpdated',\n this.#handleSnapBalancesUpdatedBound,\n );\n messenger.subscribe(\n 'PermissionController:stateChange',\n this.#handlePermissionStateChangeBound,\n );\n // Rediscover keyring snaps when any snap gets installed\n messenger.subscribe('SnapController:snapInstalled', () => {\n this.#discoverKeyringSnaps();\n });\n }\n\n /**\n * Handle snap balance updates from the keyring.\n * Transforms the payload and publishes to AssetsController.\n *\n * @param payload - The balance update payload from AccountsController.\n */\n #handleSnapBalancesUpdated(\n payload: AccountBalancesUpdatedEventPayload,\n ): void {\n // Transform the snap keyring payload to DataResponse format\n let assetsBalance: NonNullable<DataResponse['assetsBalance']> | undefined;\n\n for (const [accountId, assets] of Object.entries(payload.balances)) {\n let accountAssets: Record<Caip19AssetId, AssetBalance> | undefined;\n\n for (const [assetId, balance] of Object.entries(assets)) {\n let chainId: ChainId;\n try {\n chainId = extractChainFromAssetId(assetId);\n } catch (error) {\n log('Skipping snap balance for malformed asset ID', {\n assetId,\n error,\n });\n continue;\n }\n if (this.#isChainSupportedBySnap(chainId)) {\n accountAssets ??= {};\n accountAssets[assetId as Caip19AssetId] = {\n amount: balance.amount,\n };\n }\n }\n\n if (accountAssets) {\n assetsBalance ??= {};\n assetsBalance[accountId] = accountAssets;\n }\n }\n\n // Only report if we have snap-related updates\n if (assetsBalance) {\n const response: DataResponse = { assetsBalance, updateMode: 'merge' };\n for (const subscription of this.activeSubscriptions.values()) {\n subscription.onAssetsUpdate(response)?.catch(console.error);\n }\n }\n }\n\n /**\n * Check if a chain ID is supported by any discovered snap.\n *\n * @param chainId - The CAIP-2 chain ID to check.\n * @returns True if we have a snap that supports this chain.\n */\n #isChainSupportedBySnap(chainId: ChainId): boolean {\n return this.state.activeChains.includes(chainId);\n }\n\n // ============================================================================\n // SNAP DISCOVERY (Dynamic via PermissionController)\n // ============================================================================\n\n /**\n * Get all runnable snaps from SnapController.\n * Runnable snaps are enabled and not blocked.\n *\n * @returns Array of runnable snaps.\n */\n #getRunnableSnaps(): Snap[] {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (this.#messenger as any).call(\n 'SnapController:getRunnableSnaps',\n ) as Snap[];\n } catch (error) {\n log('Failed to get runnable snaps', error);\n return [];\n }\n }\n\n /**\n * Get permissions for a snap from PermissionController.\n *\n * @param snapId - The snap ID to get permissions for.\n * @returns The snap's permissions, or undefined if none.\n */\n #getSnapPermissions(\n snapId: string,\n ): SubjectPermissions<PermissionConstraint> | undefined {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (this.#messenger as any).call(\n 'PermissionController:getPermissions',\n snapId,\n ) as SubjectPermissions<PermissionConstraint>;\n } catch (error) {\n log('Failed to get permissions for snap', { snapId, error });\n return undefined;\n }\n }\n\n /**\n * Discover all snaps with keyring capabilities and their supported chains.\n * Uses PermissionController to find snaps with endowment:keyring permission.\n * Updates chainToSnap mapping and activeChains.\n *\n * Called on initialization and whenever PermissionController state changes\n * (e.g., new snaps installed, permissions granted/revoked).\n *\n * @remarks\n * **Known limitation:** If discovery fails (e.g., SnapController not ready),\n * the data source continues with empty chainToSnap. This means no snap\n * chains will be supported until a re-discovery is triggered by a permission\n * change. Callers should be aware that initialization may complete with no\n * active chains.\n */\n #discoverKeyringSnaps(): void {\n try {\n const runnableSnaps = this.#getRunnableSnaps();\n const chainToSnap: Record<ChainId, string> = {};\n const supportedChains: ChainId[] = [];\n\n for (const snap of runnableSnaps) {\n const permissions = this.#getSnapPermissions(snap.id);\n // Must have endowment:keyring permission to be a keyring snap\n if (!permissions?.[KEYRING_PERMISSION]) {\n continue;\n }\n\n // Get chainIds caveat from the assets permission (not keyring permission)\n // The chainIds are stored in endowment:assets\n const assetsPermission = permissions[ASSETS_PERMISSION];\n const chainIds = getChainIdsCaveat(assetsPermission);\n\n // Map each chain to this snap (first snap wins if multiple support same chain)\n if (chainIds) {\n for (const chainId of chainIds) {\n if (!(chainId in chainToSnap)) {\n chainToSnap[chainId] = snap.id;\n supportedChains.push(chainId);\n }\n }\n }\n }\n\n // Update chainToSnap mapping\n this.state.chainToSnap = chainToSnap;\n\n // Notify if chains changed\n try {\n const previous = [...this.state.activeChains];\n this.updateActiveChains(supportedChains, (updatedChains) => {\n this.#onActiveChainsUpdated(this.getName(), updatedChains, previous);\n });\n } catch {\n // AssetsController not ready yet - expected during initialization\n }\n } catch (error) {\n log('Keyring snap discovery failed', { error });\n this.state.chainToSnap = {};\n try {\n const previous = [...this.state.activeChains];\n this.updateActiveChains([], (updatedChains) => {\n this.#onActiveChainsUpdated(this.getName(), updatedChains, previous);\n });\n } catch {\n // AssetsController not ready yet - expected during initialization\n }\n }\n }\n\n // ============================================================================\n // FETCH\n // ============================================================================\n\n async fetch(request: DataRequest): Promise<DataResponse> {\n // Guard against undefined request\n // Note: chainIds filtering is done by middleware/subscribe before calling fetch\n if (!request?.chainIds?.length) {\n return {};\n }\n if (!request?.accountsWithSupportedChains?.length) {\n return { assetsBalance: {}, assetsInfo: {}, updateMode: 'full' };\n }\n\n const results: DataResponse = {\n assetsBalance: {},\n assetsInfo: {},\n updateMode: 'full',\n };\n\n // Fetch balances for each account using its snap ID from metadata\n for (const { account } of request.accountsWithSupportedChains) {\n // Skip accounts without snap metadata (non-snap accounts)\n const snapId = account.metadata.snap?.id;\n if (!snapId) {\n continue;\n }\n\n // Skip accounts whose snap doesn't support any of the requested chains\n const snapSupportsRequestedChains = request.chainIds.some(\n (chainId) => this.state.chainToSnap[chainId] === snapId,\n );\n if (!snapSupportsRequestedChains) {\n continue;\n }\n\n const accountId = account.id;\n try {\n const client = this.#getKeyringClient(snapId);\n\n // Step 1: Get the list of assets for this account\n const accountAssets = await client.listAccountAssets(accountId);\n\n // If no assets, skip to next account\n if (!accountAssets || accountAssets.length === 0) {\n continue;\n }\n\n // Step 2: Get balances for those specific assets\n const balances: Record<CaipAssetType, Balance> =\n await client.getAccountBalances(\n accountId,\n accountAssets as CaipAssetType[],\n );\n\n // Transform keyring response to DataResponse format\n if (balances && typeof balances === 'object' && results.assetsBalance) {\n for (const [assetId, balance] of Object.entries(balances)) {\n results.assetsBalance[accountId] ??= {};\n const accountBalances = results.assetsBalance[accountId];\n if (accountBalances) {\n (accountBalances as Record<string, unknown>)[assetId] = {\n amount: balance.amount,\n };\n }\n }\n }\n } catch {\n // Expected when account doesn't belong to this snap\n }\n }\n\n return results;\n }\n\n // ============================================================================\n // MIDDLEWARE\n // ============================================================================\n\n /**\n * Get the middleware for fetching balances via Snaps.\n * This middleware:\n * - Supports multiple accounts in a single request\n * - Filters request to only chains this data source supports\n * - Fetches balances for those chains for all accounts\n * - Merges response into context\n * - Removes handled chains from request for next middleware\n *\n * @returns The middleware function for the assets pipeline.\n */\n get assetsMiddleware(): Middleware {\n return async (context, next) => {\n const { request } = context;\n\n // Filter to chains this data source supports\n const supportedChains = request.chainIds.filter((chainId) =>\n this.state.activeChains.includes(chainId),\n );\n\n // If no supported chains, skip and pass to next middleware\n if (supportedChains.length === 0) {\n return next(context);\n }\n\n let successfullyHandledChains: ChainId[] = [];\n\n try {\n // Fetch for supported chains\n const response = await this.fetch({\n ...request,\n chainIds: supportedChains,\n });\n\n // Merge response into context\n if (response.assetsBalance) {\n context.response.assetsBalance ??= {};\n for (const [accountId, accountBalances] of Object.entries(\n response.assetsBalance,\n )) {\n context.response.assetsBalance[accountId] = {\n ...context.response.assetsBalance[accountId],\n ...accountBalances,\n };\n }\n }\n if (response.assetsInfo) {\n context.response.assetsInfo = {\n ...context.response.assetsInfo,\n ...response.assetsInfo,\n };\n }\n if (response.assetsPrice) {\n context.response.assetsPrice = {\n ...context.response.assetsPrice,\n ...response.assetsPrice,\n };\n }\n\n // Determine successfully handled chains (exclude errors)\n const failedChains = new Set(Object.keys(response.errors ?? {}));\n successfullyHandledChains = supportedChains.filter(\n (chainId) => !failedChains.has(chainId),\n );\n } catch (error) {\n log('Middleware fetch failed', { error });\n successfullyHandledChains = [];\n }\n\n // Prepare context for next middleware\n let nextContext = context;\n if (successfullyHandledChains.length > 0) {\n const remainingChains = request.chainIds.filter(\n (chainId) => !successfullyHandledChains.includes(chainId),\n );\n nextContext = {\n ...context,\n request: {\n ...request,\n chainIds: remainingChains,\n },\n };\n }\n\n // Call next middleware\n return next(nextContext);\n };\n }\n\n // ============================================================================\n // SUBSCRIBE - Routes to appropriate snap(s)\n // ============================================================================\n\n async subscribe(subscriptionRequest: SubscriptionRequest): Promise<void> {\n const { request, subscriptionId, isUpdate } = subscriptionRequest;\n\n // Guard against undefined request or chainIds\n if (!request?.chainIds) {\n return;\n }\n\n // Filter to chains we have a snap for\n const supportedChains = request.chainIds.filter((chainId) =>\n this.#isChainSupportedBySnap(chainId),\n );\n\n if (supportedChains.length === 0) {\n return;\n }\n\n if (isUpdate) {\n const existing = this.activeSubscriptions.get(subscriptionId);\n if (existing) {\n existing.chains = supportedChains;\n // Do a fetch to get latest data on subscription update\n this.fetch({\n ...request,\n chainIds: supportedChains,\n })\n .then(async (fetchResponse) => {\n if (Object.keys(fetchResponse.assetsBalance ?? {}).length > 0) {\n await existing.onAssetsUpdate(fetchResponse);\n }\n return fetchResponse;\n })\n .catch((error) => {\n log('Subscription update fetch failed', { subscriptionId, error });\n });\n return;\n }\n }\n\n await this.unsubscribe(subscriptionId);\n\n // Snaps provide real-time updates via AccountsController:accountBalancesUpdated\n // We only need to track the subscription and do an initial fetch\n // No polling needed - updates come through #handleSnapBalancesUpdated\n\n this.activeSubscriptions.set(subscriptionId, {\n cleanup: () => {\n // No timer to clear - we use event-based updates\n },\n chains: supportedChains,\n onAssetsUpdate: subscriptionRequest.onAssetsUpdate,\n });\n\n // Initial fetch to get current balances\n try {\n const fetchResponse = await this.fetch({\n ...request,\n chainIds: supportedChains,\n });\n\n const subscription = this.activeSubscriptions.get(subscriptionId);\n if (\n Object.keys(fetchResponse.assetsBalance ?? {}).length > 0 &&\n subscription\n ) {\n await subscription.onAssetsUpdate(fetchResponse);\n }\n } catch (error) {\n log('Initial fetch failed', { subscriptionId, error });\n }\n }\n\n // ============================================================================\n // KEYRING CLIENT\n // ============================================================================\n\n /**\n * Gets a `KeyringClient` for a Snap.\n * Caches clients per snap ID to avoid re-instantiation across multiple calls.\n *\n * @param snapId - ID of the Snap to get the client for.\n * @returns A `KeyringClient` for the Snap.\n */\n #getKeyringClient(snapId: string): KeyringClient {\n const cachedClient = this.#keyringClientCache.get(snapId);\n if (cachedClient) {\n return cachedClient;\n }\n\n const client = new KeyringClient({\n send: async (request: JsonRpcRequest): Promise<Json> =>\n await (\n this.#messenger as unknown as {\n call: (action: string, ...args: unknown[]) => Promise<Json> | Json;\n }\n ).call('SnapController:handleRequest', {\n snapId: snapId as SnapId,\n origin: 'metamask',\n handler: HandlerType.OnKeyringRequest,\n request,\n }),\n });\n\n this.#keyringClientCache.set(snapId, client);\n return client;\n }\n\n // ============================================================================\n // CLEANUP\n // ============================================================================\n\n destroy(): void {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const messenger = this.#messenger as any;\n\n // Unsubscribe from snap keyring events\n try {\n messenger.unsubscribe(\n 'AccountsController:accountBalancesUpdated',\n this.#handleSnapBalancesUpdatedBound,\n );\n } catch (error) {\n log('Failed to unsubscribe from snap keyring events', { error });\n }\n\n // Unsubscribe from permission changes\n try {\n messenger.unsubscribe(\n 'PermissionController:stateChange',\n this.#handlePermissionStateChangeBound,\n );\n } catch (error) {\n log('Failed to unsubscribe from permission changes', { error });\n }\n\n // Clean up active subscriptions\n for (const [subscriptionId] of this.activeSubscriptions) {\n this.unsubscribe(subscriptionId).catch(() => {\n // Ignore cleanup errors\n });\n }\n\n // Clear keyring client cache\n this.#keyringClientCache.clear();\n }\n}\n\n// ============================================================================\n// FACTORY\n// ============================================================================\n\nexport function createSnapDataSource(\n options: SnapDataSourceOptions,\n): SnapDataSource {\n return new SnapDataSource(options);\n}\n"]}
1
+ {"version":3,"file":"SnapDataSource.mjs","sourceRoot":"","sources":["../../src/data-sources/SnapDataSource.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Balance, CaipAssetType } from '@metamask/keyring-api';\nimport { KeyringClient } from '@metamask/keyring-snap-client';\nimport type {\n Caveat,\n GetPermissions,\n PermissionConstraint,\n PermissionControllerStateChange,\n SubjectPermissions,\n} from '@metamask/permission-controller';\nimport type {\n SnapControllerGetRunnableSnapsAction,\n SnapControllerHandleRequestAction,\n SnapControllerSnapInstalledEvent,\n} from '@metamask/snaps-controllers';\nimport type { Snap, SnapId } from '@metamask/snaps-sdk';\nimport { HandlerType, SnapCaveatType } from '@metamask/snaps-utils';\nimport { parseCaipAssetType } from '@metamask/utils';\nimport type { Json, JsonRpcRequest } from '@metamask/utils';\n\nimport type { AssetsControllerMessenger } from '../AssetsController';\nimport { projectLogger, createModuleLogger } from '../logger';\nimport type {\n AssetBalance,\n ChainId,\n Caip19AssetId,\n DataRequest,\n DataResponse,\n Middleware,\n} from '../types';\nimport { AbstractDataSource } from './AbstractDataSource';\nimport type {\n DataSourceState,\n SubscriptionRequest,\n} from './AbstractDataSource';\n\n// ============================================================================\n// SNAP KEYRING EVENT TYPES\n// ============================================================================\n\n/**\n * Payload for AccountsController:accountBalancesUpdated event.\n * Re-published from SnapKeyring:accountBalancesUpdated.\n */\nexport type AccountBalancesUpdatedEventPayload = {\n balances: {\n [accountId: string]: {\n [assetId: string]: {\n amount: string;\n unit: string;\n };\n };\n };\n};\n\n/**\n * Event from AccountsController when snap balances are updated.\n */\nexport type AccountsControllerAccountBalancesUpdatedEvent = {\n type: 'AccountsController:accountBalancesUpdated';\n payload: [AccountBalancesUpdatedEventPayload];\n};\n\nconst log = createModuleLogger(projectLogger, 'SnapDataSource');\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nexport const SNAP_DATA_SOURCE_NAME = 'SnapDataSource';\n\n/** The permission name for snap keyring endowment */\nexport const KEYRING_PERMISSION = 'endowment:keyring';\n\n/** The permission name for snap assets endowment (contains chainIds) */\nexport const ASSETS_PERMISSION = 'endowment:assets';\n\n// ============================================================================\n// PERMISSION UTILITIES\n// ============================================================================\n\n/**\n * Getter function to get the chainIds caveat from a permission.\n *\n * This does basic validation of the caveat, but does not validate the type or\n * value of the namespaces object itself, as this is handled by the\n * `PermissionsController` when the permission is requested.\n *\n * @param permission - The permission to get the `chainIds` caveat from.\n * @returns An array of `chainIds` that the snap supports, or null if none.\n */\nexport function getChainIdsCaveat(\n permission?: PermissionConstraint,\n): ChainId[] | null {\n if (!permission?.caveats) {\n return null;\n }\n\n const caveat = permission.caveats.find(\n (permCaveat) => permCaveat.type === SnapCaveatType.ChainIds,\n ) as Caveat<string, string[]> | undefined;\n\n return caveat ? (caveat.value as ChainId[]) : null;\n}\n\n/**\n * Extracts the CAIP-2 chain ID from a CAIP-19 asset ID.\n * e.g., \"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501\" -> \"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp\"\n * Uses @metamask/utils parseCaipAssetType for CAIP parsing.\n *\n * @param assetId - The CAIP-19 asset ID to extract chain from.\n * @returns The CAIP-2 chain ID portion of the asset ID.\n */\nexport function extractChainFromAssetId(assetId: string): ChainId {\n const parsed = parseCaipAssetType(assetId as CaipAssetType);\n return parsed.chainId;\n}\n\n// ============================================================================\n// STATE\n// ============================================================================\n\n/**\n * State for the SnapDataSource.\n * Uses dynamic snap discovery - chains are populated from PermissionController.\n */\nexport type SnapDataSourceState = {\n /**\n * Mapping of chain IDs to snap IDs that support them.\n * Used to filter which accounts to process for a given chain request.\n */\n chainToSnap: Record<ChainId, string>;\n} & DataSourceState;\n\nconst defaultSnapState: SnapDataSourceState = {\n activeChains: [],\n chainToSnap: {},\n};\n\n// ============================================================================\n// MESSENGER TYPES\n// ============================================================================\n\n/**\n * Allowed events that SnapDataSource can subscribe to.\n */\nexport type SnapDataSourceAllowedEvents =\n | AccountsControllerAccountBalancesUpdatedEvent\n | PermissionControllerStateChange\n | SnapControllerSnapInstalledEvent;\n\nexport type SnapDataSourceAllowedActions =\n | SnapControllerGetRunnableSnapsAction\n | SnapControllerHandleRequestAction\n | GetPermissions;\n\n// ============================================================================\n// OPTIONS\n// ============================================================================\n\nexport type SnapDataSourceOptions = {\n /** The AssetsController messenger (shared by all data sources). */\n messenger: AssetsControllerMessenger;\n /** Called when this data source's active chains change. Pass dataSourceName so the controller knows the source. */\n onActiveChainsUpdated: (\n dataSourceName: string,\n chains: ChainId[],\n previousChains: ChainId[],\n ) => void;\n /** Configured networks to support (defaults to all snap networks) */\n configuredNetworks?: ChainId[];\n /** Default polling interval in ms for subscriptions */\n pollInterval?: number;\n /** Initial state */\n state?: Partial<SnapDataSourceState>;\n};\n\n// ============================================================================\n// SNAP DATA SOURCE\n// ============================================================================\n\n/**\n * Unified Snap data source that routes requests to the appropriate wallet snap\n * based on the chain ID prefix.\n *\n * @example\n * ```typescript\n * const snapDataSource = new SnapDataSource({\n * messenger,\n * onActiveChainsUpdated: (chains) => { /* ... *\\/ },\n * });\n *\n * // Fetch will automatically route to the correct snap\n * await snapDataSource.fetch({\n * chainIds: ['solana:mainnet', 'bip122:000000000019d6689c085ae165831e93'],\n * accountIds: ['account1'],\n * });\n * ```\n */\nexport class SnapDataSource extends AbstractDataSource<\n typeof SNAP_DATA_SOURCE_NAME,\n SnapDataSourceState\n> {\n readonly #messenger: AssetsControllerMessenger;\n\n readonly #onActiveChainsUpdated: (\n dataSourceName: string,\n chains: ChainId[],\n previousChains: ChainId[],\n ) => void;\n\n /** Bound handler for snap keyring balance updates, stored for cleanup */\n readonly #handleSnapBalancesUpdatedBound: (\n payload: AccountBalancesUpdatedEventPayload,\n ) => void;\n\n readonly #handlePermissionStateChangeBound: () => void;\n\n /** Cache of KeyringClient instances per snap ID to avoid re-instantiation */\n readonly #keyringClientCache: Map<string, KeyringClient> = new Map();\n\n constructor(options: SnapDataSourceOptions) {\n super(SNAP_DATA_SOURCE_NAME, {\n ...defaultSnapState,\n ...options.state,\n });\n\n this.#messenger = options.messenger;\n this.#onActiveChainsUpdated = options.onActiveChainsUpdated;\n\n // Bind handlers for cleanup in destroy()\n this.#handleSnapBalancesUpdatedBound = this.#handleSnapBalancesUpdated.bind(\n this,\n ) as (payload: AccountBalancesUpdatedEventPayload) => void;\n this.#handlePermissionStateChangeBound =\n this.#discoverKeyringSnaps.bind(this);\n\n this.#subscribeToEvents();\n\n // Discover keyring-capable snaps and populate activeChains dynamically\n this.#discoverKeyringSnaps();\n }\n\n /**\n * Subscribe to all events needed by SnapDataSource.\n * Groups snap keyring events and permission change events.\n */\n #subscribeToEvents(): void {\n // Subscribe to snap keyring events and permission changes (not in AssetsControllerEvents).\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const messenger = this.#messenger as any;\n messenger.subscribe(\n 'AccountsController:accountBalancesUpdated',\n this.#handleSnapBalancesUpdatedBound,\n );\n messenger.subscribe(\n 'PermissionController:stateChange',\n this.#handlePermissionStateChangeBound,\n );\n // Rediscover keyring snaps when any snap gets installed\n messenger.subscribe('SnapController:snapInstalled', () => {\n this.#discoverKeyringSnaps();\n });\n }\n\n /**\n * Handle snap balance updates from the keyring.\n * Transforms the payload and publishes to AssetsController.\n *\n * @param payload - The balance update payload from AccountsController.\n */\n #handleSnapBalancesUpdated(\n payload: AccountBalancesUpdatedEventPayload,\n ): void {\n // Transform the snap keyring payload to DataResponse format\n let assetsBalance: NonNullable<DataResponse['assetsBalance']> | undefined;\n\n for (const [accountId, assets] of Object.entries(payload.balances)) {\n let accountAssets: Record<Caip19AssetId, AssetBalance> | undefined;\n\n for (const [assetId, balance] of Object.entries(assets)) {\n let chainId: ChainId;\n try {\n chainId = extractChainFromAssetId(assetId);\n } catch (error) {\n log('Skipping snap balance for malformed asset ID', {\n assetId,\n error,\n });\n continue;\n }\n if (this.#isChainSupportedBySnap(chainId)) {\n accountAssets ??= {};\n accountAssets[assetId as Caip19AssetId] = {\n amount: balance.amount,\n };\n }\n }\n\n if (accountAssets) {\n assetsBalance ??= {};\n assetsBalance[accountId] = accountAssets;\n }\n }\n\n // Only report if we have snap-related updates\n if (assetsBalance) {\n const response: DataResponse = { assetsBalance, updateMode: 'merge' };\n for (const subscription of this.activeSubscriptions.values()) {\n subscription.onAssetsUpdate(response)?.catch(console.error);\n }\n }\n }\n\n /**\n * Check if a chain ID is supported by any discovered snap.\n *\n * @param chainId - The CAIP-2 chain ID to check.\n * @returns True if we have a snap that supports this chain.\n */\n #isChainSupportedBySnap(chainId: ChainId): boolean {\n return this.state.activeChains.includes(chainId);\n }\n\n // ============================================================================\n // SNAP DISCOVERY (Dynamic via PermissionController)\n // ============================================================================\n\n /**\n * Get all runnable snaps from SnapController.\n * Runnable snaps are enabled and not blocked.\n *\n * @returns Array of runnable snaps.\n */\n #getRunnableSnaps(): Snap[] {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (this.#messenger as any).call(\n 'SnapController:getRunnableSnaps',\n ) as Snap[];\n } catch (error) {\n log('Failed to get runnable snaps', error);\n return [];\n }\n }\n\n /**\n * Get permissions for a snap from PermissionController.\n *\n * @param snapId - The snap ID to get permissions for.\n * @returns The snap's permissions, or undefined if none.\n */\n #getSnapPermissions(\n snapId: string,\n ): SubjectPermissions<PermissionConstraint> | undefined {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (this.#messenger as any).call(\n 'PermissionController:getPermissions',\n snapId,\n ) as SubjectPermissions<PermissionConstraint>;\n } catch (error) {\n log('Failed to get permissions for snap', { snapId, error });\n return undefined;\n }\n }\n\n /**\n * Discover all snaps with keyring capabilities and their supported chains.\n * Uses PermissionController to find snaps with endowment:keyring permission.\n * Updates chainToSnap mapping and activeChains.\n *\n * Called on initialization and whenever PermissionController state changes\n * (e.g., new snaps installed, permissions granted/revoked).\n *\n * @remarks\n * **Known limitation:** If discovery fails (e.g., SnapController not ready),\n * the data source continues with empty chainToSnap. This means no snap\n * chains will be supported until a re-discovery is triggered by a permission\n * change. Callers should be aware that initialization may complete with no\n * active chains.\n */\n #discoverKeyringSnaps(): void {\n try {\n const runnableSnaps = this.#getRunnableSnaps();\n const chainToSnap: Record<ChainId, string> = {};\n const supportedChains: ChainId[] = [];\n\n for (const snap of runnableSnaps) {\n const permissions = this.#getSnapPermissions(snap.id);\n // Must have endowment:keyring permission to be a keyring snap\n if (!permissions?.[KEYRING_PERMISSION]) {\n continue;\n }\n\n // Get chainIds caveat from the assets permission (not keyring permission)\n // The chainIds are stored in endowment:assets\n const assetsPermission = permissions[ASSETS_PERMISSION];\n const chainIds = getChainIdsCaveat(assetsPermission);\n\n // Map each chain to this snap (first snap wins if multiple support same chain)\n if (chainIds) {\n for (const chainId of chainIds) {\n if (!(chainId in chainToSnap)) {\n chainToSnap[chainId] = snap.id;\n supportedChains.push(chainId);\n }\n }\n }\n }\n\n // Update chainToSnap mapping\n this.state.chainToSnap = chainToSnap;\n\n // Notify if chains changed\n try {\n const previous = [...this.state.activeChains];\n this.updateActiveChains(supportedChains, (updatedChains) => {\n this.#onActiveChainsUpdated(this.getName(), updatedChains, previous);\n });\n } catch {\n // AssetsController not ready yet - expected during initialization\n }\n } catch (error) {\n log('Keyring snap discovery failed', { error });\n this.state.chainToSnap = {};\n try {\n const previous = [...this.state.activeChains];\n this.updateActiveChains([], (updatedChains) => {\n this.#onActiveChainsUpdated(this.getName(), updatedChains, previous);\n });\n } catch {\n // AssetsController not ready yet - expected during initialization\n }\n }\n }\n\n // ============================================================================\n // FETCH\n // ============================================================================\n\n async fetch(request: DataRequest): Promise<DataResponse> {\n // Guard against undefined request\n // Note: chainIds filtering is done by middleware/subscribe before calling fetch\n if (!request?.chainIds?.length) {\n return {};\n }\n if (!request?.accountsWithSupportedChains?.length) {\n return { assetsBalance: {}, assetsInfo: {}, updateMode: 'merge' };\n }\n\n const results: DataResponse = {\n assetsBalance: {},\n assetsInfo: {},\n updateMode: 'merge',\n };\n\n // Fetch balances for each account using its snap ID from metadata\n for (const { account } of request.accountsWithSupportedChains) {\n // Skip accounts without snap metadata (non-snap accounts)\n const snapId = account.metadata.snap?.id;\n if (!snapId) {\n continue;\n }\n\n // Skip accounts whose snap doesn't support any of the requested chains\n const snapSupportsRequestedChains = request.chainIds.some(\n (chainId) => this.state.chainToSnap[chainId] === snapId,\n );\n if (!snapSupportsRequestedChains) {\n continue;\n }\n\n const accountId = account.id;\n try {\n const client = this.#getKeyringClient(snapId);\n\n // Step 1: Get the list of assets for this account\n const accountAssets = await client.listAccountAssets(accountId);\n\n // If no assets, skip to next account\n if (!accountAssets || accountAssets.length === 0) {\n continue;\n }\n\n // Step 2: Get balances for those specific assets\n const balances: Record<CaipAssetType, Balance> =\n await client.getAccountBalances(\n accountId,\n accountAssets as CaipAssetType[],\n );\n\n // Transform keyring response to DataResponse format\n if (balances && typeof balances === 'object' && results.assetsBalance) {\n for (const [assetId, balance] of Object.entries(balances)) {\n results.assetsBalance[accountId] ??= {};\n const accountBalances = results.assetsBalance[accountId];\n if (accountBalances) {\n (accountBalances as Record<string, unknown>)[assetId] = {\n amount: balance.amount,\n };\n }\n }\n }\n } catch {\n // Expected when account doesn't belong to this snap\n }\n }\n\n return results;\n }\n\n // ============================================================================\n // MIDDLEWARE\n // ============================================================================\n\n /**\n * Get the middleware for fetching balances via Snaps.\n * This middleware:\n * - Supports multiple accounts in a single request\n * - Filters request to only chains this data source supports\n * - Fetches balances for those chains for all accounts\n * - Merges response into context\n * - Removes handled chains from request for next middleware\n *\n * @returns The middleware function for the assets pipeline.\n */\n get assetsMiddleware(): Middleware {\n return async (context, next) => {\n const { request } = context;\n\n // Filter to chains this data source supports\n const supportedChains = request.chainIds.filter((chainId) =>\n this.state.activeChains.includes(chainId),\n );\n\n // If no supported chains, skip and pass to next middleware\n if (supportedChains.length === 0) {\n return next(context);\n }\n\n let successfullyHandledChains: ChainId[] = [];\n\n try {\n // Fetch for supported chains\n const response = await this.fetch({\n ...request,\n chainIds: supportedChains,\n });\n\n // Merge response into context\n if (response.assetsBalance) {\n context.response.assetsBalance ??= {};\n for (const [accountId, accountBalances] of Object.entries(\n response.assetsBalance,\n )) {\n context.response.assetsBalance[accountId] = {\n ...context.response.assetsBalance[accountId],\n ...accountBalances,\n };\n }\n }\n if (response.assetsInfo) {\n context.response.assetsInfo = {\n ...context.response.assetsInfo,\n ...response.assetsInfo,\n };\n }\n if (response.assetsPrice) {\n context.response.assetsPrice = {\n ...context.response.assetsPrice,\n ...response.assetsPrice,\n };\n }\n\n // Determine successfully handled chains (exclude errors)\n const failedChains = new Set(Object.keys(response.errors ?? {}));\n successfullyHandledChains = supportedChains.filter(\n (chainId) => !failedChains.has(chainId),\n );\n } catch (error) {\n log('Middleware fetch failed', { error });\n successfullyHandledChains = [];\n }\n\n // Prepare context for next middleware\n let nextContext = context;\n if (successfullyHandledChains.length > 0) {\n const remainingChains = request.chainIds.filter(\n (chainId) => !successfullyHandledChains.includes(chainId),\n );\n nextContext = {\n ...context,\n request: {\n ...request,\n chainIds: remainingChains,\n },\n };\n }\n\n // Call next middleware\n return next(nextContext);\n };\n }\n\n // ============================================================================\n // SUBSCRIBE - Routes to appropriate snap(s)\n // ============================================================================\n\n async subscribe(subscriptionRequest: SubscriptionRequest): Promise<void> {\n const { request, subscriptionId, isUpdate } = subscriptionRequest;\n\n // Guard against undefined request or chainIds\n if (!request?.chainIds) {\n return;\n }\n\n // Filter to chains we have a snap for\n const supportedChains = request.chainIds.filter((chainId) =>\n this.#isChainSupportedBySnap(chainId),\n );\n\n if (supportedChains.length === 0) {\n return;\n }\n\n if (isUpdate) {\n const existing = this.activeSubscriptions.get(subscriptionId);\n if (existing) {\n existing.chains = supportedChains;\n // Do a fetch to get latest data on subscription update\n this.fetch({\n ...request,\n chainIds: supportedChains,\n })\n .then(async (fetchResponse) => {\n if (Object.keys(fetchResponse.assetsBalance ?? {}).length > 0) {\n await existing.onAssetsUpdate(fetchResponse);\n }\n return fetchResponse;\n })\n .catch((error) => {\n log('Subscription update fetch failed', { subscriptionId, error });\n });\n return;\n }\n }\n\n await this.unsubscribe(subscriptionId);\n\n // Snaps provide real-time updates via AccountsController:accountBalancesUpdated\n // We only need to track the subscription and do an initial fetch\n // No polling needed - updates come through #handleSnapBalancesUpdated\n\n this.activeSubscriptions.set(subscriptionId, {\n cleanup: () => {\n // No timer to clear - we use event-based updates\n },\n chains: supportedChains,\n onAssetsUpdate: subscriptionRequest.onAssetsUpdate,\n });\n\n // Initial fetch to get current balances\n try {\n const fetchResponse = await this.fetch({\n ...request,\n chainIds: supportedChains,\n });\n\n const subscription = this.activeSubscriptions.get(subscriptionId);\n if (\n Object.keys(fetchResponse.assetsBalance ?? {}).length > 0 &&\n subscription\n ) {\n await subscription.onAssetsUpdate(fetchResponse);\n }\n } catch (error) {\n log('Initial fetch failed', { subscriptionId, error });\n }\n }\n\n // ============================================================================\n // KEYRING CLIENT\n // ============================================================================\n\n /**\n * Gets a `KeyringClient` for a Snap.\n * Caches clients per snap ID to avoid re-instantiation across multiple calls.\n *\n * @param snapId - ID of the Snap to get the client for.\n * @returns A `KeyringClient` for the Snap.\n */\n #getKeyringClient(snapId: string): KeyringClient {\n const cachedClient = this.#keyringClientCache.get(snapId);\n if (cachedClient) {\n return cachedClient;\n }\n\n const client = new KeyringClient({\n send: async (request: JsonRpcRequest): Promise<Json> =>\n await (\n this.#messenger as unknown as {\n call: (action: string, ...args: unknown[]) => Promise<Json> | Json;\n }\n ).call('SnapController:handleRequest', {\n snapId: snapId as SnapId,\n origin: 'metamask',\n handler: HandlerType.OnKeyringRequest,\n request,\n }),\n });\n\n this.#keyringClientCache.set(snapId, client);\n return client;\n }\n\n // ============================================================================\n // CLEANUP\n // ============================================================================\n\n destroy(): void {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const messenger = this.#messenger as any;\n\n // Unsubscribe from snap keyring events\n try {\n messenger.unsubscribe(\n 'AccountsController:accountBalancesUpdated',\n this.#handleSnapBalancesUpdatedBound,\n );\n } catch (error) {\n log('Failed to unsubscribe from snap keyring events', { error });\n }\n\n // Unsubscribe from permission changes\n try {\n messenger.unsubscribe(\n 'PermissionController:stateChange',\n this.#handlePermissionStateChangeBound,\n );\n } catch (error) {\n log('Failed to unsubscribe from permission changes', { error });\n }\n\n // Clean up active subscriptions\n for (const [subscriptionId] of this.activeSubscriptions) {\n this.unsubscribe(subscriptionId).catch(() => {\n // Ignore cleanup errors\n });\n }\n\n // Clear keyring client cache\n this.#keyringClientCache.clear();\n }\n}\n\n// ============================================================================\n// FACTORY\n// ============================================================================\n\nexport function createSnapDataSource(\n options: SnapDataSourceOptions,\n): SnapDataSource {\n return new SnapDataSource(options);\n}\n"]}
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getInternalAccountsForGroup = exports.getGroupIdForAccount = exports.getAggregatedBalanceForAccount = exports.formatStateForTransactionPay = exports.formatExchangeRatesForBridge = exports.normalizeAssetId = exports.RpcFallbackMiddleware = exports.DetectionMiddleware = exports.CustomAssetGraduationMiddleware = exports.PriceDataSource = exports.TokenDataSource = exports.extractChainFromAssetId = exports.getChainIdsCaveat = exports.KEYRING_PERMISSION = exports.SNAP_DATA_SOURCE_NAME = exports.createSnapDataSource = exports.SnapDataSource = exports.createRpcDataSource = exports.RpcDataSource = exports.createBackendWebsocketDataSource = exports.BackendWebsocketDataSource = exports.AccountsApiDataSource = exports.AbstractDataSource = exports.getDefaultAssetMetadata = exports.getDefaultTrackedAssetsForChain = exports.buildDefaultAssetsInfo = exports.DEFAULT_ASSET_METADATA = exports.CHAINS_WITH_DEFAULT_TRACKED_ASSETS = exports.DEFAULT_TRACKED_ASSETS_BY_CHAIN = exports.AssetsDataSourceError = exports.getDefaultAssetsControllerState = exports.AssetsController = void 0;
3
+ exports.getInternalAccountsForGroup = exports.getGroupIdForAccount = exports.getAggregatedBalanceForAccount = exports.calculateBalanceChangeForAccountGroup = exports.calculateBalanceForAllWallets = exports.formatStateForTransactionPay = exports.formatExchangeRatesForBridge = exports.normalizeAssetId = exports.RpcFallbackMiddleware = exports.DetectionMiddleware = exports.CustomAssetGraduationMiddleware = exports.PriceDataSource = exports.TokenDataSource = exports.extractChainFromAssetId = exports.getChainIdsCaveat = exports.KEYRING_PERMISSION = exports.SNAP_DATA_SOURCE_NAME = exports.createSnapDataSource = exports.SnapDataSource = exports.createRpcDataSource = exports.RpcDataSource = exports.createBackendWebsocketDataSource = exports.BackendWebsocketDataSource = exports.AccountsApiDataSource = exports.AbstractDataSource = exports.getDefaultAssetMetadata = exports.getDefaultTrackedAssetsForChain = exports.buildDefaultAssetsInfo = exports.DEFAULT_ASSET_METADATA = exports.CHAINS_WITH_DEFAULT_TRACKED_ASSETS = exports.DEFAULT_TRACKED_ASSETS_BY_CHAIN = exports.AssetsDataSourceError = exports.getDefaultAssetsControllerState = exports.AssetsController = void 0;
4
4
  // Main controller export
5
5
  var AssetsController_1 = require("./AssetsController.cjs");
6
6
  Object.defineProperty(exports, "AssetsController", { enumerable: true, get: function () { return AssetsController_1.AssetsController; } });
@@ -54,6 +54,8 @@ Object.defineProperty(exports, "formatExchangeRatesForBridge", { enumerable: tru
54
54
  Object.defineProperty(exports, "formatStateForTransactionPay", { enumerable: true, get: function () { return utils_1.formatStateForTransactionPay; } });
55
55
  // Selectors
56
56
  var balance_1 = require("./selectors/balance.cjs");
57
+ Object.defineProperty(exports, "calculateBalanceForAllWallets", { enumerable: true, get: function () { return balance_1.calculateBalanceForAllWallets; } });
58
+ Object.defineProperty(exports, "calculateBalanceChangeForAccountGroup", { enumerable: true, get: function () { return balance_1.calculateBalanceChangeForAccountGroup; } });
57
59
  Object.defineProperty(exports, "getAggregatedBalanceForAccount", { enumerable: true, get: function () { return balance_1.getAggregatedBalanceForAccount; } });
58
60
  Object.defineProperty(exports, "getGroupIdForAccount", { enumerable: true, get: function () { return balance_1.getGroupIdForAccount; } });
59
61
  Object.defineProperty(exports, "getInternalAccountsForGroup", { enumerable: true, get: function () { return balance_1.getInternalAccountsForGroup; } });
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,yBAAyB;AACzB,2DAG4B;AAF1B,oHAAA,gBAAgB,OAAA;AAChB,mIAAA,+BAA+B,OAAA;AAEjC,uCAAiD;AAAxC,+GAAA,qBAAqB,OAAA;AAC9B,2CAOoB;AANlB,2HAAA,+BAA+B,OAAA;AAC/B,8HAAA,kCAAkC,OAAA;AAClC,kHAAA,sBAAsB,OAAA;AACtB,kHAAA,sBAAsB,OAAA;AACtB,2HAAA,+BAA+B,OAAA;AAC/B,mHAAA,uBAAuB,OAAA;AAsFzB,sCAAsC;AACtC,yDAAoD;AAA3C,kHAAA,kBAAkB,OAAA;AAI3B,6BAA6B;AAC7B,yDAAuD;AAA9C,qHAAA,qBAAqB,OAAA;AAQ9B,kCAAkC;AAClC,yDAGwB;AAFtB,0HAAA,0BAA0B,OAAA;AAC1B,gIAAA,gCAAgC,OAAA;AAQlC,qBAAqB;AACrB,yDAAoE;AAA3D,6GAAA,aAAa,OAAA;AAAE,mHAAA,mBAAmB,OAAA;AAS3C,gFAAgF;AAChF,yDASwB;AARtB,8GAAA,cAAc,OAAA;AACd,oHAAA,oBAAoB,OAAA;AACpB,qHAAA,qBAAqB,OAAA;AACrB,YAAY;AACZ,kHAAA,kBAAkB,OAAA;AAClB,oBAAoB;AACpB,iHAAA,iBAAiB,OAAA;AACjB,uHAAA,uBAAuB,OAAA;AAQzB,0BAA0B;AAC1B,yDAAkE;AAAzD,+GAAA,eAAe,OAAA;AAAE,+GAAA,eAAe,OAAA;AAQzC,cAAc;AACd,uDAIuB;AAHrB,8HAAA,+BAA+B,OAAA;AAC/B,kHAAA,mBAAmB,OAAA;AACnB,oHAAA,qBAAqB,OAAA;AAOvB,YAAY;AACZ,2CAIiB;AAHf,yGAAA,gBAAgB,OAAA;AAChB,qHAAA,4BAA4B,OAAA;AAC5B,qHAAA,4BAA4B,OAAA;AAS9B,YAAY;AACZ,mDAI6B;AAH3B,yHAAA,8BAA8B,OAAA;AAC9B,+GAAA,oBAAoB,OAAA;AACpB,sHAAA,2BAA2B,OAAA","sourcesContent":["// Main controller export\nexport {\n AssetsController,\n getDefaultAssetsControllerState,\n} from './AssetsController';\nexport { AssetsDataSourceError } from './errors';\nexport {\n DEFAULT_TRACKED_ASSETS_BY_CHAIN,\n CHAINS_WITH_DEFAULT_TRACKED_ASSETS,\n DEFAULT_ASSET_METADATA,\n buildDefaultAssetsInfo,\n getDefaultTrackedAssetsForChain,\n getDefaultAssetMetadata,\n} from './defaults';\nexport type { PendingTokenMetadata } from './AssetsController';\n\n// State and messenger types\nexport type {\n AssetsControllerState,\n AssetsControllerMessenger,\n AssetsControllerOptions,\n AssetsControllerGetStateAction,\n AssetsControllerActions,\n AssetsControllerStateChangeEvent,\n AssetsControllerBalanceChangedEvent,\n AssetsControllerPriceChangedEvent,\n AssetsControllerAssetsDetectedEvent,\n AssetsControllerEvents,\n} from './AssetsController';\nexport type {\n AssetsControllerGetAssetsAction,\n AssetsControllerGetAssetsBalanceAction,\n AssetsControllerGetAssetMetadataAction,\n AssetsControllerGetAssetsPriceAction,\n AssetsControllerAddCustomAssetAction,\n AssetsControllerRemoveCustomAssetAction,\n AssetsControllerGetCustomAssetsAction,\n AssetsControllerHideAssetAction,\n AssetsControllerUnhideAssetAction,\n AssetsControllerGetExchangeRatesForBridgeAction,\n AssetsControllerGetStateForTransactionPayAction,\n AssetsControllerSetSelectedCurrencyAction,\n} from './AssetsController-method-action-types';\n\n// Core types\nexport type {\n // CAIP types\n Caip19AssetId,\n AccountId,\n ChainId,\n // Asset types\n AssetType,\n TokenStandard,\n // Contract data types\n TokenFees,\n HoneypotStatus,\n StorageSlots,\n LocalizedDescription,\n // Metadata types\n BaseAssetMetadata,\n FungibleAssetMetadata,\n ERC721AssetMetadata,\n ERC1155AssetMetadata,\n AssetMetadata,\n // Price types\n BaseAssetPrice,\n FungibleAssetPrice,\n NFTAssetPrice,\n AssetPrice,\n // Balance types\n FungibleAssetBalance,\n ERC721AssetBalance,\n ERC1155AssetBalance,\n AssetBalance,\n // Data source types\n AccountWithSupportedChains,\n DataType,\n DataRequest,\n DataResponse,\n AssetsUpdateMode,\n // Middleware types\n Context,\n NextFunction,\n Middleware,\n AssetsDataSource,\n FetchContext,\n FetchNextFunction,\n FetchMiddleware,\n SubscriptionResponse,\n // Combined asset type\n Asset,\n // Event types\n BalanceChangeEvent,\n PriceChangeEvent,\n MetadataChangeEvent,\n AssetsDetectedEvent,\n} from './types';\n\n// Data sources - base class and types\nexport { AbstractDataSource } from './data-sources';\n\nexport type { DataSourceState, SubscriptionRequest } from './data-sources';\n\n// Data sources - AccountsApi\nexport { AccountsApiDataSource } from './data-sources';\n\nexport type {\n AccountsApiDataSourceConfig,\n AccountsApiDataSourceOptions,\n AccountsApiDataSourceState,\n} from './data-sources';\n\n// Data sources - BackendWebsocket\nexport {\n BackendWebsocketDataSource,\n createBackendWebsocketDataSource,\n} from './data-sources';\n\nexport type {\n BackendWebsocketDataSourceOptions,\n BackendWebsocketDataSourceState,\n} from './data-sources';\n\n// Data sources - RPC\nexport { RpcDataSource, createRpcDataSource } from './data-sources';\n\nexport type {\n RpcDataSourceConfig,\n RpcDataSourceOptions,\n RpcDataSourceState,\n ChainStatus,\n} from './data-sources';\n\n// Data sources - Unified Snap Data Source (dynamically discovers keyring snaps)\nexport {\n SnapDataSource,\n createSnapDataSource,\n SNAP_DATA_SOURCE_NAME,\n // Constants\n KEYRING_PERMISSION,\n // Utility functions\n getChainIdsCaveat,\n extractChainFromAssetId,\n} from './data-sources';\n\nexport type {\n SnapDataSourceState,\n SnapDataSourceOptions,\n} from './data-sources';\n\n// Enrichment data sources\nexport { TokenDataSource, PriceDataSource } from './data-sources';\n\nexport type {\n TokenDataSourceOptions,\n PriceDataSourceConfig,\n PriceDataSourceOptions,\n} from './data-sources';\n\n// Middlewares\nexport {\n CustomAssetGraduationMiddleware,\n DetectionMiddleware,\n RpcFallbackMiddleware,\n} from './middlewares';\nexport type {\n CustomAssetGraduationMiddlewareOptions,\n RpcFallbackMiddlewareOptions,\n} from './middlewares';\n\n// Utilities\nexport {\n normalizeAssetId,\n formatExchangeRatesForBridge,\n formatStateForTransactionPay,\n} from './utils';\nexport type {\n AccountForLegacyFormat,\n BridgeExchangeRatesFormat,\n LegacyToken,\n TransactionPayLegacyFormat,\n} from './utils';\n\n// Selectors\nexport {\n getAggregatedBalanceForAccount,\n getGroupIdForAccount,\n getInternalAccountsForGroup,\n} from './selectors/balance';\n\nexport type {\n AccountsById,\n AggregatedBalanceEntry,\n AggregatedBalanceForAccount,\n EnabledNetworkMap,\n} from './selectors/balance';\n"]}
1
+ {"version":3,"file":"index.cjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,yBAAyB;AACzB,2DAG4B;AAF1B,oHAAA,gBAAgB,OAAA;AAChB,mIAAA,+BAA+B,OAAA;AAEjC,uCAAiD;AAAxC,+GAAA,qBAAqB,OAAA;AAC9B,2CAOoB;AANlB,2HAAA,+BAA+B,OAAA;AAC/B,8HAAA,kCAAkC,OAAA;AAClC,kHAAA,sBAAsB,OAAA;AACtB,kHAAA,sBAAsB,OAAA;AACtB,2HAAA,+BAA+B,OAAA;AAC/B,mHAAA,uBAAuB,OAAA;AAsFzB,sCAAsC;AACtC,yDAAoD;AAA3C,kHAAA,kBAAkB,OAAA;AAI3B,6BAA6B;AAC7B,yDAAuD;AAA9C,qHAAA,qBAAqB,OAAA;AAQ9B,kCAAkC;AAClC,yDAGwB;AAFtB,0HAAA,0BAA0B,OAAA;AAC1B,gIAAA,gCAAgC,OAAA;AAQlC,qBAAqB;AACrB,yDAAoE;AAA3D,6GAAA,aAAa,OAAA;AAAE,mHAAA,mBAAmB,OAAA;AAS3C,gFAAgF;AAChF,yDASwB;AARtB,8GAAA,cAAc,OAAA;AACd,oHAAA,oBAAoB,OAAA;AACpB,qHAAA,qBAAqB,OAAA;AACrB,YAAY;AACZ,kHAAA,kBAAkB,OAAA;AAClB,oBAAoB;AACpB,iHAAA,iBAAiB,OAAA;AACjB,uHAAA,uBAAuB,OAAA;AAQzB,0BAA0B;AAC1B,yDAAkE;AAAzD,+GAAA,eAAe,OAAA;AAAE,+GAAA,eAAe,OAAA;AAQzC,cAAc;AACd,uDAIuB;AAHrB,8HAAA,+BAA+B,OAAA;AAC/B,kHAAA,mBAAmB,OAAA;AACnB,oHAAA,qBAAqB,OAAA;AAOvB,YAAY;AACZ,2CAIiB;AAHf,yGAAA,gBAAgB,OAAA;AAChB,qHAAA,4BAA4B,OAAA;AAC5B,qHAAA,4BAA4B,OAAA;AAS9B,YAAY;AACZ,mDAM6B;AAL3B,wHAAA,6BAA6B,OAAA;AAC7B,gIAAA,qCAAqC,OAAA;AACrC,yHAAA,8BAA8B,OAAA;AAC9B,+GAAA,oBAAoB,OAAA;AACpB,sHAAA,2BAA2B,OAAA","sourcesContent":["// Main controller export\nexport {\n AssetsController,\n getDefaultAssetsControllerState,\n} from './AssetsController';\nexport { AssetsDataSourceError } from './errors';\nexport {\n DEFAULT_TRACKED_ASSETS_BY_CHAIN,\n CHAINS_WITH_DEFAULT_TRACKED_ASSETS,\n DEFAULT_ASSET_METADATA,\n buildDefaultAssetsInfo,\n getDefaultTrackedAssetsForChain,\n getDefaultAssetMetadata,\n} from './defaults';\nexport type { PendingTokenMetadata } from './AssetsController';\n\n// State and messenger types\nexport type {\n AssetsControllerState,\n AssetsControllerMessenger,\n AssetsControllerOptions,\n AssetsControllerGetStateAction,\n AssetsControllerActions,\n AssetsControllerStateChangeEvent,\n AssetsControllerBalanceChangedEvent,\n AssetsControllerPriceChangedEvent,\n AssetsControllerAssetsDetectedEvent,\n AssetsControllerEvents,\n} from './AssetsController';\nexport type {\n AssetsControllerGetAssetsAction,\n AssetsControllerGetAssetsBalanceAction,\n AssetsControllerGetAssetMetadataAction,\n AssetsControllerGetAssetsPriceAction,\n AssetsControllerAddCustomAssetAction,\n AssetsControllerRemoveCustomAssetAction,\n AssetsControllerGetCustomAssetsAction,\n AssetsControllerHideAssetAction,\n AssetsControllerUnhideAssetAction,\n AssetsControllerGetExchangeRatesForBridgeAction,\n AssetsControllerGetStateForTransactionPayAction,\n AssetsControllerSetSelectedCurrencyAction,\n} from './AssetsController-method-action-types';\n\n// Core types\nexport type {\n // CAIP types\n Caip19AssetId,\n AccountId,\n ChainId,\n // Asset types\n AssetType,\n TokenStandard,\n // Contract data types\n TokenFees,\n HoneypotStatus,\n StorageSlots,\n LocalizedDescription,\n // Metadata types\n BaseAssetMetadata,\n FungibleAssetMetadata,\n ERC721AssetMetadata,\n ERC1155AssetMetadata,\n AssetMetadata,\n // Price types\n BaseAssetPrice,\n FungibleAssetPrice,\n NFTAssetPrice,\n AssetPrice,\n // Balance types\n FungibleAssetBalance,\n ERC721AssetBalance,\n ERC1155AssetBalance,\n AssetBalance,\n // Data source types\n AccountWithSupportedChains,\n DataType,\n DataRequest,\n DataResponse,\n AssetsUpdateMode,\n // Middleware types\n Context,\n NextFunction,\n Middleware,\n AssetsDataSource,\n FetchContext,\n FetchNextFunction,\n FetchMiddleware,\n SubscriptionResponse,\n // Combined asset type\n Asset,\n // Event types\n BalanceChangeEvent,\n PriceChangeEvent,\n MetadataChangeEvent,\n AssetsDetectedEvent,\n} from './types';\n\n// Data sources - base class and types\nexport { AbstractDataSource } from './data-sources';\n\nexport type { DataSourceState, SubscriptionRequest } from './data-sources';\n\n// Data sources - AccountsApi\nexport { AccountsApiDataSource } from './data-sources';\n\nexport type {\n AccountsApiDataSourceConfig,\n AccountsApiDataSourceOptions,\n AccountsApiDataSourceState,\n} from './data-sources';\n\n// Data sources - BackendWebsocket\nexport {\n BackendWebsocketDataSource,\n createBackendWebsocketDataSource,\n} from './data-sources';\n\nexport type {\n BackendWebsocketDataSourceOptions,\n BackendWebsocketDataSourceState,\n} from './data-sources';\n\n// Data sources - RPC\nexport { RpcDataSource, createRpcDataSource } from './data-sources';\n\nexport type {\n RpcDataSourceConfig,\n RpcDataSourceOptions,\n RpcDataSourceState,\n ChainStatus,\n} from './data-sources';\n\n// Data sources - Unified Snap Data Source (dynamically discovers keyring snaps)\nexport {\n SnapDataSource,\n createSnapDataSource,\n SNAP_DATA_SOURCE_NAME,\n // Constants\n KEYRING_PERMISSION,\n // Utility functions\n getChainIdsCaveat,\n extractChainFromAssetId,\n} from './data-sources';\n\nexport type {\n SnapDataSourceState,\n SnapDataSourceOptions,\n} from './data-sources';\n\n// Enrichment data sources\nexport { TokenDataSource, PriceDataSource } from './data-sources';\n\nexport type {\n TokenDataSourceOptions,\n PriceDataSourceConfig,\n PriceDataSourceOptions,\n} from './data-sources';\n\n// Middlewares\nexport {\n CustomAssetGraduationMiddleware,\n DetectionMiddleware,\n RpcFallbackMiddleware,\n} from './middlewares';\nexport type {\n CustomAssetGraduationMiddlewareOptions,\n RpcFallbackMiddlewareOptions,\n} from './middlewares';\n\n// Utilities\nexport {\n normalizeAssetId,\n formatExchangeRatesForBridge,\n formatStateForTransactionPay,\n} from './utils';\nexport type {\n AccountForLegacyFormat,\n BridgeExchangeRatesFormat,\n LegacyToken,\n TransactionPayLegacyFormat,\n} from './utils';\n\n// Selectors\nexport {\n calculateBalanceForAllWallets,\n calculateBalanceChangeForAccountGroup,\n getAggregatedBalanceForAccount,\n getGroupIdForAccount,\n getInternalAccountsForGroup,\n} from './selectors/balance';\n\nexport type {\n AccountGroupBalance,\n AccountsById,\n AggregatedBalanceEntry,\n AggregatedBalanceForAccount,\n AllWalletsBalance,\n BalanceChangePeriod,\n BalanceChangeResult,\n EnabledNetworkMap,\n WalletBalance,\n} from './selectors/balance';\n"]}
package/dist/index.d.cts CHANGED
@@ -21,6 +21,6 @@ export { CustomAssetGraduationMiddleware, DetectionMiddleware, RpcFallbackMiddle
21
21
  export type { CustomAssetGraduationMiddlewareOptions, RpcFallbackMiddlewareOptions, } from "./middlewares/index.cjs";
22
22
  export { normalizeAssetId, formatExchangeRatesForBridge, formatStateForTransactionPay, } from "./utils/index.cjs";
23
23
  export type { AccountForLegacyFormat, BridgeExchangeRatesFormat, LegacyToken, TransactionPayLegacyFormat, } from "./utils/index.cjs";
24
- export { getAggregatedBalanceForAccount, getGroupIdForAccount, getInternalAccountsForGroup, } from "./selectors/balance.cjs";
25
- export type { AccountsById, AggregatedBalanceEntry, AggregatedBalanceForAccount, EnabledNetworkMap, } from "./selectors/balance.cjs";
24
+ export { calculateBalanceForAllWallets, calculateBalanceChangeForAccountGroup, getAggregatedBalanceForAccount, getGroupIdForAccount, getInternalAccountsForGroup, } from "./selectors/balance.cjs";
25
+ export type { AccountGroupBalance, AccountsById, AggregatedBalanceEntry, AggregatedBalanceForAccount, AllWalletsBalance, BalanceChangePeriod, BalanceChangeResult, EnabledNetworkMap, WalletBalance, } from "./selectors/balance.cjs";
26
26
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.d.mts CHANGED
@@ -21,6 +21,6 @@ export { CustomAssetGraduationMiddleware, DetectionMiddleware, RpcFallbackMiddle
21
21
  export type { CustomAssetGraduationMiddlewareOptions, RpcFallbackMiddlewareOptions, } from "./middlewares/index.mjs";
22
22
  export { normalizeAssetId, formatExchangeRatesForBridge, formatStateForTransactionPay, } from "./utils/index.mjs";
23
23
  export type { AccountForLegacyFormat, BridgeExchangeRatesFormat, LegacyToken, TransactionPayLegacyFormat, } from "./utils/index.mjs";
24
- export { getAggregatedBalanceForAccount, getGroupIdForAccount, getInternalAccountsForGroup, } from "./selectors/balance.mjs";
25
- export type { AccountsById, AggregatedBalanceEntry, AggregatedBalanceForAccount, EnabledNetworkMap, } from "./selectors/balance.mjs";
24
+ export { calculateBalanceForAllWallets, calculateBalanceChangeForAccountGroup, getAggregatedBalanceForAccount, getGroupIdForAccount, getInternalAccountsForGroup, } from "./selectors/balance.mjs";
25
+ export type { AccountGroupBalance, AccountsById, AggregatedBalanceEntry, AggregatedBalanceForAccount, AllWalletsBalance, BalanceChangePeriod, BalanceChangeResult, EnabledNetworkMap, WalletBalance, } from "./selectors/balance.mjs";
26
26
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -23,5 +23,5 @@ export { CustomAssetGraduationMiddleware, DetectionMiddleware, RpcFallbackMiddle
23
23
  // Utilities
24
24
  export { normalizeAssetId, formatExchangeRatesForBridge, formatStateForTransactionPay } from "./utils/index.mjs";
25
25
  // Selectors
26
- export { getAggregatedBalanceForAccount, getGroupIdForAccount, getInternalAccountsForGroup } from "./selectors/balance.mjs";
26
+ export { calculateBalanceForAllWallets, calculateBalanceChangeForAccountGroup, getAggregatedBalanceForAccount, getGroupIdForAccount, getInternalAccountsForGroup } from "./selectors/balance.mjs";
27
27
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,yBAAyB;AACzB,OAAO,EACL,gBAAgB,EAChB,+BAA+B,EAChC,+BAA2B;AAC5B,OAAO,EAAE,qBAAqB,EAAE,qBAAiB;AACjD,OAAO,EACL,+BAA+B,EAC/B,kCAAkC,EAClC,sBAAsB,EACtB,sBAAsB,EACtB,+BAA+B,EAC/B,uBAAuB,EACxB,uBAAmB;AAqFpB,sCAAsC;AACtC,OAAO,EAAE,kBAAkB,EAAE,iCAAuB;AAIpD,6BAA6B;AAC7B,OAAO,EAAE,qBAAqB,EAAE,iCAAuB;AAQvD,kCAAkC;AAClC,OAAO,EACL,0BAA0B,EAC1B,gCAAgC,EACjC,iCAAuB;AAOxB,qBAAqB;AACrB,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,iCAAuB;AASpE,gFAAgF;AAChF,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,qBAAqB;AACrB,YAAY;AACZ,kBAAkB;AAClB,oBAAoB;AACpB,iBAAiB,EACjB,uBAAuB,EACxB,iCAAuB;AAOxB,0BAA0B;AAC1B,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,iCAAuB;AAQlE,cAAc;AACd,OAAO,EACL,+BAA+B,EAC/B,mBAAmB,EACnB,qBAAqB,EACtB,gCAAsB;AAMvB,YAAY;AACZ,OAAO,EACL,gBAAgB,EAChB,4BAA4B,EAC5B,4BAA4B,EAC7B,0BAAgB;AAQjB,YAAY;AACZ,OAAO,EACL,8BAA8B,EAC9B,oBAAoB,EACpB,2BAA2B,EAC5B,gCAA4B","sourcesContent":["// Main controller export\nexport {\n AssetsController,\n getDefaultAssetsControllerState,\n} from './AssetsController';\nexport { AssetsDataSourceError } from './errors';\nexport {\n DEFAULT_TRACKED_ASSETS_BY_CHAIN,\n CHAINS_WITH_DEFAULT_TRACKED_ASSETS,\n DEFAULT_ASSET_METADATA,\n buildDefaultAssetsInfo,\n getDefaultTrackedAssetsForChain,\n getDefaultAssetMetadata,\n} from './defaults';\nexport type { PendingTokenMetadata } from './AssetsController';\n\n// State and messenger types\nexport type {\n AssetsControllerState,\n AssetsControllerMessenger,\n AssetsControllerOptions,\n AssetsControllerGetStateAction,\n AssetsControllerActions,\n AssetsControllerStateChangeEvent,\n AssetsControllerBalanceChangedEvent,\n AssetsControllerPriceChangedEvent,\n AssetsControllerAssetsDetectedEvent,\n AssetsControllerEvents,\n} from './AssetsController';\nexport type {\n AssetsControllerGetAssetsAction,\n AssetsControllerGetAssetsBalanceAction,\n AssetsControllerGetAssetMetadataAction,\n AssetsControllerGetAssetsPriceAction,\n AssetsControllerAddCustomAssetAction,\n AssetsControllerRemoveCustomAssetAction,\n AssetsControllerGetCustomAssetsAction,\n AssetsControllerHideAssetAction,\n AssetsControllerUnhideAssetAction,\n AssetsControllerGetExchangeRatesForBridgeAction,\n AssetsControllerGetStateForTransactionPayAction,\n AssetsControllerSetSelectedCurrencyAction,\n} from './AssetsController-method-action-types';\n\n// Core types\nexport type {\n // CAIP types\n Caip19AssetId,\n AccountId,\n ChainId,\n // Asset types\n AssetType,\n TokenStandard,\n // Contract data types\n TokenFees,\n HoneypotStatus,\n StorageSlots,\n LocalizedDescription,\n // Metadata types\n BaseAssetMetadata,\n FungibleAssetMetadata,\n ERC721AssetMetadata,\n ERC1155AssetMetadata,\n AssetMetadata,\n // Price types\n BaseAssetPrice,\n FungibleAssetPrice,\n NFTAssetPrice,\n AssetPrice,\n // Balance types\n FungibleAssetBalance,\n ERC721AssetBalance,\n ERC1155AssetBalance,\n AssetBalance,\n // Data source types\n AccountWithSupportedChains,\n DataType,\n DataRequest,\n DataResponse,\n AssetsUpdateMode,\n // Middleware types\n Context,\n NextFunction,\n Middleware,\n AssetsDataSource,\n FetchContext,\n FetchNextFunction,\n FetchMiddleware,\n SubscriptionResponse,\n // Combined asset type\n Asset,\n // Event types\n BalanceChangeEvent,\n PriceChangeEvent,\n MetadataChangeEvent,\n AssetsDetectedEvent,\n} from './types';\n\n// Data sources - base class and types\nexport { AbstractDataSource } from './data-sources';\n\nexport type { DataSourceState, SubscriptionRequest } from './data-sources';\n\n// Data sources - AccountsApi\nexport { AccountsApiDataSource } from './data-sources';\n\nexport type {\n AccountsApiDataSourceConfig,\n AccountsApiDataSourceOptions,\n AccountsApiDataSourceState,\n} from './data-sources';\n\n// Data sources - BackendWebsocket\nexport {\n BackendWebsocketDataSource,\n createBackendWebsocketDataSource,\n} from './data-sources';\n\nexport type {\n BackendWebsocketDataSourceOptions,\n BackendWebsocketDataSourceState,\n} from './data-sources';\n\n// Data sources - RPC\nexport { RpcDataSource, createRpcDataSource } from './data-sources';\n\nexport type {\n RpcDataSourceConfig,\n RpcDataSourceOptions,\n RpcDataSourceState,\n ChainStatus,\n} from './data-sources';\n\n// Data sources - Unified Snap Data Source (dynamically discovers keyring snaps)\nexport {\n SnapDataSource,\n createSnapDataSource,\n SNAP_DATA_SOURCE_NAME,\n // Constants\n KEYRING_PERMISSION,\n // Utility functions\n getChainIdsCaveat,\n extractChainFromAssetId,\n} from './data-sources';\n\nexport type {\n SnapDataSourceState,\n SnapDataSourceOptions,\n} from './data-sources';\n\n// Enrichment data sources\nexport { TokenDataSource, PriceDataSource } from './data-sources';\n\nexport type {\n TokenDataSourceOptions,\n PriceDataSourceConfig,\n PriceDataSourceOptions,\n} from './data-sources';\n\n// Middlewares\nexport {\n CustomAssetGraduationMiddleware,\n DetectionMiddleware,\n RpcFallbackMiddleware,\n} from './middlewares';\nexport type {\n CustomAssetGraduationMiddlewareOptions,\n RpcFallbackMiddlewareOptions,\n} from './middlewares';\n\n// Utilities\nexport {\n normalizeAssetId,\n formatExchangeRatesForBridge,\n formatStateForTransactionPay,\n} from './utils';\nexport type {\n AccountForLegacyFormat,\n BridgeExchangeRatesFormat,\n LegacyToken,\n TransactionPayLegacyFormat,\n} from './utils';\n\n// Selectors\nexport {\n getAggregatedBalanceForAccount,\n getGroupIdForAccount,\n getInternalAccountsForGroup,\n} from './selectors/balance';\n\nexport type {\n AccountsById,\n AggregatedBalanceEntry,\n AggregatedBalanceForAccount,\n EnabledNetworkMap,\n} from './selectors/balance';\n"]}
1
+ {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,yBAAyB;AACzB,OAAO,EACL,gBAAgB,EAChB,+BAA+B,EAChC,+BAA2B;AAC5B,OAAO,EAAE,qBAAqB,EAAE,qBAAiB;AACjD,OAAO,EACL,+BAA+B,EAC/B,kCAAkC,EAClC,sBAAsB,EACtB,sBAAsB,EACtB,+BAA+B,EAC/B,uBAAuB,EACxB,uBAAmB;AAqFpB,sCAAsC;AACtC,OAAO,EAAE,kBAAkB,EAAE,iCAAuB;AAIpD,6BAA6B;AAC7B,OAAO,EAAE,qBAAqB,EAAE,iCAAuB;AAQvD,kCAAkC;AAClC,OAAO,EACL,0BAA0B,EAC1B,gCAAgC,EACjC,iCAAuB;AAOxB,qBAAqB;AACrB,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,iCAAuB;AASpE,gFAAgF;AAChF,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,qBAAqB;AACrB,YAAY;AACZ,kBAAkB;AAClB,oBAAoB;AACpB,iBAAiB,EACjB,uBAAuB,EACxB,iCAAuB;AAOxB,0BAA0B;AAC1B,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,iCAAuB;AAQlE,cAAc;AACd,OAAO,EACL,+BAA+B,EAC/B,mBAAmB,EACnB,qBAAqB,EACtB,gCAAsB;AAMvB,YAAY;AACZ,OAAO,EACL,gBAAgB,EAChB,4BAA4B,EAC5B,4BAA4B,EAC7B,0BAAgB;AAQjB,YAAY;AACZ,OAAO,EACL,6BAA6B,EAC7B,qCAAqC,EACrC,8BAA8B,EAC9B,oBAAoB,EACpB,2BAA2B,EAC5B,gCAA4B","sourcesContent":["// Main controller export\nexport {\n AssetsController,\n getDefaultAssetsControllerState,\n} from './AssetsController';\nexport { AssetsDataSourceError } from './errors';\nexport {\n DEFAULT_TRACKED_ASSETS_BY_CHAIN,\n CHAINS_WITH_DEFAULT_TRACKED_ASSETS,\n DEFAULT_ASSET_METADATA,\n buildDefaultAssetsInfo,\n getDefaultTrackedAssetsForChain,\n getDefaultAssetMetadata,\n} from './defaults';\nexport type { PendingTokenMetadata } from './AssetsController';\n\n// State and messenger types\nexport type {\n AssetsControllerState,\n AssetsControllerMessenger,\n AssetsControllerOptions,\n AssetsControllerGetStateAction,\n AssetsControllerActions,\n AssetsControllerStateChangeEvent,\n AssetsControllerBalanceChangedEvent,\n AssetsControllerPriceChangedEvent,\n AssetsControllerAssetsDetectedEvent,\n AssetsControllerEvents,\n} from './AssetsController';\nexport type {\n AssetsControllerGetAssetsAction,\n AssetsControllerGetAssetsBalanceAction,\n AssetsControllerGetAssetMetadataAction,\n AssetsControllerGetAssetsPriceAction,\n AssetsControllerAddCustomAssetAction,\n AssetsControllerRemoveCustomAssetAction,\n AssetsControllerGetCustomAssetsAction,\n AssetsControllerHideAssetAction,\n AssetsControllerUnhideAssetAction,\n AssetsControllerGetExchangeRatesForBridgeAction,\n AssetsControllerGetStateForTransactionPayAction,\n AssetsControllerSetSelectedCurrencyAction,\n} from './AssetsController-method-action-types';\n\n// Core types\nexport type {\n // CAIP types\n Caip19AssetId,\n AccountId,\n ChainId,\n // Asset types\n AssetType,\n TokenStandard,\n // Contract data types\n TokenFees,\n HoneypotStatus,\n StorageSlots,\n LocalizedDescription,\n // Metadata types\n BaseAssetMetadata,\n FungibleAssetMetadata,\n ERC721AssetMetadata,\n ERC1155AssetMetadata,\n AssetMetadata,\n // Price types\n BaseAssetPrice,\n FungibleAssetPrice,\n NFTAssetPrice,\n AssetPrice,\n // Balance types\n FungibleAssetBalance,\n ERC721AssetBalance,\n ERC1155AssetBalance,\n AssetBalance,\n // Data source types\n AccountWithSupportedChains,\n DataType,\n DataRequest,\n DataResponse,\n AssetsUpdateMode,\n // Middleware types\n Context,\n NextFunction,\n Middleware,\n AssetsDataSource,\n FetchContext,\n FetchNextFunction,\n FetchMiddleware,\n SubscriptionResponse,\n // Combined asset type\n Asset,\n // Event types\n BalanceChangeEvent,\n PriceChangeEvent,\n MetadataChangeEvent,\n AssetsDetectedEvent,\n} from './types';\n\n// Data sources - base class and types\nexport { AbstractDataSource } from './data-sources';\n\nexport type { DataSourceState, SubscriptionRequest } from './data-sources';\n\n// Data sources - AccountsApi\nexport { AccountsApiDataSource } from './data-sources';\n\nexport type {\n AccountsApiDataSourceConfig,\n AccountsApiDataSourceOptions,\n AccountsApiDataSourceState,\n} from './data-sources';\n\n// Data sources - BackendWebsocket\nexport {\n BackendWebsocketDataSource,\n createBackendWebsocketDataSource,\n} from './data-sources';\n\nexport type {\n BackendWebsocketDataSourceOptions,\n BackendWebsocketDataSourceState,\n} from './data-sources';\n\n// Data sources - RPC\nexport { RpcDataSource, createRpcDataSource } from './data-sources';\n\nexport type {\n RpcDataSourceConfig,\n RpcDataSourceOptions,\n RpcDataSourceState,\n ChainStatus,\n} from './data-sources';\n\n// Data sources - Unified Snap Data Source (dynamically discovers keyring snaps)\nexport {\n SnapDataSource,\n createSnapDataSource,\n SNAP_DATA_SOURCE_NAME,\n // Constants\n KEYRING_PERMISSION,\n // Utility functions\n getChainIdsCaveat,\n extractChainFromAssetId,\n} from './data-sources';\n\nexport type {\n SnapDataSourceState,\n SnapDataSourceOptions,\n} from './data-sources';\n\n// Enrichment data sources\nexport { TokenDataSource, PriceDataSource } from './data-sources';\n\nexport type {\n TokenDataSourceOptions,\n PriceDataSourceConfig,\n PriceDataSourceOptions,\n} from './data-sources';\n\n// Middlewares\nexport {\n CustomAssetGraduationMiddleware,\n DetectionMiddleware,\n RpcFallbackMiddleware,\n} from './middlewares';\nexport type {\n CustomAssetGraduationMiddlewareOptions,\n RpcFallbackMiddlewareOptions,\n} from './middlewares';\n\n// Utilities\nexport {\n normalizeAssetId,\n formatExchangeRatesForBridge,\n formatStateForTransactionPay,\n} from './utils';\nexport type {\n AccountForLegacyFormat,\n BridgeExchangeRatesFormat,\n LegacyToken,\n TransactionPayLegacyFormat,\n} from './utils';\n\n// Selectors\nexport {\n calculateBalanceForAllWallets,\n calculateBalanceChangeForAccountGroup,\n getAggregatedBalanceForAccount,\n getGroupIdForAccount,\n getInternalAccountsForGroup,\n} from './selectors/balance';\n\nexport type {\n AccountGroupBalance,\n AccountsById,\n AggregatedBalanceEntry,\n AggregatedBalanceForAccount,\n AllWalletsBalance,\n BalanceChangePeriod,\n BalanceChangeResult,\n EnabledNetworkMap,\n WalletBalance,\n} from './selectors/balance';\n"]}
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DetectionMiddleware = void 0;
4
4
  const logger_1 = require("../logger.cjs");
5
5
  const types_1 = require("../types.cjs");
6
+ const utils_1 = require("../utils/index.cjs");
6
7
  // ============================================================================
7
8
  // CONSTANTS
8
9
  // ============================================================================
@@ -16,16 +17,14 @@ const CONTROLLER_NAME = 'DetectionMiddleware';
16
17
  * DetectionMiddleware builds the set of assets that downstream sources use for
17
18
  * metadata and price fetching.
18
19
  *
19
- * This middleware:
20
- * - Includes every asset that appears in response.assetsBalance (so prices and
21
- * metadata are fetched for existing assets as well as new ones)
22
- * - Includes each account's custom assets from state (so custom tokens get
23
- * metadata and prices even when they have no balance yet)
24
- * - Fills response.detectedAssets with these asset IDs per account
25
- *
26
- * TokenDataSource and PriceDataSource both key off detectedAssets. TokenDataSource
27
- * then filters to only fetch metadata for assets that lack it; PriceDataSource
28
- * fetches prices for all detected assets.
20
+ * An asset is included in `detectedAssets` only when it is genuinely new:
21
+ * - Assets from `response.assetsBalance` are included only if they are absent
22
+ * from BOTH `state.assetsBalance` (never tracked before) AND `state.assetsInfo`
23
+ * (no metadata yet). Assets already present in either collection are considered
24
+ * known and are intentionally excluded PriceDataSource's own subscription
25
+ * handles periodic refreshes for those.
26
+ * - Each account's custom assets from state are always included because they
27
+ * may have no balance yet and are explicitly managed by the user.
29
28
  *
30
29
  * Usage:
31
30
  * ```typescript
@@ -44,51 +43,93 @@ class DetectionMiddleware {
44
43
  * Get the middleware that builds detectedAssets for metadata and price fetching.
45
44
  *
46
45
  * This middleware:
47
- * 1. Includes all assets from response.assetsBalance (so prices are fetched for existing assets too)
48
- * 2. Merges each account's custom assets from state
49
- * 3. Fills response.detectedAssets with these asset IDs per account
46
+ * 1. Includes assets from response.assetsBalance that are absent from both
47
+ * state.assetsBalance and state.assetsInfo (brand-new assets only)
48
+ * 2. Always includes each account's custom assets from state
49
+ * 3. Fills response.detectedAssets with the resulting asset IDs per account
50
+ * 4. Queues detected assets that lack a price in state on
51
+ * request.assetsForPriceUpdate so PriceDataSource fetches them in the same
52
+ * pipeline pass (including the background RPC detection path)
50
53
  *
51
54
  * @returns The middleware function for the assets pipeline.
52
55
  */
53
56
  get assetsMiddleware() {
54
57
  return (0, types_1.forDataTypes)(['balance'], async (ctx, next) => {
55
58
  const { request, response } = ctx;
56
- // Get state for custom assets
59
+ // Get state for custom assets, existing balances, and existing metadata
57
60
  const state = ctx.getAssetsState();
58
- const { customAssets: stateCustomAssets } = state;
61
+ const { customAssets: stateCustomAssets, assetsBalance: stateAssetsBalance, assetsInfo: stateAssetsInfo, assetsPrice: stateAssetsPrice, } = state;
59
62
  const detectedAssets = {};
60
- // 1. From balance response: include every asset with balance (so prices + metadata path include existing assets)
63
+ // 1. From balance response: only include assets that are genuinely new
64
+ // not already present in state.assetsBalance or state.assetsInfo.
61
65
  if (response.assetsBalance) {
62
66
  for (const [accountId, accountBalances] of Object.entries(response.assetsBalance)) {
63
67
  const detected = [];
68
+ const stateAccountBalances = stateAssetsBalance[accountId] ?? {};
64
69
  for (const assetId of Object.keys(accountBalances)) {
65
- detected.push(assetId);
70
+ const caipAssetId = assetId;
71
+ // Skip if already tracked in state balances or already has metadata
72
+ if (stateAccountBalances[caipAssetId] !== undefined ||
73
+ stateAssetsInfo[caipAssetId] !== undefined) {
74
+ continue;
75
+ }
76
+ detected.push(caipAssetId);
66
77
  }
67
- // Merge this account's custom assets from state
78
+ // Merge custom assets for this account, applying the same filter:
79
+ // skip if already in state balance or already has metadata.
68
80
  const customForAccount = stateCustomAssets?.[accountId] ?? [];
69
81
  for (const assetId of customForAccount) {
70
- if (!detected.includes(assetId)) {
71
- detected.push(assetId);
82
+ if (detected.includes(assetId)) {
83
+ continue;
72
84
  }
85
+ if (stateAccountBalances[assetId] !== undefined ||
86
+ stateAssetsInfo[assetId] !== undefined) {
87
+ continue;
88
+ }
89
+ detected.push(assetId);
73
90
  }
74
91
  if (detected.length > 0) {
75
92
  detectedAssets[accountId] = detected;
76
93
  }
77
94
  }
78
95
  }
79
- // 2. Accounts in request that weren't in balance response: include their custom assets
96
+ // 2. Accounts in request that weren't in balance response: include their
97
+ // custom assets that are not yet in state.
80
98
  for (const { account } of request.accountsWithSupportedChains) {
81
99
  const accountId = account.id;
82
100
  if (detectedAssets[accountId]) {
83
101
  continue;
84
102
  }
103
+ const stateAccountBalances = stateAssetsBalance[accountId] ?? {};
85
104
  const customForAccount = stateCustomAssets?.[accountId] ?? [];
86
- if (customForAccount.length > 0) {
87
- detectedAssets[accountId] = customForAccount;
105
+ const newCustomAssets = customForAccount.filter((assetId) => {
106
+ const inBalance = stateAccountBalances[assetId] !== undefined;
107
+ const inInfo = stateAssetsInfo[assetId] !== undefined;
108
+ return !inBalance && !inInfo;
109
+ });
110
+ if (newCustomAssets.length > 0) {
111
+ detectedAssets[accountId] = newCustomAssets;
88
112
  }
89
113
  }
90
114
  if (Object.keys(detectedAssets).length > 0) {
91
115
  response.detectedAssets = detectedAssets;
116
+ const prices = stateAssetsPrice;
117
+ const missingPriceAssets = new Set();
118
+ for (const accountAssetIds of Object.values(detectedAssets)) {
119
+ for (const assetId of accountAssetIds) {
120
+ const normalizedAssetId = (0, utils_1.normalizeAssetId)(assetId);
121
+ if (prices[normalizedAssetId] === undefined &&
122
+ prices[assetId] === undefined) {
123
+ missingPriceAssets.add(normalizedAssetId);
124
+ }
125
+ }
126
+ }
127
+ if (missingPriceAssets.size > 0) {
128
+ request.assetsForPriceUpdate = [
129
+ ...(request.assetsForPriceUpdate ?? []),
130
+ ...missingPriceAssets,
131
+ ];
132
+ }
92
133
  }
93
134
  return next(ctx);
94
135
  });
@@ -1 +1 @@
1
- {"version":3,"file":"DetectionMiddleware.cjs","sourceRoot":"","sources":["../../src/middlewares/DetectionMiddleware.ts"],"names":[],"mappings":";;;AAAA,0CAA8D;AAC9D,wCAAwC;AAGxC,+EAA+E;AAC/E,YAAY;AACZ,+EAA+E;AAE/E,MAAM,eAAe,GAAG,qBAAqB,CAAC;AAE9C,uBAAuB;AACvB,IAAA,2BAAkB,EAAC,sBAAa,EAAE,eAAe,CAAC,CAAC;AAEnD,+EAA+E;AAC/E,uBAAuB;AACvB,+EAA+E;AAE/E;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAa,mBAAmB;IAAhC;QACW,SAAI,GAAG,eAAe,CAAC;IAwElC,CAAC;IAtEC,OAAO;QACL,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;IAED;;;;;;;;;OASG;IACH,IAAI,gBAAgB;QAClB,OAAO,IAAA,oBAAY,EAAC,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;YACnD,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAC;YAElC,8BAA8B;YAC9B,MAAM,KAAK,GAAG,GAAG,CAAC,cAAc,EAAE,CAAC;YACnC,MAAM,EAAE,YAAY,EAAE,iBAAiB,EAAE,GAAG,KAAK,CAAC;YAElD,MAAM,cAAc,GAAuC,EAAE,CAAC;YAE9D,iHAAiH;YACjH,IAAI,QAAQ,CAAC,aAAa,EAAE,CAAC;gBAC3B,KAAK,MAAM,CAAC,SAAS,EAAE,eAAe,CAAC,IAAI,MAAM,CAAC,OAAO,CACvD,QAAQ,CAAC,aAAa,CACvB,EAAE,CAAC;oBACF,MAAM,QAAQ,GAAoB,EAAE,CAAC;oBAErC,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAI,CAC/B,eAA0C,CAC3C,EAAE,CAAC;wBACF,QAAQ,CAAC,IAAI,CAAC,OAAwB,CAAC,CAAC;oBAC1C,CAAC;oBAED,gDAAgD;oBAChD,MAAM,gBAAgB,GAAG,iBAAiB,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;oBAC9D,KAAK,MAAM,OAAO,IAAI,gBAAgB,EAAE,CAAC;wBACvC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;4BAChC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;wBACzB,CAAC;oBACH,CAAC;oBAED,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBACxB,cAAc,CAAC,SAAS,CAAC,GAAG,QAAQ,CAAC;oBACvC,CAAC;gBACH,CAAC;YACH,CAAC;YAED,uFAAuF;YACvF,KAAK,MAAM,EAAE,OAAO,EAAE,IAAI,OAAO,CAAC,2BAA2B,EAAE,CAAC;gBAC9D,MAAM,SAAS,GAAG,OAAO,CAAC,EAAE,CAAC;gBAC7B,IAAI,cAAc,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC9B,SAAS;gBACX,CAAC;gBACD,MAAM,gBAAgB,GAAG,iBAAiB,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;gBAC9D,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAChC,cAAc,CAAC,SAAS,CAAC,GAAG,gBAAgB,CAAC;gBAC/C,CAAC;YACH,CAAC;YAED,IAAI,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC3C,QAAQ,CAAC,cAAc,GAAG,cAAc,CAAC;YAC3C,CAAC;YAED,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AAzED,kDAyEC","sourcesContent":["import { projectLogger, createModuleLogger } from '../logger';\nimport { forDataTypes } from '../types';\nimport type { AccountId, Caip19AssetId, Middleware } from '../types';\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nconst CONTROLLER_NAME = 'DetectionMiddleware';\n\n// Logger for debugging\ncreateModuleLogger(projectLogger, CONTROLLER_NAME);\n\n// ============================================================================\n// DETECTION MIDDLEWARE\n// ============================================================================\n\n/**\n * DetectionMiddleware builds the set of assets that downstream sources use for\n * metadata and price fetching.\n *\n * This middleware:\n * - Includes every asset that appears in response.assetsBalance (so prices and\n * metadata are fetched for existing assets as well as new ones)\n * - Includes each account's custom assets from state (so custom tokens get\n * metadata and prices even when they have no balance yet)\n * - Fills response.detectedAssets with these asset IDs per account\n *\n * TokenDataSource and PriceDataSource both key off detectedAssets. TokenDataSource\n * then filters to only fetch metadata for assets that lack it; PriceDataSource\n * fetches prices for all detected assets.\n *\n * Usage:\n * ```typescript\n * const detectionMiddleware = new DetectionMiddleware();\n * const middleware = detectionMiddleware.assetsMiddleware;\n * ```\n */\nexport class DetectionMiddleware {\n readonly name = CONTROLLER_NAME;\n\n getName(): string {\n return this.name;\n }\n\n /**\n * Get the middleware that builds detectedAssets for metadata and price fetching.\n *\n * This middleware:\n * 1. Includes all assets from response.assetsBalance (so prices are fetched for existing assets too)\n * 2. Merges each account's custom assets from state\n * 3. Fills response.detectedAssets with these asset IDs per account\n *\n * @returns The middleware function for the assets pipeline.\n */\n get assetsMiddleware(): Middleware {\n return forDataTypes(['balance'], async (ctx, next) => {\n const { request, response } = ctx;\n\n // Get state for custom assets\n const state = ctx.getAssetsState();\n const { customAssets: stateCustomAssets } = state;\n\n const detectedAssets: Record<AccountId, Caip19AssetId[]> = {};\n\n // 1. From balance response: include every asset with balance (so prices + metadata path include existing assets)\n if (response.assetsBalance) {\n for (const [accountId, accountBalances] of Object.entries(\n response.assetsBalance,\n )) {\n const detected: Caip19AssetId[] = [];\n\n for (const assetId of Object.keys(\n accountBalances as Record<string, unknown>,\n )) {\n detected.push(assetId as Caip19AssetId);\n }\n\n // Merge this account's custom assets from state\n const customForAccount = stateCustomAssets?.[accountId] ?? [];\n for (const assetId of customForAccount) {\n if (!detected.includes(assetId)) {\n detected.push(assetId);\n }\n }\n\n if (detected.length > 0) {\n detectedAssets[accountId] = detected;\n }\n }\n }\n\n // 2. Accounts in request that weren't in balance response: include their custom assets\n for (const { account } of request.accountsWithSupportedChains) {\n const accountId = account.id;\n if (detectedAssets[accountId]) {\n continue;\n }\n const customForAccount = stateCustomAssets?.[accountId] ?? [];\n if (customForAccount.length > 0) {\n detectedAssets[accountId] = customForAccount;\n }\n }\n\n if (Object.keys(detectedAssets).length > 0) {\n response.detectedAssets = detectedAssets;\n }\n\n return next(ctx);\n });\n }\n}\n"]}
1
+ {"version":3,"file":"DetectionMiddleware.cjs","sourceRoot":"","sources":["../../src/middlewares/DetectionMiddleware.ts"],"names":[],"mappings":"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","sourcesContent":["import { projectLogger, createModuleLogger } from '../logger';\nimport { forDataTypes } from '../types';\nimport type { AccountId, Caip19AssetId, Middleware } from '../types';\nimport { normalizeAssetId } from '../utils';\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nconst CONTROLLER_NAME = 'DetectionMiddleware';\n\n// Logger for debugging\ncreateModuleLogger(projectLogger, CONTROLLER_NAME);\n\n// ============================================================================\n// DETECTION MIDDLEWARE\n// ============================================================================\n\n/**\n * DetectionMiddleware builds the set of assets that downstream sources use for\n * metadata and price fetching.\n *\n * An asset is included in `detectedAssets` only when it is genuinely new:\n * - Assets from `response.assetsBalance` are included only if they are absent\n * from BOTH `state.assetsBalance` (never tracked before) AND `state.assetsInfo`\n * (no metadata yet). Assets already present in either collection are considered\n * known and are intentionally excluded PriceDataSource's own subscription\n * handles periodic refreshes for those.\n * - Each account's custom assets from state are always included because they\n * may have no balance yet and are explicitly managed by the user.\n *\n * Usage:\n * ```typescript\n * const detectionMiddleware = new DetectionMiddleware();\n * const middleware = detectionMiddleware.assetsMiddleware;\n * ```\n */\nexport class DetectionMiddleware {\n readonly name = CONTROLLER_NAME;\n\n getName(): string {\n return this.name;\n }\n\n /**\n * Get the middleware that builds detectedAssets for metadata and price fetching.\n *\n * This middleware:\n * 1. Includes assets from response.assetsBalance that are absent from both\n * state.assetsBalance and state.assetsInfo (brand-new assets only)\n * 2. Always includes each account's custom assets from state\n * 3. Fills response.detectedAssets with the resulting asset IDs per account\n * 4. Queues detected assets that lack a price in state on\n * request.assetsForPriceUpdate so PriceDataSource fetches them in the same\n * pipeline pass (including the background RPC detection path)\n *\n * @returns The middleware function for the assets pipeline.\n */\n get assetsMiddleware(): Middleware {\n return forDataTypes(['balance'], async (ctx, next) => {\n const { request, response } = ctx;\n\n // Get state for custom assets, existing balances, and existing metadata\n const state = ctx.getAssetsState();\n const {\n customAssets: stateCustomAssets,\n assetsBalance: stateAssetsBalance,\n assetsInfo: stateAssetsInfo,\n assetsPrice: stateAssetsPrice,\n } = state;\n\n const detectedAssets: Record<AccountId, Caip19AssetId[]> = {};\n\n // 1. From balance response: only include assets that are genuinely new —\n // not already present in state.assetsBalance or state.assetsInfo.\n if (response.assetsBalance) {\n for (const [accountId, accountBalances] of Object.entries(\n response.assetsBalance,\n )) {\n const detected: Caip19AssetId[] = [];\n\n const stateAccountBalances = stateAssetsBalance[accountId] ?? {};\n\n for (const assetId of Object.keys(\n accountBalances as Record<string, unknown>,\n )) {\n const caipAssetId = assetId as Caip19AssetId;\n // Skip if already tracked in state balances or already has metadata\n if (\n stateAccountBalances[caipAssetId] !== undefined ||\n stateAssetsInfo[caipAssetId] !== undefined\n ) {\n continue;\n }\n detected.push(caipAssetId);\n }\n\n // Merge custom assets for this account, applying the same filter:\n // skip if already in state balance or already has metadata.\n const customForAccount = stateCustomAssets?.[accountId] ?? [];\n for (const assetId of customForAccount) {\n if (detected.includes(assetId)) {\n continue;\n }\n if (\n stateAccountBalances[assetId] !== undefined ||\n stateAssetsInfo[assetId] !== undefined\n ) {\n continue;\n }\n detected.push(assetId);\n }\n\n if (detected.length > 0) {\n detectedAssets[accountId] = detected;\n }\n }\n }\n\n // 2. Accounts in request that weren't in balance response: include their\n // custom assets that are not yet in state.\n for (const { account } of request.accountsWithSupportedChains) {\n const accountId = account.id;\n if (detectedAssets[accountId]) {\n continue;\n }\n const stateAccountBalances = stateAssetsBalance[accountId] ?? {};\n const customForAccount = stateCustomAssets?.[accountId] ?? [];\n const newCustomAssets = customForAccount.filter((assetId) => {\n const inBalance = stateAccountBalances[assetId] !== undefined;\n const inInfo = stateAssetsInfo[assetId] !== undefined;\n return !inBalance && !inInfo;\n });\n if (newCustomAssets.length > 0) {\n detectedAssets[accountId] = newCustomAssets;\n }\n }\n\n if (Object.keys(detectedAssets).length > 0) {\n response.detectedAssets = detectedAssets;\n\n const prices = stateAssetsPrice as Record<string, unknown>;\n const missingPriceAssets = new Set<Caip19AssetId>();\n\n for (const accountAssetIds of Object.values(detectedAssets)) {\n for (const assetId of accountAssetIds) {\n const normalizedAssetId = normalizeAssetId(assetId);\n if (\n prices[normalizedAssetId] === undefined &&\n prices[assetId] === undefined\n ) {\n missingPriceAssets.add(normalizedAssetId);\n }\n }\n }\n\n if (missingPriceAssets.size > 0) {\n request.assetsForPriceUpdate = [\n ...(request.assetsForPriceUpdate ?? []),\n ...missingPriceAssets,\n ];\n }\n }\n\n return next(ctx);\n });\n }\n}\n"]}
@@ -3,16 +3,14 @@ import type { Middleware } from "../types.cjs";
3
3
  * DetectionMiddleware builds the set of assets that downstream sources use for
4
4
  * metadata and price fetching.
5
5
  *
6
- * This middleware:
7
- * - Includes every asset that appears in response.assetsBalance (so prices and
8
- * metadata are fetched for existing assets as well as new ones)
9
- * - Includes each account's custom assets from state (so custom tokens get
10
- * metadata and prices even when they have no balance yet)
11
- * - Fills response.detectedAssets with these asset IDs per account
12
- *
13
- * TokenDataSource and PriceDataSource both key off detectedAssets. TokenDataSource
14
- * then filters to only fetch metadata for assets that lack it; PriceDataSource
15
- * fetches prices for all detected assets.
6
+ * An asset is included in `detectedAssets` only when it is genuinely new:
7
+ * - Assets from `response.assetsBalance` are included only if they are absent
8
+ * from BOTH `state.assetsBalance` (never tracked before) AND `state.assetsInfo`
9
+ * (no metadata yet). Assets already present in either collection are considered
10
+ * known and are intentionally excluded PriceDataSource's own subscription
11
+ * handles periodic refreshes for those.
12
+ * - Each account's custom assets from state are always included because they
13
+ * may have no balance yet and are explicitly managed by the user.
16
14
  *
17
15
  * Usage:
18
16
  * ```typescript
@@ -27,9 +25,13 @@ export declare class DetectionMiddleware {
27
25
  * Get the middleware that builds detectedAssets for metadata and price fetching.
28
26
  *
29
27
  * This middleware:
30
- * 1. Includes all assets from response.assetsBalance (so prices are fetched for existing assets too)
31
- * 2. Merges each account's custom assets from state
32
- * 3. Fills response.detectedAssets with these asset IDs per account
28
+ * 1. Includes assets from response.assetsBalance that are absent from both
29
+ * state.assetsBalance and state.assetsInfo (brand-new assets only)
30
+ * 2. Always includes each account's custom assets from state
31
+ * 3. Fills response.detectedAssets with the resulting asset IDs per account
32
+ * 4. Queues detected assets that lack a price in state on
33
+ * request.assetsForPriceUpdate so PriceDataSource fetches them in the same
34
+ * pipeline pass (including the background RPC detection path)
33
35
  *
34
36
  * @returns The middleware function for the assets pipeline.
35
37
  */
@@ -1 +1 @@
1
- {"version":3,"file":"DetectionMiddleware.d.cts","sourceRoot":"","sources":["../../src/middlewares/DetectionMiddleware.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAA4B,UAAU,EAAE,qBAAiB;AAerE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,mBAAmB;IAC9B,QAAQ,CAAC,IAAI,yBAAmB;IAEhC,OAAO,IAAI,MAAM;IAIjB;;;;;;;;;OASG;IACH,IAAI,gBAAgB,IAAI,UAAU,CAuDjC;CACF"}
1
+ {"version":3,"file":"DetectionMiddleware.d.cts","sourceRoot":"","sources":["../../src/middlewares/DetectionMiddleware.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAA4B,UAAU,EAAE,qBAAiB;AAgBrE;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,mBAAmB;IAC9B,QAAQ,CAAC,IAAI,yBAAmB;IAEhC,OAAO,IAAI,MAAM;IAIjB;;;;;;;;;;;;;OAaG;IACH,IAAI,gBAAgB,IAAI,UAAU,CA4GjC;CACF"}