tapimo 0.10.0 → 0.10.2
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/dist/ApiKey.d.ts +2 -1
- package/dist/ApiKey.d.ts.map +1 -1
- package/dist/ApiKeys.d.ts +4 -2
- package/dist/ApiKeys.d.ts.map +1 -1
- package/dist/ApiKeys.js +26 -1
- package/dist/ApiKeys.js.map +1 -1
- package/dist/App.d.ts +6007 -5003
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +1 -0
- package/dist/App.js.map +1 -1
- package/dist/Webhooks.d.ts +1 -1
- package/dist/Webhooks.d.ts.map +1 -1
- package/dist/Webhooks.js +6 -6
- package/dist/Webhooks.js.map +1 -1
- package/dist/admin/App.d.ts +91 -84
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.d.ts +22 -13
- package/dist/admin/apps/api-keys.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.js +6 -0
- package/dist/admin/apps/api-keys.js.map +1 -1
- package/dist/admin/apps/organizations.d.ts +4 -2
- package/dist/admin/apps/organizations.d.ts.map +1 -1
- package/dist/admin/ui.generated.d.ts +1 -1
- package/dist/admin/ui.generated.js +69 -69
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/analytics/Analytics.d.ts.map +1 -1
- package/dist/analytics/Analytics.js +232 -0
- package/dist/analytics/Analytics.js.map +1 -1
- package/dist/analytics/tables/requestEvents.d.ts +4 -0
- package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
- package/dist/analytics/tables/requestEvents.js +2 -0
- package/dist/analytics/tables/requestEvents.js.map +1 -1
- package/dist/apps/Zones.d.ts +4266 -82
- package/dist/apps/Zones.d.ts.map +1 -1
- package/dist/apps/Zones.js +27 -8
- package/dist/apps/Zones.js.map +1 -1
- package/dist/apps/data/App.d.ts +3467 -2696
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/App.js +2 -0
- package/dist/apps/data/App.js.map +1 -1
- package/dist/apps/data/routes/activities.d.ts +2999 -100
- package/dist/apps/data/routes/activities.d.ts.map +1 -1
- package/dist/apps/data/routes/activities.js +340 -2
- package/dist/apps/data/routes/activities.js.map +1 -1
- package/dist/apps/data/routes/addresses.d.ts +514 -0
- package/dist/apps/data/routes/addresses.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.d.ts +26 -26
- package/dist/apps/data/routes/balances.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.js +7 -3
- package/dist/apps/data/routes/balances.js.map +1 -1
- package/dist/apps/data/routes/blocks.d.ts +4 -4
- package/dist/apps/data/routes/earn.d.ts +75 -75
- package/dist/apps/data/routes/earn.d.ts.map +1 -1
- package/dist/apps/data/routes/earn.js +65 -36
- package/dist/apps/data/routes/earn.js.map +1 -1
- package/dist/apps/data/routes/exchanges.d.ts +124 -120
- package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
- package/dist/apps/data/routes/exchanges.js +115 -77
- package/dist/apps/data/routes/exchanges.js.map +1 -1
- package/dist/apps/data/routes/fee-amm.d.ts +24 -24
- package/dist/apps/data/routes/indexer.d.ts +2 -1
- package/dist/apps/data/routes/indexer.d.ts.map +1 -1
- package/dist/apps/data/routes/indexer.js +3 -2
- package/dist/apps/data/routes/indexer.js.map +1 -1
- package/dist/apps/data/routes/receipts.d.ts +4 -3
- package/dist/apps/data/routes/receipts.d.ts.map +1 -1
- package/dist/apps/data/routes/receipts.js +15 -14
- package/dist/apps/data/routes/receipts.js.map +1 -1
- package/dist/apps/data/routes/rpc.d.ts.map +1 -1
- package/dist/apps/data/routes/rpc.js +5 -1
- package/dist/apps/data/routes/rpc.js.map +1 -1
- package/dist/apps/data/routes/tokenlist.d.ts +12 -12
- package/dist/apps/data/routes/tokens.d.ts +1 -1
- package/dist/apps/data/routes/tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/tokens.js +4 -3
- package/dist/apps/data/routes/tokens.js.map +1 -1
- package/dist/apps/data/routes/transfers.d.ts.map +1 -1
- package/dist/apps/data/routes/transfers.js +25 -7
- package/dist/apps/data/routes/transfers.js.map +1 -1
- package/dist/apps/data/routes/verified-tokens.d.ts +37 -37
- package/dist/apps/data/routes/webhooks.d.ts +71 -1
- package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/data/routes/webhooks.js +177 -23
- package/dist/apps/data/routes/webhooks.js.map +1 -1
- package/dist/apps/data/routes/zones.js +1 -1
- package/dist/apps/data/routes/zones.js.map +1 -1
- package/dist/apps/funding/App.d.ts +322 -89
- package/dist/apps/funding/App.d.ts.map +1 -1
- package/dist/apps/funding/routes/deposit-addresses.d.ts +375 -45
- package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -1
- package/dist/apps/funding/routes/deposit-addresses.js +208 -12
- package/dist/apps/funding/routes/deposit-addresses.js.map +1 -1
- package/dist/apps/funding/routes/transfers.d.ts +32 -32
- package/dist/apps/funding/routes/transfers.js +1 -1
- package/dist/apps/funding/routes/transfers.js.map +1 -1
- package/dist/apps/management/App.d.ts +418 -418
- package/dist/apps/management/routes/api-keys.d.ts +50 -50
- package/dist/apps/management/routes/billing.d.ts +72 -72
- package/dist/apps/management/routes/billing.js +1 -1
- package/dist/apps/management/routes/billing.js.map +1 -1
- package/dist/apps/management/routes/faucet.d.ts +8 -8
- package/dist/apps/management/routes/faucet.js +2 -2
- package/dist/apps/management/routes/faucet.js.map +1 -1
- package/dist/apps/management/routes/invite-links.d.ts +42 -42
- package/dist/apps/management/routes/me.d.ts +6 -6
- package/dist/apps/management/routes/members.d.ts +14 -14
- package/dist/apps/management/routes/projects.d.ts +40 -40
- package/dist/apps/management/routes/usage.d.ts +8 -8
- package/dist/apps/management/routes/webhooks.d.ts +49 -49
- package/dist/apps/mcp.d.ts.map +1 -1
- package/dist/apps/mcp.js +4 -1
- package/dist/apps/mcp.js.map +1 -1
- package/dist/db/Db.d.ts +2 -0
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +90 -0
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +19 -0
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +24 -0
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/fundingDepositAddresses.d.ts +20 -0
- package/dist/db/tables/fundingDepositAddresses.d.ts.map +1 -1
- package/dist/db/tables/fundingDepositAddresses.js +17 -0
- package/dist/db/tables/fundingDepositAddresses.js.map +1 -1
- package/dist/db/tables/fundingDeposits.d.ts +41 -0
- package/dist/db/tables/fundingDeposits.d.ts.map +1 -1
- package/dist/db/tables/fundingDeposits.js +69 -0
- package/dist/db/tables/fundingDeposits.js.map +1 -1
- package/dist/db/tables/webhookQueueEvents.d.ts +122 -0
- package/dist/db/tables/webhookQueueEvents.d.ts.map +1 -0
- package/dist/db/tables/webhookQueueEvents.js +160 -0
- package/dist/db/tables/webhookQueueEvents.js.map +1 -0
- package/dist/db/tables/webhookSubscriptions.d.ts +2 -0
- package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
- package/dist/db/tables/webhookSubscriptions.js +10 -0
- package/dist/db/tables/webhookSubscriptions.js.map +1 -1
- package/dist/internal/Auth.d.ts +3 -26
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +6 -37
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/Credential.d.ts +27 -0
- package/dist/internal/Credential.d.ts.map +1 -0
- package/dist/internal/Credential.js +34 -0
- package/dist/internal/Credential.js.map +1 -0
- package/dist/internal/EarnRates.js +11 -7
- package/dist/internal/EarnRates.js.map +1 -1
- package/dist/internal/Log.d.ts +8 -0
- 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/MetricSink.d.ts +51 -0
- package/dist/internal/MetricSink.d.ts.map +1 -1
- package/dist/internal/MetricSink.js +54 -2
- package/dist/internal/MetricSink.js.map +1 -1
- package/dist/internal/RateLimit.d.ts +4 -6
- package/dist/internal/RateLimit.d.ts.map +1 -1
- package/dist/internal/RateLimit.js +15 -14
- package/dist/internal/RateLimit.js.map +1 -1
- package/dist/internal/Store.d.ts +2 -1
- package/dist/internal/Store.d.ts.map +1 -1
- package/dist/internal/Store.js +57 -18
- package/dist/internal/Store.js.map +1 -1
- package/dist/internal/Tidx.d.ts +4 -3
- package/dist/internal/Tidx.d.ts.map +1 -1
- package/dist/internal/Tidx.js +2 -2
- package/dist/internal/Tidx.js.map +1 -1
- package/dist/internal/Value.d.ts +2 -0
- package/dist/internal/Value.d.ts.map +1 -1
- package/dist/internal/Value.js +25 -0
- package/dist/internal/Value.js.map +1 -1
- package/dist/internal/Viem.d.ts +4 -3
- package/dist/internal/Viem.d.ts.map +1 -1
- package/dist/internal/Viem.js +3 -2
- package/dist/internal/Viem.js.map +1 -1
- package/dist/internal/WebhookTransfer.d.ts +20 -0
- package/dist/internal/WebhookTransfer.d.ts.map +1 -1
- package/dist/internal/WebhookTransfer.js +7 -4
- package/dist/internal/WebhookTransfer.js.map +1 -1
- package/dist/internal/Webhooks.d.ts +141 -0
- package/dist/internal/Webhooks.d.ts.map +1 -1
- package/dist/internal/Webhooks.js +139 -10
- package/dist/internal/Webhooks.js.map +1 -1
- package/dist/internal/funding/providers/relay.d.ts +3 -4
- package/dist/internal/funding/providers/relay.d.ts.map +1 -1
- package/dist/internal/funding/providers/relay.js +59 -8
- package/dist/internal/funding/providers/relay.js.map +1 -1
- package/dist/internal/index.d.ts +1 -0
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +1 -0
- package/dist/internal/index.js.map +1 -1
- package/package.json +3 -3
- package/src/ApiKeys.test.ts +20 -0
- package/src/ApiKeys.ts +28 -1
- package/src/App.test.ts +40 -8
- package/src/App.ts +1 -0
- package/src/Client.test-d.ts +34 -1
- package/src/Webhooks.ts +20 -18
- package/src/admin/apps/api-keys.test.ts +9 -0
- package/src/admin/apps/api-keys.ts +15 -0
- package/src/admin/ui/src/lib/query-client.ts +3 -2
- package/src/admin/ui/src/routes/api-keys.tsx +31 -9
- package/src/admin/ui.generated.ts +69 -69
- package/src/analytics/Analytics.test.ts +42 -0
- package/src/analytics/Analytics.ts +232 -0
- package/src/analytics/tables/requestEvents.ts +6 -0
- package/src/apps/Zones.test.ts +20 -3
- package/src/apps/Zones.ts +28 -8
- package/src/apps/composition.test.ts +2 -0
- package/src/apps/data/App.test.ts +2 -2
- package/src/apps/data/App.ts +2 -0
- package/src/apps/data/routes/activities.bench.ts +41 -0
- package/src/apps/data/routes/activities.test.ts +103 -2
- package/src/apps/data/routes/activities.ts +401 -2
- package/src/apps/data/routes/balances.bench.ts +21 -0
- package/src/apps/data/routes/balances.ts +7 -3
- package/src/apps/data/routes/blocks.bench.ts +34 -0
- package/src/apps/data/routes/coingecko.bench.ts +53 -0
- package/src/apps/data/routes/earn.bench.ts +71 -0
- package/src/apps/data/routes/earn.ts +66 -36
- package/src/apps/data/routes/exchanges.bench.ts +85 -0
- package/src/apps/data/routes/exchanges.test.ts +8 -3
- package/src/apps/data/routes/exchanges.ts +134 -82
- package/src/apps/data/routes/fee-amm.bench.ts +13 -0
- package/src/apps/data/routes/indexer.bench.ts +8 -0
- package/src/apps/data/routes/indexer.test.ts +2 -1
- package/src/apps/data/routes/indexer.ts +3 -2
- package/src/apps/data/routes/receipts.bench.ts +39 -0
- package/src/apps/data/routes/receipts.ts +16 -14
- package/src/apps/data/routes/rpc.bench.ts +14 -0
- package/src/apps/data/routes/rpc.test.ts +27 -0
- package/src/apps/data/routes/rpc.ts +5 -1
- package/src/apps/data/routes/tokenlist.bench.ts +3 -0
- package/src/apps/data/routes/tokens.bench.ts +76 -0
- package/src/apps/data/routes/tokens.ts +3 -2
- package/src/apps/data/routes/transactions.bench.ts +48 -0
- package/src/apps/data/routes/transfers.bench.ts +28 -0
- package/src/apps/data/routes/transfers.ts +36 -10
- package/src/apps/data/routes/valuation.bench.ts +14 -0
- package/src/apps/data/routes/verified-tokens.bench.ts +8 -0
- package/src/apps/data/routes/webhooks.bench.ts +26 -0
- package/src/apps/data/routes/webhooks.scan.test.ts +326 -0
- package/src/apps/data/routes/webhooks.test.ts +53 -6
- package/src/apps/data/routes/webhooks.ts +252 -24
- package/src/apps/data/routes/zones.bench.ts +32 -0
- package/src/apps/data/routes/zones.test.ts +1 -1
- package/src/apps/data/routes/zones.ts +1 -1
- package/src/apps/funding/routes/chains.test.ts +1 -1
- package/src/apps/funding/routes/deposit-addresses.test.ts +217 -3
- package/src/apps/funding/routes/deposit-addresses.ts +255 -19
- package/src/apps/funding/routes/providers.test.ts +1 -1
- package/src/apps/funding/routes/quotes.test.ts +1 -1
- package/src/apps/funding/routes/transfers.test.ts +20 -8
- package/src/apps/funding/routes/transfers.ts +1 -1
- package/src/apps/management/routes/billing.ts +1 -1
- package/src/apps/management/routes/faucet.ts +2 -2
- package/src/apps/management/routes/me.test.ts +31 -0
- package/src/apps/mcp.ts +4 -1
- package/src/db/Db.ts +117 -0
- package/src/db/Schema.ts +32 -0
- package/src/db/tables/fundingDepositAddresses.test.ts +28 -0
- package/src/db/tables/fundingDepositAddresses.ts +39 -0
- package/src/db/tables/fundingDeposits.test.ts +82 -0
- package/src/db/tables/fundingDeposits.ts +130 -0
- package/src/db/tables/webhookQueueEvents.ts +285 -0
- package/src/db/tables/webhookSubscriptions.ts +15 -0
- package/src/internal/Auth.test.ts +61 -98
- package/src/internal/Auth.ts +9 -63
- package/src/internal/Credential.test.ts +51 -0
- package/src/internal/Credential.ts +58 -0
- package/src/internal/EarnRates.ts +13 -7
- package/src/internal/EarnVaults.test.ts +2 -2
- package/src/internal/Log.test.ts +3 -1
- package/src/internal/Log.ts +14 -0
- package/src/internal/MetricSink.test.ts +108 -2
- package/src/internal/MetricSink.ts +112 -2
- package/src/internal/RateLimit.test.ts +23 -18
- package/src/internal/RateLimit.ts +23 -15
- package/src/internal/Store.test.ts +99 -13
- package/src/internal/Store.ts +52 -17
- package/src/internal/Tidx.test.ts +4 -4
- package/src/internal/Tidx.ts +5 -4
- package/src/internal/Value.test.ts +25 -0
- package/src/internal/Value.ts +29 -0
- package/src/internal/Viem.test.ts +4 -0
- package/src/internal/Viem.ts +6 -4
- package/src/internal/WebhookTransfer.test.ts +89 -0
- package/src/internal/WebhookTransfer.ts +11 -7
- package/src/internal/Webhooks.test.ts +488 -0
- package/src/internal/Webhooks.ts +291 -10
- package/src/internal/funding/Provider.test.ts +1 -1
- package/src/internal/funding/providers/index.test.ts +73 -3
- package/src/internal/funding/providers/relay.test.ts +1 -5
- package/src/internal/funding/providers/relay.ts +56 -12
- package/src/internal/index.test.ts +1 -0
- package/src/internal/index.ts +1 -0
|
@@ -27,11 +27,8 @@ const db = TestApp.database()
|
|
|
27
27
|
/** Providers exercised against executable calls on a forked source chain. */
|
|
28
28
|
const executableProviders = [
|
|
29
29
|
{
|
|
30
|
-
create: (
|
|
31
|
-
Relay.relay({
|
|
32
|
-
...(process.env.RELAY_API_KEY ? { apiKey: process.env.RELAY_API_KEY } : {}),
|
|
33
|
-
userAddress: sender,
|
|
34
|
-
}),
|
|
30
|
+
create: (_sender: string) =>
|
|
31
|
+
Relay.relay(process.env.RELAY_API_KEY ? { apiKey: process.env.RELAY_API_KEY } : {}),
|
|
35
32
|
id: 'relay',
|
|
36
33
|
},
|
|
37
34
|
] as const
|
|
@@ -282,7 +279,7 @@ describe('POST /funding/transfers', () => {
|
|
|
282
279
|
providerResponse(),
|
|
283
280
|
) {
|
|
284
281
|
return FundingProvider.from({
|
|
285
|
-
...Relay.relay(
|
|
282
|
+
...Relay.relay(),
|
|
286
283
|
prepareTransfer,
|
|
287
284
|
})
|
|
288
285
|
}
|
|
@@ -844,6 +841,7 @@ describe('funding transfers OpenAPI', () => {
|
|
|
844
841
|
deposit: summarize(spec.paths[depositPath]!.get!),
|
|
845
842
|
depositAddress: summarize(spec.paths[depositAddressPath]!.get!),
|
|
846
843
|
depositAddressCreate: summarize(spec.paths['/v1/funding/deposit-addresses']!.post!),
|
|
844
|
+
depositAddressList: summarize(spec.paths['/v1/funding/deposit-addresses']!.get!),
|
|
847
845
|
depositAddressReconcile: summarize(spec.paths[reconcilePath]!.post!),
|
|
848
846
|
deposits: summarize(spec.paths['/v1/funding/deposits']!.get!),
|
|
849
847
|
detail: summarize(spec.paths[detailPath]!.get!),
|
|
@@ -892,12 +890,16 @@ describe('funding transfers OpenAPI', () => {
|
|
|
892
890
|
"description": "Creates or returns a reusable deposit address for funding a Tempo account.",
|
|
893
891
|
"summary": "Create deposit address",
|
|
894
892
|
},
|
|
893
|
+
"depositAddressList": {
|
|
894
|
+
"description": "Lists reusable funding deposit addresses, newest first.",
|
|
895
|
+
"summary": "List deposit addresses",
|
|
896
|
+
},
|
|
895
897
|
"depositAddressReconcile": {
|
|
896
898
|
"description": "Queues an immediate provider reconciliation for one reusable funding deposit address.",
|
|
897
899
|
"summary": "Reconcile deposit address",
|
|
898
900
|
},
|
|
899
901
|
"deposits": {
|
|
900
|
-
"description": "Lists deposits
|
|
902
|
+
"description": "Lists visible deposits, optionally filtered, newest first.",
|
|
901
903
|
"summary": "List deposits",
|
|
902
904
|
},
|
|
903
905
|
"detail": {
|
|
@@ -929,6 +931,7 @@ describe('funding transfers OpenAPI', () => {
|
|
|
929
931
|
"Get quotes",
|
|
930
932
|
"Get chains",
|
|
931
933
|
"Get providers",
|
|
934
|
+
"List deposit addresses",
|
|
932
935
|
"Create deposit address",
|
|
933
936
|
"List transfers",
|
|
934
937
|
"Create transfer",
|
|
@@ -1090,7 +1093,11 @@ describe('funding transfers OpenAPI', () => {
|
|
|
1090
1093
|
const operation = spec.paths['/v1/funding/transfers']!.get
|
|
1091
1094
|
const fields = Object.keys(operation.responses['200']!.content['application/json']!.example.data[0]!) // prettier-ignore
|
|
1092
1095
|
expect(fields).toEqual([...fields].sort())
|
|
1093
|
-
for (const route of [
|
|
1096
|
+
for (const route of [
|
|
1097
|
+
operation,
|
|
1098
|
+
spec.paths['/v1/funding/deposit-addresses']!.get,
|
|
1099
|
+
spec.paths['/v1/funding/deposits']!.get,
|
|
1100
|
+
])
|
|
1094
1101
|
for (const parameter of route.parameters ?? [])
|
|
1095
1102
|
expect(parameter.example ?? parameter.schema?.examples, parameter.name).toBeDefined()
|
|
1096
1103
|
})
|
|
@@ -1129,6 +1136,9 @@ describe('funding transfers OpenAPI', () => {
|
|
|
1129
1136
|
depositAddressCreate: spec.paths['/v1/funding/deposit-addresses']?.post?.operationId,
|
|
1130
1137
|
depositAddressCreateScopes:
|
|
1131
1138
|
spec.paths['/v1/funding/deposit-addresses']?.post?.['x-required-scopes'],
|
|
1139
|
+
depositAddressList: spec.paths['/v1/funding/deposit-addresses']?.get.operationId,
|
|
1140
|
+
depositAddressListScopes:
|
|
1141
|
+
spec.paths['/v1/funding/deposit-addresses']?.get['x-required-scopes'],
|
|
1132
1142
|
depositAddressReconcile: reconcilePath && spec.paths[reconcilePath]?.post?.operationId,
|
|
1133
1143
|
depositAddressReconcileScopes:
|
|
1134
1144
|
reconcilePath && spec.paths[reconcilePath]?.post?.['x-required-scopes'],
|
|
@@ -1153,6 +1163,8 @@ describe('funding transfers OpenAPI', () => {
|
|
|
1153
1163
|
depositAddress: 'getFundingDepositAddress',
|
|
1154
1164
|
depositAddressCreate: 'createFundingDepositAddress',
|
|
1155
1165
|
depositAddressCreateScopes: ['funding:write'],
|
|
1166
|
+
depositAddressList: 'listFundingDepositAddresses',
|
|
1167
|
+
depositAddressListScopes: ['funding:read'],
|
|
1156
1168
|
depositAddressReconcile: 'reconcileFundingDepositAddress',
|
|
1157
1169
|
depositAddressReconcileScopes: ['funding:write'],
|
|
1158
1170
|
depositAddressScopes: ['funding:read'],
|
|
@@ -110,7 +110,7 @@ export namespace schema {
|
|
|
110
110
|
.check(
|
|
111
111
|
z.regex(/^eip155:\d+$/),
|
|
112
112
|
z.describe('Configured Zone CAIP-2 id.'),
|
|
113
|
-
z.meta({ examples: ['eip155:
|
|
113
|
+
z.meta({ examples: ['eip155:1424310001'] }),
|
|
114
114
|
),
|
|
115
115
|
destinationToken: FundingTransfer.schema.CreateRequest.shape.destinationToken,
|
|
116
116
|
mode: destinationCreateFields.mode,
|
|
@@ -699,7 +699,7 @@ export function billing() {
|
|
|
699
699
|
Auth.policy({
|
|
700
700
|
apiKey: false,
|
|
701
701
|
mpp: false,
|
|
702
|
-
public: { rateLimit: {
|
|
702
|
+
public: { rateLimit: { limit: 600, period: 'minute' } },
|
|
703
703
|
session: false,
|
|
704
704
|
}),
|
|
705
705
|
OpenApi.describeRoute({ hide: true, summary: 'Stripe webhook' }),
|
|
@@ -10,8 +10,8 @@ import * as Value from '../../../internal/Value.js'
|
|
|
10
10
|
import * as Viem from '../../../internal/Viem.js'
|
|
11
11
|
import type { Environment } from '../App.js'
|
|
12
12
|
|
|
13
|
-
const addressRateLimit = {
|
|
14
|
-
const userRateLimit = {
|
|
13
|
+
const addressRateLimit = { limit: 1, period: 'minute' } satisfies RateLimit.Limit
|
|
14
|
+
const userRateLimit = { limit: 3, period: 'minute' } satisfies RateLimit.Limit
|
|
15
15
|
|
|
16
16
|
/** OpenAPI schemas owned by the sandbox faucet. */
|
|
17
17
|
export namespace schema {
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { serve } from '@hono/node-server'
|
|
2
2
|
import * as Accounts from 'accounts/server'
|
|
3
3
|
import { exportJWK, generateKeyPair } from 'jose'
|
|
4
|
+
import { P256 } from 'ox'
|
|
4
5
|
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'
|
|
6
|
+
import { Account } from 'viem/tempo'
|
|
7
|
+
import { tempoTestnet } from 'viem/tempo/chains'
|
|
5
8
|
|
|
6
9
|
import * as TestApp from '../../../../test/App.js'
|
|
7
10
|
import * as Me from './me.js'
|
|
@@ -18,6 +21,34 @@ function createApp() {
|
|
|
18
21
|
})
|
|
19
22
|
}
|
|
20
23
|
|
|
24
|
+
describe('SIWE verification', () => {
|
|
25
|
+
test('surfaces configured RPC failures as dependency errors', async () => {
|
|
26
|
+
const account = Account.fromHeadlessWebAuthn(P256.randomPrivateKey(), {
|
|
27
|
+
origin,
|
|
28
|
+
rpId: 'localhost',
|
|
29
|
+
})
|
|
30
|
+
let chainId_seen: number | undefined
|
|
31
|
+
const app = TestApp.create({
|
|
32
|
+
rpc: ({ chainId }) => {
|
|
33
|
+
chainId_seen = chainId
|
|
34
|
+
return { url: 'http://127.0.0.1:1' }
|
|
35
|
+
},
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const { verifyResponse } = await TestApp.signIn(app, account, {
|
|
39
|
+
chainId: tempoTestnet.id,
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
expect(verifyResponse.status).toBe(502)
|
|
43
|
+
expect(chainId_seen).toMatchInlineSnapshot(`42431`)
|
|
44
|
+
expect(await verifyResponse.json()).toMatchInlineSnapshot(`
|
|
45
|
+
{
|
|
46
|
+
"error": "signature verification unavailable",
|
|
47
|
+
}
|
|
48
|
+
`)
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
|
|
21
52
|
describe('GET /me', () => {
|
|
22
53
|
test('returns the signed-in user', async () => {
|
|
23
54
|
const app = createApp()
|
package/src/apps/mcp.ts
CHANGED
|
@@ -138,7 +138,10 @@ export function mcp(options: mcp.Options) {
|
|
|
138
138
|
const app = new Hono<App.Environment>()
|
|
139
139
|
.post(
|
|
140
140
|
'/mcp',
|
|
141
|
-
Auth.policy({
|
|
141
|
+
Auth.policy({
|
|
142
|
+
apiKey: { scopes: [] },
|
|
143
|
+
public: { rateLimit: { limit: 100, period: 'minute' } },
|
|
144
|
+
}),
|
|
142
145
|
OpenApi.describeRoute({
|
|
143
146
|
description:
|
|
144
147
|
"A stateless streamable-HTTP MCP server: send JSON-RPC 2.0 messages as `POST` with `Accept: application/json, text/event-stream`. Tools mirror the API's data domain, plus `docs_*` documentation search. Anonymous requests allow 100 requests per minute; send `Authorization: Bearer <api key>` for protected tools.",
|
package/src/db/Db.ts
CHANGED
|
@@ -36,6 +36,7 @@ import type * as Users from './tables/users.js'
|
|
|
36
36
|
import type * as VerifiedTokenRequests from './tables/verifiedTokenRequests.js'
|
|
37
37
|
import type * as VerifiedTokens from './tables/verifiedTokens.js'
|
|
38
38
|
import type * as WebhookDeliveries from './tables/webhookDeliveries.js'
|
|
39
|
+
import type * as WebhookQueueEvents from './tables/webhookQueueEvents.js'
|
|
39
40
|
import type * as WebhookSubscriptions from './tables/webhookSubscriptions.js'
|
|
40
41
|
|
|
41
42
|
/**
|
|
@@ -82,6 +83,7 @@ export type Database = {
|
|
|
82
83
|
verified_token_requests: VerifiedTokenRequests.Table
|
|
83
84
|
verified_tokens: VerifiedTokens.Table
|
|
84
85
|
webhook_deliveries: WebhookDeliveries.Table
|
|
86
|
+
webhook_queue_events: WebhookQueueEvents.Table
|
|
85
87
|
webhook_subscriptions: WebhookSubscriptions.Table
|
|
86
88
|
}
|
|
87
89
|
|
|
@@ -1281,6 +1283,121 @@ export const migrations: readonly Migration[] = [
|
|
|
1281
1283
|
.on('fundingDepositAddresses')
|
|
1282
1284
|
.columns(['orgId', 'environment', 'address', 'projectId']),
|
|
1283
1285
|
},
|
|
1286
|
+
{
|
|
1287
|
+
name: '0115_webhook_queue_events',
|
|
1288
|
+
up: (db) =>
|
|
1289
|
+
db.schema
|
|
1290
|
+
.createTable('webhookQueueEvents')
|
|
1291
|
+
.ifNotExists()
|
|
1292
|
+
.addColumn('subscriptionId', 'text', (c) =>
|
|
1293
|
+
c.notNull().references('webhookSubscriptions.id').onDelete('cascade'),
|
|
1294
|
+
)
|
|
1295
|
+
.addColumn('eventId', 'text', (c) => c.notNull())
|
|
1296
|
+
.addColumn('envelope', 'jsonb', (c) => c.notNull())
|
|
1297
|
+
.addColumn('createdAt', 'text', (c) => c.notNull())
|
|
1298
|
+
.addColumn('expiresAt', 'text', (c) => c.notNull())
|
|
1299
|
+
.addPrimaryKeyConstraint('webhook_queue_events_pkey', ['subscriptionId', 'eventId']),
|
|
1300
|
+
},
|
|
1301
|
+
{
|
|
1302
|
+
name: '0116_webhook_queue_events_expires_index',
|
|
1303
|
+
up: (db) =>
|
|
1304
|
+
db.schema
|
|
1305
|
+
.createIndex('webhook_queue_events_expires_idx')
|
|
1306
|
+
.ifNotExists()
|
|
1307
|
+
.on('webhookQueueEvents')
|
|
1308
|
+
.column('expiresAt'),
|
|
1309
|
+
},
|
|
1310
|
+
{
|
|
1311
|
+
name: '0117_webhook_queue_events_ledger_columns',
|
|
1312
|
+
up: (db) =>
|
|
1313
|
+
raw(
|
|
1314
|
+
db,
|
|
1315
|
+
sql`ALTER TABLE webhook_queue_events
|
|
1316
|
+
ADD COLUMN IF NOT EXISTS status text NOT NULL DEFAULT 'pending',
|
|
1317
|
+
ADD COLUMN IF NOT EXISTS attempt_count integer NOT NULL DEFAULT 0,
|
|
1318
|
+
ADD COLUMN IF NOT EXISTS attempting_at text,
|
|
1319
|
+
ADD COLUMN IF NOT EXISTS next_attempt_at text`,
|
|
1320
|
+
),
|
|
1321
|
+
},
|
|
1322
|
+
{
|
|
1323
|
+
name: '0118_webhook_queue_events_status_check',
|
|
1324
|
+
up: (db) =>
|
|
1325
|
+
raw(
|
|
1326
|
+
db,
|
|
1327
|
+
sql`ALTER TABLE webhook_queue_events DROP CONSTRAINT IF EXISTS webhook_queue_events_status_check, ADD CONSTRAINT webhook_queue_events_status_check CHECK (status IN ('failed', 'pending', 'skipped', 'succeeded'))`,
|
|
1328
|
+
),
|
|
1329
|
+
},
|
|
1330
|
+
{
|
|
1331
|
+
name: '0119_webhook_queue_events_pending_index',
|
|
1332
|
+
up: (db) =>
|
|
1333
|
+
// NULLS FIRST matches the due-sweep order (null = due immediately);
|
|
1334
|
+
// INCLUDE lets the pending gauge take min(created_at) index-only.
|
|
1335
|
+
raw(
|
|
1336
|
+
db,
|
|
1337
|
+
sql`CREATE INDEX IF NOT EXISTS webhook_queue_events_pending_idx ON webhook_queue_events (next_attempt_at NULLS FIRST) INCLUDE (created_at) WHERE status = 'pending'`,
|
|
1338
|
+
),
|
|
1339
|
+
},
|
|
1340
|
+
{
|
|
1341
|
+
name: '0120_webhook_queue_events_pending_index_drop',
|
|
1342
|
+
up: (db) => raw(db, sql`DROP INDEX IF EXISTS webhook_queue_events_pending_idx`),
|
|
1343
|
+
},
|
|
1344
|
+
{
|
|
1345
|
+
name: '0121_webhook_queue_events_pending_index_expiry',
|
|
1346
|
+
up: (db) =>
|
|
1347
|
+
// Recreated with expires_at so claimable and gauge queries, which all
|
|
1348
|
+
// filter expiry at query time, stay index-only.
|
|
1349
|
+
raw(
|
|
1350
|
+
db,
|
|
1351
|
+
sql`CREATE INDEX IF NOT EXISTS webhook_queue_events_pending_idx ON webhook_queue_events (next_attempt_at NULLS FIRST) INCLUDE (created_at, expires_at) WHERE status = 'pending'`,
|
|
1352
|
+
),
|
|
1353
|
+
},
|
|
1354
|
+
{
|
|
1355
|
+
name: '0122_webhook_queue_events_expired_pending_cleanup',
|
|
1356
|
+
up: (db) =>
|
|
1357
|
+
// Pre-ledger rows already past their staging TTL were dead under the
|
|
1358
|
+
// old read path; pending rows no longer expire, so drop them once
|
|
1359
|
+
// instead of resurrecting them as eternal obligations.
|
|
1360
|
+
raw(
|
|
1361
|
+
db,
|
|
1362
|
+
sql`DELETE FROM webhook_queue_events WHERE status = 'pending' AND attempting_at IS NULL AND expires_at <= to_char(now() AT TIME ZONE 'utc', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')`,
|
|
1363
|
+
),
|
|
1364
|
+
},
|
|
1365
|
+
{
|
|
1366
|
+
name: '0123_funding_deposit_addresses_owner_list_index',
|
|
1367
|
+
up: (db) =>
|
|
1368
|
+
db.schema
|
|
1369
|
+
.createIndex('funding_deposit_addresses_owner_list_idx')
|
|
1370
|
+
.ifNotExists()
|
|
1371
|
+
.on('fundingDepositAddresses')
|
|
1372
|
+
.columns(['orgId', 'environment', 'createdAt', 'id']),
|
|
1373
|
+
},
|
|
1374
|
+
{
|
|
1375
|
+
name: '0124_funding_deposit_addresses_project_list_index',
|
|
1376
|
+
up: (db) =>
|
|
1377
|
+
db.schema
|
|
1378
|
+
.createIndex('funding_deposit_addresses_project_list_idx')
|
|
1379
|
+
.ifNotExists()
|
|
1380
|
+
.on('fundingDepositAddresses')
|
|
1381
|
+
.columns(['orgId', 'environment', 'projectId', 'createdAt', 'id']),
|
|
1382
|
+
},
|
|
1383
|
+
{
|
|
1384
|
+
name: '0125_funding_deposits_owner_list_index',
|
|
1385
|
+
up: (db) =>
|
|
1386
|
+
db.schema
|
|
1387
|
+
.createIndex('funding_deposits_owner_list_idx')
|
|
1388
|
+
.ifNotExists()
|
|
1389
|
+
.on('fundingDeposits')
|
|
1390
|
+
.columns(['orgId', 'environment', 'createdAt', 'id']),
|
|
1391
|
+
},
|
|
1392
|
+
{
|
|
1393
|
+
name: '0126_funding_deposits_project_list_index',
|
|
1394
|
+
up: (db) =>
|
|
1395
|
+
db.schema
|
|
1396
|
+
.createIndex('funding_deposits_project_list_idx')
|
|
1397
|
+
.ifNotExists()
|
|
1398
|
+
.on('fundingDeposits')
|
|
1399
|
+
.columns(['orgId', 'environment', 'projectId', 'createdAt', 'id']),
|
|
1400
|
+
},
|
|
1284
1401
|
]
|
|
1285
1402
|
|
|
1286
1403
|
/**
|
package/src/db/Schema.ts
CHANGED
|
@@ -874,3 +874,35 @@ export const WebhookDelivery = z.object({
|
|
|
874
874
|
|
|
875
875
|
/** Columns of the `webhook_deliveries` table. */
|
|
876
876
|
export type WebhookDelivery = z.output<typeof WebhookDelivery>
|
|
877
|
+
|
|
878
|
+
/** A durable webhook envelope staged before its compact Queue reference is admitted. */
|
|
879
|
+
export const WebhookQueueEvent = z.object({
|
|
880
|
+
attemptCount: z.number().check(z.describe('Delivery attempts claimed so far.')),
|
|
881
|
+
attemptingAt: z
|
|
882
|
+
.nullable(z.string())
|
|
883
|
+
.check(z.describe('ISO timestamp of the current claim, or null when unclaimed.')),
|
|
884
|
+
createdAt: z.string().check(z.describe('ISO timestamp when the envelope was staged.')),
|
|
885
|
+
envelope: z
|
|
886
|
+
.custom<Webhooks.Envelope>()
|
|
887
|
+
.check(z.describe('The full webhook envelope retained outside Cloudflare Queues.')),
|
|
888
|
+
eventId: z.string().check(z.describe('Idempotent event id (`evt_...`).')),
|
|
889
|
+
expiresAt: z
|
|
890
|
+
.string()
|
|
891
|
+
.check(
|
|
892
|
+
z.describe(
|
|
893
|
+
'ISO dedupe retention deadline, restarted at terminal transitions; inert while pending.',
|
|
894
|
+
),
|
|
895
|
+
),
|
|
896
|
+
nextAttemptAt: z
|
|
897
|
+
.nullable(z.string())
|
|
898
|
+
.check(z.describe('ISO retry due time, or null when due immediately.')),
|
|
899
|
+
status: z
|
|
900
|
+
.enum(['failed', 'pending', 'skipped', 'succeeded'])
|
|
901
|
+
.check(z.describe('Delivery-obligation lifecycle status.')),
|
|
902
|
+
subscriptionId: z
|
|
903
|
+
.string()
|
|
904
|
+
.check(z.describe('Owning subscription id (`wh_...`); FK with `ON DELETE CASCADE`.')),
|
|
905
|
+
})
|
|
906
|
+
|
|
907
|
+
/** Columns of the `webhook_queue_events` table. */
|
|
908
|
+
export type WebhookQueueEvent = z.output<typeof WebhookQueueEvent>
|
|
@@ -578,3 +578,31 @@ describe('renewPoll', () => {
|
|
|
578
578
|
`)
|
|
579
579
|
})
|
|
580
580
|
})
|
|
581
|
+
|
|
582
|
+
describe('list', () => {
|
|
583
|
+
test('pages organization addresses and narrows project-attributed owners', async () => {
|
|
584
|
+
const listOwner = { environment: owner.environment, orgId: 'org_address_list' } as const
|
|
585
|
+
const first = await seed({ orgId: listOwner.orgId })
|
|
586
|
+
const project = await seed({ orgId: listOwner.orgId, projectId: 'prj_address_list' })
|
|
587
|
+
const sibling = await seed({ orgId: listOwner.orgId, projectId: 'prj_address_sibling' })
|
|
588
|
+
await seed({ orgId: 'org_address_list_foreign' })
|
|
589
|
+
await seed({ environment: 'sandbox', orgId: listOwner.orgId })
|
|
590
|
+
|
|
591
|
+
const page = await FundingDepositAddresses.list(db, { limit: 2, owner: listOwner })
|
|
592
|
+
expect(page.map((record) => record.id)).toEqual([sibling.id, project.id])
|
|
593
|
+
const rest = await FundingDepositAddresses.list(db, {
|
|
594
|
+
cursor: { createdAt: project.createdAt, id: project.id },
|
|
595
|
+
limit: 2,
|
|
596
|
+
owner: listOwner,
|
|
597
|
+
})
|
|
598
|
+
expect(rest.map((record) => record.id)).toEqual([first.id])
|
|
599
|
+
expect(
|
|
600
|
+
(
|
|
601
|
+
await FundingDepositAddresses.list(db, {
|
|
602
|
+
limit: 10,
|
|
603
|
+
owner: { ...listOwner, projectId: 'prj_address_list' },
|
|
604
|
+
})
|
|
605
|
+
).map((record) => record.id),
|
|
606
|
+
).toEqual([project.id])
|
|
607
|
+
})
|
|
608
|
+
})
|
|
@@ -148,6 +148,45 @@ export function getOwned(db: Db.Db, owner: Owner, id: string): Promise<Record |
|
|
|
148
148
|
return query.executeTakeFirst()
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
/** Lists owner-visible funding deposit addresses newest-first. */
|
|
152
|
+
export function list(db: Db.Db, options: list.Options): Promise<Record[]> {
|
|
153
|
+
const { cursor, limit, owner } = options
|
|
154
|
+
let query = db.kysely
|
|
155
|
+
.selectFrom('funding_deposit_addresses')
|
|
156
|
+
.selectAll()
|
|
157
|
+
.where('environment', '=', owner.environment)
|
|
158
|
+
.where('orgId', '=', owner.orgId)
|
|
159
|
+
if (owner.projectId !== undefined) query = query.where('projectId', '=', owner.projectId)
|
|
160
|
+
if (cursor)
|
|
161
|
+
query = query.where((eb) =>
|
|
162
|
+
eb.or([
|
|
163
|
+
eb('createdAt', '<', cursor.createdAt),
|
|
164
|
+
eb.and([eb('createdAt', '=', cursor.createdAt), eb('id', '<', cursor.id)]),
|
|
165
|
+
]),
|
|
166
|
+
)
|
|
167
|
+
return query.orderBy('createdAt', 'desc').orderBy('id', 'desc').limit(limit).execute()
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export declare namespace list {
|
|
171
|
+
/** Ownership and paging options for funding deposit addresses. */
|
|
172
|
+
type Options = {
|
|
173
|
+
/** Exclusive lower bound from the previous page. */
|
|
174
|
+
cursor?: Cursor | undefined
|
|
175
|
+
/** Maximum rows to return. */
|
|
176
|
+
limit: number
|
|
177
|
+
/** Ownership filter derived from the authenticated API key. */
|
|
178
|
+
owner: Owner
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** Cursor fields for the last address returned by the previous page. */
|
|
182
|
+
type Cursor = {
|
|
183
|
+
/** Deposit address creation time. */
|
|
184
|
+
createdAt: string
|
|
185
|
+
/** Deposit address id, used as a deterministic tie-breaker. */
|
|
186
|
+
id: string
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
151
190
|
/** Gets the reusable address that matches one owner and route identity. */
|
|
152
191
|
export function getMatching(db: Db.Db, match: Match): Promise<Record | undefined> {
|
|
153
192
|
let query = db.kysely
|
|
@@ -498,3 +498,85 @@ describe('toPublic', () => {
|
|
|
498
498
|
})
|
|
499
499
|
})
|
|
500
500
|
})
|
|
501
|
+
|
|
502
|
+
describe('list', () => {
|
|
503
|
+
test('pages organization deposits and narrows project-attributed owners', async () => {
|
|
504
|
+
const listOwner = { environment: owner.environment, orgId: 'org_deposit_list' } as const
|
|
505
|
+
const firstAddress = await seedAddress({ orgId: listOwner.orgId })
|
|
506
|
+
const projectAddress = await seedAddress({
|
|
507
|
+
orgId: listOwner.orgId,
|
|
508
|
+
projectId: 'prj_deposit_list',
|
|
509
|
+
})
|
|
510
|
+
const siblingAddress = await seedAddress({
|
|
511
|
+
orgId: listOwner.orgId,
|
|
512
|
+
projectId: 'prj_deposit_sibling',
|
|
513
|
+
})
|
|
514
|
+
const first = await seedDeposit(firstAddress)
|
|
515
|
+
const project = await seedDeposit(projectAddress)
|
|
516
|
+
const sibling = await seedDeposit(siblingAddress)
|
|
517
|
+
const foreignAddress = await seedAddress({ orgId: 'org_deposit_list_foreign' })
|
|
518
|
+
await seedDeposit(foreignAddress)
|
|
519
|
+
const sandboxAddress = await seedAddress({
|
|
520
|
+
environment: 'sandbox',
|
|
521
|
+
orgId: listOwner.orgId,
|
|
522
|
+
})
|
|
523
|
+
await seedDeposit(sandboxAddress)
|
|
524
|
+
|
|
525
|
+
const page = await FundingDeposits.list(db, { limit: 2, owner: listOwner })
|
|
526
|
+
expect(page.map((record) => record.id)).toEqual([sibling.id, project.id])
|
|
527
|
+
const rest = await FundingDeposits.list(db, {
|
|
528
|
+
cursor: { createdAt: project.createdAt, id: project.id },
|
|
529
|
+
limit: 2,
|
|
530
|
+
owner: listOwner,
|
|
531
|
+
})
|
|
532
|
+
expect(rest.map((record) => record.id)).toEqual([first.id])
|
|
533
|
+
expect(
|
|
534
|
+
(
|
|
535
|
+
await FundingDeposits.list(db, {
|
|
536
|
+
limit: 10,
|
|
537
|
+
owner: { ...listOwner, projectId: 'prj_deposit_list' },
|
|
538
|
+
})
|
|
539
|
+
).map((record) => record.id),
|
|
540
|
+
).toEqual([project.id])
|
|
541
|
+
})
|
|
542
|
+
|
|
543
|
+
test('AND-combines filters and counts through the same ownership scope', async () => {
|
|
544
|
+
const listOwner = { environment: owner.environment, orgId: 'org_deposit_filters' } as const
|
|
545
|
+
const recipient = `0x${'ab'.repeat(20)}`
|
|
546
|
+
const address = await seedAddress({
|
|
547
|
+
orgId: listOwner.orgId,
|
|
548
|
+
snapshot: TestFunding.depositAddressSnapshot({
|
|
549
|
+
provider: { id: 'relay_filters', name: 'Relay' },
|
|
550
|
+
recipient,
|
|
551
|
+
}),
|
|
552
|
+
})
|
|
553
|
+
const match = await seedDeposit(address)
|
|
554
|
+
const unrelatedAddress = await seedAddress({ orgId: listOwner.orgId })
|
|
555
|
+
await seedDeposit(unrelatedAddress)
|
|
556
|
+
const filters = {
|
|
557
|
+
depositAddress: address.address,
|
|
558
|
+
destinationTokenKeys: [address.destinationTokenKey],
|
|
559
|
+
providerId: address.providerId,
|
|
560
|
+
recipient: recipient.toLowerCase(),
|
|
561
|
+
sourceChainIds: [address.sourceChainId],
|
|
562
|
+
sourceTokenKeys: [address.sourceTokenKey],
|
|
563
|
+
status: 'detected',
|
|
564
|
+
} as const
|
|
565
|
+
|
|
566
|
+
expect(
|
|
567
|
+
(await FundingDeposits.list(db, { ...filters, limit: 10, owner: listOwner })).map(
|
|
568
|
+
(record) => record.id,
|
|
569
|
+
),
|
|
570
|
+
).toEqual([match.id])
|
|
571
|
+
expect(await FundingDeposits.count(db, { ...filters, cap: 10, owner: listOwner })).toBe(1)
|
|
572
|
+
expect(await FundingDeposits.count(db, { ...filters, cap: 0, owner: listOwner })).toBe(1)
|
|
573
|
+
expect(
|
|
574
|
+
await FundingDeposits.count(db, {
|
|
575
|
+
...filters,
|
|
576
|
+
destinationTokenKeys: [],
|
|
577
|
+
cap: 10,
|
|
578
|
+
owner: listOwner,
|
|
579
|
+
}),
|
|
580
|
+
).toBe(0)
|
|
581
|
+
})
|
|
582
|
+
})
|
|
@@ -27,6 +27,24 @@ export type Owner = {
|
|
|
27
27
|
projectId?: string | undefined
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
/** Optional filters shared by funding deposit list and count queries. */
|
|
31
|
+
export type Filters = {
|
|
32
|
+
/** Reusable source-chain address that received the funds. */
|
|
33
|
+
depositAddress?: string | undefined
|
|
34
|
+
/** Canonical destination token keys accepted by the filter. */
|
|
35
|
+
destinationTokenKeys?: readonly string[] | undefined
|
|
36
|
+
/** Funding provider id. */
|
|
37
|
+
providerId?: string | undefined
|
|
38
|
+
/** Tempo account that receives completed deposits. */
|
|
39
|
+
recipient?: string | undefined
|
|
40
|
+
/** Canonical source chain ids accepted by the filter. */
|
|
41
|
+
sourceChainIds?: readonly string[] | undefined
|
|
42
|
+
/** Canonical source token keys accepted by the filter. */
|
|
43
|
+
sourceTokenKeys?: readonly string[] | undefined
|
|
44
|
+
/** Deposit lifecycle status. */
|
|
45
|
+
status?: Record['status'] | undefined
|
|
46
|
+
}
|
|
47
|
+
|
|
30
48
|
/** Inserts one provider-observed source transfer as a funding deposit. */
|
|
31
49
|
export function insert(db: Db.Db, record: Record): Promise<Record> {
|
|
32
50
|
return db.kysely
|
|
@@ -234,6 +252,118 @@ export function getOwned(db: Db.Db, owner: Owner, id: string): Promise<Record |
|
|
|
234
252
|
return query.executeTakeFirst()
|
|
235
253
|
}
|
|
236
254
|
|
|
255
|
+
/** Lists owner-visible funding deposits newest-first. */
|
|
256
|
+
export function list(db: Db.Db, options: list.Options): Promise<Record[]> {
|
|
257
|
+
const { cursor, limit } = options
|
|
258
|
+
let builder = query(db, options).selectAll('funding_deposits')
|
|
259
|
+
if (cursor)
|
|
260
|
+
builder = builder.where((eb) =>
|
|
261
|
+
eb.or([
|
|
262
|
+
eb('funding_deposits.createdAt', '<', cursor.createdAt),
|
|
263
|
+
eb.and([
|
|
264
|
+
eb('funding_deposits.createdAt', '=', cursor.createdAt),
|
|
265
|
+
eb('funding_deposits.id', '<', cursor.id),
|
|
266
|
+
]),
|
|
267
|
+
]),
|
|
268
|
+
)
|
|
269
|
+
return builder
|
|
270
|
+
.orderBy('funding_deposits.createdAt', 'desc')
|
|
271
|
+
.orderBy('funding_deposits.id', 'desc')
|
|
272
|
+
.limit(limit)
|
|
273
|
+
.execute()
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export declare namespace list {
|
|
277
|
+
/** Ownership and paging options for funding deposits. */
|
|
278
|
+
type Options = Filters & {
|
|
279
|
+
/** Exclusive lower bound from the previous page. */
|
|
280
|
+
cursor?: listByAddress.Cursor | undefined
|
|
281
|
+
/** Maximum rows to return. */
|
|
282
|
+
limit: number
|
|
283
|
+
/** Ownership filter derived from the authenticated API key. */
|
|
284
|
+
owner: Owner
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/** Counts owner-visible funding deposits through the list's shared filters. */
|
|
289
|
+
export async function count(db: Db.Db, options: count.Options): Promise<number> {
|
|
290
|
+
const rows = query(db, options)
|
|
291
|
+
.select('funding_deposits.id')
|
|
292
|
+
.limit(options.cap + 1)
|
|
293
|
+
.as('rows')
|
|
294
|
+
const result = await db.kysely
|
|
295
|
+
.selectFrom(rows)
|
|
296
|
+
.select(sql<number>`count(*)::int`.as('count'))
|
|
297
|
+
.executeTakeFirstOrThrow()
|
|
298
|
+
return result.count
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export declare namespace count {
|
|
302
|
+
/** Ownership, filter, and cap options for counting funding deposits. */
|
|
303
|
+
type Options = Filters & {
|
|
304
|
+
/** Maximum exact count before returning the cap plus one. */
|
|
305
|
+
cap: number
|
|
306
|
+
/** Ownership filter derived from the authenticated API key. */
|
|
307
|
+
owner: Owner
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function query(db: Db.Db, options: query.Options) {
|
|
312
|
+
const owner = options.owner
|
|
313
|
+
let builder = db.kysely
|
|
314
|
+
.selectFrom('funding_deposits')
|
|
315
|
+
.innerJoin(
|
|
316
|
+
'funding_deposit_addresses',
|
|
317
|
+
'funding_deposit_addresses.id',
|
|
318
|
+
'funding_deposits.depositAddressId',
|
|
319
|
+
)
|
|
320
|
+
.where('funding_deposit_addresses.environment', '=', owner.environment)
|
|
321
|
+
.where('funding_deposit_addresses.orgId', '=', owner.orgId)
|
|
322
|
+
.where('funding_deposits.environment', '=', owner.environment)
|
|
323
|
+
.where('funding_deposits.orgId', '=', owner.orgId)
|
|
324
|
+
if (owner.projectId !== undefined)
|
|
325
|
+
builder = builder
|
|
326
|
+
.where('funding_deposit_addresses.projectId', '=', owner.projectId)
|
|
327
|
+
.where('funding_deposits.projectId', '=', owner.projectId)
|
|
328
|
+
if (options.depositAddress !== undefined) {
|
|
329
|
+
const address = /^0x[0-9a-fA-F]{40}$/.test(options.depositAddress)
|
|
330
|
+
? options.depositAddress.toLowerCase()
|
|
331
|
+
: options.depositAddress
|
|
332
|
+
builder = builder.where('funding_deposit_addresses.address', '=', address)
|
|
333
|
+
}
|
|
334
|
+
if (options.destinationTokenKeys !== undefined)
|
|
335
|
+
builder = options.destinationTokenKeys.length
|
|
336
|
+
? builder.where(
|
|
337
|
+
'funding_deposit_addresses.destinationTokenKey',
|
|
338
|
+
'in',
|
|
339
|
+
options.destinationTokenKeys,
|
|
340
|
+
)
|
|
341
|
+
: builder.where(sql<boolean>`false`)
|
|
342
|
+
if (options.providerId !== undefined)
|
|
343
|
+
builder = builder.where('funding_deposit_addresses.providerId', '=', options.providerId)
|
|
344
|
+
if (options.recipient !== undefined)
|
|
345
|
+
builder = builder.where(
|
|
346
|
+
sql<boolean>`lower(funding_deposit_addresses.recipient) = ${options.recipient.toLowerCase()}`,
|
|
347
|
+
)
|
|
348
|
+
if (options.sourceChainIds !== undefined)
|
|
349
|
+
builder = options.sourceChainIds.length
|
|
350
|
+
? builder.where('funding_deposit_addresses.sourceChainId', 'in', options.sourceChainIds)
|
|
351
|
+
: builder.where(sql<boolean>`false`)
|
|
352
|
+
if (options.sourceTokenKeys !== undefined)
|
|
353
|
+
builder = options.sourceTokenKeys.length
|
|
354
|
+
? builder.where('funding_deposit_addresses.sourceTokenKey', 'in', options.sourceTokenKeys)
|
|
355
|
+
: builder.where(sql<boolean>`false`)
|
|
356
|
+
if (options.status !== undefined)
|
|
357
|
+
builder = builder.where('funding_deposits.status', '=', options.status)
|
|
358
|
+
return builder
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
declare namespace query {
|
|
362
|
+
type Options = Filters & {
|
|
363
|
+
owner: Owner
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
237
367
|
/** Lists one owner-visible address's deposits newest-first. */
|
|
238
368
|
export function listByAddress(
|
|
239
369
|
db: Db.Db,
|