tapimo 0.1.0 → 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.
- package/README.md +28 -15
- package/dist/ApiKey.d.ts +3 -1
- package/dist/ApiKey.d.ts.map +1 -1
- package/dist/ApiKey.js +1 -0
- package/dist/ApiKey.js.map +1 -1
- package/dist/ApiKeys.d.ts +71 -3
- package/dist/ApiKeys.d.ts.map +1 -1
- package/dist/ApiKeys.js +117 -1
- package/dist/ApiKeys.js.map +1 -1
- package/dist/App.d.ts +7139 -4197
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +133 -329
- package/dist/App.js.map +1 -1
- package/dist/Scope.d.ts +11 -4
- package/dist/Scope.d.ts.map +1 -1
- package/dist/Scope.js +12 -4
- package/dist/Scope.js.map +1 -1
- package/dist/Webhooks.d.ts +2 -2
- package/dist/Webhooks.d.ts.map +1 -1
- package/dist/Webhooks.js +2 -2
- package/dist/Webhooks.js.map +1 -1
- package/dist/admin/App.d.ts +126 -53
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.d.ts +104 -6
- package/dist/admin/apps/api-keys.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.js +67 -0
- package/dist/admin/apps/api-keys.js.map +1 -1
- package/dist/admin/apps/scopes.d.ts +4 -1
- package/dist/admin/apps/scopes.d.ts.map +1 -1
- package/dist/admin/apps/verified-tokens.d.ts +115 -115
- package/dist/admin/apps/verified-tokens.js +1 -1
- package/dist/admin/apps/verified-tokens.js.map +1 -1
- package/dist/admin/ui.generated.d.ts +1 -1
- package/dist/admin/ui.generated.js +1 -1
- package/dist/apps/data/App.d.ts +19176 -0
- package/dist/apps/data/App.d.ts.map +1 -0
- package/dist/apps/data/App.js +236 -0
- package/dist/apps/data/App.js.map +1 -0
- package/dist/apps/data/index.d.ts +3 -0
- package/dist/apps/data/index.d.ts.map +1 -0
- package/dist/apps/data/index.js +3 -0
- package/dist/apps/data/index.js.map +1 -0
- package/dist/apps/{activities.d.ts → data/routes/activities.d.ts} +32 -32
- package/dist/apps/{activities.d.ts.map → data/routes/activities.d.ts.map} +1 -1
- package/dist/apps/{activities.js → data/routes/activities.js} +13 -13
- package/dist/apps/data/routes/activities.js.map +1 -0
- package/dist/apps/{addresses.d.ts → data/routes/addresses.d.ts} +22 -22
- package/dist/apps/{addresses.d.ts.map → data/routes/addresses.d.ts.map} +1 -1
- package/dist/apps/{addresses.js → data/routes/addresses.js} +5 -5
- package/dist/apps/data/routes/addresses.js.map +1 -0
- package/dist/apps/{balances.d.ts → data/routes/balances.d.ts} +49 -49
- package/dist/apps/data/routes/balances.d.ts.map +1 -0
- package/dist/apps/{balances.js → data/routes/balances.js} +42 -11
- package/dist/apps/data/routes/balances.js.map +1 -0
- package/dist/apps/{blocks.d.ts → data/routes/blocks.d.ts} +47 -47
- package/dist/apps/data/routes/blocks.d.ts.map +1 -0
- package/dist/apps/{blocks.js → data/routes/blocks.js} +13 -13
- package/dist/apps/data/routes/blocks.js.map +1 -0
- package/dist/apps/{coingecko.d.ts → data/routes/coingecko.d.ts} +183 -183
- package/dist/apps/data/routes/coingecko.d.ts.map +1 -0
- package/dist/apps/{coingecko.js → data/routes/coingecko.js} +13 -13
- package/dist/apps/data/routes/coingecko.js.map +1 -0
- package/dist/apps/{exchanges.d.ts → data/routes/exchanges.d.ts} +460 -460
- package/dist/apps/data/routes/exchanges.d.ts.map +1 -0
- package/dist/apps/{exchanges.js → data/routes/exchanges.js} +19 -19
- package/dist/apps/data/routes/exchanges.js.map +1 -0
- package/dist/apps/{fee-amm.d.ts → data/routes/fee-amm.d.ts} +94 -94
- package/dist/apps/data/routes/fee-amm.d.ts.map +1 -0
- package/dist/apps/{fee-amm.js → data/routes/fee-amm.js} +17 -17
- package/dist/apps/data/routes/fee-amm.js.map +1 -0
- package/dist/apps/{indexer.d.ts → data/routes/indexer.d.ts} +4 -4
- package/dist/apps/data/routes/indexer.d.ts.map +1 -0
- package/dist/apps/{indexer.js → data/routes/indexer.js} +13 -9
- package/dist/apps/data/routes/indexer.js.map +1 -0
- package/dist/apps/{receipts.d.ts → data/routes/receipts.d.ts} +49 -49
- package/dist/apps/data/routes/receipts.d.ts.map +1 -0
- package/dist/apps/{receipts.js → data/routes/receipts.js} +12 -12
- package/dist/apps/data/routes/receipts.js.map +1 -0
- package/dist/apps/{rpc.d.ts → data/routes/rpc.d.ts} +4 -4
- package/dist/apps/data/routes/rpc.d.ts.map +1 -0
- package/dist/apps/{rpc.js → data/routes/rpc.js} +6 -6
- package/dist/apps/data/routes/rpc.js.map +1 -0
- package/dist/apps/{tokenlist.d.ts → data/routes/tokenlist.d.ts} +4 -4
- package/dist/apps/data/routes/tokenlist.d.ts.map +1 -0
- package/dist/apps/{tokenlist.js → data/routes/tokenlist.js} +8 -8
- package/dist/apps/data/routes/tokenlist.js.map +1 -0
- package/dist/apps/{tokens.d.ts → data/routes/tokens.d.ts} +468 -468
- package/dist/apps/data/routes/tokens.d.ts.map +1 -0
- package/dist/apps/{tokens.js → data/routes/tokens.js} +19 -19
- package/dist/apps/data/routes/tokens.js.map +1 -0
- package/dist/apps/{transactions.d.ts → data/routes/transactions.d.ts} +304 -304
- package/dist/apps/data/routes/transactions.d.ts.map +1 -0
- package/dist/apps/{transactions.js → data/routes/transactions.js} +13 -13
- package/dist/apps/data/routes/transactions.js.map +1 -0
- package/dist/apps/{transfers.d.ts → data/routes/transfers.d.ts} +6 -6
- package/dist/apps/data/routes/transfers.d.ts.map +1 -0
- package/dist/apps/{transfers.js → data/routes/transfers.js} +13 -13
- package/dist/apps/data/routes/transfers.js.map +1 -0
- package/dist/apps/{verified-tokens.d.ts → data/routes/verified-tokens.d.ts} +42 -42
- package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -0
- package/dist/apps/{verified-tokens.js → data/routes/verified-tokens.js} +9 -9
- package/dist/apps/data/routes/verified-tokens.js.map +1 -0
- package/dist/apps/{webhooks.d.ts → data/routes/webhooks.d.ts} +62 -62
- package/dist/apps/{webhooks.d.ts.map → data/routes/webhooks.d.ts.map} +1 -1
- package/dist/apps/{webhooks.js → data/routes/webhooks.js} +30 -26
- package/dist/apps/data/routes/webhooks.js.map +1 -0
- package/dist/apps/docs/App.d.ts +27 -0
- package/dist/apps/docs/App.d.ts.map +1 -0
- package/dist/apps/docs/App.js +122 -0
- package/dist/apps/docs/App.js.map +1 -0
- package/dist/{internal/OpenApi.fonts.d.ts → apps/docs/fonts.d.ts} +1 -1
- package/dist/apps/docs/fonts.d.ts.map +1 -0
- package/dist/{internal/OpenApi.fonts.js → apps/docs/fonts.js} +1 -1
- package/dist/apps/docs/fonts.js.map +1 -0
- package/dist/apps/docs/index.d.ts +2 -0
- package/dist/apps/docs/index.d.ts.map +1 -0
- package/dist/apps/docs/index.js +2 -0
- package/dist/apps/docs/index.js.map +1 -0
- package/dist/{internal/OpenApi.logo.d.ts → apps/docs/logo.d.ts} +1 -1
- package/dist/apps/docs/logo.d.ts.map +1 -0
- package/dist/{internal/OpenApi.logo.js → apps/docs/logo.js} +1 -1
- package/dist/apps/docs/logo.js.map +1 -0
- package/dist/apps/index.d.ts +4 -0
- package/dist/apps/index.d.ts.map +1 -0
- package/dist/apps/index.js +4 -0
- package/dist/apps/index.js.map +1 -0
- package/dist/apps/metadata.d.ts +7 -0
- package/dist/apps/metadata.d.ts.map +1 -0
- package/dist/apps/metadata.js +15 -0
- package/dist/apps/metadata.js.map +1 -0
- package/dist/apps/platform/App.d.ts +2985 -0
- package/dist/apps/platform/App.d.ts.map +1 -0
- package/dist/apps/platform/App.js +74 -0
- package/dist/apps/platform/App.js.map +1 -0
- package/dist/apps/platform/index.d.ts +2 -0
- package/dist/apps/platform/index.d.ts.map +1 -0
- package/dist/apps/platform/index.js +2 -0
- package/dist/apps/platform/index.js.map +1 -0
- package/dist/apps/platform/routes/api-keys.d.ts +681 -0
- package/dist/apps/platform/routes/api-keys.d.ts.map +1 -0
- package/dist/apps/platform/routes/api-keys.js +328 -0
- package/dist/apps/platform/routes/api-keys.js.map +1 -0
- package/dist/apps/platform/routes/invitations.d.ts +733 -0
- package/dist/apps/platform/routes/invitations.d.ts.map +1 -0
- package/dist/apps/platform/routes/invitations.js +413 -0
- package/dist/apps/platform/routes/invitations.js.map +1 -0
- package/dist/apps/platform/routes/me.d.ts +95 -0
- package/dist/apps/platform/routes/me.d.ts.map +1 -0
- package/dist/apps/platform/routes/me.js +81 -0
- package/dist/apps/platform/routes/me.js.map +1 -0
- package/dist/apps/platform/routes/members.d.ts +554 -0
- package/dist/apps/platform/routes/members.d.ts.map +1 -0
- package/dist/apps/platform/routes/members.js +251 -0
- package/dist/apps/platform/routes/members.js.map +1 -0
- package/dist/apps/platform/routes/orgs.d.ts +582 -0
- package/dist/apps/platform/routes/orgs.d.ts.map +1 -0
- package/dist/apps/platform/routes/orgs.js +265 -0
- package/dist/apps/platform/routes/orgs.js.map +1 -0
- package/dist/apps/platform/routes/projects.d.ts +740 -0
- package/dist/apps/platform/routes/projects.d.ts.map +1 -0
- package/dist/apps/platform/routes/projects.js +286 -0
- package/dist/apps/platform/routes/projects.js.map +1 -0
- package/dist/apps/relay/App.d.ts +41 -0
- package/dist/apps/relay/App.d.ts.map +1 -0
- package/dist/apps/relay/App.js +103 -0
- package/dist/apps/relay/App.js.map +1 -0
- package/dist/apps/relay/Sponsorships.d.ts +107 -0
- package/dist/apps/relay/Sponsorships.d.ts.map +1 -0
- package/dist/apps/relay/Sponsorships.js +193 -0
- package/dist/apps/relay/Sponsorships.js.map +1 -0
- package/dist/apps/relay/index.d.ts +3 -0
- package/dist/apps/relay/index.d.ts.map +1 -0
- package/dist/apps/relay/index.js +3 -0
- package/dist/apps/relay/index.js.map +1 -0
- package/dist/cli/Cli.d.ts +1 -1
- package/dist/cli/Cli.js +1 -1
- package/dist/cli/commands/admin.d.ts +1 -1
- package/dist/cli/commands/admin.js +2 -2
- package/dist/cli/commands/admin.js.map +1 -1
- package/dist/db/Db.d.ts +10 -0
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +128 -0
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +80 -0
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +99 -0
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/invitations.d.ts +84 -0
- package/dist/db/tables/invitations.d.ts.map +1 -0
- package/dist/db/tables/invitations.js +165 -0
- package/dist/db/tables/invitations.js.map +1 -0
- package/dist/db/tables/memberships.d.ts +108 -0
- package/dist/db/tables/memberships.d.ts.map +1 -0
- package/dist/db/tables/memberships.js +153 -0
- package/dist/db/tables/memberships.js.map +1 -0
- package/dist/db/tables/organizations.d.ts +44 -0
- package/dist/db/tables/organizations.d.ts.map +1 -1
- package/dist/db/tables/organizations.js +89 -0
- package/dist/db/tables/organizations.js.map +1 -1
- package/dist/db/tables/projects.d.ts +65 -0
- package/dist/db/tables/projects.d.ts.map +1 -0
- package/dist/db/tables/projects.js +75 -0
- package/dist/db/tables/projects.js.map +1 -0
- package/dist/db/tables/sponsoredTransactions.d.ts +120 -0
- package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -0
- package/dist/db/tables/sponsoredTransactions.js +161 -0
- package/dist/db/tables/sponsoredTransactions.js.map +1 -0
- package/dist/db/tables/users.d.ts +48 -0
- package/dist/db/tables/users.d.ts.map +1 -0
- package/dist/db/tables/users.js +65 -0
- package/dist/db/tables/users.js.map +1 -0
- package/dist/handlers/internal/executionError.d.ts.map +1 -1
- package/dist/handlers/internal/executionError.js +2 -0
- package/dist/handlers/internal/executionError.js.map +1 -1
- package/dist/handlers/internal/multisig.d.ts +2 -6
- package/dist/handlers/internal/multisig.d.ts.map +1 -1
- package/dist/handlers/internal/multisig.js +11 -36
- package/dist/handlers/internal/multisig.js.map +1 -1
- package/dist/handlers/internal/sponsorship.d.ts +32 -2
- package/dist/handlers/internal/sponsorship.d.ts.map +1 -1
- package/dist/handlers/internal/sponsorship.js +34 -9
- package/dist/handlers/internal/sponsorship.js.map +1 -1
- package/dist/handlers/relay.d.ts +6 -2
- package/dist/handlers/relay.d.ts.map +1 -1
- package/dist/handlers/relay.js +16 -2
- package/dist/handlers/relay.js.map +1 -1
- package/dist/internal/Auth.d.ts +208 -6
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +500 -21
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/Log.d.ts +7 -3
- package/dist/internal/Log.d.ts.map +1 -1
- package/dist/internal/Log.js +4 -0
- package/dist/internal/Log.js.map +1 -1
- package/dist/internal/OpenApi.d.ts +0 -19
- package/dist/internal/OpenApi.d.ts.map +1 -1
- package/dist/internal/OpenApi.js +0 -57
- package/dist/internal/OpenApi.js.map +1 -1
- package/dist/internal/Store.d.ts +7 -1
- package/dist/internal/Store.d.ts.map +1 -1
- package/dist/internal/Store.js +16 -2
- package/dist/internal/Store.js.map +1 -1
- package/dist/internal/Viem.d.ts +84 -0
- package/dist/internal/Viem.d.ts.map +1 -1
- package/dist/internal/Webhooks.d.ts +1 -1
- package/dist/internal/Webhooks.d.ts.map +1 -1
- package/package.json +31 -5
- package/src/ApiKey.ts +1 -0
- package/src/ApiKeys.test.ts +104 -1
- package/src/ApiKeys.ts +154 -1
- package/src/App.test.ts +11187 -179
- package/src/App.ts +200 -420
- package/src/Client.test-d.ts +306 -15
- package/src/Scope.ts +13 -4
- package/src/Webhooks.ts +2 -2
- package/src/admin/apps/api-keys.test.ts +51 -0
- package/src/admin/apps/api-keys.ts +84 -0
- package/src/admin/apps/scopes.test.ts +5 -1
- package/src/admin/apps/verified-tokens.ts +1 -1
- package/src/admin/ui.generated.ts +1 -1
- package/src/apps/composition.test.ts +66 -0
- package/src/apps/data/App.ts +295 -0
- package/src/apps/data/index.ts +2 -0
- package/src/apps/{activities.test.ts → data/routes/activities.test.ts} +3 -3
- package/src/apps/{activities.ts → data/routes/activities.ts} +15 -15
- package/src/apps/{addresses.ts → data/routes/addresses.ts} +6 -6
- package/src/apps/{balances.test.ts → data/routes/balances.test.ts} +2 -2
- package/src/apps/{balances.ts → data/routes/balances.ts} +46 -12
- package/src/apps/{blocks.test.ts → data/routes/blocks.test.ts} +3 -3
- package/src/apps/{blocks.ts → data/routes/blocks.ts} +14 -14
- package/src/apps/{coingecko.test.ts → data/routes/coingecko.test.ts} +2 -2
- package/src/apps/{coingecko.ts → data/routes/coingecko.ts} +14 -14
- package/src/apps/{exchanges.test.ts → data/routes/exchanges.test.ts} +1 -1
- package/src/apps/{exchanges.ts → data/routes/exchanges.ts} +20 -20
- package/src/apps/{fee-amm.test.ts → data/routes/fee-amm.test.ts} +3 -3
- package/src/apps/{fee-amm.ts → data/routes/fee-amm.ts} +18 -18
- package/src/apps/{indexer.test.ts → data/routes/indexer.test.ts} +26 -5
- package/src/apps/{indexer.ts → data/routes/indexer.ts} +13 -10
- package/src/apps/{receipts.test.ts → data/routes/receipts.test.ts} +3 -3
- package/src/apps/{receipts.ts → data/routes/receipts.ts} +13 -13
- package/src/apps/{rpc.test.ts → data/routes/rpc.test.ts} +44 -5
- package/src/apps/{rpc.ts → data/routes/rpc.ts} +7 -7
- package/src/apps/{tokenlist.test.ts → data/routes/tokenlist.test.ts} +3 -3
- package/src/apps/{tokenlist.ts → data/routes/tokenlist.ts} +9 -9
- package/src/apps/{tokens.test.ts → data/routes/tokens.test.ts} +2 -2
- package/src/apps/{tokens.ts → data/routes/tokens.ts} +20 -20
- package/src/apps/{transactions.test.ts → data/routes/transactions.test.ts} +3 -3
- package/src/apps/{transactions.ts → data/routes/transactions.ts} +15 -15
- package/src/apps/{transfers.test.ts → data/routes/transfers.test.ts} +2 -2
- package/src/apps/{transfers.ts → data/routes/transfers.ts} +15 -15
- package/src/apps/{verified-tokens.test.ts → data/routes/verified-tokens.test.ts} +3 -3
- package/src/apps/{verified-tokens.ts → data/routes/verified-tokens.ts} +10 -10
- package/src/apps/{webhooks.test.ts → data/routes/webhooks.test.ts} +6 -6
- package/src/apps/{webhooks.ts → data/routes/webhooks.ts} +39 -35
- package/src/apps/docs/App.ts +136 -0
- package/src/apps/docs/index.ts +1 -0
- package/src/apps/index.ts +3 -0
- package/src/apps/metadata.ts +23 -0
- package/src/apps/platform/App.ts +130 -0
- package/src/apps/platform/index.ts +1 -0
- package/src/apps/platform/routes/api-keys.test.ts +330 -0
- package/src/apps/platform/routes/api-keys.ts +427 -0
- package/src/apps/platform/routes/invitations.test.ts +361 -0
- package/src/apps/platform/routes/invitations.ts +519 -0
- package/src/apps/platform/routes/me.test.ts +121 -0
- package/src/apps/platform/routes/me.ts +106 -0
- package/src/apps/platform/routes/members.test.ts +195 -0
- package/src/apps/platform/routes/members.ts +313 -0
- package/src/apps/platform/routes/orgs.test.ts +313 -0
- package/src/apps/platform/routes/orgs.ts +343 -0
- package/src/apps/platform/routes/projects.test.ts +181 -0
- package/src/apps/platform/routes/projects.ts +367 -0
- package/src/apps/relay/App.test.ts +484 -0
- package/src/apps/relay/App.ts +128 -0
- package/src/apps/relay/Sponsorships.test.ts +375 -0
- package/src/apps/relay/Sponsorships.ts +305 -0
- package/src/apps/relay/index.ts +2 -0
- package/src/cli/Cli.ts +1 -1
- package/src/cli/commands/admin.ts +2 -2
- package/src/db/Db.ts +159 -0
- package/src/db/Schema.ts +137 -0
- package/src/db/tables/invitations.ts +198 -0
- package/src/db/tables/memberships.ts +208 -0
- package/src/db/tables/organizations.test.ts +37 -1
- package/src/db/tables/organizations.ts +105 -0
- package/src/db/tables/projects.ts +107 -0
- package/src/db/tables/sponsoredTransactions.test.ts +218 -0
- package/src/db/tables/sponsoredTransactions.ts +236 -0
- package/src/db/tables/users.ts +85 -0
- package/src/handlers/internal/executionError.ts +2 -0
- package/src/handlers/internal/multisig.test.ts +84 -10
- package/src/handlers/internal/multisig.ts +15 -50
- package/src/handlers/internal/sponsorship.ts +74 -15
- package/src/handlers/{internal/relay.localnet.test.ts → relay.test.ts} +2 -2
- package/src/handlers/relay.ts +23 -5
- package/src/internal/Auth.test.ts +316 -0
- package/src/internal/Auth.ts +691 -25
- package/src/internal/Cursor.test.ts +3 -2
- package/src/internal/Log.test.ts +1 -1
- package/src/internal/Log.ts +11 -3
- package/src/internal/MetricSink.ts +1 -1
- package/src/internal/OpenApi.ts +0 -59
- package/src/internal/Store.test.ts +52 -0
- package/src/internal/Store.ts +24 -5
- package/src/internal/Tidx.local.test.ts +1 -1
- package/src/internal/VerifiedTokens.test.ts +1 -1
- package/src/internal/WebhookDestination.test.ts +1 -1
- package/src/internal/Webhooks.test.ts +4 -4
- package/src/internal/Webhooks.ts +1 -1
- package/dist/apps/activities.js.map +0 -1
- package/dist/apps/addresses.js.map +0 -1
- package/dist/apps/balances.d.ts.map +0 -1
- package/dist/apps/balances.js.map +0 -1
- package/dist/apps/blocks.d.ts.map +0 -1
- package/dist/apps/blocks.js.map +0 -1
- package/dist/apps/coingecko.d.ts.map +0 -1
- package/dist/apps/coingecko.js.map +0 -1
- package/dist/apps/exchanges.d.ts.map +0 -1
- package/dist/apps/exchanges.js.map +0 -1
- package/dist/apps/fee-amm.d.ts.map +0 -1
- package/dist/apps/fee-amm.js.map +0 -1
- package/dist/apps/indexer.d.ts.map +0 -1
- package/dist/apps/indexer.js.map +0 -1
- package/dist/apps/receipts.d.ts.map +0 -1
- package/dist/apps/receipts.js.map +0 -1
- package/dist/apps/rpc.d.ts.map +0 -1
- package/dist/apps/rpc.js.map +0 -1
- package/dist/apps/tokenlist.d.ts.map +0 -1
- package/dist/apps/tokenlist.js.map +0 -1
- package/dist/apps/tokens.d.ts.map +0 -1
- package/dist/apps/tokens.js.map +0 -1
- package/dist/apps/transactions.d.ts.map +0 -1
- package/dist/apps/transactions.js.map +0 -1
- package/dist/apps/transfers.d.ts.map +0 -1
- package/dist/apps/transfers.js.map +0 -1
- package/dist/apps/verified-tokens.d.ts.map +0 -1
- package/dist/apps/verified-tokens.js.map +0 -1
- package/dist/apps/webhooks.js.map +0 -1
- package/dist/internal/OpenApi.fonts.d.ts.map +0 -1
- package/dist/internal/OpenApi.fonts.js.map +0 -1
- package/dist/internal/OpenApi.logo.d.ts.map +0 -1
- package/dist/internal/OpenApi.logo.js.map +0 -1
- package/src/handlers/internal/multisig.localnet.test.ts +0 -146
- /package/src/{internal/OpenApi.fonts.ts → apps/docs/fonts.ts} +0 -0
- /package/src/{internal/OpenApi.logo.ts → apps/docs/logo.ts} +0 -0
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
import { Hash, Secp256k1 } from 'ox'
|
|
2
|
+
import { createClient, http } from 'viem'
|
|
3
|
+
import { prepareTransactionRequest, sendRawTransactionSync, signTransaction } from 'viem/actions'
|
|
4
|
+
import { Account as TempoAccount, Actions, Transaction, withRelay } from 'viem/tempo'
|
|
5
|
+
|
|
6
|
+
import * as TestApp from '../../../test/App.js'
|
|
7
|
+
import * as Relay from '../../../test/Relay.js'
|
|
8
|
+
import * as Runtime from '../../../test/runtime.js'
|
|
9
|
+
import * as Tempo from '../../../test/Tempo.js'
|
|
10
|
+
import * as SponsoredTransactions from '../../db/tables/sponsoredTransactions.js'
|
|
11
|
+
import * as RequestListener from '../../handlers/internal/requestListener.js'
|
|
12
|
+
import * as Sponsorships from './Sponsorships.js'
|
|
13
|
+
|
|
14
|
+
const runtime = Runtime.get()
|
|
15
|
+
|
|
16
|
+
const feePayerAccount = Tempo.accounts[10]!
|
|
17
|
+
const senderAccount = Tempo.accounts[9]!
|
|
18
|
+
|
|
19
|
+
/** Keys covering the sponsorship policy matrix; all hold the base relay scope. */
|
|
20
|
+
const keys = [
|
|
21
|
+
{
|
|
22
|
+
id: 'key_sponsor',
|
|
23
|
+
orgId: 'org_1',
|
|
24
|
+
projectId: 'prj_1',
|
|
25
|
+
environment: 'sandbox',
|
|
26
|
+
scopes: ['rpc-relay:read', 'rpc-relay:sponsor'],
|
|
27
|
+
token: 'secret_sponsor',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: 'key_sponsor_prod',
|
|
31
|
+
orgId: 'org_1',
|
|
32
|
+
projectId: 'prj_1',
|
|
33
|
+
environment: 'production',
|
|
34
|
+
scopes: ['rpc-relay:read', 'rpc-relay:sponsor'],
|
|
35
|
+
token: 'secret_sponsor_prod',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: 'key_wildcard',
|
|
39
|
+
orgId: 'org_2',
|
|
40
|
+
projectId: 'prj_2',
|
|
41
|
+
environment: 'sandbox',
|
|
42
|
+
scopes: ['*'],
|
|
43
|
+
token: 'secret_wildcard',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: 'key_unscoped',
|
|
47
|
+
orgId: 'org_1',
|
|
48
|
+
projectId: 'prj_1',
|
|
49
|
+
environment: 'sandbox',
|
|
50
|
+
scopes: ['rpc-relay:read'],
|
|
51
|
+
token: 'secret_unscoped',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: 'key_unattributed',
|
|
55
|
+
orgId: 'org_1',
|
|
56
|
+
environment: 'sandbox',
|
|
57
|
+
scopes: ['rpc-relay:read', 'rpc-relay:sponsor'],
|
|
58
|
+
token: 'secret_unattributed',
|
|
59
|
+
},
|
|
60
|
+
] as const satisfies readonly TestApp.kvStore.Key[]
|
|
61
|
+
|
|
62
|
+
describe.skipIf(runtime.mode !== 'localnet')('behavior: sponsorship', () => {
|
|
63
|
+
test('behavior: scoped keys sponsor, recorded with attribution', async () => {
|
|
64
|
+
const db = TestApp.database()
|
|
65
|
+
const app = TestApp.create({
|
|
66
|
+
auth: { keys },
|
|
67
|
+
db,
|
|
68
|
+
relay: { feePayer: { account: feePayerAccount } },
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
const body = await relayRequest(app, {
|
|
72
|
+
method: 'eth_signRawTransaction',
|
|
73
|
+
params: [await signSponsorable()],
|
|
74
|
+
token: 'secret_sponsor',
|
|
75
|
+
})
|
|
76
|
+
expect(body.error).toBeUndefined()
|
|
77
|
+
// The relay returned the envelope counter-signed by the fee payer.
|
|
78
|
+
const signed = Transaction.deserialize(body.result as `0x76${string}`)
|
|
79
|
+
expect(signed.feePayerSignature).toBeDefined()
|
|
80
|
+
|
|
81
|
+
const [row] = await SponsoredTransactions.listPending(db)
|
|
82
|
+
expect(row?.transactionHash).toBe(Hash.keccak256(body.result!))
|
|
83
|
+
expect(row?.signPayload).toMatch(/^0x/)
|
|
84
|
+
expect(row?.chainId).toBe(runtime.chainId)
|
|
85
|
+
// The stored envelope is exactly the counter-signed transaction returned.
|
|
86
|
+
expect(row?.transaction).toBe(body.result)
|
|
87
|
+
expect({ ...row, chainId: '<chainId>', createdAt: '<timestamp>', id: '<id>', signPayload: '<hash>', transaction: '<hex>', transactionHash: '<hash>' }) // prettier-ignore
|
|
88
|
+
.toMatchInlineSnapshot(`
|
|
89
|
+
{
|
|
90
|
+
"apiKeyId": "key_sponsor",
|
|
91
|
+
"billable": false,
|
|
92
|
+
"chainId": "<chainId>",
|
|
93
|
+
"createdAt": "<timestamp>",
|
|
94
|
+
"environment": "sandbox",
|
|
95
|
+
"feeAmount": null,
|
|
96
|
+
"feeToken": "0x20c0000000000000000000000000000000000000",
|
|
97
|
+
"finalizedAt": null,
|
|
98
|
+
"id": "<id>",
|
|
99
|
+
"orgId": "org_1",
|
|
100
|
+
"projectId": "prj_1",
|
|
101
|
+
"signPayload": "<hash>",
|
|
102
|
+
"status": "pending",
|
|
103
|
+
"transaction": "<hex>",
|
|
104
|
+
"transactionHash": "<hash>",
|
|
105
|
+
}
|
|
106
|
+
`)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
test('behavior: production keys record billable rows', async () => {
|
|
110
|
+
const db = TestApp.database()
|
|
111
|
+
const app = TestApp.create({
|
|
112
|
+
auth: { keys },
|
|
113
|
+
db,
|
|
114
|
+
relay: { feePayer: { account: feePayerAccount } },
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
const body = await relayRequest(app, {
|
|
118
|
+
method: 'eth_signRawTransaction',
|
|
119
|
+
params: [await signSponsorable()],
|
|
120
|
+
token: 'secret_sponsor_prod',
|
|
121
|
+
})
|
|
122
|
+
expect(body.error).toBeUndefined()
|
|
123
|
+
|
|
124
|
+
const [row] = await SponsoredTransactions.listPending(db)
|
|
125
|
+
expect(row?.billable).toBe(true)
|
|
126
|
+
expect(row?.environment).toMatchInlineSnapshot(`"production"`)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
test('behavior: the wildcard scope grants sponsorship', async () => {
|
|
130
|
+
const db = TestApp.database()
|
|
131
|
+
const app = TestApp.create({
|
|
132
|
+
auth: { keys },
|
|
133
|
+
db,
|
|
134
|
+
relay: { feePayer: { account: feePayerAccount } },
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
const body = await relayRequest(app, {
|
|
138
|
+
method: 'eth_signRawTransaction',
|
|
139
|
+
params: [await signSponsorable()],
|
|
140
|
+
token: 'secret_wildcard',
|
|
141
|
+
})
|
|
142
|
+
expect(body.error).toBeUndefined()
|
|
143
|
+
|
|
144
|
+
const [row] = await SponsoredTransactions.listPending(db)
|
|
145
|
+
expect(row?.orgId).toMatchInlineSnapshot(`"org_2"`)
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
test('behavior: keys without the sponsor scope are refused', async () => {
|
|
149
|
+
const db = TestApp.database()
|
|
150
|
+
const app = TestApp.create({
|
|
151
|
+
auth: { keys },
|
|
152
|
+
db,
|
|
153
|
+
relay: { feePayer: { account: feePayerAccount } },
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
const body = await relayRequest(app, {
|
|
157
|
+
method: 'eth_signRawTransaction',
|
|
158
|
+
params: [await signSponsorable()],
|
|
159
|
+
token: 'secret_unscoped',
|
|
160
|
+
})
|
|
161
|
+
// "Sponsorship rejected." surfaces as invalid params (-32602).
|
|
162
|
+
expect(body.result).toBeUndefined()
|
|
163
|
+
expect(body.error?.code).toBe(-32602)
|
|
164
|
+
expect(await SponsoredTransactions.listPending(db)).toHaveLength(0)
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
test('behavior: keys without project attribution are refused', async () => {
|
|
168
|
+
const db = TestApp.database()
|
|
169
|
+
const app = TestApp.create({
|
|
170
|
+
auth: { keys },
|
|
171
|
+
db,
|
|
172
|
+
relay: { feePayer: { account: feePayerAccount } },
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
const body = await relayRequest(app, {
|
|
176
|
+
method: 'eth_signRawTransaction',
|
|
177
|
+
params: [await signSponsorable()],
|
|
178
|
+
token: 'secret_unattributed',
|
|
179
|
+
})
|
|
180
|
+
expect(body.result).toBeUndefined()
|
|
181
|
+
expect(body.error?.code).toBe(-32602)
|
|
182
|
+
expect(await SponsoredTransactions.listPending(db)).toHaveLength(0)
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
test('behavior: super admin principals are refused', async () => {
|
|
186
|
+
const db = TestApp.database()
|
|
187
|
+
const app = TestApp.create({
|
|
188
|
+
auth: { keys, superAdmin: { secret: 'super-admin-secret' } },
|
|
189
|
+
db,
|
|
190
|
+
relay: { feePayer: { account: feePayerAccount } },
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
const body = await relayRequest(app, {
|
|
194
|
+
method: 'eth_signRawTransaction',
|
|
195
|
+
params: [await signSponsorable()],
|
|
196
|
+
token: 'super-admin-secret',
|
|
197
|
+
})
|
|
198
|
+
expect(body.result).toBeUndefined()
|
|
199
|
+
expect(body.error?.code).toBe(-32602)
|
|
200
|
+
expect(await SponsoredTransactions.listPending(db)).toHaveLength(0)
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
test('behavior: sponsorship stays closed without a fee payer', async () => {
|
|
204
|
+
const db = TestApp.database()
|
|
205
|
+
const app = TestApp.create({ auth: { keys }, db })
|
|
206
|
+
|
|
207
|
+
const body = await relayRequest(app, {
|
|
208
|
+
method: 'eth_signRawTransaction',
|
|
209
|
+
params: [await signSponsorable()],
|
|
210
|
+
token: 'secret_sponsor',
|
|
211
|
+
})
|
|
212
|
+
expect(body.error?.code).toBe(-32601)
|
|
213
|
+
expect(await SponsoredTransactions.listPending(db)).toHaveLength(0)
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
test('behavior: mainnet sponsorship is refused until billing ships', async () => {
|
|
217
|
+
const db = TestApp.database()
|
|
218
|
+
const app = TestApp.create({
|
|
219
|
+
auth: { keys },
|
|
220
|
+
db,
|
|
221
|
+
relay: { feePayer: { account: feePayerAccount } },
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
const body = await relayRequest(app, {
|
|
225
|
+
method: 'eth_signRawTransaction',
|
|
226
|
+
params: [
|
|
227
|
+
await senderAccount.signTransaction({
|
|
228
|
+
chainId: 4217,
|
|
229
|
+
feePayer: true,
|
|
230
|
+
gas: 100_000n,
|
|
231
|
+
maxFeePerGas: 1_000_000n,
|
|
232
|
+
maxPriorityFeePerGas: 0n,
|
|
233
|
+
nonce: 0,
|
|
234
|
+
to: Tempo.accounts[8]!.address,
|
|
235
|
+
value: 0n,
|
|
236
|
+
}),
|
|
237
|
+
],
|
|
238
|
+
token: 'secret_sponsor',
|
|
239
|
+
})
|
|
240
|
+
expect(body.result).toBeUndefined()
|
|
241
|
+
expect(body.error?.code).toBe(-32602)
|
|
242
|
+
expect(await SponsoredTransactions.listPending(db)).toHaveLength(0)
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
test('behavior: an app-provided fee payer URL cannot trigger the managed fee payer', async () => {
|
|
246
|
+
const db = TestApp.database()
|
|
247
|
+
const app = TestApp.create({
|
|
248
|
+
auth: { keys },
|
|
249
|
+
db,
|
|
250
|
+
relay: { feePayer: { account: feePayerAccount } },
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
const body = await relayRequest(app, {
|
|
254
|
+
method: 'eth_fillTransaction',
|
|
255
|
+
params: [
|
|
256
|
+
{
|
|
257
|
+
calls: [{ to: Tempo.accounts[8]!.address, value: '0x0' }],
|
|
258
|
+
chainId: `0x${runtime.chainId.toString(16)}`,
|
|
259
|
+
feePayer: 'https://example.com',
|
|
260
|
+
from: senderAccount.address,
|
|
261
|
+
gas: '0x186a0',
|
|
262
|
+
maxFeePerGas: '0xf4240',
|
|
263
|
+
maxPriorityFeePerGas: '0x0',
|
|
264
|
+
nonce: '0x0',
|
|
265
|
+
},
|
|
266
|
+
],
|
|
267
|
+
token: 'secret_unscoped',
|
|
268
|
+
})
|
|
269
|
+
const result = body.result as unknown as
|
|
270
|
+
| { capabilities?: { sponsored?: boolean }; tx?: { feePayerSignature?: string | null } }
|
|
271
|
+
| undefined
|
|
272
|
+
expect(result?.tx?.feePayerSignature ?? null).toBeNull()
|
|
273
|
+
expect(result?.capabilities?.sponsored).toBe(false)
|
|
274
|
+
expect(await SponsoredTransactions.listPending(db)).toHaveLength(0)
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
test('behavior: sponsorship is refused for chains this deployment does not serve', async () => {
|
|
278
|
+
const db = TestApp.database()
|
|
279
|
+
const app = TestApp.create({
|
|
280
|
+
auth: { keys },
|
|
281
|
+
db,
|
|
282
|
+
relay: { feePayer: { account: feePayerAccount } },
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
const body = await relayRequest(app, {
|
|
286
|
+
method: 'eth_signRawTransaction',
|
|
287
|
+
params: [
|
|
288
|
+
await senderAccount.signTransaction({
|
|
289
|
+
chainId: 999_999,
|
|
290
|
+
feePayer: true,
|
|
291
|
+
gas: 100_000n,
|
|
292
|
+
maxFeePerGas: 1_000_000n,
|
|
293
|
+
maxPriorityFeePerGas: 0n,
|
|
294
|
+
nonce: 0,
|
|
295
|
+
to: Tempo.accounts[8]!.address,
|
|
296
|
+
value: 0n,
|
|
297
|
+
}),
|
|
298
|
+
],
|
|
299
|
+
token: 'secret_sponsor',
|
|
300
|
+
})
|
|
301
|
+
expect(body.result).toBeUndefined()
|
|
302
|
+
expect(body.error?.code).toBe(-32602)
|
|
303
|
+
expect(await SponsoredTransactions.listPending(db)).toHaveLength(0)
|
|
304
|
+
})
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
describe.skipIf(runtime.mode !== 'localnet')('behavior: sponsorship (viem)', () => {
|
|
308
|
+
const alphaUsd = '0x20c0000000000000000000000000000000000001'
|
|
309
|
+
const feePayer = TempoAccount.fromSecp256k1(Secp256k1.randomPrivateKey())
|
|
310
|
+
const sender = TempoAccount.fromSecp256k1(Secp256k1.randomPrivateKey())
|
|
311
|
+
const relayClient = Relay.getClient()
|
|
312
|
+
|
|
313
|
+
let db: ReturnType<typeof TestApp.database>
|
|
314
|
+
let server: Relay.Server
|
|
315
|
+
|
|
316
|
+
function getClient(policy?: 'sign-and-broadcast' | 'sign-only') {
|
|
317
|
+
return createClient({
|
|
318
|
+
account: sender,
|
|
319
|
+
chain: Tempo.chain,
|
|
320
|
+
transport: withRelay(
|
|
321
|
+
Relay.http(),
|
|
322
|
+
http(`${server.url}/rpc/relay`, {
|
|
323
|
+
fetchOptions: { headers: { 'tempo-api-key': 'secret_sponsor' } },
|
|
324
|
+
}),
|
|
325
|
+
policy ? { policy } : {},
|
|
326
|
+
),
|
|
327
|
+
})
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
beforeAll(async () => {
|
|
331
|
+
await Promise.all(
|
|
332
|
+
[feePayer, sender].map((account) =>
|
|
333
|
+
Actions.faucet.fundSync(relayClient, { account, timeout: 60_000 }),
|
|
334
|
+
),
|
|
335
|
+
)
|
|
336
|
+
db = TestApp.database()
|
|
337
|
+
const app = TestApp.create({
|
|
338
|
+
auth: { keys },
|
|
339
|
+
db,
|
|
340
|
+
// Explicit fee token: exercises the consumer override of the pathUSD default.
|
|
341
|
+
relay: { feePayer: { account: feePayer, feeToken: alphaUsd } },
|
|
342
|
+
})
|
|
343
|
+
server = await Relay.createServer(RequestListener.fromFetchHandler((req) => app.fetch(req)))
|
|
344
|
+
}, 120_000)
|
|
345
|
+
|
|
346
|
+
afterAll(async () => {
|
|
347
|
+
await server.closeAsync()
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
test('behavior: transferSync sponsors end-to-end, recording a fill intent', async () => {
|
|
351
|
+
const { receipt } = await Actions.token.transferSync(getClient(), {
|
|
352
|
+
amount: 1n,
|
|
353
|
+
feePayer: true,
|
|
354
|
+
to: Tempo.accounts[8]!.address,
|
|
355
|
+
token: alphaUsd,
|
|
356
|
+
})
|
|
357
|
+
|
|
358
|
+
// Sponsored on-chain: the fee payer paid, the sender's transfer landed.
|
|
359
|
+
expect(receipt.status).toBe('success')
|
|
360
|
+
expect(receipt.feePayer?.toLowerCase()).toBe(feePayer.address.toLowerCase())
|
|
361
|
+
|
|
362
|
+
const intent = (await SponsoredTransactions.listPending(db)).find(
|
|
363
|
+
(row) => row.transactionHash === null,
|
|
364
|
+
)
|
|
365
|
+
expect(intent?.apiKeyId).toBe('key_sponsor')
|
|
366
|
+
expect(intent?.orgId).toBe('org_1')
|
|
367
|
+
expect(intent?.billable).toBe(false)
|
|
368
|
+
expect(intent?.feeToken?.toLowerCase()).toBe(alphaUsd)
|
|
369
|
+
|
|
370
|
+
const finalizer = Sponsorships.createFinalizer({
|
|
371
|
+
db,
|
|
372
|
+
defaultChainId: runtime.chainId,
|
|
373
|
+
feePayer: feePayer.address,
|
|
374
|
+
rpc: TestApp.rpc,
|
|
375
|
+
tidx: { baseUrl: runtime.tidxUrl! },
|
|
376
|
+
})
|
|
377
|
+
let finalized: Awaited<ReturnType<typeof SponsoredTransactions.get>>
|
|
378
|
+
for (let attempt = 0; attempt < 30; attempt++) {
|
|
379
|
+
await finalizer.tick()
|
|
380
|
+
finalized = await SponsoredTransactions.get(db, intent!.id)
|
|
381
|
+
if (finalized?.status === 'finalized') break
|
|
382
|
+
await new Promise((resolve) => setTimeout(resolve, 1_000))
|
|
383
|
+
}
|
|
384
|
+
expect(finalized?.status).toBe('finalized')
|
|
385
|
+
expect(finalized?.transactionHash).toBe(receipt.transactionHash)
|
|
386
|
+
expect(finalized?.feeAmount).toBe((receipt.gasUsed * receipt.effectiveGasPrice).toString())
|
|
387
|
+
}, 60_000)
|
|
388
|
+
|
|
389
|
+
test('behavior: sign-only sends record and finalize from the real receipt', async () => {
|
|
390
|
+
const request = await prepareTransactionRequest(relayClient, {
|
|
391
|
+
account: sender,
|
|
392
|
+
calls: [
|
|
393
|
+
Actions.token.transfer.call(relayClient, {
|
|
394
|
+
amount: 1n,
|
|
395
|
+
to: Tempo.accounts[8]!.address,
|
|
396
|
+
token: alphaUsd,
|
|
397
|
+
}),
|
|
398
|
+
],
|
|
399
|
+
feePayer: true,
|
|
400
|
+
})
|
|
401
|
+
const signed = await signTransaction(relayClient, request)
|
|
402
|
+
|
|
403
|
+
const receipt = await sendRawTransactionSync(getClient(), {
|
|
404
|
+
serializedTransaction: signed,
|
|
405
|
+
})
|
|
406
|
+
|
|
407
|
+
const row = (await SponsoredTransactions.listPending(db)).find(
|
|
408
|
+
(row) => row.transactionHash === receipt.transactionHash,
|
|
409
|
+
)
|
|
410
|
+
expect(row?.apiKeyId).toBe('key_sponsor')
|
|
411
|
+
expect(row?.orgId).toBe('org_1')
|
|
412
|
+
expect(row?.transaction.startsWith('0x76')).toBe(true)
|
|
413
|
+
|
|
414
|
+
// The finalizer resolves the row against the real localnet receipt.
|
|
415
|
+
const result = await Sponsorships.finalize(db, { getClient: () => relayClient })
|
|
416
|
+
expect(result.failed).toBe(0)
|
|
417
|
+
expect(result.finalized).toBeGreaterThanOrEqual(1)
|
|
418
|
+
const finalized = await SponsoredTransactions.get(db, row!.id)
|
|
419
|
+
expect(finalized?.status).toBe('finalized')
|
|
420
|
+
expect(finalized?.feeAmount).toBe((receipt.gasUsed * receipt.effectiveGasPrice).toString())
|
|
421
|
+
}, 30_000)
|
|
422
|
+
|
|
423
|
+
test('behavior: sign-and-broadcast sends record through the relay', async () => {
|
|
424
|
+
const request = await prepareTransactionRequest(relayClient, {
|
|
425
|
+
account: sender,
|
|
426
|
+
calls: [
|
|
427
|
+
Actions.token.transfer.call(relayClient, {
|
|
428
|
+
amount: 2n,
|
|
429
|
+
to: Tempo.accounts[8]!.address,
|
|
430
|
+
token: alphaUsd,
|
|
431
|
+
}),
|
|
432
|
+
],
|
|
433
|
+
feePayer: true,
|
|
434
|
+
})
|
|
435
|
+
const signed = await signTransaction(relayClient, request)
|
|
436
|
+
|
|
437
|
+
const receipt = await sendRawTransactionSync(getClient('sign-and-broadcast'), {
|
|
438
|
+
serializedTransaction: signed,
|
|
439
|
+
})
|
|
440
|
+
|
|
441
|
+
const rows = await SponsoredTransactions.listPending(db)
|
|
442
|
+
expect(rows.find((row) => row.transactionHash === receipt.transactionHash)).toBeDefined()
|
|
443
|
+
}, 30_000)
|
|
444
|
+
})
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Signs a sponsorship-requesting Tempo transaction (fee-payer slot present but
|
|
448
|
+
* empty; the serializer strips `feeToken`, the sponsor picks it) entirely
|
|
449
|
+
* offline: `eth_signRawTransaction` never touches the chain, so the suite
|
|
450
|
+
* runs without a node.
|
|
451
|
+
*/
|
|
452
|
+
async function signSponsorable() {
|
|
453
|
+
return await senderAccount.signTransaction({
|
|
454
|
+
chainId: runtime.chainId,
|
|
455
|
+
feePayer: true,
|
|
456
|
+
gas: 100_000n,
|
|
457
|
+
maxFeePerGas: 1_000_000n,
|
|
458
|
+
maxPriorityFeePerGas: 0n,
|
|
459
|
+
nonce: 0,
|
|
460
|
+
to: Tempo.accounts[8]!.address,
|
|
461
|
+
value: 0n,
|
|
462
|
+
})
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/** Posts one JSON-RPC request to the composed app's relay mount. */
|
|
466
|
+
async function relayRequest(
|
|
467
|
+
app: ReturnType<typeof TestApp.create>,
|
|
468
|
+
options: { method: string; params: readonly unknown[]; token?: string | undefined },
|
|
469
|
+
) {
|
|
470
|
+
const response = await app.fetch(
|
|
471
|
+
new Request('http://tempo-api.test/rpc/relay', {
|
|
472
|
+
body: JSON.stringify({ id: 1, jsonrpc: '2.0', method: options.method, params: options.params }), // prettier-ignore
|
|
473
|
+
headers: {
|
|
474
|
+
'content-type': 'application/json',
|
|
475
|
+
...(options.token ? { 'tempo-api-key': options.token } : {}),
|
|
476
|
+
},
|
|
477
|
+
method: 'POST',
|
|
478
|
+
}),
|
|
479
|
+
)
|
|
480
|
+
return (await response.json()) as {
|
|
481
|
+
error?: { code: number; message: string } | undefined
|
|
482
|
+
result?: `0x${string}` | undefined
|
|
483
|
+
}
|
|
484
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { Hono } from 'hono'
|
|
2
|
+
import { createMiddleware } from 'hono/factory'
|
|
3
|
+
import { Addresses } from 'viem/tempo'
|
|
4
|
+
|
|
5
|
+
import type * as App from '../../App.js'
|
|
6
|
+
import * as Db from '../../db/Db.js'
|
|
7
|
+
import * as SponsoredTransactions from '../../db/tables/sponsoredTransactions.js'
|
|
8
|
+
import * as Handler from '../../Handler.js'
|
|
9
|
+
import * as Auth from '../../internal/Auth.js'
|
|
10
|
+
import * as OpenApi from '../../internal/OpenApi.js'
|
|
11
|
+
import * as Path from '../../internal/Path.js'
|
|
12
|
+
import * as VerifiedTokens from '../../internal/VerifiedTokens.js'
|
|
13
|
+
import * as Viem from '../../internal/Viem.js'
|
|
14
|
+
import * as Scope from '../../Scope.js'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The relay route group: the wallet relay at `/rpc/relay`. Mount
|
|
18
|
+
* after `data()`, whose published chain clients and supported-chain set the
|
|
19
|
+
* middleware reads from request context.
|
|
20
|
+
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* App.create(options).route('/', data()).route('/', relay())
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export function relay(options: relay.Options = {}) {
|
|
26
|
+
const handler = middleware(options)
|
|
27
|
+
return new Hono<App.Environment>()
|
|
28
|
+
.use(
|
|
29
|
+
'/rpc/relay',
|
|
30
|
+
Auth.policy({ apiKey: { scopes: ['rpc-relay:read'] }, mpp: false, public: false }),
|
|
31
|
+
OpenApi.describeRoute({ hide: true, summary: 'Relay RPC' }),
|
|
32
|
+
)
|
|
33
|
+
.use(
|
|
34
|
+
'/rpc/relay/*',
|
|
35
|
+
Auth.policy({ apiKey: { scopes: ['rpc-relay:read'] }, mpp: false, public: false }),
|
|
36
|
+
OpenApi.describeRoute({ hide: true, summary: 'Relay RPC' }),
|
|
37
|
+
)
|
|
38
|
+
.all('/rpc/relay', handler)
|
|
39
|
+
.all('/rpc/relay/:chainId', handler)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export declare namespace relay {
|
|
43
|
+
/** Options for the relay route group. */
|
|
44
|
+
type Options = {
|
|
45
|
+
feePayer?:
|
|
46
|
+
| Pick<
|
|
47
|
+
NonNullable<Handler.relay.Options['feePayer']>,
|
|
48
|
+
'account' | 'feeToken' | 'name' | 'url'
|
|
49
|
+
>
|
|
50
|
+
| undefined
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// The wallet relay middleware: proxies wallet JSON-RPC (fills, sponsored
|
|
55
|
+
// sends) with wallet-aware enrichment. Built per request from context rather
|
|
56
|
+
// than closed over group locals; relay traffic is low-QPS and the viem
|
|
57
|
+
// clients are memoized upstream, so per-request handler construction is cheap.
|
|
58
|
+
function middleware(options: relay.Options = {}) {
|
|
59
|
+
return createMiddleware<App.Environment>(async (c) => {
|
|
60
|
+
const verifiedTokens = c.get('verifiedTokens')
|
|
61
|
+
|
|
62
|
+
const feePayer = (() => {
|
|
63
|
+
if (!options.feePayer) return undefined
|
|
64
|
+
const principal = Auth.getPrincipal(c)
|
|
65
|
+
const attribution =
|
|
66
|
+
principal?.type === 'api_key' && principal.projectId !== undefined
|
|
67
|
+
? {
|
|
68
|
+
apiKeyId: principal.id,
|
|
69
|
+
environment: principal.environment,
|
|
70
|
+
orgId: principal.orgId,
|
|
71
|
+
projectId: principal.projectId,
|
|
72
|
+
}
|
|
73
|
+
: undefined
|
|
74
|
+
const sponsorable =
|
|
75
|
+
attribution !== undefined &&
|
|
76
|
+
principal?.type === 'api_key' &&
|
|
77
|
+
(principal.apiKey.scopes.includes('rpc-relay:sponsor') ||
|
|
78
|
+
principal.apiKey.scopes.includes(Scope.wildcard))
|
|
79
|
+
return {
|
|
80
|
+
...options.feePayer,
|
|
81
|
+
feeToken: options.feePayer.feeToken ?? Addresses.pathUsd,
|
|
82
|
+
validate: ({ chainId }) => {
|
|
83
|
+
// Allowlist the chains this deployment serves rather than denylisting
|
|
84
|
+
// only mainnet: the relay resolves chainId from the request body, so
|
|
85
|
+
// the query-string chain guard never sees it. Mainnet stays closed
|
|
86
|
+
// until billing ships; nothing meters real fee-payer spend yet.
|
|
87
|
+
const id = Number(chainId)
|
|
88
|
+
return sponsorable && c.get('supportedChainIds').has(id) && !Viem.isMainnet(id)
|
|
89
|
+
},
|
|
90
|
+
onSponsored: async (event) => {
|
|
91
|
+
if (!attribution) return
|
|
92
|
+
await SponsoredTransactions.upsert(Db.get(c.get('db')), {
|
|
93
|
+
apiKeyId: attribution.apiKeyId,
|
|
94
|
+
billable: attribution.environment === 'production',
|
|
95
|
+
chainId: event.chainId,
|
|
96
|
+
environment: attribution.environment,
|
|
97
|
+
orgId: attribution.orgId,
|
|
98
|
+
projectId: attribution.projectId,
|
|
99
|
+
signPayload: event.signPayload,
|
|
100
|
+
transaction: event.transaction,
|
|
101
|
+
...(event.feeToken ? { feeToken: event.feeToken } : {}),
|
|
102
|
+
...(event.transactionHash ? { transactionHash: event.transactionHash } : {}),
|
|
103
|
+
})
|
|
104
|
+
},
|
|
105
|
+
} satisfies Handler.relay.Options['feePayer']
|
|
106
|
+
})()
|
|
107
|
+
|
|
108
|
+
// Dispatching a standalone Hono via `fetch(c.req.raw)` bypasses Hono's
|
|
109
|
+
// mount-path stripping, so its internal routes register at the request's
|
|
110
|
+
// full external path (`basePath` + `/rpc/relay`).
|
|
111
|
+
const handler = Handler.relay({
|
|
112
|
+
cache: c.get('store'),
|
|
113
|
+
features: 'all',
|
|
114
|
+
getClient: (chainId) => c.get('getClient')(chainId as Viem.ChainId | undefined),
|
|
115
|
+
path: Path.join(c.get('basePath'), 'rpc/relay'),
|
|
116
|
+
...(feePayer ? { feePayer } : {}),
|
|
117
|
+
...(verifiedTokens
|
|
118
|
+
? {
|
|
119
|
+
resolveTokens: async (chainId: number) => {
|
|
120
|
+
const snapshot = await VerifiedTokens.read(Db.get(c.get('dbCached')), chainId)
|
|
121
|
+
return snapshot?.list.map((token) => token.address) ?? []
|
|
122
|
+
},
|
|
123
|
+
}
|
|
124
|
+
: {}),
|
|
125
|
+
})
|
|
126
|
+
return handler.fetch(c.req.raw)
|
|
127
|
+
})
|
|
128
|
+
}
|