tapimo 0.10.2 → 0.12.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 (386) hide show
  1. package/dist/App.d.ts +4896 -2166
  2. package/dist/App.d.ts.map +1 -1
  3. package/dist/App.js +2 -2
  4. package/dist/App.js.map +1 -1
  5. package/dist/Assets.d.ts +15 -0
  6. package/dist/Assets.d.ts.map +1 -1
  7. package/dist/Assets.js +7 -0
  8. package/dist/Assets.js.map +1 -1
  9. package/dist/Webhooks.d.ts +0 -75
  10. package/dist/Webhooks.d.ts.map +1 -1
  11. package/dist/Webhooks.js +1 -143
  12. package/dist/Webhooks.js.map +1 -1
  13. package/dist/admin/App.d.ts +127 -78
  14. package/dist/admin/App.d.ts.map +1 -1
  15. package/dist/admin/apps/earn-vaults.d.ts +115 -1
  16. package/dist/admin/apps/earn-vaults.d.ts.map +1 -1
  17. package/dist/admin/apps/earn-vaults.js +59 -6
  18. package/dist/admin/apps/earn-vaults.js.map +1 -1
  19. package/dist/admin/apps/organizations.d.ts +3 -3
  20. package/dist/admin/apps/organizations.d.ts.map +1 -1
  21. package/dist/admin/apps/organizations.js +4 -2
  22. package/dist/admin/apps/organizations.js.map +1 -1
  23. package/dist/admin/apps/verified-tokens.d.ts +83 -83
  24. package/dist/admin/ui.generated.d.ts +1 -1
  25. package/dist/admin/ui.generated.d.ts.map +1 -1
  26. package/dist/admin/ui.generated.js +151 -151
  27. package/dist/admin/ui.generated.js.map +1 -1
  28. package/dist/apps/data/App.d.ts +5924 -4310
  29. package/dist/apps/data/App.d.ts.map +1 -1
  30. package/dist/apps/data/App.js +61 -9
  31. package/dist/apps/data/App.js.map +1 -1
  32. package/dist/apps/data/ZoneSelection.d.ts +62 -0
  33. package/dist/apps/data/ZoneSelection.d.ts.map +1 -0
  34. package/dist/apps/data/ZoneSelection.js +44 -0
  35. package/dist/apps/data/ZoneSelection.js.map +1 -0
  36. package/dist/apps/data/routes/activities.d.ts +2368 -5
  37. package/dist/apps/data/routes/activities.d.ts.map +1 -1
  38. package/dist/apps/data/routes/activities.js +423 -114
  39. package/dist/apps/data/routes/activities.js.map +1 -1
  40. package/dist/apps/data/routes/addresses.d.ts +131 -0
  41. package/dist/apps/data/routes/addresses.d.ts.map +1 -1
  42. package/dist/apps/data/routes/balances.d.ts +25 -25
  43. package/dist/apps/data/routes/balances.js +1 -1
  44. package/dist/apps/data/routes/balances.js.map +1 -1
  45. package/dist/apps/data/routes/blocks.d.ts +1 -1
  46. package/dist/apps/data/routes/blocks.js +1 -1
  47. package/dist/apps/data/routes/blocks.js.map +1 -1
  48. package/dist/apps/data/routes/earn.d.ts +135 -10
  49. package/dist/apps/data/routes/earn.d.ts.map +1 -1
  50. package/dist/apps/data/routes/earn.js +157 -25
  51. package/dist/apps/data/routes/earn.js.map +1 -1
  52. package/dist/apps/data/routes/exchanges.d.ts +829 -123
  53. package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
  54. package/dist/apps/data/routes/exchanges.js +16 -13
  55. package/dist/apps/data/routes/exchanges.js.map +1 -1
  56. package/dist/apps/data/routes/fee-amm.d.ts +26 -26
  57. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
  58. package/dist/apps/data/routes/fee-amm.js +9 -11
  59. package/dist/apps/data/routes/fee-amm.js.map +1 -1
  60. package/dist/apps/data/routes/receipts.d.ts +54 -54
  61. package/dist/apps/data/routes/receipts.js +1 -1
  62. package/dist/apps/data/routes/receipts.js.map +1 -1
  63. package/dist/apps/data/routes/rpc.js +24 -10
  64. package/dist/apps/data/routes/rpc.js.map +1 -1
  65. package/dist/apps/data/routes/tokenlist.d.ts +12 -12
  66. package/dist/apps/data/routes/tokenlist.d.ts.map +1 -1
  67. package/dist/apps/data/routes/tokenlist.js +16 -15
  68. package/dist/apps/data/routes/tokenlist.js.map +1 -1
  69. package/dist/apps/data/routes/tokens.d.ts +17 -3
  70. package/dist/apps/data/routes/tokens.d.ts.map +1 -1
  71. package/dist/apps/data/routes/tokens.js +34 -15
  72. package/dist/apps/data/routes/tokens.js.map +1 -1
  73. package/dist/apps/data/routes/transactions.d.ts +1 -1
  74. package/dist/apps/data/routes/transactions.js +1 -1
  75. package/dist/apps/data/routes/transactions.js.map +1 -1
  76. package/dist/apps/data/routes/transfers.d.ts +13 -13
  77. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  78. package/dist/apps/data/routes/transfers.js +7 -9
  79. package/dist/apps/data/routes/transfers.js.map +1 -1
  80. package/dist/apps/data/routes/valuation.d.ts +21 -21
  81. package/dist/apps/data/routes/verified-tokens.d.ts +5 -5
  82. package/dist/apps/data/routes/webhooks.d.ts +1011 -151
  83. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  84. package/dist/apps/data/routes/webhooks.js +126 -739
  85. package/dist/apps/data/routes/webhooks.js.map +1 -1
  86. package/dist/apps/data/routes/zones.d.ts +15 -15
  87. package/dist/apps/funding/App.d.ts +27 -27
  88. package/dist/apps/funding/routes/chains.d.ts.map +1 -1
  89. package/dist/apps/funding/routes/chains.js +5 -4
  90. package/dist/apps/funding/routes/chains.js.map +1 -1
  91. package/dist/apps/funding/routes/deposit-addresses.d.ts +18 -18
  92. package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -1
  93. package/dist/apps/funding/routes/deposit-addresses.js +8 -8
  94. package/dist/apps/funding/routes/deposit-addresses.js.map +1 -1
  95. package/dist/apps/funding/routes/providers.d.ts.map +1 -1
  96. package/dist/apps/funding/routes/providers.js +3 -2
  97. package/dist/apps/funding/routes/providers.js.map +1 -1
  98. package/dist/apps/funding/routes/quotes.d.ts.map +1 -1
  99. package/dist/apps/funding/routes/quotes.js +2 -2
  100. package/dist/apps/funding/routes/quotes.js.map +1 -1
  101. package/dist/apps/funding/routes/transfers.d.ts +33 -33
  102. package/dist/apps/management/App.d.ts +1433 -322
  103. package/dist/apps/management/App.d.ts.map +1 -1
  104. package/dist/apps/management/Billing.d.ts.map +1 -1
  105. package/dist/apps/management/Billing.js +19 -6
  106. package/dist/apps/management/Billing.js.map +1 -1
  107. package/dist/apps/management/routes/api-keys.d.ts +154 -94
  108. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  109. package/dist/apps/management/routes/api-keys.js +76 -30
  110. package/dist/apps/management/routes/api-keys.js.map +1 -1
  111. package/dist/apps/management/routes/billing.d.ts +103 -30
  112. package/dist/apps/management/routes/billing.d.ts.map +1 -1
  113. package/dist/apps/management/routes/billing.js +91 -32
  114. package/dist/apps/management/routes/billing.js.map +1 -1
  115. package/dist/apps/management/routes/faucet.d.ts +12 -12
  116. package/dist/apps/management/routes/faucet.d.ts.map +1 -1
  117. package/dist/apps/management/routes/faucet.js +23 -12
  118. package/dist/apps/management/routes/faucet.js.map +1 -1
  119. package/dist/apps/management/routes/invitations.d.ts +46 -13
  120. package/dist/apps/management/routes/invitations.d.ts.map +1 -1
  121. package/dist/apps/management/routes/invitations.js +79 -29
  122. package/dist/apps/management/routes/invitations.js.map +1 -1
  123. package/dist/apps/management/routes/invite-links.d.ts +128 -83
  124. package/dist/apps/management/routes/invite-links.d.ts.map +1 -1
  125. package/dist/apps/management/routes/invite-links.js +100 -41
  126. package/dist/apps/management/routes/invite-links.js.map +1 -1
  127. package/dist/apps/management/routes/me.d.ts +5 -5
  128. package/dist/apps/management/routes/me.d.ts.map +1 -1
  129. package/dist/apps/management/routes/me.js +14 -10
  130. package/dist/apps/management/routes/me.js.map +1 -1
  131. package/dist/apps/management/routes/members.d.ts +68 -47
  132. package/dist/apps/management/routes/members.d.ts.map +1 -1
  133. package/dist/apps/management/routes/members.js +37 -18
  134. package/dist/apps/management/routes/members.js.map +1 -1
  135. package/dist/apps/management/routes/orgs.d.ts +48 -8
  136. package/dist/apps/management/routes/orgs.d.ts.map +1 -1
  137. package/dist/apps/management/routes/orgs.js +48 -19
  138. package/dist/apps/management/routes/orgs.js.map +1 -1
  139. package/dist/apps/management/routes/projects.d.ts +130 -68
  140. package/dist/apps/management/routes/projects.d.ts.map +1 -1
  141. package/dist/apps/management/routes/projects.js +72 -21
  142. package/dist/apps/management/routes/projects.js.map +1 -1
  143. package/dist/apps/management/routes/scopes.d.ts.map +1 -1
  144. package/dist/apps/management/routes/scopes.js +8 -5
  145. package/dist/apps/management/routes/scopes.js.map +1 -1
  146. package/dist/apps/management/routes/usage.d.ts +38 -38
  147. package/dist/apps/management/routes/usage.d.ts.map +1 -1
  148. package/dist/apps/management/routes/usage.js +41 -31
  149. package/dist/apps/management/routes/usage.js.map +1 -1
  150. package/dist/apps/management/routes/verified-token-requests.d.ts +34 -6
  151. package/dist/apps/management/routes/verified-token-requests.d.ts.map +1 -1
  152. package/dist/apps/management/routes/verified-token-requests.js +36 -17
  153. package/dist/apps/management/routes/verified-token-requests.js.map +1 -1
  154. package/dist/apps/management/routes/webhooks.d.ts +1186 -197
  155. package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
  156. package/dist/apps/management/routes/webhooks.js +106 -38
  157. package/dist/apps/management/routes/webhooks.js.map +1 -1
  158. package/dist/apps/mpp/internal/Adapter.d.ts +1 -1
  159. package/dist/apps/mpp/internal/Adapter.d.ts.map +1 -1
  160. package/dist/apps/mpp/internal/Adapter.js +12 -0
  161. package/dist/apps/mpp/internal/Adapter.js.map +1 -1
  162. package/dist/db/Db.d.ts +2 -0
  163. package/dist/db/Db.d.ts.map +1 -1
  164. package/dist/db/Db.js +61 -0
  165. package/dist/db/Db.js.map +1 -1
  166. package/dist/db/Schema.d.ts +30 -2
  167. package/dist/db/Schema.d.ts.map +1 -1
  168. package/dist/db/Schema.js +28 -3
  169. package/dist/db/Schema.js.map +1 -1
  170. package/dist/db/tables/earnVaults.d.ts +5 -1
  171. package/dist/db/tables/earnVaults.d.ts.map +1 -1
  172. package/dist/db/tables/earnVaults.js +5 -0
  173. package/dist/db/tables/earnVaults.js.map +1 -1
  174. package/dist/db/tables/webhookDeliveries.d.ts.map +1 -1
  175. package/dist/db/tables/webhookDeliveries.js +10 -0
  176. package/dist/db/tables/webhookDeliveries.js.map +1 -1
  177. package/dist/db/tables/webhookQueueCompletions.d.ts +15 -0
  178. package/dist/db/tables/webhookQueueCompletions.d.ts.map +1 -0
  179. package/dist/db/tables/webhookQueueCompletions.js +32 -0
  180. package/dist/db/tables/webhookQueueCompletions.js.map +1 -0
  181. package/dist/db/tables/webhookQueueEvents.d.ts +16 -20
  182. package/dist/db/tables/webhookQueueEvents.d.ts.map +1 -1
  183. package/dist/db/tables/webhookQueueEvents.js +68 -43
  184. package/dist/db/tables/webhookQueueEvents.js.map +1 -1
  185. package/dist/db/tables/webhookSubscriptions.d.ts +47 -72
  186. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
  187. package/dist/db/tables/webhookSubscriptions.js +92 -105
  188. package/dist/db/tables/webhookSubscriptions.js.map +1 -1
  189. package/dist/internal/Auth.d.ts +8 -1
  190. package/dist/internal/Auth.d.ts.map +1 -1
  191. package/dist/internal/Auth.js +13 -3
  192. package/dist/internal/Auth.js.map +1 -1
  193. package/dist/internal/Cache.d.ts.map +1 -1
  194. package/dist/internal/Cache.js +11 -7
  195. package/dist/internal/Cache.js.map +1 -1
  196. package/dist/internal/EarnVaults.d.ts +41 -2
  197. package/dist/internal/EarnVaults.d.ts.map +1 -1
  198. package/dist/internal/EarnVaults.js +167 -0
  199. package/dist/internal/EarnVaults.js.map +1 -1
  200. package/dist/internal/MetricSink.d.ts +35 -125
  201. package/dist/internal/MetricSink.d.ts.map +1 -1
  202. package/dist/internal/MetricSink.js +50 -121
  203. package/dist/internal/MetricSink.js.map +1 -1
  204. package/dist/internal/OpenApi.d.ts +26 -22
  205. package/dist/internal/OpenApi.d.ts.map +1 -1
  206. package/dist/internal/OpenApi.js +128 -39
  207. package/dist/internal/OpenApi.js.map +1 -1
  208. package/dist/internal/Response.d.ts +13 -8
  209. package/dist/internal/Response.d.ts.map +1 -1
  210. package/dist/internal/Response.js +16 -5
  211. package/dist/internal/Response.js.map +1 -1
  212. package/dist/internal/Schema.d.ts +8 -8
  213. package/dist/internal/Schema.d.ts.map +1 -1
  214. package/dist/internal/Schema.js +10 -9
  215. package/dist/internal/Schema.js.map +1 -1
  216. package/dist/internal/Store.d.ts +15 -6
  217. package/dist/internal/Store.d.ts.map +1 -1
  218. package/dist/internal/Store.js +45 -15
  219. package/dist/internal/Store.js.map +1 -1
  220. package/dist/internal/Tidx.d.ts +2 -0
  221. package/dist/internal/Tidx.d.ts.map +1 -1
  222. package/dist/internal/Tidx.js +16 -4
  223. package/dist/internal/Tidx.js.map +1 -1
  224. package/dist/internal/WebhookTransfer.d.ts +1 -55
  225. package/dist/internal/WebhookTransfer.d.ts.map +1 -1
  226. package/dist/internal/WebhookTransfer.js +1 -239
  227. package/dist/internal/WebhookTransfer.js.map +1 -1
  228. package/dist/internal/Webhooks.d.ts +112 -56
  229. package/dist/internal/Webhooks.d.ts.map +1 -1
  230. package/dist/internal/Webhooks.js +124 -93
  231. package/dist/internal/Webhooks.js.map +1 -1
  232. package/dist/internal/funding/Deposit.d.ts +2 -2
  233. package/dist/internal/funding/Deposit.d.ts.map +1 -1
  234. package/dist/internal/funding/Deposit.js +9 -8
  235. package/dist/internal/funding/Deposit.js.map +1 -1
  236. package/dist/internal/funding/DepositAddress.d.ts +4 -4
  237. package/dist/internal/funding/DepositAddress.d.ts.map +1 -1
  238. package/dist/internal/funding/DepositAddress.js +9 -8
  239. package/dist/internal/funding/DepositAddress.js.map +1 -1
  240. package/dist/internal/funding/DepositWebhook.d.ts +25 -0
  241. package/dist/internal/funding/DepositWebhook.d.ts.map +1 -0
  242. package/dist/internal/funding/DepositWebhook.js +74 -0
  243. package/dist/internal/funding/DepositWebhook.js.map +1 -0
  244. package/dist/internal/funding/Provider.d.ts.map +1 -1
  245. package/dist/internal/funding/Provider.js +13 -12
  246. package/dist/internal/funding/Provider.js.map +1 -1
  247. package/dist/internal/funding/Reconciliation.d.ts +6 -0
  248. package/dist/internal/funding/Reconciliation.d.ts.map +1 -1
  249. package/dist/internal/funding/Reconciliation.js +42 -10
  250. package/dist/internal/funding/Reconciliation.js.map +1 -1
  251. package/dist/internal/funding/index.d.ts +1 -0
  252. package/dist/internal/funding/index.d.ts.map +1 -1
  253. package/dist/internal/funding/index.js +1 -0
  254. package/dist/internal/funding/index.js.map +1 -1
  255. package/dist/internal/funding/providers/stargate.d.ts +19 -0
  256. package/dist/internal/funding/providers/stargate.d.ts.map +1 -0
  257. package/dist/internal/funding/providers/stargate.js +143 -0
  258. package/dist/internal/funding/providers/stargate.js.map +1 -0
  259. package/dist/internal/webhookDestinations/slack.d.ts.map +1 -1
  260. package/dist/internal/webhookDestinations/slack.js +16 -0
  261. package/dist/internal/webhookDestinations/slack.js.map +1 -1
  262. package/dist/providers/index.d.ts +7 -0
  263. package/dist/providers/index.d.ts.map +1 -1
  264. package/dist/providers/index.js +3 -0
  265. package/dist/providers/index.js.map +1 -1
  266. package/package.json +2 -2
  267. package/src/App.test.ts +48 -0
  268. package/src/App.ts +7 -2
  269. package/src/Assets.test.ts +18 -0
  270. package/src/Assets.ts +22 -0
  271. package/src/Client.test-d.ts +287 -17
  272. package/src/Webhooks.ts +1 -220
  273. package/src/admin/apps/earn-vaults.test.ts +143 -0
  274. package/src/admin/apps/earn-vaults.ts +83 -6
  275. package/src/admin/apps/organizations.ts +6 -2
  276. package/src/admin/ui/src/routes/api-keys.tsx +36 -0
  277. package/src/admin/ui.generated.ts +151 -189
  278. package/src/apps/data/App.test.ts +31 -0
  279. package/src/apps/data/App.ts +64 -10
  280. package/src/apps/data/ZoneSelection.test.ts +196 -0
  281. package/src/apps/data/ZoneSelection.ts +108 -0
  282. package/src/apps/data/routes/activities.test.ts +435 -12
  283. package/src/apps/data/routes/activities.ts +562 -125
  284. package/src/apps/data/routes/balances.ts +1 -1
  285. package/src/apps/data/routes/blocks.ts +1 -1
  286. package/src/apps/data/routes/earn.test.ts +183 -23
  287. package/src/apps/data/routes/earn.ts +225 -29
  288. package/src/apps/data/routes/earn.zone-access.test.ts +46 -0
  289. package/src/apps/data/routes/exchanges.test.ts +31 -1
  290. package/src/apps/data/routes/exchanges.ts +19 -15
  291. package/src/apps/data/routes/fee-amm.ts +18 -11
  292. package/src/apps/data/routes/receipts.ts +1 -1
  293. package/src/apps/data/routes/rpc.test.ts +75 -0
  294. package/src/apps/data/routes/rpc.ts +23 -9
  295. package/src/apps/data/routes/tokenlist.ts +21 -18
  296. package/src/apps/data/routes/tokens.test.ts +29 -0
  297. package/src/apps/data/routes/tokens.ts +38 -15
  298. package/src/apps/data/routes/transactions.ts +1 -1
  299. package/src/apps/data/routes/transfers.test.ts +3 -3
  300. package/src/apps/data/routes/transfers.ts +12 -9
  301. package/src/apps/data/routes/webhooks.test.ts +179 -1497
  302. package/src/apps/data/routes/webhooks.ts +156 -976
  303. package/src/apps/data/routes/zones.test.ts +19 -13
  304. package/src/apps/funding/routes/chains.test.ts +211 -7
  305. package/src/apps/funding/routes/chains.ts +19 -12
  306. package/src/apps/funding/routes/deposit-addresses.test.ts +394 -0
  307. package/src/apps/funding/routes/deposit-addresses.ts +84 -72
  308. package/src/apps/funding/routes/providers.ts +9 -5
  309. package/src/apps/funding/routes/quotes.test.ts +46 -0
  310. package/src/apps/funding/routes/quotes.ts +8 -5
  311. package/src/apps/funding/routes/transfers.test.ts +35 -32
  312. package/src/apps/management/Billing.test.ts +26 -0
  313. package/src/apps/management/Billing.ts +24 -7
  314. package/src/apps/management/routes/api-keys.test.ts +178 -0
  315. package/src/apps/management/routes/api-keys.ts +203 -144
  316. package/src/apps/management/routes/billing.test.ts +209 -0
  317. package/src/apps/management/routes/billing.ts +254 -177
  318. package/src/apps/management/routes/faucet.test.ts +39 -0
  319. package/src/apps/management/routes/faucet.ts +75 -41
  320. package/src/apps/management/routes/invitations.test.ts +216 -0
  321. package/src/apps/management/routes/invitations.ts +186 -109
  322. package/src/apps/management/routes/invite-links.test.ts +263 -1
  323. package/src/apps/management/routes/invite-links.ts +295 -214
  324. package/src/apps/management/routes/me.test.ts +49 -0
  325. package/src/apps/management/routes/me.ts +42 -32
  326. package/src/apps/management/routes/members.test.ts +124 -1
  327. package/src/apps/management/routes/members.ts +81 -51
  328. package/src/apps/management/routes/orgs.test.ts +176 -0
  329. package/src/apps/management/routes/orgs.ts +101 -59
  330. package/src/apps/management/routes/projects.test.ts +183 -0
  331. package/src/apps/management/routes/projects.ts +124 -60
  332. package/src/apps/management/routes/scopes.test.ts +44 -0
  333. package/src/apps/management/routes/scopes.ts +37 -28
  334. package/src/apps/management/routes/usage.test.ts +92 -0
  335. package/src/apps/management/routes/usage.ts +186 -151
  336. package/src/apps/management/routes/verified-token-requests.test.ts +114 -0
  337. package/src/apps/management/routes/verified-token-requests.ts +166 -133
  338. package/src/apps/management/routes/webhooks.test.ts +313 -1
  339. package/src/apps/management/routes/webhooks.ts +220 -71
  340. package/src/apps/mpp/internal/Adapter.ts +13 -1
  341. package/src/db/Db.test.ts +42 -5
  342. package/src/db/Db.ts +85 -0
  343. package/src/db/Schema.ts +41 -3
  344. package/src/db/tables/earnVaults.test.ts +22 -0
  345. package/src/db/tables/earnVaults.ts +11 -1
  346. package/src/db/tables/organizations.test.ts +5 -2
  347. package/src/db/tables/webhookDeliveries.ts +13 -0
  348. package/src/db/tables/webhookQueueCompletions.ts +55 -0
  349. package/src/db/tables/webhookQueueEvents.ts +97 -57
  350. package/src/db/tables/webhookSubscriptions.ts +134 -144
  351. package/src/internal/Auth.ts +23 -5
  352. package/src/internal/Cache.ts +14 -7
  353. package/src/internal/EarnVaults.capabilities.test.ts +33 -0
  354. package/src/internal/EarnVaults.test.ts +79 -0
  355. package/src/internal/EarnVaults.ts +277 -0
  356. package/src/internal/MetricSink.test.ts +41 -373
  357. package/src/internal/MetricSink.ts +92 -273
  358. package/src/internal/OpenApi.ts +165 -49
  359. package/src/internal/Response.test.ts +95 -0
  360. package/src/internal/Response.ts +40 -2
  361. package/src/internal/Schema.test.ts +34 -0
  362. package/src/internal/Schema.ts +20 -9
  363. package/src/internal/Store.test.ts +186 -0
  364. package/src/internal/Store.ts +57 -17
  365. package/src/internal/Tidx.test.ts +19 -0
  366. package/src/internal/Tidx.ts +16 -4
  367. package/src/internal/WebhookTransfer.ts +1 -351
  368. package/src/internal/Webhooks.test.ts +222 -145
  369. package/src/internal/Webhooks.ts +234 -135
  370. package/src/internal/funding/Deposit.ts +71 -64
  371. package/src/internal/funding/DepositAddress.ts +50 -43
  372. package/src/internal/funding/DepositWebhook.test.ts +131 -0
  373. package/src/internal/funding/DepositWebhook.ts +101 -0
  374. package/src/internal/funding/Provider.ts +164 -142
  375. package/src/internal/funding/Reconciliation.test.ts +14 -0
  376. package/src/internal/funding/Reconciliation.ts +161 -86
  377. package/src/internal/funding/index.test.ts +1 -0
  378. package/src/internal/funding/index.ts +1 -0
  379. package/src/internal/funding/providers/index.test.ts +185 -0
  380. package/src/internal/funding/providers/stargate.ts +184 -0
  381. package/src/internal/webhookDestinations/slack.ts +15 -0
  382. package/src/providers/index.test.ts +1 -0
  383. package/src/providers/index.ts +9 -0
  384. package/src/Webhooks.bench.ts +0 -714
  385. package/src/Webhooks.memory.bench.ts +0 -250
  386. package/src/Webhooks.test.ts +0 -146
