tapimo 0.0.2 → 0.2.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 (517) hide show
  1. package/README.md +38 -16
  2. package/dist/ApiKey.d.ts +14 -11
  3. package/dist/ApiKey.d.ts.map +1 -1
  4. package/dist/ApiKey.js +11 -12
  5. package/dist/ApiKey.js.map +1 -1
  6. package/dist/ApiKeys.d.ts +207 -0
  7. package/dist/ApiKeys.d.ts.map +1 -0
  8. package/dist/ApiKeys.js +282 -0
  9. package/dist/ApiKeys.js.map +1 -0
  10. package/dist/App.d.ts +6771 -3819
  11. package/dist/App.d.ts.map +1 -1
  12. package/dist/App.js +145 -317
  13. package/dist/App.js.map +1 -1
  14. package/dist/Handler.d.ts +86 -0
  15. package/dist/Handler.d.ts.map +1 -0
  16. package/dist/Handler.js +83 -0
  17. package/dist/Handler.js.map +1 -0
  18. package/dist/Scope.d.ts +14 -4
  19. package/dist/Scope.d.ts.map +1 -1
  20. package/dist/Scope.js +16 -4
  21. package/dist/Scope.js.map +1 -1
  22. package/dist/Webhooks.d.ts +5 -7
  23. package/dist/Webhooks.d.ts.map +1 -1
  24. package/dist/Webhooks.js +11 -4
  25. package/dist/Webhooks.js.map +1 -1
  26. package/dist/admin/Access.d.ts +1 -3
  27. package/dist/admin/Access.d.ts.map +1 -1
  28. package/dist/admin/Access.js +1 -3
  29. package/dist/admin/Access.js.map +1 -1
  30. package/dist/admin/App.d.ts +167 -205
  31. package/dist/admin/App.d.ts.map +1 -1
  32. package/dist/admin/App.js +2 -4
  33. package/dist/admin/App.js.map +1 -1
  34. package/dist/admin/apps/api-keys.d.ts +105 -5
  35. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  36. package/dist/admin/apps/api-keys.js +75 -9
  37. package/dist/admin/apps/api-keys.js.map +1 -1
  38. package/dist/admin/apps/scopes.d.ts +6 -0
  39. package/dist/admin/apps/scopes.d.ts.map +1 -1
  40. package/dist/admin/apps/verified-tokens.d.ts +35 -149
  41. package/dist/admin/apps/verified-tokens.d.ts.map +1 -1
  42. package/dist/admin/apps/verified-tokens.js +18 -39
  43. package/dist/admin/apps/verified-tokens.js.map +1 -1
  44. package/dist/admin/ui.generated.d.ts +1 -1
  45. package/dist/admin/ui.generated.js +1 -1
  46. package/dist/apps/data/App.d.ts +19176 -0
  47. package/dist/apps/data/App.d.ts.map +1 -0
  48. package/dist/apps/data/App.js +236 -0
  49. package/dist/apps/data/App.js.map +1 -0
  50. package/dist/apps/data/index.d.ts +3 -0
  51. package/dist/apps/data/index.d.ts.map +1 -0
  52. package/dist/apps/data/index.js +3 -0
  53. package/dist/apps/data/index.js.map +1 -0
  54. package/dist/apps/{activities.d.ts → data/routes/activities.d.ts} +32 -32
  55. package/dist/apps/{activities.d.ts.map → data/routes/activities.d.ts.map} +1 -1
  56. package/dist/apps/{activities.js → data/routes/activities.js} +13 -13
  57. package/dist/apps/data/routes/activities.js.map +1 -0
  58. package/dist/apps/{addresses.d.ts → data/routes/addresses.d.ts} +22 -22
  59. package/dist/apps/{addresses.d.ts.map → data/routes/addresses.d.ts.map} +1 -1
  60. package/dist/apps/{addresses.js → data/routes/addresses.js} +5 -5
  61. package/dist/apps/data/routes/addresses.js.map +1 -0
  62. package/dist/apps/{balances.d.ts → data/routes/balances.d.ts} +49 -49
  63. package/dist/apps/data/routes/balances.d.ts.map +1 -0
  64. package/dist/apps/{balances.js → data/routes/balances.js} +42 -11
  65. package/dist/apps/data/routes/balances.js.map +1 -0
  66. package/dist/apps/{blocks.d.ts → data/routes/blocks.d.ts} +47 -47
  67. package/dist/apps/data/routes/blocks.d.ts.map +1 -0
  68. package/dist/apps/{blocks.js → data/routes/blocks.js} +13 -13
  69. package/dist/apps/data/routes/blocks.js.map +1 -0
  70. package/dist/apps/{coingecko.d.ts → data/routes/coingecko.d.ts} +183 -183
  71. package/dist/apps/data/routes/coingecko.d.ts.map +1 -0
  72. package/dist/apps/{coingecko.js → data/routes/coingecko.js} +13 -13
  73. package/dist/apps/data/routes/coingecko.js.map +1 -0
  74. package/dist/apps/{exchanges.d.ts → data/routes/exchanges.d.ts} +460 -460
  75. package/dist/apps/data/routes/exchanges.d.ts.map +1 -0
  76. package/dist/apps/{exchanges.js → data/routes/exchanges.js} +19 -19
  77. package/dist/apps/data/routes/exchanges.js.map +1 -0
  78. package/dist/apps/{fee-amm.d.ts → data/routes/fee-amm.d.ts} +94 -94
  79. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -0
  80. package/dist/apps/{fee-amm.js → data/routes/fee-amm.js} +17 -17
  81. package/dist/apps/data/routes/fee-amm.js.map +1 -0
  82. package/dist/apps/{indexer.d.ts → data/routes/indexer.d.ts} +4 -4
  83. package/dist/apps/data/routes/indexer.d.ts.map +1 -0
  84. package/dist/apps/{indexer.js → data/routes/indexer.js} +13 -9
  85. package/dist/apps/data/routes/indexer.js.map +1 -0
  86. package/dist/apps/{receipts.d.ts → data/routes/receipts.d.ts} +6 -6
  87. package/dist/apps/data/routes/receipts.d.ts.map +1 -0
  88. package/dist/apps/{receipts.js → data/routes/receipts.js} +12 -12
  89. package/dist/apps/data/routes/receipts.js.map +1 -0
  90. package/dist/apps/{rpc.d.ts → data/routes/rpc.d.ts} +4 -4
  91. package/dist/apps/data/routes/rpc.d.ts.map +1 -0
  92. package/dist/apps/{rpc.js → data/routes/rpc.js} +6 -6
  93. package/dist/apps/data/routes/rpc.js.map +1 -0
  94. package/dist/apps/{tokenlist.d.ts → data/routes/tokenlist.d.ts} +4 -4
  95. package/dist/apps/data/routes/tokenlist.d.ts.map +1 -0
  96. package/dist/apps/{tokenlist.js → data/routes/tokenlist.js} +8 -8
  97. package/dist/apps/data/routes/tokenlist.js.map +1 -0
  98. package/dist/apps/{tokens.d.ts → data/routes/tokens.d.ts} +11 -11
  99. package/dist/apps/data/routes/tokens.d.ts.map +1 -0
  100. package/dist/apps/{tokens.js → data/routes/tokens.js} +19 -19
  101. package/dist/apps/data/routes/tokens.js.map +1 -0
  102. package/dist/apps/{transactions.d.ts → data/routes/transactions.d.ts} +288 -288
  103. package/dist/apps/data/routes/transactions.d.ts.map +1 -0
  104. package/dist/apps/{transactions.js → data/routes/transactions.js} +13 -13
  105. package/dist/apps/data/routes/transactions.js.map +1 -0
  106. package/dist/apps/{transfers.d.ts → data/routes/transfers.d.ts} +35 -35
  107. package/dist/apps/data/routes/transfers.d.ts.map +1 -0
  108. package/dist/apps/{transfers.js → data/routes/transfers.js} +13 -13
  109. package/dist/apps/data/routes/transfers.js.map +1 -0
  110. package/dist/apps/{verified-tokens.d.ts → data/routes/verified-tokens.d.ts} +42 -42
  111. package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -0
  112. package/dist/apps/{verified-tokens.js → data/routes/verified-tokens.js} +9 -9
  113. package/dist/apps/data/routes/verified-tokens.js.map +1 -0
  114. package/dist/apps/{webhooks.d.ts → data/routes/webhooks.d.ts} +66 -63
  115. package/dist/apps/{webhooks.d.ts.map → data/routes/webhooks.d.ts.map} +1 -1
  116. package/dist/apps/{webhooks.js → data/routes/webhooks.js} +67 -59
  117. package/dist/apps/data/routes/webhooks.js.map +1 -0
  118. package/dist/apps/docs/App.d.ts +27 -0
  119. package/dist/apps/docs/App.d.ts.map +1 -0
  120. package/dist/apps/docs/App.js +122 -0
  121. package/dist/apps/docs/App.js.map +1 -0
  122. package/dist/{internal/OpenApi.fonts.d.ts → apps/docs/fonts.d.ts} +1 -1
  123. package/dist/apps/docs/fonts.d.ts.map +1 -0
  124. package/dist/{internal/OpenApi.fonts.js → apps/docs/fonts.js} +1 -1
  125. package/dist/apps/docs/fonts.js.map +1 -0
  126. package/dist/apps/docs/index.d.ts +2 -0
  127. package/dist/apps/docs/index.d.ts.map +1 -0
  128. package/dist/apps/docs/index.js +2 -0
  129. package/dist/apps/docs/index.js.map +1 -0
  130. package/dist/{internal/OpenApi.logo.d.ts → apps/docs/logo.d.ts} +1 -1
  131. package/dist/apps/docs/logo.d.ts.map +1 -0
  132. package/dist/{internal/OpenApi.logo.js → apps/docs/logo.js} +1 -1
  133. package/dist/apps/docs/logo.js.map +1 -0
  134. package/dist/apps/index.d.ts +4 -0
  135. package/dist/apps/index.d.ts.map +1 -0
  136. package/dist/apps/index.js +4 -0
  137. package/dist/apps/index.js.map +1 -0
  138. package/dist/apps/metadata.d.ts +7 -0
  139. package/dist/apps/metadata.d.ts.map +1 -0
  140. package/dist/apps/metadata.js +15 -0
  141. package/dist/apps/metadata.js.map +1 -0
  142. package/dist/apps/platform/App.d.ts +2985 -0
  143. package/dist/apps/platform/App.d.ts.map +1 -0
  144. package/dist/apps/platform/App.js +74 -0
  145. package/dist/apps/platform/App.js.map +1 -0
  146. package/dist/apps/platform/index.d.ts +2 -0
  147. package/dist/apps/platform/index.d.ts.map +1 -0
  148. package/dist/apps/platform/index.js +2 -0
  149. package/dist/apps/platform/index.js.map +1 -0
  150. package/dist/apps/platform/routes/api-keys.d.ts +681 -0
  151. package/dist/apps/platform/routes/api-keys.d.ts.map +1 -0
  152. package/dist/apps/platform/routes/api-keys.js +328 -0
  153. package/dist/apps/platform/routes/api-keys.js.map +1 -0
  154. package/dist/apps/platform/routes/invitations.d.ts +733 -0
  155. package/dist/apps/platform/routes/invitations.d.ts.map +1 -0
  156. package/dist/apps/platform/routes/invitations.js +413 -0
  157. package/dist/apps/platform/routes/invitations.js.map +1 -0
  158. package/dist/apps/platform/routes/me.d.ts +95 -0
  159. package/dist/apps/platform/routes/me.d.ts.map +1 -0
  160. package/dist/apps/platform/routes/me.js +81 -0
  161. package/dist/apps/platform/routes/me.js.map +1 -0
  162. package/dist/apps/platform/routes/members.d.ts +554 -0
  163. package/dist/apps/platform/routes/members.d.ts.map +1 -0
  164. package/dist/apps/platform/routes/members.js +251 -0
  165. package/dist/apps/platform/routes/members.js.map +1 -0
  166. package/dist/apps/platform/routes/orgs.d.ts +582 -0
  167. package/dist/apps/platform/routes/orgs.d.ts.map +1 -0
  168. package/dist/apps/platform/routes/orgs.js +265 -0
  169. package/dist/apps/platform/routes/orgs.js.map +1 -0
  170. package/dist/apps/platform/routes/projects.d.ts +740 -0
  171. package/dist/apps/platform/routes/projects.d.ts.map +1 -0
  172. package/dist/apps/platform/routes/projects.js +286 -0
  173. package/dist/apps/platform/routes/projects.js.map +1 -0
  174. package/dist/apps/relay/App.d.ts +41 -0
  175. package/dist/apps/relay/App.d.ts.map +1 -0
  176. package/dist/apps/relay/App.js +103 -0
  177. package/dist/apps/relay/App.js.map +1 -0
  178. package/dist/apps/relay/Sponsorships.d.ts +107 -0
  179. package/dist/apps/relay/Sponsorships.d.ts.map +1 -0
  180. package/dist/apps/relay/Sponsorships.js +193 -0
  181. package/dist/apps/relay/Sponsorships.js.map +1 -0
  182. package/dist/apps/relay/index.d.ts +3 -0
  183. package/dist/apps/relay/index.d.ts.map +1 -0
  184. package/dist/apps/relay/index.js +3 -0
  185. package/dist/apps/relay/index.js.map +1 -0
  186. package/dist/{Cli.d.ts → cli/Cli.d.ts} +13 -2
  187. package/dist/cli/Cli.d.ts.map +1 -0
  188. package/dist/cli/Cli.js +19 -0
  189. package/dist/cli/Cli.js.map +1 -0
  190. package/dist/cli/bin.d.ts +3 -0
  191. package/dist/cli/bin.d.ts.map +1 -0
  192. package/dist/cli/bin.js +4 -0
  193. package/dist/cli/bin.js.map +1 -0
  194. package/dist/cli/commands/admin.d.ts +15 -0
  195. package/dist/cli/commands/admin.d.ts.map +1 -0
  196. package/dist/cli/commands/admin.js +60 -0
  197. package/dist/cli/commands/admin.js.map +1 -0
  198. package/dist/cloudflare.d.ts +7 -0
  199. package/dist/cloudflare.d.ts.map +1 -1
  200. package/dist/cloudflare.js +9 -0
  201. package/dist/cloudflare.js.map +1 -1
  202. package/dist/db/Db.d.ts +113 -0
  203. package/dist/db/Db.d.ts.map +1 -0
  204. package/dist/db/Db.js +402 -0
  205. package/dist/db/Db.js.map +1 -0
  206. package/dist/db/Schema.d.ts +168 -0
  207. package/dist/db/Schema.d.ts.map +1 -0
  208. package/dist/db/Schema.js +229 -0
  209. package/dist/db/Schema.js.map +1 -0
  210. package/dist/db/tables/invitations.d.ts +84 -0
  211. package/dist/db/tables/invitations.d.ts.map +1 -0
  212. package/dist/db/tables/invitations.js +165 -0
  213. package/dist/db/tables/invitations.js.map +1 -0
  214. package/dist/db/tables/memberships.d.ts +108 -0
  215. package/dist/db/tables/memberships.d.ts.map +1 -0
  216. package/dist/db/tables/memberships.js +153 -0
  217. package/dist/db/tables/memberships.js.map +1 -0
  218. package/dist/db/tables/organizations.d.ts +123 -0
  219. package/dist/db/tables/organizations.d.ts.map +1 -0
  220. package/dist/db/tables/organizations.js +170 -0
  221. package/dist/db/tables/organizations.js.map +1 -0
  222. package/dist/db/tables/projects.d.ts +65 -0
  223. package/dist/db/tables/projects.d.ts.map +1 -0
  224. package/dist/db/tables/projects.js +75 -0
  225. package/dist/db/tables/projects.js.map +1 -0
  226. package/dist/db/tables/sponsoredTransactions.d.ts +120 -0
  227. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -0
  228. package/dist/db/tables/sponsoredTransactions.js +161 -0
  229. package/dist/db/tables/sponsoredTransactions.js.map +1 -0
  230. package/dist/db/tables/users.d.ts +48 -0
  231. package/dist/db/tables/users.d.ts.map +1 -0
  232. package/dist/db/tables/users.js +65 -0
  233. package/dist/db/tables/users.js.map +1 -0
  234. package/dist/db/tables/verifiedTokens.d.ts +53 -0
  235. package/dist/db/tables/verifiedTokens.d.ts.map +1 -0
  236. package/dist/db/tables/verifiedTokens.js +74 -0
  237. package/dist/db/tables/verifiedTokens.js.map +1 -0
  238. package/dist/db/tables/webhookDeliveries.d.ts +71 -0
  239. package/dist/db/tables/webhookDeliveries.d.ts.map +1 -0
  240. package/dist/db/tables/webhookDeliveries.js +119 -0
  241. package/dist/db/tables/webhookDeliveries.js.map +1 -0
  242. package/dist/db/tables/webhookSubscriptions.d.ts +158 -0
  243. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -0
  244. package/dist/db/tables/webhookSubscriptions.js +272 -0
  245. package/dist/db/tables/webhookSubscriptions.js.map +1 -0
  246. package/dist/handlers/internal/executionError.d.ts +25 -0
  247. package/dist/handlers/internal/executionError.d.ts.map +1 -0
  248. package/dist/handlers/internal/executionError.js +186 -0
  249. package/dist/handlers/internal/executionError.js.map +1 -0
  250. package/dist/handlers/internal/multisig.d.ts +233 -0
  251. package/dist/handlers/internal/multisig.d.ts.map +1 -0
  252. package/dist/handlers/internal/multisig.js +542 -0
  253. package/dist/handlers/internal/multisig.js.map +1 -0
  254. package/dist/handlers/internal/requestListener.d.ts +124 -0
  255. package/dist/handlers/internal/requestListener.d.ts.map +1 -0
  256. package/dist/handlers/internal/requestListener.js +173 -0
  257. package/dist/handlers/internal/requestListener.js.map +1 -0
  258. package/dist/handlers/internal/sponsorship.d.ts +113 -0
  259. package/dist/handlers/internal/sponsorship.d.ts.map +1 -0
  260. package/dist/handlers/internal/sponsorship.js +128 -0
  261. package/dist/handlers/internal/sponsorship.js.map +1 -0
  262. package/dist/handlers/internal/types.d.ts +284 -0
  263. package/dist/handlers/internal/types.d.ts.map +1 -0
  264. package/dist/handlers/internal/types.js +2 -0
  265. package/dist/handlers/internal/types.js.map +1 -0
  266. package/dist/handlers/internal/utils.d.ts +34 -0
  267. package/dist/handlers/internal/utils.d.ts.map +1 -0
  268. package/dist/handlers/internal/utils.js +131 -0
  269. package/dist/handlers/internal/utils.js.map +1 -0
  270. package/dist/handlers/relay.d.ts +123 -0
  271. package/dist/handlers/relay.d.ts.map +1 -0
  272. package/dist/handlers/relay.js +1278 -0
  273. package/dist/handlers/relay.js.map +1 -0
  274. package/dist/index.d.ts +4 -2
  275. package/dist/index.d.ts.map +1 -1
  276. package/dist/index.js +4 -2
  277. package/dist/index.js.map +1 -1
  278. package/dist/internal/Auth.d.ts +224 -16
  279. package/dist/internal/Auth.d.ts.map +1 -1
  280. package/dist/internal/Auth.js +572 -33
  281. package/dist/internal/Auth.js.map +1 -1
  282. package/dist/internal/Log.d.ts +7 -3
  283. package/dist/internal/Log.d.ts.map +1 -1
  284. package/dist/internal/Log.js +4 -0
  285. package/dist/internal/Log.js.map +1 -1
  286. package/dist/internal/OpenApi.d.ts +0 -19
  287. package/dist/internal/OpenApi.d.ts.map +1 -1
  288. package/dist/internal/OpenApi.js +0 -57
  289. package/dist/internal/OpenApi.js.map +1 -1
  290. package/dist/internal/RpcSchema.d.ts +10 -0
  291. package/dist/internal/RpcSchema.d.ts.map +1 -0
  292. package/dist/internal/RpcSchema.js +2 -0
  293. package/dist/internal/RpcSchema.js.map +1 -0
  294. package/dist/internal/Store.d.ts +49 -4
  295. package/dist/internal/Store.d.ts.map +1 -1
  296. package/dist/internal/Store.js +81 -5
  297. package/dist/internal/Store.js.map +1 -1
  298. package/dist/internal/VerifiedTokens.d.ts +15 -34
  299. package/dist/internal/VerifiedTokens.d.ts.map +1 -1
  300. package/dist/internal/VerifiedTokens.js +115 -159
  301. package/dist/internal/VerifiedTokens.js.map +1 -1
  302. package/dist/internal/Viem.d.ts +84 -0
  303. package/dist/internal/Viem.d.ts.map +1 -1
  304. package/dist/internal/Webhooks.d.ts +50 -36
  305. package/dist/internal/Webhooks.d.ts.map +1 -1
  306. package/dist/internal/Webhooks.js +92 -205
  307. package/dist/internal/Webhooks.js.map +1 -1
  308. package/package.json +37 -6
  309. package/src/ApiKey.test.ts +3 -3
  310. package/src/ApiKey.ts +11 -12
  311. package/src/ApiKeys.test.ts +278 -0
  312. package/src/ApiKeys.ts +358 -0
  313. package/src/App.test-d.ts +1 -1
  314. package/src/App.test.ts +40508 -19083
  315. package/src/App.ts +239 -416
  316. package/src/Client.test-d.ts +307 -16
  317. package/src/Handler.ts +154 -0
  318. package/src/Scope.ts +17 -4
  319. package/src/Webhooks.test.ts +8 -7
  320. package/src/Webhooks.ts +13 -7
  321. package/src/admin/Access.ts +1 -3
  322. package/src/admin/App.ts +19 -24
  323. package/src/admin/apps/api-keys.test.ts +70 -18
  324. package/src/admin/apps/api-keys.ts +92 -9
  325. package/src/admin/apps/scopes.test.ts +8 -0
  326. package/src/admin/apps/verified-tokens.test.ts +3 -30
  327. package/src/admin/apps/verified-tokens.ts +18 -37
  328. package/src/admin/ui.generated.ts +1 -1
  329. package/src/apps/composition.test.ts +66 -0
  330. package/src/apps/data/App.ts +295 -0
  331. package/src/apps/data/index.ts +2 -0
  332. package/src/apps/{activities.test.ts → data/routes/activities.test.ts} +3 -3
  333. package/src/apps/{activities.ts → data/routes/activities.ts} +15 -15
  334. package/src/apps/{addresses.ts → data/routes/addresses.ts} +6 -6
  335. package/src/apps/{balances.test.ts → data/routes/balances.test.ts} +6 -5
  336. package/src/apps/{balances.ts → data/routes/balances.ts} +46 -12
  337. package/src/apps/{blocks.test.ts → data/routes/blocks.test.ts} +4 -4
  338. package/src/apps/{blocks.ts → data/routes/blocks.ts} +14 -14
  339. package/src/apps/{coingecko.test.ts → data/routes/coingecko.test.ts} +2 -2
  340. package/src/apps/{coingecko.ts → data/routes/coingecko.ts} +14 -14
  341. package/src/apps/{exchanges.test.ts → data/routes/exchanges.test.ts} +2 -2
  342. package/src/apps/{exchanges.ts → data/routes/exchanges.ts} +20 -20
  343. package/src/apps/{fee-amm.test.ts → data/routes/fee-amm.test.ts} +3 -3
  344. package/src/apps/{fee-amm.ts → data/routes/fee-amm.ts} +18 -18
  345. package/src/apps/{indexer.test.ts → data/routes/indexer.test.ts} +27 -6
  346. package/src/apps/{indexer.ts → data/routes/indexer.ts} +13 -10
  347. package/src/apps/{receipts.test.ts → data/routes/receipts.test.ts} +4 -4
  348. package/src/apps/{receipts.ts → data/routes/receipts.ts} +13 -13
  349. package/src/apps/{rpc.test.ts → data/routes/rpc.test.ts} +142 -13
  350. package/src/apps/{rpc.ts → data/routes/rpc.ts} +7 -7
  351. package/src/apps/{tokenlist.test.ts → data/routes/tokenlist.test.ts} +8 -10
  352. package/src/apps/{tokenlist.ts → data/routes/tokenlist.ts} +9 -9
  353. package/src/apps/{tokens.test.ts → data/routes/tokens.test.ts} +6 -5
  354. package/src/apps/{tokens.ts → data/routes/tokens.ts} +20 -20
  355. package/src/apps/{transactions.test.ts → data/routes/transactions.test.ts} +4 -4
  356. package/src/apps/{transactions.ts → data/routes/transactions.ts} +15 -15
  357. package/src/apps/{transfers.test.ts → data/routes/transfers.test.ts} +3 -3
  358. package/src/apps/{transfers.ts → data/routes/transfers.ts} +15 -15
  359. package/src/apps/{verified-tokens.test.ts → data/routes/verified-tokens.test.ts} +27 -15
  360. package/src/apps/{verified-tokens.ts → data/routes/verified-tokens.ts} +10 -10
  361. package/src/apps/{webhooks.test.ts → data/routes/webhooks.test.ts} +137 -150
  362. package/src/apps/{webhooks.ts → data/routes/webhooks.ts} +83 -81
  363. package/src/apps/docs/App.ts +136 -0
  364. package/src/apps/docs/index.ts +1 -0
  365. package/src/apps/index.ts +3 -0
  366. package/src/apps/metadata.ts +23 -0
  367. package/src/apps/platform/App.ts +130 -0
  368. package/src/apps/platform/index.ts +1 -0
  369. package/src/apps/platform/routes/api-keys.test.ts +330 -0
  370. package/src/apps/platform/routes/api-keys.ts +427 -0
  371. package/src/apps/platform/routes/invitations.test.ts +361 -0
  372. package/src/apps/platform/routes/invitations.ts +519 -0
  373. package/src/apps/platform/routes/me.test.ts +121 -0
  374. package/src/apps/platform/routes/me.ts +106 -0
  375. package/src/apps/platform/routes/members.test.ts +195 -0
  376. package/src/apps/platform/routes/members.ts +313 -0
  377. package/src/apps/platform/routes/orgs.test.ts +313 -0
  378. package/src/apps/platform/routes/orgs.ts +343 -0
  379. package/src/apps/platform/routes/projects.test.ts +181 -0
  380. package/src/apps/platform/routes/projects.ts +367 -0
  381. package/src/apps/relay/App.test.ts +484 -0
  382. package/src/apps/relay/App.ts +128 -0
  383. package/src/apps/relay/Sponsorships.test.ts +375 -0
  384. package/src/apps/relay/Sponsorships.ts +305 -0
  385. package/src/apps/relay/index.ts +2 -0
  386. package/src/{Cli.test.ts → cli/Cli.test.ts} +10 -9
  387. package/src/{Cli.ts → cli/Cli.ts} +16 -9
  388. package/src/cli/bin.ts +4 -0
  389. package/src/cli/commands/admin.test.ts +71 -0
  390. package/src/cli/commands/admin.ts +60 -0
  391. package/src/cloudflare.ts +10 -0
  392. package/src/db/Db.test.ts +84 -0
  393. package/src/db/Db.ts +542 -0
  394. package/src/db/Schema.ts +301 -0
  395. package/src/db/tables/invitations.ts +198 -0
  396. package/src/db/tables/memberships.ts +208 -0
  397. package/src/db/tables/organizations.test.ts +160 -0
  398. package/src/db/tables/organizations.ts +232 -0
  399. package/src/db/tables/projects.ts +107 -0
  400. package/src/db/tables/sponsoredTransactions.test.ts +218 -0
  401. package/src/db/tables/sponsoredTransactions.ts +236 -0
  402. package/src/db/tables/users.ts +85 -0
  403. package/src/db/tables/verifiedTokens.ts +111 -0
  404. package/src/db/tables/webhookDeliveries.ts +159 -0
  405. package/src/db/tables/webhookSubscriptions.ts +358 -0
  406. package/src/handlers/internal/executionError.ts +207 -0
  407. package/src/handlers/internal/multisig.test.ts +818 -0
  408. package/src/handlers/internal/multisig.ts +823 -0
  409. package/src/handlers/internal/requestListener.ts +273 -0
  410. package/src/handlers/internal/sponsorship.ts +245 -0
  411. package/src/handlers/internal/types.ts +378 -0
  412. package/src/handlers/internal/utils.ts +153 -0
  413. package/src/handlers/relay.test.ts +2035 -0
  414. package/src/handlers/relay.ts +1623 -0
  415. package/src/index.ts +4 -2
  416. package/src/internal/Auth.test.ts +337 -21
  417. package/src/internal/Auth.ts +783 -44
  418. package/src/internal/Cursor.test.ts +3 -2
  419. package/src/internal/Log.test.ts +1 -1
  420. package/src/internal/Log.ts +11 -3
  421. package/src/internal/MetricSink.ts +1 -1
  422. package/src/internal/OpenApi.ts +0 -59
  423. package/src/internal/RateLimit.test.ts +1 -1
  424. package/src/internal/RpcSchema.ts +10 -0
  425. package/src/internal/Store.test.ts +150 -10
  426. package/src/internal/Store.ts +136 -11
  427. package/src/internal/Tidx.local.test.ts +1 -1
  428. package/src/internal/VerifiedTokens.test.ts +99 -115
  429. package/src/internal/VerifiedTokens.ts +123 -175
  430. package/src/internal/WebhookDestination.test.ts +1 -1
  431. package/src/internal/Webhooks.test.ts +111 -111
  432. package/src/internal/Webhooks.ts +103 -221
  433. package/dist/ApiKeySource.d.ts +0 -166
  434. package/dist/ApiKeySource.d.ts.map +0 -1
  435. package/dist/ApiKeySource.js +0 -234
  436. package/dist/ApiKeySource.js.map +0 -1
  437. package/dist/Cli.d.ts.map +0 -1
  438. package/dist/Cli.js +0 -13
  439. package/dist/Cli.js.map +0 -1
  440. package/dist/apps/activities.js.map +0 -1
  441. package/dist/apps/addresses.js.map +0 -1
  442. package/dist/apps/balances.d.ts.map +0 -1
  443. package/dist/apps/balances.js.map +0 -1
  444. package/dist/apps/blocks.d.ts.map +0 -1
  445. package/dist/apps/blocks.js.map +0 -1
  446. package/dist/apps/coingecko.d.ts.map +0 -1
  447. package/dist/apps/coingecko.js.map +0 -1
  448. package/dist/apps/exchanges.d.ts.map +0 -1
  449. package/dist/apps/exchanges.js.map +0 -1
  450. package/dist/apps/fee-amm.d.ts.map +0 -1
  451. package/dist/apps/fee-amm.js.map +0 -1
  452. package/dist/apps/indexer.d.ts.map +0 -1
  453. package/dist/apps/indexer.js.map +0 -1
  454. package/dist/apps/receipts.d.ts.map +0 -1
  455. package/dist/apps/receipts.js.map +0 -1
  456. package/dist/apps/rpc.d.ts.map +0 -1
  457. package/dist/apps/rpc.js.map +0 -1
  458. package/dist/apps/tokenlist.d.ts.map +0 -1
  459. package/dist/apps/tokenlist.js.map +0 -1
  460. package/dist/apps/tokens.d.ts.map +0 -1
  461. package/dist/apps/tokens.js.map +0 -1
  462. package/dist/apps/transactions.d.ts.map +0 -1
  463. package/dist/apps/transactions.js.map +0 -1
  464. package/dist/apps/transfers.d.ts.map +0 -1
  465. package/dist/apps/transfers.js.map +0 -1
  466. package/dist/apps/verified-tokens.d.ts.map +0 -1
  467. package/dist/apps/verified-tokens.js.map +0 -1
  468. package/dist/apps/webhooks.js.map +0 -1
  469. package/dist/internal/OpenApi.fonts.d.ts.map +0 -1
  470. package/dist/internal/OpenApi.fonts.js.map +0 -1
  471. package/dist/internal/OpenApi.logo.d.ts.map +0 -1
  472. package/dist/internal/OpenApi.logo.js.map +0 -1
  473. package/seeds/31318/icons/0x20c0000000000000000000000000000000000000.svg +0 -1
  474. package/seeds/31318/icons/0x20c0000000000000000000000000000000000001.svg +0 -1
  475. package/seeds/31318/icons/0x20c0000000000000000000000000000000000002.svg +0 -1
  476. package/seeds/31318/icons/0x20c0000000000000000000000000000000000003.svg +0 -1
  477. package/seeds/31318/verified-tokens.json +0 -30
  478. package/seeds/4217/icons/0x20c0000000000000000000000000000000000000.svg +0 -25
  479. package/seeds/4217/icons/0x20c000000000000000000000048c8f36df1c9a4a.svg +0 -1
  480. package/seeds/4217/icons/0x20c0000000000000000000000520792dcccccccc.svg +0 -1
  481. package/seeds/4217/icons/0x20c0000000000000000000000a6da882d075a4c3.svg +0 -4
  482. package/seeds/4217/icons/0x20c000000000000000000000111111111e910f0f.svg +0 -30
  483. package/seeds/4217/icons/0x20c00000000000000000000014f22ca97301eb73.svg +0 -1
  484. package/seeds/4217/icons/0x20c0000000000000000000001621e21f71cf12fb.svg +0 -1
  485. package/seeds/4217/icons/0x20c0000000000000000000002f52d5cc21a3207b.svg +0 -1
  486. package/seeds/4217/icons/0x20c0000000000000000000003158081efd85bfc2.svg +0 -11
  487. package/seeds/4217/icons/0x20c0000000000000000000003554d28269e0f3c2.svg +0 -1
  488. package/seeds/4217/icons/0x20c000000000000000000000383a23bacb546ab9.svg +0 -1
  489. package/seeds/4217/icons/0x20c00000000000000000000042109aef2f8b28e1.png +0 -0
  490. package/seeds/4217/icons/0x20c0000000000000000000005c0bac7cef389a11.svg +0 -1
  491. package/seeds/4217/icons/0x20c0000000000000000000006fd9a167923ba194.png +0 -0
  492. package/seeds/4217/icons/0x20c0000000000000000000007f7ba549dd0251b9.svg +0 -1
  493. package/seeds/4217/icons/0x20c0000000000000000000008191667423f70e67.svg +0 -5
  494. package/seeds/4217/icons/0x20c0000000000000000000008ee4fcff88888888.svg +0 -1
  495. package/seeds/4217/icons/0x20c0000000000000000000009a4a4b17e0dc6651.svg +0 -1
  496. package/seeds/4217/icons/0x20c000000000000000000000ab02d39df30bd17e.svg +0 -1
  497. package/seeds/4217/icons/0x20c000000000000000000000ae247a1130450f09.svg +0 -1
  498. package/seeds/4217/icons/0x20c000000000000000000000aeed2ec36a54d0e5.svg +0 -1
  499. package/seeds/4217/icons/0x20c000000000000000000000b9537d11c60e8b50.svg +0 -1
  500. package/seeds/4217/icons/0x20c000000000000000000000bd95bfb69fbe6ce3.svg +0 -1
  501. package/seeds/4217/icons/0x20c000000000000000000000c412ec89d0c08be5.svg +0 -1
  502. package/seeds/4217/icons/0x20c000000000000000000000f047dd7018e50367.png +0 -0
  503. package/seeds/4217/verified-tokens.json +0 -177
  504. package/seeds/42431/icons/0x20c0000000000000000000000000000000000000.svg +0 -25
  505. package/seeds/42431/icons/0x20c0000000000000000000000000000000000001.svg +0 -1
  506. package/seeds/42431/icons/0x20c0000000000000000000000000000000000002.svg +0 -1
  507. package/seeds/42431/icons/0x20c0000000000000000000000000000000000003.svg +0 -1
  508. package/seeds/42431/icons/0x20c0000000000000000000009e8d7eb59b783726.svg +0 -1
  509. package/seeds/42431/icons/0x20c000000000000000000000d72572838bbee59c.svg +0 -1
  510. package/seeds/42431/verified-tokens.json +0 -44
  511. package/seeds/openrpc/README.md +0 -27
  512. package/seeds/openrpc/execution-apis.json +0 -8989
  513. package/src/ApiKeySource.test-d.ts +0 -11
  514. package/src/ApiKeySource.test.ts +0 -295
  515. package/src/ApiKeySource.ts +0 -303
  516. /package/src/{internal/OpenApi.fonts.ts → apps/docs/fonts.ts} +0 -0
  517. /package/src/{internal/OpenApi.logo.ts → apps/docs/logo.ts} +0 -0
@@ -0,0 +1,1278 @@
1
+ import { AbiEvent, Hex, RpcRequest, RpcResponse } from 'ox';
2
+ import { Transaction as core_Transaction } from 'ox/tempo';
3
+ import { createClient, decodeFunctionData, formatUnits, http, isAddress, parseEventLogs, zeroAddress, } from 'viem';
4
+ import { simulateCalls } from 'viem/actions';
5
+ import { Abis, Actions, Addresses, Capabilities, VirtualAddress } from 'viem/tempo';
6
+ import * as ExecutionError from './internal/executionError.js';
7
+ import { from } from '../Handler.js';
8
+ import * as Store from '../internal/Store.js';
9
+ import * as Multisig from './internal/multisig.js';
10
+ import * as Sponsorship from './internal/sponsorship.js';
11
+ import * as Utils from './internal/utils.js';
12
+ /**
13
+ * Instantiates a relay handler that proxies `eth_fillTransaction`
14
+ * with wallet-aware enrichment (fee token resolution, simulation,
15
+ * sponsorship, AMM resolution).
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * import { Handler } from 'tempo-api/server'
20
+ *
21
+ * const handler = Handler.relay({ getClient })
22
+ *
23
+ * // Plug handler into your server framework of choice:
24
+ * createServer(handler.listener) // Node.js
25
+ * Bun.serve(handler) // Bun
26
+ * Deno.serve(handler) // Deno
27
+ * app.use(handler.listener) // Express
28
+ * app.all('*', c => handler.fetch(c.req.raw)) // Hono
29
+ * export const GET = handler.fetch // Next.js
30
+ * export const POST = handler.fetch // Next.js
31
+ * ```
32
+ *
33
+ * @example
34
+ * With sponsorship
35
+ *
36
+ * ```ts
37
+ * import { privateKeyToAccount } from 'viem/accounts'
38
+ * import { Handler } from 'tempo-api/server'
39
+ *
40
+ * const handler = Handler.relay({
41
+ * getClient,
42
+ * feePayer: {
43
+ * account: privateKeyToAccount('0x...'),
44
+ * },
45
+ * })
46
+ * ```
47
+ *
48
+ * @param options - Options.
49
+ * @returns Request handler.
50
+ */
51
+ export function relay(options) {
52
+ const { getClient, internal_allowUnsafeUrls = false, onRequest, path = '/', resolveTokens, ...rest } = options;
53
+ const feePayerOptions = options.feePayer;
54
+ // Adapt the state store into the multisig operation store once, up front.
55
+ const multisigOptions = options.multisig
56
+ ? { ...options.multisig, store: Multisig.fromStore(options.multisig.store) }
57
+ : undefined;
58
+ // Resolves the chain's fee-token candidates through the caller-provided
59
+ // resolver. An unconfigured resolver (or a chain it has no list for) yields
60
+ // an empty candidate set.
61
+ const getTokens = async (chainId) => {
62
+ if (!resolveTokens || chainId === undefined)
63
+ return [];
64
+ return await resolveTokens(chainId);
65
+ };
66
+ // Memoize cache for TIP-20 metadata and user fee-token preferences. Kept
67
+ // opt-in: an always-on cache would pin a raced null preference (set, then
68
+ // fill before the set mines) for the whole TTL.
69
+ const store = options.cache;
70
+ const features = {
71
+ autoSwap: options.autoSwap ?? options.features === 'all',
72
+ feeTokenResolution: options.features === 'all' || Boolean(resolveTokens),
73
+ simulate: options.features === 'all',
74
+ };
75
+ const autoSwap = (() => {
76
+ if (!features.autoSwap)
77
+ return undefined;
78
+ if (options.autoSwap === false)
79
+ return undefined;
80
+ return {
81
+ slippage: (typeof options.autoSwap === 'object' ? options.autoSwap?.slippage : undefined) ?? 0.05,
82
+ };
83
+ })();
84
+ async function handleRequest(request, options) {
85
+ await onRequest?.(request);
86
+ // Resolve chainId from: 1) explicit option (URL path), 2) first param object, 3) default chain.
87
+ const params = 'params' in request && Array.isArray(request.params) ? request.params : [];
88
+ const first = typeof params[0] === 'object' && params[0]
89
+ ? params[0]
90
+ : undefined;
91
+ const chainId = Utils.resolveChainId(first?.['chainId']) ?? options?.chainId ?? getClient().chain?.id;
92
+ const client = getClient(chainId);
93
+ switch (request.method) {
94
+ case 'eth_fillTransaction': {
95
+ const parameters = params[0];
96
+ const capabilities = (parameters['capabilities'] ?? {});
97
+ try {
98
+ const from = typeof parameters['from'] === 'string' ? parameters['from'] : undefined;
99
+ const requestFeeToken = typeof parameters['feeToken'] === 'string'
100
+ ? parameters['feeToken']
101
+ : undefined;
102
+ const externalFeePayerUrl = typeof parameters['feePayer'] === 'string'
103
+ ? normalizeExternalFeePayerUrl(parameters['feePayer'], {
104
+ allowUnsafe: internal_allowUnsafeUrls,
105
+ })
106
+ : undefined;
107
+ const requestsSponsorship = (!!feePayerOptions || !!externalFeePayerUrl) && parameters['feePayer'] !== false;
108
+ // Default to `true`. Dapps that don't render diffs can pass
109
+ // `capabilities.balanceDiffs: false` to skip the post-fill
110
+ // `tempo_simulateV1` round trip (~250-400ms).
111
+ const requireBalanceDiffs = capabilities['balanceDiffs'] !== false;
112
+ const { feePayer: _feePayer, ...normalized } = Utils.normalizeFillTransactionRequest(parameters);
113
+ const hasMultisigHints = (typeof normalized['multisig'] === 'object' && normalized['multisig'] !== null) ||
114
+ (typeof normalized['multisigInit'] === 'object' &&
115
+ normalized['multisigInit'] !== null) ||
116
+ typeof normalized['multisigSignatureCount'] !== 'undefined';
117
+ const shouldDeferFeePayerSignature = feePayerOptions && multisigOptions && hasMultisigHints;
118
+ // When sponsoring, the sponsor's preferred fee token (if set)
119
+ // overrides whatever the consumer asked for — the sponsor is
120
+ // paying, so the sponsor picks. Falls back to the request's
121
+ // feeToken otherwise.
122
+ const sponsoredFeeToken = requestsSponsorship
123
+ ? (feePayerOptions?.feeToken ?? requestFeeToken)
124
+ : requestFeeToken;
125
+ const baseTx = {
126
+ ...normalized,
127
+ ...(typeof chainId !== 'undefined' ? { chainId } : {}),
128
+ ...(sponsoredFeeToken ? { feeToken: sponsoredFeeToken } : {}),
129
+ };
130
+ let filled;
131
+ let sponsored = false;
132
+ let feeToken = sponsoredFeeToken;
133
+ // Lazily resolve a swap source token when autoSwap needs one.
134
+ const resolveFeeTokenForSwap = from
135
+ ? async (insufficientToken) => resolveFeeToken(client, {
136
+ account: from,
137
+ feeToken: undefined,
138
+ store,
139
+ tokens: (await getTokens(chainId)).filter((t) => t.toLowerCase() !== insufficientToken.toLowerCase()),
140
+ })
141
+ : undefined;
142
+ // When no sponsor will pay, prefer fee tokens the user actually
143
+ // holds. Extend the configured token list with any TIP20 token
144
+ // the transaction is calling — typically the token being
145
+ // transferred — so a user transferring USDC.e can pay gas in
146
+ // USDC.e even when the configured list defaults to pathUSD.
147
+ const configuredTokens = await getTokens(chainId);
148
+ const unsponsoredTokens = [
149
+ ...configuredTokens,
150
+ ...callTargetTokens(baseTx).filter((t) => !configuredTokens.some((rt) => rt.toLowerCase() === t.toLowerCase())),
151
+ ];
152
+ // When the app provides its own fee payer URL, route the fill
153
+ // through that service so it can sign the transaction.
154
+ const fillClient = externalFeePayerUrl
155
+ ? createClient({
156
+ chain: client.chain,
157
+ batch: { multicall: { deployless: true } },
158
+ transport: http(externalFeePayerUrl),
159
+ })
160
+ : client;
161
+ if (requestsSponsorship && (externalFeePayerUrl || !feePayerOptions?.validate)) {
162
+ // Path A: sponsorship guaranteed — skip fee token resolution,
163
+ // fill once with feePayer, then parallelize the rest. Taken when:
164
+ // - no validate is configured (no gating), OR
165
+ // - the app supplied its own external fee payer URL (that
166
+ // relay is the sponsorship authority — the wallet's own
167
+ // `validate` only governs the wallet's own fee payer).
168
+ // Default to the chain's first token so the broadcast envelope
169
+ // always carries a feeToken the chain can charge.
170
+ if (!feeToken)
171
+ feeToken = (await getTokens(chainId))[0];
172
+ const transaction = {
173
+ ...baseTx,
174
+ feePayer: true,
175
+ ...(feeToken ? { feeToken } : {}),
176
+ };
177
+ if (Sponsorship.isPreparedTransaction(transaction)) {
178
+ filled = {
179
+ transaction: Utils.normalizeTempoTransaction(transaction),
180
+ sponsor: undefined,
181
+ };
182
+ }
183
+ else {
184
+ filled = await fill(fillClient, {
185
+ autoSwap,
186
+ feeToken,
187
+ store,
188
+ resolveFeeToken: resolveFeeTokenForSwap,
189
+ transaction,
190
+ });
191
+ // The chain echoes feeToken: null for sponsored fills served
192
+ // by viem clients that strip feeToken pre-sign; re-inject
193
+ // before the spread in mergeCallsFromRequest drops it.
194
+ if (feeToken &&
195
+ filled?.transaction &&
196
+ filled.transaction.feeToken == null)
197
+ filled.transaction.feeToken = feeToken;
198
+ }
199
+ sponsored = true;
200
+ }
201
+ else if (requestsSponsorship && feePayerOptions?.validate) {
202
+ // Path B: sponsorship possible but may be rejected.
203
+ // Speculatively fill the sponsored variant, validate, and only
204
+ // fall back to the unsponsored path on rejection. The accept
205
+ // case (common) avoids the upfront `resolveFeeToken` round
206
+ // trips and the second `eth_fillTransaction`.
207
+ const sponsoredTx = { ...baseTx, feePayer: true };
208
+ if (Sponsorship.isPreparedTransaction(sponsoredTx)) {
209
+ // Already prepared — skip fills, just validate sponsorship.
210
+ const prepared = {
211
+ transaction: Utils.normalizeTempoTransaction(sponsoredTx),
212
+ sponsor: undefined,
213
+ };
214
+ sponsored = await Sponsorship.shouldSponsor({
215
+ sender: from,
216
+ transaction: prepared.transaction,
217
+ validate: feePayerOptions.validate,
218
+ });
219
+ filled = prepared;
220
+ }
221
+ else {
222
+ const options = {
223
+ autoSwap,
224
+ store,
225
+ resolveFeeToken: resolveFeeTokenForSwap,
226
+ };
227
+ const fill_sponsored = await fill(fillClient, {
228
+ ...options,
229
+ feeToken,
230
+ transaction: sponsoredTx,
231
+ });
232
+ sponsored = await Sponsorship.shouldSponsor({
233
+ sender: from,
234
+ transaction: fill_sponsored.transaction,
235
+ validate: feePayerOptions.validate,
236
+ });
237
+ if (sponsored) {
238
+ filled = fill_sponsored;
239
+ }
240
+ else {
241
+ // Sponsor rejected — resolve fee token and fill unsponsored.
242
+ const feeToken_unsponsored = features.feeTokenResolution
243
+ ? await resolveFeeToken(client, {
244
+ account: from,
245
+ feeToken: requestFeeToken,
246
+ store,
247
+ tokens: unsponsoredTokens,
248
+ })
249
+ : requestFeeToken;
250
+ const tx_unsponsored = {
251
+ ...baseTx,
252
+ ...(feeToken_unsponsored ? { feeToken: feeToken_unsponsored } : {}),
253
+ };
254
+ filled = await fill(client, {
255
+ ...options,
256
+ feeToken: feeToken_unsponsored,
257
+ transaction: tx_unsponsored,
258
+ });
259
+ feeToken = feeToken_unsponsored;
260
+ }
261
+ }
262
+ }
263
+ else {
264
+ // Path C: no sponsorship configured — resolve fee token, fill once.
265
+ feeToken = features.feeTokenResolution
266
+ ? await resolveFeeToken(client, {
267
+ account: from,
268
+ feeToken: requestFeeToken,
269
+ store,
270
+ tokens: unsponsoredTokens,
271
+ })
272
+ : requestFeeToken;
273
+ const transaction = { ...baseTx, ...(feeToken ? { feeToken } : {}) };
274
+ filled = await fill(client, {
275
+ autoSwap,
276
+ feeToken,
277
+ store,
278
+ resolveFeeToken: resolveFeeTokenForSwap,
279
+ transaction,
280
+ });
281
+ }
282
+ const transaction_filled = filled.transaction;
283
+ const swap = 'swap' in filled ? filled.swap : undefined;
284
+ if (!feeToken)
285
+ feeToken =
286
+ transaction_filled.feeToken ?? (await getTokens(chainId))[0];
287
+ // Parallelize: simulate, fee payer signing, and autoSwap metadata.
288
+ const alreadySigned = 'feePayerSignature' in transaction_filled &&
289
+ transaction_filled.feePayerSignature != null;
290
+ const calls = extractCalls(transaction_filled);
291
+ const [simulation, transaction_final, autoSwap_, virtualAddresses] = await Promise.all([
292
+ // Simulate and compute balance diffs + fee.
293
+ // When `capabilities.balanceDiffs` is `false`, skip simulate
294
+ // and compute fee directly from cached metadata.
295
+ (async () => {
296
+ if (!features.simulate)
297
+ return { balanceDiffs: undefined, fee: undefined };
298
+ if (requireBalanceDiffs)
299
+ return simulateAndParseDiffs(client, {
300
+ account: from,
301
+ calls,
302
+ swap,
303
+ feeToken,
304
+ gas: transaction_filled.gas,
305
+ store,
306
+ maxFeePerGas: transaction_filled.maxFeePerGas,
307
+ });
308
+ const fee = await computeFee(client, {
309
+ feeToken,
310
+ gas: transaction_filled.gas,
311
+ store,
312
+ maxFeePerGas: transaction_filled.maxFeePerGas,
313
+ }).catch(() => undefined);
314
+ return { balanceDiffs: undefined, fee };
315
+ })(),
316
+ // Sign as fee payer (if sponsored and not already signed).
317
+ (async () => {
318
+ // Multisig finalization changes the sender signature. Sign after quorum.
319
+ if (shouldDeferFeePayerSignature)
320
+ return transaction_filled;
321
+ // Never sign with the managed fee payer for an app-provided
322
+ // external fee payer URL: that relay is the sponsorship authority,
323
+ // and signing here would bypass this fee payer's `validate` gate.
324
+ if (!(sponsored && feePayerOptions && !externalFeePayerUrl && !alreadySigned))
325
+ return transaction_filled;
326
+ return Sponsorship.sign({
327
+ account: feePayerOptions.account,
328
+ onSponsored: feePayerOptions.onSponsored,
329
+ sender: from,
330
+ transaction: transaction_filled,
331
+ });
332
+ })(),
333
+ // Resolve autoSwap metadata (when AMM path was taken).
334
+ resolveAutoSwapMetadata(client, { autoSwap, store, swap }),
335
+ // Resolve virtual-address recipients so wallets can show the
336
+ // eventual master address before signing.
337
+ resolveVirtualAddresses(client, { calls }),
338
+ ]);
339
+ const { balanceDiffs, fee } = simulation;
340
+ const sponsor = (() => {
341
+ if (!sponsored)
342
+ return undefined;
343
+ // App-provided fee payer: relay back the sponsor from the upstream response.
344
+ if (externalFeePayerUrl)
345
+ return filled.sponsor;
346
+ if (feePayerOptions)
347
+ return Sponsorship.getSponsor(feePayerOptions);
348
+ return filled.sponsor;
349
+ })();
350
+ return RpcResponse.from({
351
+ result: {
352
+ ...(sponsor ? { sponsor } : {}),
353
+ tx: core_Transaction.toRpc(transaction_final),
354
+ capabilities: {
355
+ balanceDiffs,
356
+ fee,
357
+ sponsored: !!sponsor,
358
+ ...(sponsor ? { sponsor } : {}),
359
+ ...(autoSwap_ ? { autoSwap: autoSwap_ } : {}),
360
+ ...(virtualAddresses ? { virtualAddresses } : {}),
361
+ },
362
+ },
363
+ }, { request });
364
+ }
365
+ catch (error) {
366
+ if (!(error instanceof Error))
367
+ return Utils.rpcErrorJson(request, error);
368
+ if (capabilities['errors'] !== true)
369
+ return Utils.rpcErrorJson(request, error);
370
+ const revert = ExecutionError.parse(error);
371
+ const parameters = request.params?.[0];
372
+ const stub = {
373
+ from: parameters['from'],
374
+ to: parameters['to'] ?? null,
375
+ gas: '0x0',
376
+ nonce: '0x0',
377
+ value: '0x0',
378
+ maxFeePerGas: '0x0',
379
+ maxPriorityFeePerGas: '0x0',
380
+ };
381
+ if (revert?.errorName === 'InsufficientBalance') {
382
+ const args = revert.args;
383
+ const [available, required, token] = args;
384
+ const normalized = Utils.normalizeFillTransactionRequest(parameters);
385
+ // Simulate from zero address for optimistic balance diffs.
386
+ const optimisticCalls = normalized ? extractCalls(normalized) : undefined;
387
+ const [{ balanceDiffs }, virtualAddresses] = optimisticCalls
388
+ ? await Promise.all([
389
+ simulateAndParseDiffs(client, {
390
+ account: zeroAddress,
391
+ calls: optimisticCalls,
392
+ store,
393
+ }),
394
+ resolveVirtualAddresses(client, { calls: optimisticCalls }),
395
+ ])
396
+ : [{ balanceDiffs: undefined }, undefined];
397
+ // Re-key balance diffs from zero address to the real sender.
398
+ const senderDiffs = parameters['from'] && balanceDiffs
399
+ ? { [parameters['from']]: balanceDiffs[zeroAddress] ?? [] }
400
+ : balanceDiffs;
401
+ const metadata = await resolveTokenMetadata(client, { token, store }).catch(() => undefined);
402
+ const deficit = required - available;
403
+ return RpcResponse.from({
404
+ result: {
405
+ tx: stub,
406
+ capabilities: {
407
+ balanceDiffs: senderDiffs,
408
+ error: ExecutionError.serialize(revert),
409
+ requireFunds: metadata
410
+ ? {
411
+ amount: Hex.fromNumber(deficit),
412
+ decimals: metadata.decimals,
413
+ formatted: formatUnits(deficit, metadata.decimals),
414
+ token,
415
+ symbol: metadata.symbol,
416
+ }
417
+ : undefined,
418
+ sponsored: false,
419
+ ...(virtualAddresses ? { virtualAddresses } : {}),
420
+ },
421
+ },
422
+ }, { request });
423
+ }
424
+ const normalized = Utils.normalizeFillTransactionRequest(parameters);
425
+ const virtualAddresses = normalized
426
+ ? await resolveVirtualAddresses(client, { calls: extractCalls(normalized) }).catch(() => undefined)
427
+ : undefined;
428
+ return RpcResponse.from({
429
+ result: {
430
+ tx: stub,
431
+ capabilities: {
432
+ error: ExecutionError.serialize(revert),
433
+ sponsored: false,
434
+ ...(virtualAddresses ? { virtualAddresses } : {}),
435
+ },
436
+ },
437
+ }, { request });
438
+ }
439
+ }
440
+ case 'eth_signRawTransaction':
441
+ case 'eth_sendRawTransaction':
442
+ case 'eth_sendRawTransactionSync': {
443
+ try {
444
+ const serialized = params[0];
445
+ if (multisigOptions &&
446
+ (request.method === 'eth_sendRawTransaction' ||
447
+ request.method === 'eth_sendRawTransactionSync') &&
448
+ typeof serialized === 'string' &&
449
+ Multisig.isMultisigTransaction(serialized)) {
450
+ const result = await Multisig.handleRawTransaction({
451
+ claimTtl: multisigOptions.claimTtl,
452
+ finalize: multisigOptions.finalize,
453
+ getClient,
454
+ method: request.method,
455
+ request: { params: 'params' in request ? request.params : undefined },
456
+ resolveConfig: multisigOptions.resolveConfig,
457
+ sponsor: feePayerOptions
458
+ ? {
459
+ account: feePayerOptions.account,
460
+ feeToken: feePayerOptions.feeToken,
461
+ onSponsored: feePayerOptions.onSponsored,
462
+ resolveFeeToken: async (chainId) => (await getTokens(chainId))[0],
463
+ validate: feePayerOptions.validate,
464
+ }
465
+ : undefined,
466
+ store: multisigOptions.store,
467
+ });
468
+ return RpcResponse.from({ result }, { request });
469
+ }
470
+ if (!feePayerOptions) {
471
+ // eth_signRawTransaction is a signing method that only the
472
+ // relay can fulfill — forwarding it to the RPC node returns
473
+ // an opaque "Method not found". Return an actionable error.
474
+ if (request.method === 'eth_signRawTransaction') {
475
+ return RpcResponse.from({
476
+ error: {
477
+ code: -32601,
478
+ message: 'eth_signRawTransaction requires a fee payer to be configured on the relay. ' +
479
+ 'Set the `feePayer` option in `Handler.relay()` to enable transaction sponsorship.',
480
+ },
481
+ }, { request });
482
+ }
483
+ const result = await client.request(request);
484
+ return RpcResponse.from({ result }, { request });
485
+ }
486
+ if (typeof serialized !== 'string' ||
487
+ !Sponsorship.requestsRawSponsorship(serialized)) {
488
+ const result = await client.request(request);
489
+ return RpcResponse.from({ result }, { request });
490
+ }
491
+ const result = await Sponsorship.handleRawTransaction({
492
+ account: feePayerOptions.account,
493
+ feeToken: feePayerOptions.feeToken,
494
+ getFeeToken: async (chainId) => (await getTokens(chainId))[0],
495
+ getClient,
496
+ method: request.method,
497
+ onSponsored: feePayerOptions.onSponsored,
498
+ request: { params: 'params' in request ? request.params : undefined },
499
+ validate: feePayerOptions.validate,
500
+ });
501
+ return RpcResponse.from({ result }, { request });
502
+ }
503
+ catch (error) {
504
+ return Utils.rpcErrorJson(request, error);
505
+ }
506
+ }
507
+ case 'eth_getTransactionByHash':
508
+ case 'eth_getTransactionReceipt': {
509
+ try {
510
+ if (multisigOptions) {
511
+ const aliased = await Multisig.handleGetTransaction({
512
+ getClient,
513
+ method: request.method,
514
+ request: { params: 'params' in request ? request.params : undefined },
515
+ store: multisigOptions.store,
516
+ });
517
+ if (aliased)
518
+ return RpcResponse.from({ result: aliased.result }, { request });
519
+ }
520
+ const result = await client.request(request);
521
+ return RpcResponse.from({ result }, { request });
522
+ }
523
+ catch (error) {
524
+ return Utils.rpcErrorJson(request, error);
525
+ }
526
+ }
527
+ default: {
528
+ try {
529
+ const result = await client.request(request);
530
+ return RpcResponse.from({ result }, { request });
531
+ }
532
+ catch (error) {
533
+ return Utils.rpcErrorJson(request, error);
534
+ }
535
+ }
536
+ }
537
+ }
538
+ const router = from(rest);
539
+ async function handlePost(c) {
540
+ const chainId = Utils.resolveChainId(c.req.param('chainId'));
541
+ const body = await c.req.raw.json();
542
+ const isBatch = Array.isArray(body);
543
+ if (!isBatch) {
544
+ const request = RpcRequest.from(body);
545
+ return Response.json(await handleRequest(request, { chainId }));
546
+ }
547
+ const responses = await Promise.all(body.map((item) => handleRequest(RpcRequest.from(item), {
548
+ chainId,
549
+ })));
550
+ return Response.json(responses);
551
+ }
552
+ router.post(path, handlePost);
553
+ router.post(`${path === '/' ? '' : path}/:chainId`, handlePost);
554
+ return router;
555
+ }
556
+ // TODO: cleanup
557
+ async function fill(client, options) {
558
+ const { autoSwap, feeToken, store, transaction: request } = options;
559
+ // Skip re-formatting if already in RPC format (e.g. from viem's fillTransaction).
560
+ const format = (value) => value['type'] === '0x76' ? value : Utils.formatFillTransactionRequest(client, value);
561
+ // Re-fill the transaction with prepended swap calls so the user can
562
+ // mint the missing `insufficientToken` (typically the fee token) from
563
+ // a token they already hold. Returns null if no source token is
564
+ // available or autoSwap is disabled.
565
+ async function fillWithSwap(insufficientToken, deficit) {
566
+ if (!autoSwap)
567
+ return null;
568
+ const sourceToken = feeToken && feeToken.toLowerCase() !== insufficientToken.toLowerCase()
569
+ ? feeToken
570
+ : await options.resolveFeeToken?.(insufficientToken);
571
+ if (!sourceToken || sourceToken.toLowerCase() === insufficientToken.toLowerCase())
572
+ return null;
573
+ const maxAmountIn = deficit + (deficit * BigInt(Math.round(autoSwap.slippage * 1000))) / 1000n;
574
+ const originalCalls = request['calls'] ?? [];
575
+ const swapCalls = buildSwapCalls(client, sourceToken, insufficientToken, deficit, maxAmountIn);
576
+ const result = await client.request({
577
+ method: 'eth_fillTransaction',
578
+ params: [
579
+ format({
580
+ ...request,
581
+ calls: [...swapCalls, ...originalCalls],
582
+ }),
583
+ ],
584
+ });
585
+ const sponsor = result['capabilities']?.sponsor;
586
+ const mergedTx = mergeCallsFromRequest(result.tx, {
587
+ ...request,
588
+ calls: [...swapCalls, ...originalCalls],
589
+ });
590
+ return {
591
+ transaction: Utils.normalizeTempoTransaction(mergedTx),
592
+ sponsor,
593
+ swap: {
594
+ calls: swapCalls,
595
+ tokenIn: sourceToken,
596
+ tokenOut: insufficientToken,
597
+ amountOut: deficit,
598
+ maxAmountIn,
599
+ },
600
+ };
601
+ }
602
+ try {
603
+ const formatted = format(request);
604
+ const result = await client.request({
605
+ method: 'eth_fillTransaction',
606
+ params: [formatted],
607
+ });
608
+ // FIXME: node estimates gas with secp256k1 dummy sig + null feePayerSignature.
609
+ // Actual tx has larger keychain/webAuthn sigs + real fee payer sig, costing
610
+ // more intrinsic gas. Mirror the bump from viem's tempo chainConfig.
611
+ // Skip if another relay already bumped (indicated by feePayerSignature).
612
+ // @ts-expect-error
613
+ if (result.tx.gas && request.feePayer && !result.tx.feePayerSignature)
614
+ result.tx.gas = Hex.fromNumber(BigInt(result.tx.gas) + 20000n);
615
+ const upstreamCapabilities = result.capabilities;
616
+ const sponsor = upstreamCapabilities?.['sponsor'];
617
+ // External fee-payer relays surface chain reverts (e.g. InsufficientBalance,
618
+ // InsufficientAllowance) inside `capabilities.error` with a stub `tx`
619
+ // instead of throwing. Re-throw all upstream errors so the outer handler
620
+ // either recovers via autoSwap (InsufficientBalance) or propagates the
621
+ // error to the caller. Without this, the wallet would otherwise sign the
622
+ // zero-stub tx with its own fee payer and broadcast garbage.
623
+ const upstreamError = upstreamCapabilities?.['error'];
624
+ if (upstreamError) {
625
+ const synthetic = new Error(upstreamError.message ?? upstreamError.errorName ?? 'UpstreamRevert');
626
+ synthetic.name = 'UpstreamRevertError';
627
+ synthetic.data = upstreamError.data;
628
+ throw synthetic;
629
+ }
630
+ // Reconstruct a `swap` shape from upstream's `capabilities.autoSwap` so the
631
+ // wallet relay's outer code can re-resolve autoSwap metadata locally —
632
+ // otherwise upstream-driven swaps are silently dropped from the response.
633
+ const swap = extractSwapFromCapabilities(upstreamCapabilities?.['autoSwap']);
634
+ // The chain's `eth_fillTransaction` doesn't echo back `calls`, so merge
635
+ // them in from the original request before normalizing — otherwise the
636
+ // typed envelope built for sponsorship signing throws CallsEmptyError.
637
+ const mergedTx = mergeCallsFromRequest(result.tx, request);
638
+ // The node's `eth_fillTransaction` may pick a fee token the user
639
+ // doesn't yet hold (e.g. one this transaction will mint via a
640
+ // swap). Tempo's gas check runs before the calls execute, so the
641
+ // tx would revert at broadcast with `have 0 want N`. Validate the
642
+ // sender's pre-tx balance against the computed gas cost and, if
643
+ // short, autoSwap a token they do hold into the fee token.
644
+ if (autoSwap && !swap) {
645
+ const fromAddress = request['from'];
646
+ const resolvedFeeToken = (mergedTx['feeToken'] ?? feeToken);
647
+ const gas = mergedTx['gas'] ? BigInt(mergedTx['gas']) : 0n;
648
+ const maxFeePerGas = mergedTx['maxFeePerGas']
649
+ ? BigInt(mergedTx['maxFeePerGas'])
650
+ : 0n;
651
+ if (fromAddress && resolvedFeeToken && gas > 0n && maxFeePerGas > 0n) {
652
+ const [balance, metadata] = await Promise.all([
653
+ Actions.token
654
+ .getBalance(client, { account: fromAddress, token: resolvedFeeToken })
655
+ .then((balance) => balance.amount)
656
+ .catch(() => 0n),
657
+ resolveTokenMetadata(client, { token: resolvedFeeToken, store }).catch(() => undefined),
658
+ ]);
659
+ if (metadata) {
660
+ const requiredFee = (gas * maxFeePerGas) / 10n ** BigInt(Math.max(0, 18 - metadata.decimals));
661
+ if (balance < requiredFee) {
662
+ // Best-effort: if the swap itself can't be filled (e.g. the
663
+ // source token also has insufficient balance), fall through
664
+ // and return the original tx with the resolved feeToken
665
+ // rather than failing the whole request.
666
+ const swapResult = await fillWithSwap(resolvedFeeToken, requiredFee - balance).catch(() => null);
667
+ if (swapResult)
668
+ return swapResult;
669
+ }
670
+ }
671
+ }
672
+ }
673
+ return {
674
+ transaction: Utils.normalizeTempoTransaction(mergedTx),
675
+ sponsor,
676
+ ...(swap ? { swap } : {}),
677
+ };
678
+ }
679
+ catch (error) {
680
+ if (!(error instanceof Error))
681
+ throw error;
682
+ if (!autoSwap)
683
+ throw error;
684
+ const revert = ExecutionError.parse(error);
685
+ if (revert?.errorName !== 'InsufficientBalance')
686
+ throw error;
687
+ const [available, required, token] = revert.args;
688
+ if (typeof available === 'undefined' || typeof required === 'undefined' || !token)
689
+ throw error;
690
+ const swapResult = await fillWithSwap(token, required - available);
691
+ if (!swapResult)
692
+ throw error;
693
+ return swapResult;
694
+ }
695
+ }
696
+ async function resolveFeeToken(client, options) {
697
+ const { feeToken, account, store, tokens } = options;
698
+ if (feeToken)
699
+ return feeToken;
700
+ if (!account)
701
+ return undefined;
702
+ // Cache the user's preferred fee token for `userTokenCacheTtl` seconds.
703
+ // The on-chain preference rarely changes; a short TTL avoids the
704
+ // ~150-250ms `userTokens` multicall on every unsponsored fill while
705
+ // still picking up updates within a minute. The cached value is just a
706
+ // hint — if the user's balance is 0 we fall back to the highest-balance
707
+ // token from the configured list.
708
+ const getUserToken = () => Actions.fee.getUserToken(client, { account }).catch(() => null);
709
+ const userTokenPromise = store
710
+ ? Store.memoize(async () => {
711
+ const result = await getUserToken();
712
+ return result ? { address: result.address } : null;
713
+ }, {
714
+ key: `fee.userToken:${client.chain?.id ?? 0}:${account.toLowerCase()}`,
715
+ store,
716
+ ttl: (options.userTokenCacheTtl ?? 60) * 1000,
717
+ })
718
+ : getUserToken();
719
+ const [userToken, balances] = await Promise.all([
720
+ userTokenPromise,
721
+ tokens
722
+ ? Promise.all(tokens.map(async (token) => ({
723
+ address: token,
724
+ balance: await Actions.token
725
+ .getBalance(client, { account, token })
726
+ .then((balance) => balance.amount)
727
+ .catch(() => 0n),
728
+ })))
729
+ : [],
730
+ ]);
731
+ // If on-chain preference is set and user has balance, use it.
732
+ if (userToken) {
733
+ const match = balances.find((b) => b.address.toLowerCase() === userToken.address.toLowerCase() && b.balance > 0n);
734
+ if (match)
735
+ return userToken.address;
736
+ // Token list may not include the preference — check on-chain directly.
737
+ if (!match) {
738
+ try {
739
+ const { amount: balance } = await Actions.token.getBalance(client, {
740
+ account,
741
+ token: userToken.address,
742
+ });
743
+ if (balance > 0n)
744
+ return userToken.address;
745
+ }
746
+ catch { }
747
+ }
748
+ }
749
+ // Pick the token with the highest balance.
750
+ let best;
751
+ for (const asset of balances) {
752
+ if (asset.balance <= 0n)
753
+ continue;
754
+ if (!best || asset.balance > best.balance)
755
+ best = asset;
756
+ }
757
+ if (best)
758
+ return best.address;
759
+ return undefined;
760
+ }
761
+ /**
762
+ * Extracts unique TIP20 token addresses (Tempo `0x20c0…` prefix) that the
763
+ * transaction's calls target. Used as fallback fee-token candidates so a
764
+ * user transferring a token they hold can pay gas in that token even when
765
+ * the configured fee-token list defaults to one they don't hold.
766
+ */
767
+ function callTargetTokens(transaction) {
768
+ const calls = transaction['calls'];
769
+ if (!calls)
770
+ return [];
771
+ const out = [];
772
+ const seen = new Set();
773
+ for (const c of calls) {
774
+ if (!c.to)
775
+ continue;
776
+ const lower = c.to.toLowerCase();
777
+ if (!lower.startsWith('0x20c0'))
778
+ continue;
779
+ if (seen.has(lower))
780
+ continue;
781
+ seen.add(lower);
782
+ out.push(c.to);
783
+ }
784
+ return out;
785
+ }
786
+ async function resolveVirtualAddresses(client, options) {
787
+ const targets = getVirtualAddressTargets(options.calls);
788
+ if (targets.length === 0)
789
+ return undefined;
790
+ const masters = new Map();
791
+ for (const address of targets) {
792
+ const { masterId } = VirtualAddress.parse(address);
793
+ const lower = masterId.toLowerCase();
794
+ const entry = masters.get(lower) ?? { addresses: [], masterId };
795
+ entry.addresses.push(address);
796
+ masters.set(lower, entry);
797
+ }
798
+ const entries = await Promise.all([...masters.values()].map(async ({ addresses, masterId }) => {
799
+ const master = await Actions.virtualAddress.getMasterAddress(client, { masterId });
800
+ return addresses.map((address) => [address, master]);
801
+ }));
802
+ return Object.fromEntries(entries.flat());
803
+ }
804
+ function getVirtualAddressTargets(calls) {
805
+ const targets = new Set();
806
+ for (const call of calls) {
807
+ for (const address of [call.to, decodeTransferRecipient(call.data)]) {
808
+ if (!address || !isAddress(address) || !VirtualAddress.isVirtual(address))
809
+ continue;
810
+ targets.add(address.toLowerCase());
811
+ }
812
+ }
813
+ return [...targets];
814
+ }
815
+ function decodeTransferRecipient(data) {
816
+ if (!data)
817
+ return undefined;
818
+ const selector = data.slice(0, 10).toLowerCase();
819
+ if (!transferSelectors.has(selector))
820
+ return undefined;
821
+ try {
822
+ const { args, functionName } = decodeFunctionData({
823
+ abi: Abis.tip20,
824
+ data: data,
825
+ });
826
+ if ((functionName === 'transfer' || functionName === 'transferWithMemo') &&
827
+ typeof args[0] === 'string')
828
+ return args[0];
829
+ if ((functionName === 'transferFrom' || functionName === 'transferFromWithMemo') &&
830
+ typeof args[1] === 'string')
831
+ return args[1];
832
+ }
833
+ catch { }
834
+ return undefined;
835
+ }
836
+ const transferSelectors = new Set(['0xa9059cbb', '0x95777d59', '0x23b872dd', '0x929c2539']);
837
+ // TODO: cleanup/remove
838
+ function extractCalls(transaction) {
839
+ const calls = transaction['calls'];
840
+ if (calls && calls.length > 0)
841
+ return calls.map((c) => ({
842
+ ...(c.to ? { to: c.to } : {}),
843
+ ...(c.data ? { data: c.data } : {}),
844
+ ...(c.value ? { value: c.value } : {}),
845
+ }));
846
+ return [
847
+ {
848
+ ...(transaction['to'] ? { to: transaction['to'] } : {}),
849
+ ...(transaction['data'] ? { data: transaction['data'] } : {}),
850
+ ...(transaction['value'] ? { value: transaction['value'] } : {}),
851
+ },
852
+ ];
853
+ }
854
+ // TODO: cleanup/remove
855
+ async function simulate(client, options) {
856
+ const { account, calls } = options;
857
+ try {
858
+ return await Actions.simulate.simulateCalls(client, {
859
+ ...(account ? { account } : {}),
860
+ calls: calls,
861
+ traceTransfers: true,
862
+ });
863
+ }
864
+ catch (error) {
865
+ // TODO: Remove fallback once all nodes support tempo_simulateV1.
866
+ // Fall back to viem's simulateCalls (eth_simulateV1) if the Tempo
867
+ // method (tempo_simulateV1) is not supported.
868
+ const code = error.code ??
869
+ error.cause?.code;
870
+ if (code !== -32601)
871
+ throw error;
872
+ const { results } = await simulateCalls(client, {
873
+ ...(account ? { account } : {}),
874
+ calls: calls,
875
+ });
876
+ return { results, tokenMetadata: undefined };
877
+ }
878
+ }
879
+ async function simulateAndParseDiffs(client, options) {
880
+ const { account, calls, swap, feeToken, gas, store, maxFeePerGas } = options;
881
+ try {
882
+ const { results, tokenMetadata } = await simulate(client, {
883
+ account: account === zeroAddress ? undefined : account,
884
+ calls,
885
+ });
886
+ // Collect all logs across all call results.
887
+ const logs = [];
888
+ for (const result of results)
889
+ if (result.logs)
890
+ logs.push(...result.logs);
891
+ // Build per-token balance diffs relative to the sender.
892
+ const balanceDiffs = account
893
+ ? await buildBalanceDiffs(client, {
894
+ account,
895
+ store,
896
+ logs,
897
+ swap,
898
+ tokenMetadata: tokenMetadata,
899
+ })
900
+ : {};
901
+ // Compute fee breakdown.
902
+ const fee = await computeFee(client, {
903
+ feeToken,
904
+ gas,
905
+ store,
906
+ maxFeePerGas,
907
+ tokenMetadata: tokenMetadata,
908
+ }).catch(() => undefined);
909
+ return { balanceDiffs, fee };
910
+ }
911
+ catch {
912
+ // Simulation failures should not block the fill response —
913
+ // return empty diffs with fee computed from transaction fields.
914
+ const fee = await computeFee(client, { feeToken, gas, store, maxFeePerGas });
915
+ return { balanceDiffs: undefined, fee };
916
+ }
917
+ }
918
+ async function buildBalanceDiffs(client, options) {
919
+ const { account, store, logs, swap, tokenMetadata } = options;
920
+ const accountLower = account.toLowerCase();
921
+ const dexLower = Addresses.stablecoinDex.toLowerCase();
922
+ const swapTokenIn = swap?.tokenIn.toLowerCase();
923
+ const swapTokenOut = swap?.tokenOut.toLowerCase();
924
+ const transferLogs = parseEventLogs({
925
+ abi: [AbiEvent.fromAbi(Abis.tip20, 'Transfer')],
926
+ eventName: 'Transfer',
927
+ logs,
928
+ });
929
+ const approvalLogs = parseEventLogs({
930
+ abi: [AbiEvent.fromAbi(Abis.tip20, 'Approval')],
931
+ eventName: 'Approval',
932
+ logs,
933
+ });
934
+ // Track net movement per token: incoming vs outgoing.
935
+ const tokenMap = new Map();
936
+ // Track total transferred per (token, spender) so we can suppress covered approvals.
937
+ const transferredBySpender = new Map();
938
+ for (const log of transferLogs) {
939
+ const token = log.address.toLowerCase();
940
+ const fromLower = log.args.from.toLowerCase();
941
+ const toLower = log.args.to.toLowerCase();
942
+ // Skip swap-related transfers (reported in capabilities.autoSwap instead).
943
+ if (swap) {
944
+ if (token === swapTokenIn && fromLower === accountLower && toLower === dexLower)
945
+ continue;
946
+ if (token === swapTokenOut && fromLower === dexLower && toLower === accountLower)
947
+ continue;
948
+ }
949
+ const entry = tokenMap.get(token) ?? {
950
+ incoming: 0n,
951
+ outgoing: 0n,
952
+ recipients: new Set(),
953
+ token: log.address,
954
+ };
955
+ if (fromLower === accountLower) {
956
+ entry.outgoing += log.args.amount;
957
+ entry.recipients.add(log.args.to);
958
+ const key = `${token}:${toLower}`;
959
+ transferredBySpender.set(key, (transferredBySpender.get(key) ?? 0n) + log.args.amount);
960
+ }
961
+ if (toLower === accountLower)
962
+ entry.incoming += log.args.amount;
963
+ tokenMap.set(token, entry);
964
+ }
965
+ // Treat approvals as outgoing unless the spender already transferred >= approval amount.
966
+ for (const log of approvalLogs) {
967
+ if (log.args.owner.toLowerCase() !== accountLower)
968
+ continue;
969
+ const token = log.address.toLowerCase();
970
+ // Skip swap-related approvals (reported in capabilities.autoSwap instead).
971
+ if (swap && token === swapTokenIn && log.args.spender.toLowerCase() === dexLower)
972
+ continue;
973
+ const spenderKey = `${token}:${log.args.spender.toLowerCase()}`;
974
+ const transferred = transferredBySpender.get(spenderKey) ?? 0n;
975
+ if (log.args.amount <= transferred)
976
+ continue;
977
+ const entry = tokenMap.get(token) ?? {
978
+ incoming: 0n,
979
+ outgoing: 0n,
980
+ recipients: new Set(),
981
+ token: log.address,
982
+ };
983
+ entry.outgoing += log.args.amount - transferred;
984
+ entry.recipients.add(log.args.spender);
985
+ tokenMap.set(token, entry);
986
+ }
987
+ // Collect unique tokens that need decimals.
988
+ const entries = [...tokenMap.values()].filter((e) => {
989
+ const net = e.outgoing > e.incoming ? e.outgoing - e.incoming : e.incoming - e.outgoing;
990
+ return net > 0n;
991
+ });
992
+ if (entries.length === 0)
993
+ return {};
994
+ // Resolve metadata for all tokens in parallel (simulation metadata first, RPC fallback).
995
+ const metadataMap = new Map();
996
+ await Promise.all(entries.map(async (entry) => {
997
+ try {
998
+ const metadata = await resolveTokenMetadata(client, {
999
+ token: entry.token,
1000
+ tokenMetadata,
1001
+ store,
1002
+ });
1003
+ metadataMap.set(entry.token.toLowerCase(), metadata);
1004
+ }
1005
+ catch { }
1006
+ }));
1007
+ // Build the diff array for this account.
1008
+ const diffs = [];
1009
+ for (const entry of entries) {
1010
+ const net = entry.outgoing > entry.incoming
1011
+ ? entry.outgoing - entry.incoming
1012
+ : entry.incoming - entry.outgoing;
1013
+ const direction = entry.outgoing > entry.incoming ? 'outgoing' : 'incoming';
1014
+ const meta = metadataMap.get(entry.token.toLowerCase());
1015
+ const decimals = meta?.decimals ?? 0;
1016
+ diffs.push({
1017
+ address: entry.token,
1018
+ decimals,
1019
+ direction,
1020
+ formatted: formatUnits(net, decimals),
1021
+ name: meta?.name ?? '',
1022
+ symbol: meta?.symbol ?? '',
1023
+ recipients: [...entry.recipients],
1024
+ value: Hex.fromNumber(net),
1025
+ });
1026
+ }
1027
+ return { [account]: diffs };
1028
+ }
1029
+ async function resolveTokenMetadata(client, options) {
1030
+ const { token, tokenMetadata, store } = options;
1031
+ const meta = tokenMetadata?.[token] ?? tokenMetadata?.[token.toLowerCase()];
1032
+ // TIP-20 metadata (decimals/symbol/name) is immutable per token, so cache
1033
+ // long-term. Skips the multicall RPC on cache hits.
1034
+ const fetcher = () => Actions.token.getMetadata(client, { token });
1035
+ const fallback = store
1036
+ ? await Store.memoize(fetcher, {
1037
+ key: `tokenMetadata:${client.chain?.id ?? 0}:${token.toLowerCase()}`,
1038
+ store,
1039
+ ttl: 24 * 60 * 60 * 1000,
1040
+ })
1041
+ : await fetcher();
1042
+ return {
1043
+ decimals: fallback.decimals ?? 6,
1044
+ symbol: meta?.symbol || fallback.symbol,
1045
+ name: meta?.name || fallback.name,
1046
+ };
1047
+ }
1048
+ async function resolveAutoSwapMetadata(client, options) {
1049
+ const { autoSwap, store, swap } = options;
1050
+ if (!autoSwap || !swap)
1051
+ return undefined;
1052
+ const [inMeta, outMeta] = await Promise.all([
1053
+ resolveTokenMetadata(client, { token: swap.tokenIn, store }).catch(() => undefined),
1054
+ resolveTokenMetadata(client, { token: swap.tokenOut, store }).catch(() => undefined),
1055
+ ]);
1056
+ if (!inMeta || !outMeta)
1057
+ return undefined;
1058
+ return {
1059
+ calls: swap.calls.map((c) => ({ to: c.to, data: c.data })),
1060
+ slippage: autoSwap.slippage,
1061
+ maxIn: {
1062
+ token: swap.tokenIn,
1063
+ value: Hex.fromNumber(swap.maxAmountIn),
1064
+ formatted: formatUnits(swap.maxAmountIn, inMeta.decimals),
1065
+ decimals: inMeta.decimals,
1066
+ symbol: inMeta.symbol,
1067
+ name: inMeta.name,
1068
+ },
1069
+ minOut: {
1070
+ token: swap.tokenOut,
1071
+ value: Hex.fromNumber(swap.amountOut),
1072
+ formatted: formatUnits(swap.amountOut, outMeta.decimals),
1073
+ decimals: outMeta.decimals,
1074
+ symbol: outMeta.symbol,
1075
+ name: outMeta.name,
1076
+ },
1077
+ };
1078
+ }
1079
+ async function computeFee(client, options) {
1080
+ const { feeToken, gas, store, maxFeePerGas, tokenMetadata } = options;
1081
+ if (!feeToken || !gas || !maxFeePerGas)
1082
+ return undefined;
1083
+ try {
1084
+ const metadata = await resolveTokenMetadata(client, { token: feeToken, tokenMetadata, store });
1085
+ const raw = gas * maxFeePerGas;
1086
+ const amount = raw / 10n ** BigInt(18 - metadata.decimals);
1087
+ return {
1088
+ amount: Hex.fromNumber(amount),
1089
+ decimals: metadata.decimals,
1090
+ formatted: formatUnits(amount, metadata.decimals),
1091
+ symbol: metadata.symbol,
1092
+ };
1093
+ }
1094
+ catch {
1095
+ return undefined;
1096
+ }
1097
+ }
1098
+ function buildSwapCalls(client, sourceToken, targetToken, deficit, maxAmountIn) {
1099
+ const approve = Actions.token.approve.call(client, {
1100
+ token: sourceToken,
1101
+ spender: Addresses.stablecoinDex,
1102
+ amount: maxAmountIn,
1103
+ });
1104
+ const buy = Actions.dex.buy.call({
1105
+ tokenIn: sourceToken,
1106
+ tokenOut: targetToken,
1107
+ amountOut: deficit,
1108
+ maxAmountIn,
1109
+ });
1110
+ return [
1111
+ { to: approve.to, data: approve.data, value: 0n },
1112
+ { to: buy.to, data: buy.data, value: 0n },
1113
+ ];
1114
+ }
1115
+ /**
1116
+ * Merges the original fill request into the result tx. The chain's
1117
+ * `eth_fillTransaction` returns only the "filled" gas/nonce/fee fields and
1118
+ * omits envelope inputs like `calls`, `chainId`, `validBefore`, `nonceKey`,
1119
+ * `keyData`, `keyType`, `feePayer`. Without these the typed Tempo envelope
1120
+ * built for sponsorship signing throws `CallsEmptyError` or
1121
+ * `Cannot convert undefined to a BigInt` when serializing.
1122
+ *
1123
+ * Result fields take precedence (they are the chain's authoritative filled
1124
+ * values); request fields fill in everything else. Calls are normalized
1125
+ * separately so legacy `to`/`data`/`value` requests are also supported.
1126
+ */
1127
+ function mergeCallsFromRequest(resultTx, request) {
1128
+ const merged = { ...request, ...resultTx };
1129
+ const resultCalls = resultTx['calls'];
1130
+ if (Array.isArray(resultCalls) && resultCalls.length > 0)
1131
+ return merged;
1132
+ const reqCalls = request['calls'];
1133
+ if (Array.isArray(reqCalls) && reqCalls.length > 0) {
1134
+ merged['calls'] = reqCalls;
1135
+ return merged;
1136
+ }
1137
+ const { to, data, value } = request;
1138
+ if (typeof to === 'undefined' && typeof data === 'undefined' && typeof value === 'undefined')
1139
+ return merged;
1140
+ merged['calls'] = [
1141
+ {
1142
+ ...(typeof to !== 'undefined' ? { to } : {}),
1143
+ ...(typeof data !== 'undefined' ? { data } : {}),
1144
+ ...(typeof value !== 'undefined' ? { value } : {}),
1145
+ },
1146
+ ];
1147
+ return merged;
1148
+ }
1149
+ /** Normalizes external fee-payer relay URLs before forwarding fill payloads. */
1150
+ function normalizeExternalFeePayerUrl(value, options = {}) {
1151
+ let url;
1152
+ try {
1153
+ url = new URL(value);
1154
+ }
1155
+ catch {
1156
+ throw new RpcResponse.InvalidParamsError({ message: 'Invalid fee payer URL.' });
1157
+ }
1158
+ const allowUnsafe = options.allowUnsafe === true;
1159
+ if (url.protocol !== 'http:' && url.protocol !== 'https:')
1160
+ throw new RpcResponse.InvalidParamsError({ message: 'Invalid fee payer URL protocol.' });
1161
+ if (!allowUnsafe && url.protocol !== 'https:')
1162
+ throw new RpcResponse.InvalidParamsError({ message: 'Invalid fee payer URL protocol.' });
1163
+ if (!allowUnsafe && isUnsafeHostname(url.hostname))
1164
+ throw new RpcResponse.InvalidParamsError({ message: 'Invalid fee payer URL host.' });
1165
+ url.username = '';
1166
+ url.password = '';
1167
+ url.hash = '';
1168
+ return url.href;
1169
+ }
1170
+ function isUnsafeHostname(hostname) {
1171
+ const host = normalizeHostname(hostname);
1172
+ if (host === 'localhost' || host.endsWith('.localhost'))
1173
+ return true;
1174
+ const ipv4 = parseIpv4(host);
1175
+ if (ipv4)
1176
+ return isUnsafeIpv4(ipv4);
1177
+ if (!host.includes(':'))
1178
+ return false;
1179
+ const ipv6 = parseIpv6(host);
1180
+ if (!ipv6)
1181
+ return true;
1182
+ return isUnsafeIpv6(ipv6);
1183
+ }
1184
+ function normalizeHostname(hostname) {
1185
+ return hostname
1186
+ .toLowerCase()
1187
+ .replace(/^\[|\]$/g, '')
1188
+ .split('%')[0];
1189
+ }
1190
+ function parseIpv4(value) {
1191
+ const parts = value.split('.');
1192
+ if (parts.length !== 4 || parts.some((part) => !/^\d+$/.test(part)))
1193
+ return undefined;
1194
+ const numbers = parts.map((part) => Number(part));
1195
+ if (numbers.some((part) => !Number.isInteger(part) || part < 0 || part > 255))
1196
+ return undefined;
1197
+ return numbers;
1198
+ }
1199
+ function isUnsafeIpv4(parts) {
1200
+ const [a, b] = parts;
1201
+ if (a === 0 || a === 10 || a === 127)
1202
+ return true;
1203
+ if (a === 100 && b >= 64 && b <= 127)
1204
+ return true;
1205
+ if (a === 169 && b === 254)
1206
+ return true;
1207
+ if (a === 172 && b >= 16 && b <= 31)
1208
+ return true;
1209
+ if (a === 192 && b === 168)
1210
+ return true;
1211
+ if (a >= 224)
1212
+ return true;
1213
+ return false;
1214
+ }
1215
+ function parseIpv6(value) {
1216
+ const halves = value.split('::');
1217
+ if (halves.length > 2)
1218
+ return undefined;
1219
+ const left = halves[0] ? halves[0].split(':') : [];
1220
+ const right = halves[1] ? halves[1].split(':') : [];
1221
+ const missing = halves.length === 2 ? 8 - left.length - right.length : 0;
1222
+ if (missing < 0)
1223
+ return undefined;
1224
+ const parts = [...left, ...Array(missing).fill('0'), ...right];
1225
+ if (parts.length !== 8)
1226
+ return undefined;
1227
+ const numbers = parts.map((part) => /^[\da-f]{1,4}$/i.test(part) ? Number.parseInt(part, 16) : NaN);
1228
+ if (numbers.some((part) => !Number.isInteger(part) || part < 0 || part > 0xffff))
1229
+ return undefined;
1230
+ return numbers;
1231
+ }
1232
+ function isUnsafeIpv6(parts) {
1233
+ const [a] = parts;
1234
+ if (parts.every((part) => part === 0))
1235
+ return true;
1236
+ if (parts.slice(0, 7).every((part) => part === 0) && parts[7] === 1)
1237
+ return true;
1238
+ if ((a & 0xfe00) === 0xfc00)
1239
+ return true;
1240
+ if ((a & 0xffc0) === 0xfe80)
1241
+ return true;
1242
+ if ((a & 0xffc0) === 0xfec0)
1243
+ return true;
1244
+ if ((a & 0xff00) === 0xff00)
1245
+ return true;
1246
+ const ipv4 = ipv4FromIpv6(parts);
1247
+ if (ipv4)
1248
+ return isUnsafeIpv4(ipv4);
1249
+ return false;
1250
+ }
1251
+ function ipv4FromIpv6(parts) {
1252
+ const mapped = parts.slice(0, 5).every((part) => part === 0) && parts[5] === 0xffff;
1253
+ const compatible = parts.slice(0, 6).every((part) => part === 0);
1254
+ if (!mapped && !compatible)
1255
+ return undefined;
1256
+ return [parts[6] >> 8, parts[6] & 0xff, parts[7] >> 8, parts[7] & 0xff];
1257
+ }
1258
+ /**
1259
+ * Reconstructs a `swap` shape (matching the inner autoSwap branch's return
1260
+ * value) from an upstream relay's `capabilities.autoSwap`. Used so a wallet
1261
+ * relay forwarding to an external feePayer URL can re-emit autoSwap metadata
1262
+ * locally without losing track of the upstream's swap.
1263
+ */
1264
+ function extractSwapFromCapabilities(autoSwap) {
1265
+ if (!autoSwap || typeof autoSwap !== 'object')
1266
+ return undefined;
1267
+ const a = autoSwap;
1268
+ if (!a.calls || !a.maxIn?.token || !a.maxIn.value || !a.minOut?.token || !a.minOut.value)
1269
+ return undefined;
1270
+ return {
1271
+ calls: a.calls,
1272
+ tokenIn: a.maxIn.token,
1273
+ tokenOut: a.minOut.token,
1274
+ amountOut: BigInt(a.minOut.value),
1275
+ maxAmountIn: BigInt(a.maxIn.value),
1276
+ };
1277
+ }
1278
+ //# sourceMappingURL=relay.js.map