@@ -3,7 +3,7 @@
3
3
  import { Address, Hex, Value as core_Value } from 'ox'
4
4
  import { FxOracle } from 'tapimo/apps'
5
5
  import { createClient, encodeAbiParameters, encodeEventTopics, http } from 'viem'
6
- import { sendTransactionSync } from 'viem/actions'
6
+ import { getLogs, sendTransactionSync } from 'viem/actions'
7
7
  import { Abis, Actions, Addresses } from 'viem/tempo'
8
8
 
9
9
  import * as TestApp from '../../../../test/App.js'
@@ -35,8 +35,6 @@ const liveAccounts = {
35
35
  channel: '0x5217c502adf3c96a9551770af4caa5f5144a6984',
36
36
  // TokenCreated + Mint + Transfer.
37
37
  creator: '0x253fb3bf1e3d75ad6ee1554f68f4ea0568f7d24e',
38
- // Token burns.
39
- burner: '0x69519497b59bd64e109d993157d45f14455cc7e6',
40
38
  // KeyAuthorized + KeyRevoked + Approval (+ transfer/mint/exchange).
41
39
  keychain: '0xfe776fd28e06ecc71a157398b2c9991318f1ea2e',
42
40
  // Swaps through the stablecoin DEX (output kept, so it stays a plain `swap`).
@@ -50,27 +48,29 @@ const accounts =
50
48
  ? {
51
49
  channel: localAccounts.keychain,
52
50
  creator: localAccounts.creator,
53
- burner: localAccounts.burner,
54
51
  keychain: localAccounts.keychain,
55
52
  swapper: localAccounts.swapper,
56
53
  }
57
54
  : liveAccounts
58
55
 
59
- /** The account expected to surface each activity type. */
56
+ /**
57
+ * The account expected to surface each activity type. Burns are seeded per run
58
+ * instead: a live burner keeps accruing activity until its last burn falls off
59
+ * the first page.
60
+ */
60
61
  const coverage =
61
- runtime.mode === 'localnet'
62
+ runtime.mode === 'localnet' && localAccounts
62
63
  ? {
63
- approval: accounts.keychain,
64
- burn: accounts.burner,
65
- mint: accounts.creator,
66
- 'token-created': accounts.creator,
67
- transfer: accounts.creator,
64
+ approval: localAccounts.keychain,
65
+ burn: localAccounts.burner,
66
+ mint: localAccounts.creator,
67
+ 'token-created': localAccounts.creator,
68
+ transfer: localAccounts.creator,
68
69
  }
69
70
  : {
70
71
  'access-key-created': accounts.keychain,
71
72
  'access-key-revoked': accounts.keychain,
72
73
  approval: accounts.keychain,
73
- burn: accounts.burner,
74
74
  mint: accounts.creator,
75
75
  'session-closed': accounts.channel,
76
76
  swap: accounts.swapper,
@@ -143,6 +143,9 @@ describe('GET /addresses/:address/activities', () => {
143
143
  expect(
144
144
  Activities.schema.getAddressActivities.Query.safeParse({ include: 'token' }).success,
145
145
  ).toBe(false)
146
+ expect(
147
+ Activities.schema.getAddressActivities.Query.parse({ include: 'zones' }).include,
148
+ ).toStrictEqual(['zones'])
146
149
  })
147
150
 
148
151
  test('returns a newest-first, schema-valid feed with timing', async () => {
@@ -165,6 +168,54 @@ describe('GET /addresses/:address/activities', () => {
165
168
  expect(find(body.data, type)).toBeDefined()
166
169
  })
167
170
 
171
+ // Localnet seeds its burner through the shared fixtures, so this covers the
172
+ // hosted testnet, where no address stays burn-topped for long.
173
+ test.skipIf(runtime.mode === 'localnet')(
174
+ 'classifies a seeded burn',
175
+ async () => {
176
+ const account = Tempo.accounts[11]
177
+ const wallet = Tempo.getClient({ account })
178
+ await Actions.faucet.fundSync(Tempo.client, { account, timeout: 60_000 })
179
+
180
+ // Burning needs the `issuer` role, so the account burns a token it owns.
181
+ const { token } = await Actions.token.createSync(wallet, {
182
+ currency: 'USD',
183
+ name: 'Seeded Burn USD',
184
+ symbol: 'SBUSD',
185
+ })
186
+ await sendTransactionSync(wallet, {
187
+ account,
188
+ calls: [
189
+ Actions.token.grantRoles.call(wallet, { role: 'issuer', to: account.address, token }),
190
+ Actions.token.mint.call(wallet, { amount: 1_000_000n, to: account.address, token }),
191
+ ],
192
+ })
193
+ const { receipt } = await Actions.token.burnSync(wallet, { amount: 100_000n, token })
194
+
195
+ const deadline = Date.now() + 90_000
196
+ let burn: Activities.Entry | undefined
197
+ while (Date.now() < deadline) {
198
+ const response = await client.v1.addresses[':address'].activities.$get(
199
+ { param: { address: account.address }, query: { limit: '50' } },
200
+ { headers: { 'cache-control': 'no-store', ...TestApp.auth.headers } },
201
+ )
202
+ if (response.status === 200) {
203
+ const body = await TestApp.json(response, Activities.schema.getAddressActivities.Response)
204
+ burn = find(body.data, 'burn')
205
+ if (burn?.type === 'burn' && burn.transactionHash === receipt.transactionHash) break
206
+ }
207
+ await new Promise((resolve) => setTimeout(resolve, 500))
208
+ }
209
+ expect(burn?.type).toBe('burn')
210
+ if (burn?.type !== 'burn') return
211
+ expect(burn.transactionHash).toBe(receipt.transactionHash)
212
+ expect(burn.data.sender.toLowerCase()).toBe(account.address.toLowerCase())
213
+ expect(burn.data.sourceToken.address.toLowerCase()).toBe(token.toLowerCase())
214
+ expect(burn.data.sourceAmount.baseUnits).toBe('100000')
215
+ },
216
+ 180_000,
217
+ )
218
+
168
219
  test('transfer data mirrors the `Transfer` resource', async () => {
169
220
  const transfer = find((await activities(accounts.creator)).data, 'transfer')
170
221
  expect(transfer?.type).toBe('transfer')
@@ -488,6 +539,7 @@ describe('GET /transactions/:transactionHash/activities', () => {
488
539
  )
489
540
 
490
541
  const body = await TestApp.json(response, Activities.schema.getTransactionActivities.Response)
542
+ expect(body.chainId).toBe(runtime.chainId)
491
543
  expect(body.data.length).toBeGreaterThan(0)
492
544
  // Every classified item belongs to the requested transaction.
493
545
  expect(body.data.every((item) => item.transactionHash === transactionHash)).toBe(true)
@@ -559,6 +611,174 @@ describe('GET /transactions/:transactionHash/activities', () => {
559
611
  const response = await transactionActivitiesRequest(`0x${'0'.repeat(64)}`)
560
612
  expect(response.status).toBe(404)
561
613
  })
614
+
615
+ test.skipIf(runtime.mode !== 'localnet')(
616
+ 'infers indexed Zone activity by transaction and address',
617
+ async () => {
618
+ const account = Tempo.accounts[0]
619
+ const [zoneCreated] = await getLogs(Tempo.client, {
620
+ event: {
621
+ inputs: [
622
+ { indexed: true, name: 'zoneId', type: 'uint32' },
623
+ { indexed: true, name: 'portal', type: 'address' },
624
+ { indexed: false, name: 'initialToken', type: 'address' },
625
+ { indexed: false, name: 'admin', type: 'address' },
626
+ { indexed: false, name: 'sequencer', type: 'address' },
627
+ { indexed: false, name: 'verifier', type: 'address' },
628
+ { indexed: false, name: 'genesisBlockHash', type: 'bytes32' },
629
+ { indexed: false, name: 'genesisTempoBlockHash', type: 'bytes32' },
630
+ { indexed: false, name: 'genesisTempoBlockNumber', type: 'uint64' },
631
+ ],
632
+ name: 'ZoneCreated',
633
+ type: 'event',
634
+ },
635
+ fromBlock: 0n,
636
+ strict: true,
637
+ toBlock: 'latest',
638
+ })
639
+ expect(zoneCreated).toBeDefined()
640
+ if (!zoneCreated) return
641
+
642
+ const zone = TestApp.zone({
643
+ chainId: runtime.zone.chainId,
644
+ rpcUrl: runtime.zone.internalRpcUrl,
645
+ sourceChainId: runtime.chainId,
646
+ tidxUrl: runtime.tidxUrl,
647
+ })
648
+ const zoneClient = createClient({
649
+ account,
650
+ chain: zone,
651
+ pollingInterval: 100,
652
+ transport: http(runtime.zone.internalRpcUrl),
653
+ })
654
+ const balanceBefore = await Actions.token.getBalance(zoneClient, {
655
+ account: account.address,
656
+ token: Addresses.pathUsd,
657
+ })
658
+ await Actions.zone.depositSync(Tempo.getClient({ account }), {
659
+ amount: 1_000_000n,
660
+ portalAddress: zoneCreated.args.portal,
661
+ token: Addresses.pathUsd,
662
+ zoneId: zoneCreated.args.zoneId,
663
+ })
664
+
665
+ let balance = balanceBefore
666
+ const balanceDeadline = Date.now() + 60_000
667
+ while (balance.amount <= balanceBefore.amount && Date.now() < balanceDeadline) {
668
+ await new Promise((resolve) => setTimeout(resolve, 100))
669
+ balance = await Actions.token.getBalance(zoneClient, {
670
+ account: account.address,
671
+ token: Addresses.pathUsd,
672
+ })
673
+ }
674
+ expect(balance.amount).toBeGreaterThan(balanceBefore.amount)
675
+
676
+ const { receipt } = await Actions.token.transferSync(zoneClient, {
677
+ amount: 1n,
678
+ to: Tempo.accounts[1].address,
679
+ token: Addresses.pathUsd,
680
+ })
681
+ const zoneReader = {
682
+ ...TestApp.key,
683
+ id: 'key_inferred_zone',
684
+ scopes: [...TestApp.key.scopes, `zone:${runtime.zone.chainId}:read`],
685
+ token: 'secret_inferred_zone',
686
+ } satisfies TestApp.kvStore.Key
687
+ const client = TestApp.client({
688
+ auth: { keys: [zoneReader] },
689
+ zones: [zone],
690
+ })
691
+ let response = await client.v1.transactions[':transactionHash'].activities.$get(
692
+ {
693
+ param: { transactionHash: receipt.transactionHash },
694
+ query: { chainId: String(runtime.chainId), include: 'zones' },
695
+ },
696
+ { headers: { authorization: `Bearer ${zoneReader.token}` } },
697
+ )
698
+ const deadline = Date.now() + 60_000
699
+ while ((response.status === 404 || response.status === 502) && Date.now() < deadline) {
700
+ await new Promise((resolve) => setTimeout(resolve, 500))
701
+ response = await client.v1.transactions[':transactionHash'].activities.$get(
702
+ {
703
+ param: { transactionHash: receipt.transactionHash },
704
+ query: { chainId: String(runtime.chainId), include: 'zones' },
705
+ },
706
+ { headers: { authorization: `Bearer ${zoneReader.token}` } },
707
+ )
708
+ }
709
+ const body = await TestApp.json(response, Activities.schema.getTransactionActivities.Response)
710
+
711
+ expect(response.status).toBe(200)
712
+ expect(response.headers.get('cache-control')).toBe('no-store')
713
+ expect(body.chainId).toBe(runtime.zone.chainId)
714
+ const cached = await client.v1.transactions[':transactionHash'].activities.$get(
715
+ {
716
+ param: { transactionHash: receipt.transactionHash },
717
+ query: { chainId: String(runtime.chainId), include: 'zones' },
718
+ },
719
+ { headers: { authorization: `Bearer ${zoneReader.token}` } },
720
+ )
721
+ expect(cached.status).toBe(200)
722
+ expect(cached.headers.get('server-timing')).not.toContain('tidx;dur=')
723
+
724
+ const missingHash = `0x${'ff'.repeat(32)}` as Hex.Hex
725
+ const getMissing = () =>
726
+ client.v1.transactions[':transactionHash'].activities.$get(
727
+ {
728
+ param: { transactionHash: missingHash },
729
+ query: { chainId: String(runtime.chainId), include: 'zones' },
730
+ },
731
+ { headers: { authorization: `Bearer ${zoneReader.token}` } },
732
+ )
733
+ const missing = [await getMissing(), await getMissing()]
734
+ expect(missing.map(({ status }) => status)).toStrictEqual([404, 404])
735
+ expect(
736
+ missing.every((item) => item.headers.get('server-timing')?.includes('tidx;dur=')),
737
+ ).toBe(true)
738
+
739
+ const transfer = body.data.find((item) => item.type === 'transfer')
740
+ expect(transfer?.transactionHash).toBe(receipt.transactionHash)
741
+ if (transfer?.type !== 'transfer') return
742
+ expect(transfer.data.recipient).toBe(Tempo.accounts[1].address.toLowerCase())
743
+
744
+ const zoneAddressActivities = await (async () => {
745
+ const deadline = Date.now() + 60_000
746
+ while (Date.now() < deadline) {
747
+ const response = await client.v1.addresses[':address'].activities.$get(
748
+ {
749
+ param: { address: account.address },
750
+ query: {
751
+ chainId: String(runtime.chainId),
752
+ include: 'zones',
753
+ limit: '50',
754
+ },
755
+ },
756
+ { headers: { authorization: `Bearer ${zoneReader.token}` } },
757
+ )
758
+ if (response.status === 200) {
759
+ const body = await TestApp.json(
760
+ response,
761
+ Activities.schema.getAddressActivities.Response,
762
+ )
763
+ if (
764
+ body.data.some(
765
+ (item) => item.type !== 'group' && item.transactionHash === receipt.transactionHash,
766
+ )
767
+ )
768
+ return { body, response }
769
+ }
770
+ await new Promise((resolve) => setTimeout(resolve, 500))
771
+ }
772
+ throw new Error('Timed out waiting for inferred Zone address activity')
773
+ })()
774
+ const addressTransfer = zoneAddressActivities.body.data.find(
775
+ (item) => item.type !== 'group' && item.transactionHash === receipt.transactionHash,
776
+ )
777
+ expect(zoneAddressActivities.response.headers.get('cache-control')).toBe('no-store')
778
+ expect(addressTransfer?.chainId).toBe(runtime.zone.chainId)
779
+ },
780
+ 180_000,
781
+ )
562
782
  })
563
783
 
564
784
  describe('Activities.list classification', () => {
@@ -579,6 +799,161 @@ describe('Activities.list classification', () => {
579
799
  }
580
800
  }
581
801
 
802
+ function pagedProvider(rows: Activities.Row[]): Activities.Provider {
803
+ return {
804
+ cursorForGroup: (group) => `${group.blockNum}:${group.txIdx}`,
805
+ fetchRows: async ({ cursor, limit }) => {
806
+ const [blockNum, txIdx] = cursor?.split(':').map(Number) ?? []
807
+ return {
808
+ rows: rows
809
+ .filter(
810
+ (row) =>
811
+ blockNum === undefined ||
812
+ row.blockNum < blockNum ||
813
+ (row.blockNum === blockNum && row.txIdx < txIdx!),
814
+ )
815
+ .slice(0, limit + 1),
816
+ }
817
+ },
818
+ }
819
+ }
820
+
821
+ function unknownRow(options: {
822
+ blockNum: number
823
+ blockTimestamp: number
824
+ hash: string
825
+ }): Activities.Row {
826
+ return {
827
+ blockNum: options.blockNum,
828
+ blockTimestamp: options.blockTimestamp,
829
+ data: '0x',
830
+ feePayer: null,
831
+ logAddress: '0x2222222222222222222222222222222222222222',
832
+ logIdx: 0,
833
+ selector: null,
834
+ source: 'q1',
835
+ topic1: null,
836
+ topic2: null,
837
+ topic3: null,
838
+ txHash: `0x${options.hash.repeat(64)}` as Hex.Hex,
839
+ txIdx: 0,
840
+ txSender: viewer,
841
+ }
842
+ }
843
+
844
+ test('merges Zone pages and retains each provider cursor', async () => {
845
+ const providers = [
846
+ {
847
+ chainId: 10,
848
+ provider: pagedProvider([
849
+ unknownRow({ blockNum: 30, blockTimestamp: 300, hash: 'a' }),
850
+ unknownRow({ blockNum: 10, blockTimestamp: 100, hash: 'c' }),
851
+ ]),
852
+ },
853
+ {
854
+ chainId: 20,
855
+ provider: pagedProvider([unknownRow({ blockNum: 20, blockTimestamp: 200, hash: 'b' })]),
856
+ },
857
+ ]
858
+ const first = await Activities.listZones({
859
+ address: viewer,
860
+ limit: 2,
861
+ providers,
862
+ store: store(),
863
+ })
864
+ const second = await Activities.listZones({
865
+ address: viewer,
866
+ cursor: first.nextCursor!,
867
+ limit: 2,
868
+ providers,
869
+ store: store(),
870
+ })
871
+
872
+ expect(first.items.map((item) => [item.chainId, item.timestamp])).toStrictEqual([
873
+ [10, '1970-01-01T00:05:00.000Z'],
874
+ [20, '1970-01-01T00:03:20.000Z'],
875
+ ])
876
+ expect(first.nextCursor).not.toBeNull()
877
+ expect(second.items.map((item) => item.chainId)).toStrictEqual([10])
878
+ expect(second.nextCursor).toBeNull()
879
+ })
880
+
881
+ test('retains unused Zone buffers while filling a grouped page', async () => {
882
+ const signer = '0x3333333333333333333333333333333333333333' as Address.Address
883
+ const calls = new Map<number, number>()
884
+ const providers = [
885
+ { chainId: 10, hashes: ['1', '3', '5'], timestamps: [600, 400, 200] },
886
+ { chainId: 20, hashes: ['2', '4', '6'], timestamps: [500, 300, 100] },
887
+ ].map(({ chainId, hashes, timestamps }) => {
888
+ const source = pagedProvider(
889
+ timestamps.map((blockTimestamp, index) => ({
890
+ ...unknownRow({ blockNum: blockTimestamp, blockTimestamp, hash: hashes[index]! }),
891
+ feePayer: viewer,
892
+ txSender: signer,
893
+ })),
894
+ )
895
+ return {
896
+ chainId,
897
+ provider: {
898
+ cursorForGroup: (group) => source.cursorForGroup(group),
899
+ fetchRows: async (options) => {
900
+ calls.set(chainId, (calls.get(chainId) ?? 0) + 1)
901
+ return source.fetchRows(options)
902
+ },
903
+ } satisfies Activities.Provider,
904
+ }
905
+ })
906
+
907
+ const result = await Activities.listZones({
908
+ address: viewer,
909
+ group: true,
910
+ limit: 2,
911
+ providers,
912
+ store: store(),
913
+ })
914
+
915
+ expect(result.items.map((item) => item.timestamp)).toStrictEqual([
916
+ '1970-01-01T00:10:00.000Z',
917
+ '1970-01-01T00:08:20.000Z',
918
+ '1970-01-01T00:06:40.000Z',
919
+ '1970-01-01T00:05:00.000Z',
920
+ '1970-01-01T00:03:20.000Z',
921
+ '1970-01-01T00:01:40.000Z',
922
+ ])
923
+ expect([...calls]).toStrictEqual([
924
+ [10, 2],
925
+ [20, 2],
926
+ ])
927
+ expect(result.nextCursor).toBeNull()
928
+ })
929
+
930
+ test('does not fold access-key activity across Zones', async () => {
931
+ const signer = '0x3333333333333333333333333333333333333333' as Address.Address
932
+ const providers = [10, 20].map((chainId) => ({
933
+ chainId,
934
+ provider: pagedProvider([
935
+ {
936
+ ...unknownRow({ blockNum: chainId, blockTimestamp: 300, hash: String(chainId / 10) }),
937
+ feePayer: viewer,
938
+ txSender: signer,
939
+ },
940
+ ]),
941
+ }))
942
+ const result = await Activities.listZones({
943
+ address: viewer,
944
+ providers,
945
+ store: store(),
946
+ })
947
+ const grouped = Activities.groupByAccessKey(
948
+ result.items.map((item) => Activities.schema.Item.parse(item)),
949
+ )
950
+
951
+ expect(grouped.map((item) => [item.chainId, item.type])).toStrictEqual([
952
+ [10, 'unknown'],
953
+ [20, 'unknown'],
954
+ ])
955
+ })
956
+
582
957
  test('classifies an unrecognized Q1 tx as `unknown` with decoded events', async () => {
583
958
  // A gas-fee Transfer to the fee manager decodes as a known `Transfer`
584
959
  // event, but the classifier excludes fee-manager transfers, so the tx has
@@ -666,6 +1041,54 @@ describe('Activities.list classification', () => {
666
1041
  expect(log.data).toBe('0x1234')
667
1042
  expect(log.topics).toEqual([unknownSelector, pad(viewer)])
668
1043
  })
1044
+
1045
+ test('degrades a topic-encoded approval with a fee transfer to `unknown`', async () => {
1046
+ const approvalToken = '0x4120c7ec7e9ead9dcf44b35b5134ebdbfc6e3de9' as Address.Address
1047
+ const feeToken = '0x20c0000000000000000000000000000000000000' as Address.Address
1048
+ const feeManager = '0xfeec000000000000000000000000000000000000'
1049
+ const approvalSelector = '0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925'
1050
+ const transferSelector = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
1051
+ const pad = (value: string) => `0x${value.slice(2).padStart(64, '0')}`
1052
+ const txHash = '0xd2439ad8016d4ffd6aebe835734ee4d118a688714a4f9aebcc11f87a2b22e896' as Hex.Hex
1053
+ const common = {
1054
+ blockNum: 15_781_805,
1055
+ blockTimestamp: 1_776_703_284,
1056
+ feePayer: viewer,
1057
+ source: 'q1' as const,
1058
+ txHash,
1059
+ txIdx: 0,
1060
+ txSender: viewer,
1061
+ }
1062
+ const { items } = await Activities.list(
1063
+ provider([
1064
+ {
1065
+ ...common,
1066
+ data: '0x',
1067
+ logAddress: approvalToken,
1068
+ logIdx: 0,
1069
+ selector: approvalSelector,
1070
+ topic1: pad(viewer),
1071
+ topic2: pad('0x6063259f720ee125e5bff693779ab8118b0118a7'),
1072
+ topic3: '0x2a9063ed52b7d417e441f378325359c9ce274f5d6c3ecf11b7d42f24b9c90b7f',
1073
+ },
1074
+ {
1075
+ ...common,
1076
+ data: `0x${(5_815).toString(16).padStart(64, '0')}`,
1077
+ logAddress: feeToken,
1078
+ logIdx: 1,
1079
+ selector: transferSelector,
1080
+ topic1: pad(viewer),
1081
+ topic2: pad(feeManager),
1082
+ topic3: null,
1083
+ },
1084
+ ]),
1085
+ { address: viewer, store: store() },
1086
+ )
1087
+
1088
+ expect(items).toHaveLength(1)
1089
+ expect(items[0]?.type).toBe('unknown')
1090
+ expect(items[0]?.events).toHaveLength(2)
1091
+ })
669
1092
  })
670
1093
 
671
1094
  describe('Activities.list precompile classification', () => {