tapimo 0.7.2 → 0.8.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 +10 -7
- package/dist/ApiKey.d.ts +3 -0
- package/dist/ApiKey.d.ts.map +1 -1
- package/dist/ApiKey.js +10 -0
- package/dist/ApiKey.js.map +1 -1
- package/dist/ApiKeys.d.ts +7 -3
- package/dist/ApiKeys.d.ts.map +1 -1
- package/dist/ApiKeys.js +9 -2
- package/dist/ApiKeys.js.map +1 -1
- package/dist/App.d.ts +20434 -19484
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +62 -4
- package/dist/App.js.map +1 -1
- package/dist/admin/App.d.ts +84 -74
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.d.ts +14 -1
- package/dist/admin/apps/api-keys.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.js +6 -2
- package/dist/admin/apps/api-keys.js.map +1 -1
- package/dist/admin/apps/organizations.d.ts +2 -0
- package/dist/admin/apps/organizations.d.ts.map +1 -1
- package/dist/admin/apps/verified-tokens.d.ts +168 -168
- package/dist/admin/ui.generated.d.ts +1 -1
- package/dist/admin/ui.generated.js +67 -67
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/analytics/Analytics.d.ts +2 -0
- package/dist/analytics/Analytics.d.ts.map +1 -1
- package/dist/analytics/Analytics.js +3 -0
- package/dist/analytics/Analytics.js.map +1 -1
- package/dist/apps/data/App.d.ts +10305 -12785
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/App.js +4 -8
- package/dist/apps/data/App.js.map +1 -1
- package/dist/apps/data/routes/activities.d.ts +24 -24
- package/dist/apps/data/routes/addresses.d.ts +288 -288
- package/dist/apps/data/routes/balances.d.ts +6 -6
- package/dist/apps/data/routes/blocks.d.ts +39 -39
- package/dist/apps/data/routes/coingecko.d.ts +170 -170
- package/dist/apps/data/routes/earn.d.ts +436 -6
- package/dist/apps/data/routes/earn.d.ts.map +1 -1
- package/dist/apps/data/routes/earn.js +631 -4
- package/dist/apps/data/routes/earn.js.map +1 -1
- package/dist/apps/data/routes/exchanges.d.ts +10 -10
- package/dist/apps/data/routes/fee-amm.d.ts +2 -2
- package/dist/apps/data/routes/indexer.d.ts +6 -6
- package/dist/apps/data/routes/indexer.js +4 -1
- package/dist/apps/data/routes/indexer.js.map +1 -1
- package/dist/apps/data/routes/receipts.d.ts +235 -235
- package/dist/apps/data/routes/rpc.d.ts +1 -1
- package/dist/apps/data/routes/rpc.js +4 -1
- package/dist/apps/data/routes/rpc.js.map +1 -1
- package/dist/apps/data/routes/tokenlist.d.ts +36 -36
- package/dist/apps/data/routes/tokens.d.ts +6 -6
- package/dist/apps/data/routes/transactions.d.ts +2 -2
- package/dist/apps/data/routes/transfers.d.ts +1 -1
- package/dist/apps/data/routes/valuation.d.ts +1 -1
- package/dist/apps/data/routes/verified-tokens.d.ts +36 -36
- package/dist/apps/data/routes/webhooks.d.ts +9 -9
- package/dist/apps/data/routes/zones.d.ts +14 -14
- package/dist/apps/funding/Access.d.ts +16 -0
- package/dist/apps/funding/Access.d.ts.map +1 -0
- package/dist/apps/funding/Access.js +22 -0
- package/dist/apps/funding/Access.js.map +1 -0
- package/dist/apps/{data/routes/funding.d.ts → funding/App.d.ts} +1717 -3035
- package/dist/apps/funding/App.d.ts.map +1 -0
- package/dist/apps/funding/App.js +46 -0
- package/dist/apps/funding/App.js.map +1 -0
- package/dist/apps/funding/Inventory.d.ts +5 -0
- package/dist/apps/funding/Inventory.d.ts.map +1 -0
- package/dist/apps/funding/Inventory.js +25 -0
- package/dist/apps/funding/Inventory.js.map +1 -0
- package/dist/apps/funding/ProviderAttempts.d.ts +5 -0
- package/dist/apps/funding/ProviderAttempts.d.ts.map +1 -0
- package/dist/apps/funding/ProviderAttempts.js +24 -0
- package/dist/apps/funding/ProviderAttempts.js.map +1 -0
- package/dist/apps/funding/index.d.ts +2 -0
- package/dist/apps/funding/index.d.ts.map +1 -0
- package/dist/apps/funding/index.js +2 -0
- package/dist/apps/funding/index.js.map +1 -0
- package/dist/apps/funding/routes/chains.d.ts +169 -0
- package/dist/apps/funding/routes/chains.d.ts.map +1 -0
- package/dist/apps/funding/routes/chains.js +80 -0
- package/dist/apps/funding/routes/chains.js.map +1 -0
- package/dist/apps/funding/routes/deposit-addresses.d.ts +2388 -0
- package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -0
- package/dist/apps/funding/routes/deposit-addresses.js +860 -0
- package/dist/apps/funding/routes/deposit-addresses.js.map +1 -0
- package/dist/apps/funding/routes/providers.d.ts +91 -0
- package/dist/apps/funding/routes/providers.d.ts.map +1 -0
- package/dist/apps/funding/routes/providers.js +56 -0
- package/dist/apps/funding/routes/providers.js.map +1 -0
- package/dist/apps/funding/routes/quotes.d.ts +426 -0
- package/dist/apps/funding/routes/quotes.d.ts.map +1 -0
- package/dist/apps/funding/routes/quotes.js +188 -0
- package/dist/apps/funding/routes/quotes.js.map +1 -0
- package/dist/apps/funding/routes/transfers.d.ts +1999 -0
- package/dist/apps/funding/routes/transfers.d.ts.map +1 -0
- package/dist/apps/funding/routes/transfers.js +614 -0
- package/dist/apps/funding/routes/transfers.js.map +1 -0
- package/dist/apps/index.d.ts +1 -0
- package/dist/apps/index.d.ts.map +1 -1
- package/dist/apps/index.js +1 -0
- package/dist/apps/index.js.map +1 -1
- package/dist/apps/management/App.d.ts +430 -165
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +298 -21
- package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.js +75 -1
- package/dist/apps/management/routes/api-keys.js.map +1 -1
- package/dist/apps/management/routes/billing.d.ts +6 -6
- package/dist/apps/management/routes/faucet.d.ts +35 -35
- package/dist/apps/management/routes/invitations.d.ts +48 -48
- package/dist/apps/management/routes/invite-links.d.ts +38 -38
- package/dist/apps/management/routes/members.d.ts +69 -69
- package/dist/apps/management/routes/orgs.d.ts +37 -37
- package/dist/apps/management/routes/projects.d.ts +25 -25
- package/dist/apps/management/routes/scopes.d.ts +1 -1
- package/dist/apps/management/routes/usage.d.ts +72 -72
- package/dist/apps/management/routes/verified-token-requests.d.ts +18 -18
- package/dist/apps/management/routes/webhooks.d.ts +128 -128
- package/dist/apps/mcp.d.ts +2 -3
- package/dist/apps/mcp.d.ts.map +1 -1
- package/dist/apps/mcp.js +16 -14
- package/dist/apps/mcp.js.map +1 -1
- package/dist/apps/metadata.d.ts +4 -0
- package/dist/apps/metadata.d.ts.map +1 -1
- package/dist/apps/metadata.js +10 -0
- package/dist/apps/metadata.js.map +1 -1
- package/dist/apps/mpp/App.d.ts +2 -2
- package/dist/cloudflare.d.ts +50 -0
- package/dist/cloudflare.d.ts.map +1 -1
- package/dist/cloudflare.js +178 -0
- package/dist/cloudflare.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 +37 -0
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +12 -0
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +20 -0
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/fundingDepositAddresses.d.ts +13 -0
- package/dist/db/tables/fundingDepositAddresses.d.ts.map +1 -1
- package/dist/db/tables/fundingDepositAddresses.js +12 -0
- package/dist/db/tables/fundingDepositAddresses.js.map +1 -1
- package/dist/db/tables/fundingDepositRequestObservations.d.ts +45 -0
- package/dist/db/tables/fundingDepositRequestObservations.d.ts.map +1 -0
- package/dist/db/tables/fundingDepositRequestObservations.js +46 -0
- package/dist/db/tables/fundingDepositRequestObservations.js.map +1 -0
- package/dist/db/tables/fundingDeposits.d.ts +57 -1
- package/dist/db/tables/fundingDeposits.d.ts.map +1 -1
- package/dist/db/tables/fundingDeposits.js +82 -0
- package/dist/db/tables/fundingDeposits.js.map +1 -1
- package/dist/internal/Auth.d.ts +11 -11
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +41 -15
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/Cache.js +1 -1
- package/dist/internal/Cache.js.map +1 -1
- package/dist/internal/EdgeCache.d.ts +2 -2
- package/dist/internal/EdgeCache.d.ts.map +1 -1
- package/dist/internal/EdgeCache.js +10 -4
- package/dist/internal/EdgeCache.js.map +1 -1
- package/dist/internal/IpAllowlist.d.ts +15 -0
- package/dist/internal/IpAllowlist.d.ts.map +1 -0
- package/dist/internal/IpAllowlist.js +27 -0
- package/dist/internal/IpAllowlist.js.map +1 -0
- package/dist/internal/OpenApi.d.ts +1 -1
- package/dist/internal/OpenApi.js +1 -1
- package/dist/internal/OpenApi.js.map +1 -1
- package/dist/internal/Tidx.d.ts +6 -0
- package/dist/internal/Tidx.d.ts.map +1 -1
- package/dist/internal/Tidx.js +13 -3
- package/dist/internal/Tidx.js.map +1 -1
- package/dist/internal/Viem.d.ts +8 -0
- package/dist/internal/Viem.d.ts.map +1 -1
- package/dist/internal/Viem.js +12 -5
- package/dist/internal/Viem.js.map +1 -1
- package/dist/internal/funding/Deposit.d.ts +42 -0
- package/dist/internal/funding/Deposit.d.ts.map +1 -1
- package/dist/internal/funding/Deposit.js +37 -5
- package/dist/internal/funding/Deposit.js.map +1 -1
- package/dist/internal/funding/Evidence.d.ts +1 -5
- package/dist/internal/funding/Evidence.d.ts.map +1 -1
- package/dist/internal/funding/Evidence.js +4 -3
- package/dist/internal/funding/Evidence.js.map +1 -1
- package/dist/internal/funding/Provider.d.ts +102 -0
- package/dist/internal/funding/Provider.d.ts.map +1 -1
- package/dist/internal/funding/Provider.js +5 -0
- package/dist/internal/funding/Provider.js.map +1 -1
- package/dist/internal/funding/Reconciliation.d.ts +16 -2
- package/dist/internal/funding/Reconciliation.d.ts.map +1 -1
- package/dist/internal/funding/Reconciliation.js +257 -48
- package/dist/internal/funding/Reconciliation.js.map +1 -1
- package/dist/internal/funding/SourceObservation.d.ts +108 -0
- package/dist/internal/funding/SourceObservation.d.ts.map +1 -0
- package/dist/internal/funding/SourceObservation.js +143 -0
- package/dist/internal/funding/SourceObservation.js.map +1 -0
- package/dist/internal/funding/Subsidy.d.ts +2 -2
- package/dist/internal/funding/Subsidy.d.ts.map +1 -1
- package/dist/internal/funding/Subsidy.js +1 -1
- package/dist/internal/funding/Subsidy.js.map +1 -1
- package/dist/internal/funding/index.d.ts +1 -0
- package/dist/internal/funding/index.d.ts.map +1 -1
- package/dist/internal/funding/index.js +1 -0
- package/dist/internal/funding/index.js.map +1 -1
- package/dist/internal/funding/providers/relay.d.ts +43 -1
- package/dist/internal/funding/providers/relay.d.ts.map +1 -1
- package/dist/internal/funding/providers/relay.js +434 -27
- package/dist/internal/funding/providers/relay.js.map +1 -1
- package/package.json +3 -3
- package/src/ApiKey.test.ts +35 -0
- package/src/ApiKey.ts +27 -0
- package/src/ApiKeys.test.ts +29 -0
- package/src/ApiKeys.ts +11 -3
- package/src/App.test.ts +23 -5
- package/src/App.ts +76 -5
- package/src/Client.test-d.ts +171 -19
- package/src/admin/apps/api-keys.test.ts +25 -1
- package/src/admin/apps/api-keys.ts +6 -2
- package/src/admin/ui.generated.ts +67 -67
- package/src/analytics/Analytics.ts +4 -0
- package/src/apps/composition.test.ts +100 -2
- package/src/apps/data/App.test.ts +0 -12
- package/src/apps/data/App.ts +4 -8
- package/src/apps/data/routes/earn.test.ts +283 -0
- package/src/apps/data/routes/earn.ts +800 -3
- package/src/apps/data/routes/indexer.ts +2 -1
- package/src/apps/data/routes/rpc.ts +2 -0
- package/src/apps/data/routes/tokens.test.ts +6 -8
- package/src/apps/funding/Access.ts +26 -0
- package/src/apps/funding/App.test.ts +78 -0
- package/src/apps/funding/App.ts +63 -0
- package/src/apps/funding/Inventory.ts +30 -0
- package/src/apps/funding/ProviderAttempts.ts +26 -0
- package/src/apps/funding/index.ts +1 -0
- package/src/apps/funding/routes/chains.test.ts +78 -0
- package/src/apps/funding/routes/chains.ts +101 -0
- package/src/apps/funding/routes/deposit-addresses.test.ts +1628 -0
- package/src/apps/funding/routes/deposit-addresses.ts +1015 -0
- package/src/apps/funding/routes/providers.test.ts +165 -0
- package/src/apps/funding/routes/providers.ts +73 -0
- package/src/apps/funding/routes/quotes.test.ts +511 -0
- package/src/apps/funding/routes/quotes.ts +205 -0
- package/src/apps/funding/routes/transfers.test.ts +1255 -0
- package/src/apps/funding/routes/transfers.ts +704 -0
- package/src/apps/index.ts +1 -0
- package/src/apps/management/routes/api-keys.test.ts +98 -0
- package/src/apps/management/routes/api-keys.ts +96 -1
- package/src/apps/mcp.test.ts +17 -2
- package/src/apps/mcp.ts +18 -13
- package/src/apps/metadata.ts +12 -0
- package/src/apps/mpp/internal/Policy.test.ts +7 -1
- package/src/cloudflare.ts +279 -0
- package/src/db/Db.ts +62 -0
- package/src/db/Schema.ts +24 -0
- package/src/db/tables/fundingDepositAddresses.ts +28 -0
- package/src/db/tables/fundingDepositRequestObservations.test.ts +106 -0
- package/src/db/tables/fundingDepositRequestObservations.ts +99 -0
- package/src/db/tables/fundingDeposits.test.ts +138 -0
- package/src/db/tables/fundingDeposits.ts +145 -1
- package/src/internal/Auth.test.ts +177 -5
- package/src/internal/Auth.ts +48 -18
- package/src/internal/Cache.test.ts +37 -3
- package/src/internal/Cache.ts +1 -1
- package/src/internal/EdgeCache.test.ts +7 -7
- package/src/internal/EdgeCache.ts +13 -4
- package/src/internal/IpAllowlist.test.ts +52 -0
- package/src/internal/IpAllowlist.ts +39 -0
- package/src/internal/Log.test.ts +7 -1
- package/src/internal/OpenApi.ts +1 -1
- package/src/internal/Tidx.test.ts +28 -0
- package/src/internal/Tidx.ts +19 -3
- package/src/internal/Viem.test.ts +28 -0
- package/src/internal/Viem.ts +21 -5
- package/src/internal/funding/Deposit.ts +74 -5
- package/src/internal/funding/Evidence.test.ts +2 -2
- package/src/internal/funding/Evidence.ts +4 -9
- package/src/internal/funding/Provider.ts +115 -0
- package/src/internal/funding/Reconciliation.local.test.ts +7 -0
- package/src/internal/funding/Reconciliation.test.ts +1052 -35
- package/src/internal/funding/Reconciliation.ts +328 -56
- package/src/internal/funding/SourceObservation.test.ts +301 -0
- package/src/internal/funding/SourceObservation.ts +267 -0
- package/src/internal/funding/Subsidy.ts +2 -2
- package/src/internal/funding/index.test.ts +1 -0
- package/src/internal/funding/index.ts +1 -0
- package/src/internal/funding/providers/relay.test.ts +94 -4
- package/src/internal/funding/providers/relay.ts +528 -27
- package/dist/apps/data/routes/funding.d.ts.map +0 -1
- package/dist/apps/data/routes/funding.js +0 -1557
- package/dist/apps/data/routes/funding.js.map +0 -1
- package/src/apps/data/routes/funding.test.ts +0 -2994
- package/src/apps/data/routes/funding.ts +0 -1821
|
@@ -0,0 +1,860 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { etag } from 'hono/etag';
|
|
3
|
+
import * as z from 'zod/mini';
|
|
4
|
+
import * as Db from '../../../db/Db.js';
|
|
5
|
+
import * as FundingDepositAddresses from '../../../db/tables/fundingDepositAddresses.js';
|
|
6
|
+
import * as FundingDeposits from '../../../db/tables/fundingDeposits.js';
|
|
7
|
+
import * as FundingIdempotency from '../../../db/tables/fundingIdempotency.js';
|
|
8
|
+
import * as Auth from '../../../internal/Auth.js';
|
|
9
|
+
import * as Cache from '../../../internal/Cache.js';
|
|
10
|
+
import * as Cursor from '../../../internal/Cursor.js';
|
|
11
|
+
import * as FundingCatalog from '../../../internal/funding/Catalog.js';
|
|
12
|
+
import * as FundingDeposit from '../../../internal/funding/Deposit.js';
|
|
13
|
+
import * as FundingDepositAddress from '../../../internal/funding/DepositAddress.js';
|
|
14
|
+
import * as FundingProvider from '../../../internal/funding/Provider.js';
|
|
15
|
+
import * as FundingTransfer from '../../../internal/funding/Transfer.js';
|
|
16
|
+
import * as Idempotency from '../../../internal/Idempotency.js';
|
|
17
|
+
import * as OpenApi from '../../../internal/OpenApi.js';
|
|
18
|
+
import * as Response from '../../../internal/Response.js';
|
|
19
|
+
import * as Schema from '../../../internal/Schema.js';
|
|
20
|
+
import * as Value from '../../../internal/Value.js';
|
|
21
|
+
import * as Access from '../Access.js';
|
|
22
|
+
import * as ProviderAttempts from '../ProviderAttempts.js';
|
|
23
|
+
/** Zod schemas owned by funding deposit-address resources. */
|
|
24
|
+
export var schema;
|
|
25
|
+
(function (schema) {
|
|
26
|
+
/** One reusable funding deposit address. */
|
|
27
|
+
schema.DepositAddress = FundingDepositAddress.schema.FundingDepositAddress;
|
|
28
|
+
/** One detected funding deposit. */
|
|
29
|
+
schema.Deposit = FundingDeposit.schema.FundingDeposit;
|
|
30
|
+
/** Schemas for the createFundingDepositAddress operation. */
|
|
31
|
+
let createFundingDepositAddress;
|
|
32
|
+
(function (createFundingDepositAddress) {
|
|
33
|
+
/** Funding deposit address creation request body. */
|
|
34
|
+
createFundingDepositAddress.Body = z
|
|
35
|
+
.strictObject({
|
|
36
|
+
amount: FundingTransfer.schema.CreateRequest.shape.amount,
|
|
37
|
+
destinationToken: z
|
|
38
|
+
.string()
|
|
39
|
+
.check(z.minLength(1), z.describe('Tempo destination token symbol, contract address, or token key.'), z.meta({ examples: ['usdt0'] })),
|
|
40
|
+
recipient: z
|
|
41
|
+
.string()
|
|
42
|
+
.check(z.regex(/^0x[0-9a-fA-F]{40}$/), z.describe('Tempo account that receives completed deposits.'), z.meta({ examples: [`0x${'11'.repeat(20)}`] })),
|
|
43
|
+
refundAddress: z
|
|
44
|
+
.string()
|
|
45
|
+
.check(z.minLength(1), z.describe('Source-chain account that receives refunds.'), z.meta({ examples: ['TJRabPrwbZy45sbavfcjinPJC18kjpRTv8'] })),
|
|
46
|
+
sourceChain: z
|
|
47
|
+
.string()
|
|
48
|
+
.check(z.minLength(1), z.describe('Source chain CAIP-2 id, slug, or alias.'), z.meta({ examples: ['tron', 'tron:0x2b6653dc'] })),
|
|
49
|
+
sourceToken: z
|
|
50
|
+
.string()
|
|
51
|
+
.check(z.minLength(1), z.describe('Source token symbol, contract address, or token key.'), z.meta({ examples: ['usdt'] })),
|
|
52
|
+
subsidize: z
|
|
53
|
+
.optional(z.boolean())
|
|
54
|
+
.check(z.describe('Guarantees normalized 1:1 delivery through a Tempo-funded subsidy.'), z.meta({ examples: [false] })),
|
|
55
|
+
})
|
|
56
|
+
.check(z.describe('Reusable funding deposit address creation request.'));
|
|
57
|
+
/** Required headers for funding deposit address creation. */
|
|
58
|
+
createFundingDepositAddress.Headers = z.object({
|
|
59
|
+
'idempotency-key': z
|
|
60
|
+
.string()
|
|
61
|
+
.check(z.minLength(1), z.describe('Opaque retry key scoped to the API key. Matching requests replay successful responses for 24 hours; changed input conflicts, in-flight requests block, and failed attempts release the key.'), z.meta({ examples: ['funding_01k1c5j8q8p0be6j5v9m6d1e4r'] })),
|
|
62
|
+
});
|
|
63
|
+
/** The reusable address, with quoted terms only when the provider address was newly stored. */
|
|
64
|
+
createFundingDepositAddress.Response = z
|
|
65
|
+
.union([
|
|
66
|
+
FundingDepositAddress.schema.CreatedFundingDepositAddress,
|
|
67
|
+
FundingDepositAddress.schema.FundingDepositAddress,
|
|
68
|
+
])
|
|
69
|
+
.check(z.describe('A reusable funding deposit address, including terms sampled for a newly stored provider address.'));
|
|
70
|
+
})(createFundingDepositAddress = schema.createFundingDepositAddress || (schema.createFundingDepositAddress = {}));
|
|
71
|
+
/** Schemas for the getFundingDepositAddress operation. */
|
|
72
|
+
let getFundingDepositAddress;
|
|
73
|
+
(function (getFundingDepositAddress) {
|
|
74
|
+
/** Path parameters for reading a funding deposit address. */
|
|
75
|
+
getFundingDepositAddress.Params = z
|
|
76
|
+
.object({
|
|
77
|
+
id: z
|
|
78
|
+
.string()
|
|
79
|
+
.check(z.regex(/^fda_[A-Za-z0-9_-]+$/), z.describe('Funding deposit address ID (`fda_…`).'), z.meta({ examples: ['fda_001785792000000_2ZPE2gvateYEQ0dQslgvkhjx'] })),
|
|
80
|
+
})
|
|
81
|
+
.check(z.describe('Path parameters for reading a funding deposit address.'));
|
|
82
|
+
/** One reusable funding deposit address. */
|
|
83
|
+
getFundingDepositAddress.Response = schema.DepositAddress;
|
|
84
|
+
})(getFundingDepositAddress = schema.getFundingDepositAddress || (schema.getFundingDepositAddress = {}));
|
|
85
|
+
/** Schemas for the reconcileFundingDepositAddress operation. */
|
|
86
|
+
let reconcileFundingDepositAddress;
|
|
87
|
+
(function (reconcileFundingDepositAddress) {
|
|
88
|
+
/** Path parameters for reconciling a funding deposit address. */
|
|
89
|
+
reconcileFundingDepositAddress.Params = getFundingDepositAddress.Params;
|
|
90
|
+
})(reconcileFundingDepositAddress = schema.reconcileFundingDepositAddress || (schema.reconcileFundingDepositAddress = {}));
|
|
91
|
+
/** Schemas for the listFundingDepositAddressDeposits operation. */
|
|
92
|
+
let listFundingDepositAddressDeposits;
|
|
93
|
+
(function (listFundingDepositAddressDeposits) {
|
|
94
|
+
/** Path parameters for listing one address's deposits. */
|
|
95
|
+
listFundingDepositAddressDeposits.Params = getFundingDepositAddress.Params;
|
|
96
|
+
/** Query parameters for listing one address's deposits. */
|
|
97
|
+
listFundingDepositAddressDeposits.Query = z
|
|
98
|
+
.object({ cursor: Schema.Cursor, limit: Schema.Limit })
|
|
99
|
+
.check(z.describe('Paging parameters for one funding deposit address.'));
|
|
100
|
+
/** One page of detected deposits. */
|
|
101
|
+
listFundingDepositAddressDeposits.Response = z
|
|
102
|
+
.object({
|
|
103
|
+
data: z.array(schema.Deposit).check(z.describe('Detected deposits on this page, newest first.')),
|
|
104
|
+
nextCursor: Schema.NextCursor,
|
|
105
|
+
})
|
|
106
|
+
.check(z.describe('A page of deposits detected for one funding deposit address.'));
|
|
107
|
+
})(listFundingDepositAddressDeposits = schema.listFundingDepositAddressDeposits || (schema.listFundingDepositAddressDeposits = {}));
|
|
108
|
+
/** Schemas for the listFundingDeposits operation. */
|
|
109
|
+
let listFundingDeposits;
|
|
110
|
+
(function (listFundingDeposits) {
|
|
111
|
+
/** Query parameters for listing one account's funding deposits. */
|
|
112
|
+
listFundingDeposits.Query = z
|
|
113
|
+
.object({
|
|
114
|
+
cursor: Schema.Cursor,
|
|
115
|
+
limit: Schema.Limit,
|
|
116
|
+
recipient: Schema.Address.check(z.describe('Tempo account that received the deposits.')),
|
|
117
|
+
})
|
|
118
|
+
.check(z.describe('Recipient filter and paging parameters for funding deposits.'));
|
|
119
|
+
/** One page of detected deposits. */
|
|
120
|
+
listFundingDeposits.Response = z
|
|
121
|
+
.object({
|
|
122
|
+
data: z.array(schema.Deposit).check(z.describe('Detected deposits on this page, newest first.')),
|
|
123
|
+
nextCursor: Schema.NextCursor,
|
|
124
|
+
})
|
|
125
|
+
.check(z.describe('A page of deposits received by one Tempo account.'));
|
|
126
|
+
})(listFundingDeposits = schema.listFundingDeposits || (schema.listFundingDeposits = {}));
|
|
127
|
+
/** Schemas for the getFundingDeposit operation. */
|
|
128
|
+
let getFundingDeposit;
|
|
129
|
+
(function (getFundingDeposit) {
|
|
130
|
+
/** Path parameters for reading a funding deposit. */
|
|
131
|
+
getFundingDeposit.Params = z
|
|
132
|
+
.object({
|
|
133
|
+
id: z
|
|
134
|
+
.string()
|
|
135
|
+
.check(z.regex(/^fdp_[A-Za-z0-9_-]+$/), z.describe('Funding deposit id (`fdp_…`).'), z.meta({ examples: ['fdp_001785792060000_2ZPE2gvateYEQ0dQslgvkhjx'] })),
|
|
136
|
+
})
|
|
137
|
+
.check(z.describe('Path parameters for reading a funding deposit.'));
|
|
138
|
+
/** One detected funding deposit. */
|
|
139
|
+
getFundingDeposit.Response = schema.Deposit;
|
|
140
|
+
})(getFundingDeposit = schema.getFundingDeposit || (schema.getFundingDeposit = {}));
|
|
141
|
+
})(schema || (schema = {}));
|
|
142
|
+
const tronChainExample = {
|
|
143
|
+
addressFormat: 'base58check',
|
|
144
|
+
id: 'tron:0x2b6653dc',
|
|
145
|
+
kind: 'tron',
|
|
146
|
+
name: 'Tron',
|
|
147
|
+
};
|
|
148
|
+
const tronUsdtExample = {
|
|
149
|
+
address: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
|
|
150
|
+
currency: 'USD',
|
|
151
|
+
decimals: 6,
|
|
152
|
+
name: 'Tether USD',
|
|
153
|
+
standard: 'TRC-20',
|
|
154
|
+
symbol: 'USDT',
|
|
155
|
+
tokenKey: 'tron:0x2b6653dc/trc20:TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
|
|
156
|
+
verified: true,
|
|
157
|
+
};
|
|
158
|
+
const tempoChainExample = {
|
|
159
|
+
addressFormat: 'hex',
|
|
160
|
+
id: 'eip155:4217',
|
|
161
|
+
kind: 'evm',
|
|
162
|
+
name: 'Tempo',
|
|
163
|
+
};
|
|
164
|
+
const tempoUsdt0Example = {
|
|
165
|
+
address: '0x20c00000000000000000000014f22ca97301eb73',
|
|
166
|
+
currency: 'USD',
|
|
167
|
+
decimals: 6,
|
|
168
|
+
name: 'USDT0',
|
|
169
|
+
standard: 'TIP-20',
|
|
170
|
+
symbol: 'USDT0',
|
|
171
|
+
tokenKey: 'eip155:4217/erc20:0x20c00000000000000000000014f22ca97301eb73',
|
|
172
|
+
verified: true,
|
|
173
|
+
};
|
|
174
|
+
const fundingDepositAddressExample = {
|
|
175
|
+
address: 'TJRabPrwbZy45sbavfcjinPJC18kjpRTv8',
|
|
176
|
+
createdAt: '2026-08-04T00:00:00.000Z',
|
|
177
|
+
destinationChain: tempoChainExample,
|
|
178
|
+
destinationToken: tempoUsdt0Example,
|
|
179
|
+
id: 'fda_001785792000000_2ZPE2gvateYEQ0dQslgvkhjx',
|
|
180
|
+
provider: { id: 'relay', name: 'Relay' },
|
|
181
|
+
recipient: `0x${'11'.repeat(20)}`,
|
|
182
|
+
refundAddress: 'TJRabPrwbZy45sbavfcjinPJC18kjpRTv8',
|
|
183
|
+
sourceChain: tronChainExample,
|
|
184
|
+
sourceToken: tronUsdtExample,
|
|
185
|
+
status: 'active',
|
|
186
|
+
subsidize: false,
|
|
187
|
+
updatedAt: '2026-08-04T00:00:00.000Z',
|
|
188
|
+
};
|
|
189
|
+
const createdFundingDepositAddressExample = {
|
|
190
|
+
...fundingDepositAddressExample,
|
|
191
|
+
destinationAmount: Value.tokenAmount({ baseUnits: '1000000', currency: 'USD', decimals: 6 }),
|
|
192
|
+
destinationAmountMin: Value.tokenAmount({
|
|
193
|
+
baseUnits: '1000000',
|
|
194
|
+
currency: 'USD',
|
|
195
|
+
decimals: 6,
|
|
196
|
+
}),
|
|
197
|
+
fees: [],
|
|
198
|
+
quote: { sampledAt: '2026-08-04T00:00:00.000Z' },
|
|
199
|
+
sourceAmount: Value.tokenAmount({ baseUnits: '1000000', currency: 'USD', decimals: 6 }),
|
|
200
|
+
};
|
|
201
|
+
const fundingDepositExample = {
|
|
202
|
+
createdAt: '2026-08-04T00:01:00.000Z',
|
|
203
|
+
depositAddressId: fundingDepositAddressExample.id,
|
|
204
|
+
detectionTrigger: 'webhook',
|
|
205
|
+
destinationChain: tempoChainExample,
|
|
206
|
+
destinationToken: tempoUsdt0Example,
|
|
207
|
+
id: 'fdp_001785792060000_2ZPE2gvateYEQ0dQslgvkhjx',
|
|
208
|
+
provider: { id: 'relay', name: 'Relay' },
|
|
209
|
+
recipient: fundingDepositAddressExample.recipient,
|
|
210
|
+
refundAddress: fundingDepositAddressExample.refundAddress,
|
|
211
|
+
sourceChain: tronChainExample,
|
|
212
|
+
sourceToken: tronUsdtExample,
|
|
213
|
+
sourceTransactionHashes: ['9f2c4c80dbb468878baa8c07ff523e0464bf6437f2a486268508485d83f14d12'],
|
|
214
|
+
status: 'detected',
|
|
215
|
+
updatedAt: '2026-08-04T00:01:00.000Z',
|
|
216
|
+
};
|
|
217
|
+
const fundingDepositAddressDepositsExample = {
|
|
218
|
+
data: [fundingDepositExample],
|
|
219
|
+
nextCursor: null,
|
|
220
|
+
};
|
|
221
|
+
const fundingDepositsExample = {
|
|
222
|
+
data: [fundingDepositExample],
|
|
223
|
+
nextCursor: null,
|
|
224
|
+
};
|
|
225
|
+
const reconcileFundingDepositAddressResponses = (() => {
|
|
226
|
+
const responses = OpenApi.responses({
|
|
227
|
+
errors: {
|
|
228
|
+
400: { codes: ['param_invalid'] },
|
|
229
|
+
403: {
|
|
230
|
+
codes: ['api_key_forbidden'],
|
|
231
|
+
description: 'The API key does not grant funding write access.',
|
|
232
|
+
},
|
|
233
|
+
404: {
|
|
234
|
+
codes: ['funding_deposit_address_not_found'],
|
|
235
|
+
description: 'No visible funding deposit address exists for the identifier.',
|
|
236
|
+
},
|
|
237
|
+
409: {
|
|
238
|
+
codes: ['funding_deposit_address_not_active'],
|
|
239
|
+
description: 'The funding deposit address is not active.',
|
|
240
|
+
},
|
|
241
|
+
501: {
|
|
242
|
+
codes: ['not_implemented'],
|
|
243
|
+
description: 'Manual funding reconciliation is not configured.',
|
|
244
|
+
},
|
|
245
|
+
502: {
|
|
246
|
+
codes: ['upstream_error'],
|
|
247
|
+
description: 'Reconciliation could not be queued.',
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
success: {
|
|
251
|
+
description: 'Unused because reconciliation returns an empty `202` response.',
|
|
252
|
+
schema: z.never(),
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
const { 200: success, ...errors } = responses;
|
|
256
|
+
void success;
|
|
257
|
+
return {
|
|
258
|
+
202: {
|
|
259
|
+
description: 'The funding deposit address was queued for reconciliation.',
|
|
260
|
+
headers: { ...OpenApi.successHeaders },
|
|
261
|
+
},
|
|
262
|
+
...errors,
|
|
263
|
+
};
|
|
264
|
+
})();
|
|
265
|
+
// Replay the exact successful creation response for 24 hours.
|
|
266
|
+
const idempotencyReplayTtlMs = 86_400_000;
|
|
267
|
+
// Pending claims only cover the bounded provider preparation window.
|
|
268
|
+
const idempotencyPendingTtlMs = 60_000;
|
|
269
|
+
const depositAddressCheckpoint = Schema.describe(z.object({
|
|
270
|
+
candidate: Schema.describe(z.object({
|
|
271
|
+
destinationChain: FundingProvider.schema.ChainRef,
|
|
272
|
+
destinationToken: FundingProvider.schema.TokenRef,
|
|
273
|
+
sourceChain: FundingProvider.schema.ChainRef,
|
|
274
|
+
sourceToken: FundingProvider.schema.TokenRef,
|
|
275
|
+
}), 'Normalized route retained after provider provisioning.'),
|
|
276
|
+
provider: FundingProvider.schema.ProviderRef,
|
|
277
|
+
recipient: z.string().check(z.describe('Normalized Tempo recipient.')),
|
|
278
|
+
refundAddress: z.string().check(z.describe('Normalized source-chain refund account.')),
|
|
279
|
+
result: FundingProvider.schema.DepositAddressResult,
|
|
280
|
+
}), 'Provider result retained until deposit address creation completes.');
|
|
281
|
+
const fundingEtag = etag();
|
|
282
|
+
/** Mounts reusable deposit-address and detected-deposit routes. */
|
|
283
|
+
export function depositAddresses(options = {}) {
|
|
284
|
+
return new Hono()
|
|
285
|
+
.post('/webhooks/funding/:providerId', async (c) => {
|
|
286
|
+
const provider = c
|
|
287
|
+
.get('providers')
|
|
288
|
+
.filter(FundingProvider.is)
|
|
289
|
+
.find((candidate) => candidate.id === c.req.param('providerId'));
|
|
290
|
+
if (!provider || !FundingProvider.canReceiveWebhook(provider))
|
|
291
|
+
return c.body(null, 404);
|
|
292
|
+
try {
|
|
293
|
+
await provider.webhook.receive({
|
|
294
|
+
body: await c.req.text(),
|
|
295
|
+
headers: c.req.raw.headers,
|
|
296
|
+
});
|
|
297
|
+
return c.body(null, 204);
|
|
298
|
+
}
|
|
299
|
+
catch (cause) {
|
|
300
|
+
if (cause instanceof FundingProvider.WebhookAuthenticationError)
|
|
301
|
+
return c.body(null, 401);
|
|
302
|
+
if (cause instanceof FundingProvider.WebhookPayloadError)
|
|
303
|
+
return c.body(null, 400);
|
|
304
|
+
throw cause;
|
|
305
|
+
}
|
|
306
|
+
})
|
|
307
|
+
.post('/v1/funding/deposit-addresses', Auth.policy({ apiKey: { scopes: ['funding:write'] } }), OpenApi.validate('header', schema.createFundingDepositAddress.Headers, {
|
|
308
|
+
code: 'idempotency_key_required',
|
|
309
|
+
message: 'Funding deposit address creation requires an `Idempotency-Key` header.',
|
|
310
|
+
}), OpenApi.validate('json', schema.createFundingDepositAddress.Body, {
|
|
311
|
+
code: 'body_invalid',
|
|
312
|
+
message: 'Check the request body and try again.',
|
|
313
|
+
}), OpenApi.describeRoute({
|
|
314
|
+
description: 'Creates or returns a reusable deposit address for funding a Tempo account.',
|
|
315
|
+
operationId: 'createFundingDepositAddress',
|
|
316
|
+
responses: OpenApi.responses({
|
|
317
|
+
errors: {
|
|
318
|
+
400: { codes: ['body_invalid', 'idempotency_key_required'] },
|
|
319
|
+
404: {
|
|
320
|
+
codes: ['deposit_address_not_available'],
|
|
321
|
+
description: 'No provider can provision the requested reusable address route.',
|
|
322
|
+
},
|
|
323
|
+
409: {
|
|
324
|
+
codes: ['idempotency_conflict', 'idempotency_in_progress'],
|
|
325
|
+
description: 'The Idempotency-Key conflicts with an existing or in-flight request.',
|
|
326
|
+
},
|
|
327
|
+
502: {
|
|
328
|
+
codes: ['upstream_error'],
|
|
329
|
+
description: 'The selected provider could not provision the address.',
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
success: {
|
|
333
|
+
description: 'The created or matching reusable funding deposit address and quote.',
|
|
334
|
+
example: createdFundingDepositAddressExample,
|
|
335
|
+
schema: schema.createFundingDepositAddress.Response,
|
|
336
|
+
},
|
|
337
|
+
}),
|
|
338
|
+
summary: 'Create deposit address',
|
|
339
|
+
tags: ['Deposit Addresses'],
|
|
340
|
+
}), async (c) => {
|
|
341
|
+
if (Auth.narrowAccess)
|
|
342
|
+
return Auth.accessError(c);
|
|
343
|
+
if (OpenApi.narrowValidation)
|
|
344
|
+
return OpenApi.validationError(c, {
|
|
345
|
+
code: 'body_invalid',
|
|
346
|
+
message: 'Check the request body and try again.',
|
|
347
|
+
});
|
|
348
|
+
const principal = Auth.getPrincipal(c);
|
|
349
|
+
const owner = Access.owner(c);
|
|
350
|
+
if (!owner || principal?.type !== 'api_key')
|
|
351
|
+
return Access.unauthorized(c);
|
|
352
|
+
c.header('Cache-Control', 'no-store');
|
|
353
|
+
const body = c.req.valid('json');
|
|
354
|
+
const subsidize = body.subsidize ?? false;
|
|
355
|
+
const db = Db.get(c.get('db'));
|
|
356
|
+
const identity = {
|
|
357
|
+
apiKeyId: principal.id,
|
|
358
|
+
keyHash: Idempotency.inputHash(c.req.valid('header')['idempotency-key']),
|
|
359
|
+
requestHash: Idempotency.inputHash({
|
|
360
|
+
body,
|
|
361
|
+
endpoint: 'POST /v1/funding/deposit-addresses',
|
|
362
|
+
}),
|
|
363
|
+
};
|
|
364
|
+
const claim = await FundingIdempotency.claim(db, {
|
|
365
|
+
...identity,
|
|
366
|
+
ttlMs: idempotencyPendingTtlMs,
|
|
367
|
+
});
|
|
368
|
+
if (claim.type === 'mismatch')
|
|
369
|
+
return Response.error(c, {
|
|
370
|
+
code: 'idempotency_conflict',
|
|
371
|
+
message: 'This Idempotency-Key was already used with different input.',
|
|
372
|
+
status: 409,
|
|
373
|
+
});
|
|
374
|
+
if (claim.type === 'pending')
|
|
375
|
+
return Response.error(c, {
|
|
376
|
+
code: 'idempotency_in_progress',
|
|
377
|
+
message: 'An equivalent funding deposit address creation is still running.',
|
|
378
|
+
status: 409,
|
|
379
|
+
});
|
|
380
|
+
if (claim.type === 'replay') {
|
|
381
|
+
const response = JSON.parse(claim.response);
|
|
382
|
+
return c.json(Response.validated(schema.createFundingDepositAddress.Response, {
|
|
383
|
+
...response,
|
|
384
|
+
subsidize: response['subsidize'] ?? false,
|
|
385
|
+
}), 200);
|
|
386
|
+
}
|
|
387
|
+
const release = async () => {
|
|
388
|
+
try {
|
|
389
|
+
await FundingIdempotency.release(db, {
|
|
390
|
+
apiKeyId: identity.apiKeyId,
|
|
391
|
+
createdAt: claim.createdAt,
|
|
392
|
+
keyHash: identity.keyHash,
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
catch {
|
|
396
|
+
// The pending claim expires on its own; the response stays authoritative.
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
const complete = async (database, response) => {
|
|
400
|
+
const validated = Response.validated(schema.createFundingDepositAddress.Response, response);
|
|
401
|
+
const completion = await FundingIdempotency.complete(database, {
|
|
402
|
+
apiKeyId: identity.apiKeyId,
|
|
403
|
+
createdAt: claim.createdAt,
|
|
404
|
+
keyHash: identity.keyHash,
|
|
405
|
+
replayTtlMs: idempotencyReplayTtlMs,
|
|
406
|
+
response: JSON.stringify(validated),
|
|
407
|
+
});
|
|
408
|
+
if (!completion)
|
|
409
|
+
throw new IdempotencyLeaseLostError('Funding deposit address creation lease was lost.');
|
|
410
|
+
return validated;
|
|
411
|
+
};
|
|
412
|
+
const attempts = [];
|
|
413
|
+
const provisioning = { succeeded: claim.type === 'resume' };
|
|
414
|
+
try {
|
|
415
|
+
const selected = await (async () => {
|
|
416
|
+
if (claim.type === 'resume')
|
|
417
|
+
return depositAddressCheckpoint.parse(JSON.parse(claim.response));
|
|
418
|
+
const catalog = await FundingCatalog.snapshot(c);
|
|
419
|
+
const providers = c.get('providers').filter(FundingProvider.is);
|
|
420
|
+
const provisioned = await FundingProvider.createDepositAddress({
|
|
421
|
+
amount: body.amount,
|
|
422
|
+
catalog,
|
|
423
|
+
destinationChainId: c.get('chainId'),
|
|
424
|
+
destinationToken: body.destinationToken,
|
|
425
|
+
onAttempt: (attempt) => attempts.push(attempt),
|
|
426
|
+
providers,
|
|
427
|
+
recipient: body.recipient,
|
|
428
|
+
refundAddress: body.refundAddress,
|
|
429
|
+
sourceChain: body.sourceChain,
|
|
430
|
+
sourceToken: body.sourceToken,
|
|
431
|
+
subsidize,
|
|
432
|
+
});
|
|
433
|
+
if (!provisioned)
|
|
434
|
+
return undefined;
|
|
435
|
+
provisioning.succeeded = true;
|
|
436
|
+
const checkpoint = depositAddressCheckpoint.parse({
|
|
437
|
+
candidate: provisioned.candidate,
|
|
438
|
+
provider: provisioned.provider,
|
|
439
|
+
recipient: provisioned.recipient,
|
|
440
|
+
refundAddress: provisioned.refundAddress,
|
|
441
|
+
result: provisioned.result,
|
|
442
|
+
});
|
|
443
|
+
const stored = await FundingIdempotency.checkpoint(db, {
|
|
444
|
+
apiKeyId: identity.apiKeyId,
|
|
445
|
+
createdAt: claim.createdAt,
|
|
446
|
+
keyHash: identity.keyHash,
|
|
447
|
+
replayTtlMs: idempotencyReplayTtlMs,
|
|
448
|
+
response: JSON.stringify(checkpoint),
|
|
449
|
+
});
|
|
450
|
+
if (!stored)
|
|
451
|
+
throw new IdempotencyLeaseLostError('Funding deposit address creation lease was lost.');
|
|
452
|
+
return checkpoint;
|
|
453
|
+
})();
|
|
454
|
+
if (attempts.length > 0)
|
|
455
|
+
c.set('fundingProviderAttempts', attempts.map(ProviderAttempts.format));
|
|
456
|
+
if (!selected) {
|
|
457
|
+
await release();
|
|
458
|
+
return depositAddressNotAvailable(c);
|
|
459
|
+
}
|
|
460
|
+
const { candidate: selectedCandidate, provider, recipient, refundAddress, result, } = selected;
|
|
461
|
+
const feeToken = (side) => side === 'source' ? selectedCandidate.sourceToken : selectedCandidate.destinationToken;
|
|
462
|
+
const created = await db.transaction(async (tx) => {
|
|
463
|
+
const stored = await FundingDepositAddress.createOrGet(tx, {
|
|
464
|
+
apiKeyId: principal.id,
|
|
465
|
+
deliveryStrategy: 'provider',
|
|
466
|
+
environment: owner.environment,
|
|
467
|
+
orgId: owner.orgId,
|
|
468
|
+
...(owner.projectId === undefined ? {} : { projectId: owner.projectId }),
|
|
469
|
+
providerOutputToken: selectedCandidate.destinationToken,
|
|
470
|
+
providerRequestIds: [result.correlation.requestId],
|
|
471
|
+
providerState: result.correlation.checkEndpoint === undefined
|
|
472
|
+
? undefined
|
|
473
|
+
: { checkEndpoint: result.correlation.checkEndpoint },
|
|
474
|
+
snapshot: {
|
|
475
|
+
address: result.address,
|
|
476
|
+
destinationChain: selectedCandidate.destinationChain,
|
|
477
|
+
destinationToken: selectedCandidate.destinationToken,
|
|
478
|
+
provider,
|
|
479
|
+
recipient,
|
|
480
|
+
refundAddress,
|
|
481
|
+
sourceChain: selectedCandidate.sourceChain,
|
|
482
|
+
sourceToken: selectedCandidate.sourceToken,
|
|
483
|
+
subsidize,
|
|
484
|
+
},
|
|
485
|
+
});
|
|
486
|
+
const record = FundingDepositAddress.toPublic(stored.record);
|
|
487
|
+
if (stored.type === 'existing')
|
|
488
|
+
return complete(tx, record);
|
|
489
|
+
return complete(tx, {
|
|
490
|
+
...record,
|
|
491
|
+
destinationAmount: Value.tokenAmount({
|
|
492
|
+
baseUnits: result.destinationAmount,
|
|
493
|
+
currency: selectedCandidate.destinationToken.currency,
|
|
494
|
+
decimals: selectedCandidate.destinationToken.decimals,
|
|
495
|
+
}),
|
|
496
|
+
destinationAmountMin: Value.tokenAmount({
|
|
497
|
+
baseUnits: result.destinationAmountMin,
|
|
498
|
+
currency: selectedCandidate.destinationToken.currency,
|
|
499
|
+
decimals: selectedCandidate.destinationToken.decimals,
|
|
500
|
+
}),
|
|
501
|
+
fees: result.fees.map((fee) => ({
|
|
502
|
+
amount: Value.tokenAmount({
|
|
503
|
+
baseUnits: fee.amount,
|
|
504
|
+
currency: feeToken(fee.side).currency,
|
|
505
|
+
decimals: feeToken(fee.side).decimals,
|
|
506
|
+
}),
|
|
507
|
+
side: fee.side,
|
|
508
|
+
token: feeToken(fee.side),
|
|
509
|
+
type: 'provider',
|
|
510
|
+
})),
|
|
511
|
+
quote: {
|
|
512
|
+
...(result.expiresAt === undefined ? {} : { expiresAt: result.expiresAt }),
|
|
513
|
+
sampledAt: result.sampledAt,
|
|
514
|
+
},
|
|
515
|
+
sourceAmount: Value.tokenAmount({
|
|
516
|
+
baseUnits: body.amount,
|
|
517
|
+
currency: selectedCandidate.sourceToken.currency,
|
|
518
|
+
decimals: selectedCandidate.sourceToken.decimals,
|
|
519
|
+
}),
|
|
520
|
+
});
|
|
521
|
+
});
|
|
522
|
+
if (options.registerDepositAddress)
|
|
523
|
+
await options.registerDepositAddress(FundingDepositAddress.schema.FundingDepositAddress.parse(created));
|
|
524
|
+
return c.json(created, 200);
|
|
525
|
+
}
|
|
526
|
+
catch (cause) {
|
|
527
|
+
if (!provisioning.succeeded)
|
|
528
|
+
await release();
|
|
529
|
+
if (cause instanceof FundingProvider.InvalidRefundAddressError)
|
|
530
|
+
return Response.error(c, {
|
|
531
|
+
code: 'body_invalid',
|
|
532
|
+
message: 'Invalid `refundAddress`.',
|
|
533
|
+
status: 400,
|
|
534
|
+
});
|
|
535
|
+
if (cause instanceof FundingProvider.UnsupportedSourceChainError)
|
|
536
|
+
return depositAddressNotAvailable(c);
|
|
537
|
+
if (cause instanceof FundingDepositAddress.ConflictError ||
|
|
538
|
+
cause instanceof FundingProvider.ProviderQuoteError) {
|
|
539
|
+
if (cause instanceof FundingDepositAddress.ConflictError)
|
|
540
|
+
return Response.error(c, {
|
|
541
|
+
code: 'upstream_error',
|
|
542
|
+
message: 'The funding provider returned a conflicting deposit address.',
|
|
543
|
+
status: 502,
|
|
544
|
+
});
|
|
545
|
+
if (cause.fundingProviderAttempts.length > 0)
|
|
546
|
+
c.set('fundingProviderAttempts', cause.fundingProviderAttempts);
|
|
547
|
+
if (cause.providerFailure)
|
|
548
|
+
c.set('providerFailure', cause.providerFailure);
|
|
549
|
+
if (cause.providerFailures)
|
|
550
|
+
c.set('providerFailures', cause.providerFailures);
|
|
551
|
+
return Response.error(c, {
|
|
552
|
+
code: 'upstream_error',
|
|
553
|
+
message: 'The funding provider could not provision the deposit address.',
|
|
554
|
+
status: 502,
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
throw cause;
|
|
558
|
+
}
|
|
559
|
+
})
|
|
560
|
+
.get('/v1/funding/deposit-addresses/:id{fda_[A-Za-z0-9_-]+}/deposits', Auth.policy({ apiKey: { scopes: ['funding:read'] } }), OpenApi.validate('param', schema.listFundingDepositAddressDeposits.Params, {
|
|
561
|
+
code: 'param_invalid',
|
|
562
|
+
message: 'Check the path parameters and try again.',
|
|
563
|
+
}), OpenApi.validate('query', schema.listFundingDepositAddressDeposits.Query, {
|
|
564
|
+
code: 'query_invalid',
|
|
565
|
+
message: 'Check the query parameters and try again.',
|
|
566
|
+
}), OpenApi.describeRoute({
|
|
567
|
+
description: 'Lists deposits detected for one funding deposit address, newest first.',
|
|
568
|
+
operationId: 'listFundingDepositAddressDeposits',
|
|
569
|
+
responses: OpenApi.responses({
|
|
570
|
+
errors: {
|
|
571
|
+
400: { codes: ['param_invalid', 'query_invalid'] },
|
|
572
|
+
404: {
|
|
573
|
+
codes: ['funding_deposit_address_not_found'],
|
|
574
|
+
description: 'No visible funding deposit address exists for the identifier.',
|
|
575
|
+
},
|
|
576
|
+
},
|
|
577
|
+
success: {
|
|
578
|
+
description: 'A page of deposits detected for the address.',
|
|
579
|
+
example: fundingDepositAddressDepositsExample,
|
|
580
|
+
schema: schema.listFundingDepositAddressDeposits.Response,
|
|
581
|
+
},
|
|
582
|
+
}),
|
|
583
|
+
summary: 'List deposits by deposit address',
|
|
584
|
+
tags: ['Deposit Addresses'],
|
|
585
|
+
}), async (c) => {
|
|
586
|
+
if (Auth.narrowAccess)
|
|
587
|
+
return Auth.accessError(c);
|
|
588
|
+
if (OpenApi.narrowValidation)
|
|
589
|
+
return OpenApi.validationError(c, {
|
|
590
|
+
code: 'query_invalid',
|
|
591
|
+
message: 'Check the request parameters and try again.',
|
|
592
|
+
});
|
|
593
|
+
const owner = Access.owner(c);
|
|
594
|
+
if (!owner)
|
|
595
|
+
return Access.unauthorized(c);
|
|
596
|
+
const db = Db.get(c.get('db'));
|
|
597
|
+
const { id } = c.req.valid('param');
|
|
598
|
+
const { cursor, limit } = c.req.valid('query');
|
|
599
|
+
const decoded = cursor ? Cursor.decode(cursor, ['timestamp', 'id']) : undefined;
|
|
600
|
+
try {
|
|
601
|
+
const address = await FundingDepositAddresses.getOwned(db, owner, id);
|
|
602
|
+
if (!address)
|
|
603
|
+
return Response.error(c, {
|
|
604
|
+
code: 'funding_deposit_address_not_found',
|
|
605
|
+
message: 'Funding deposit address not found',
|
|
606
|
+
status: 404,
|
|
607
|
+
});
|
|
608
|
+
const rows = await FundingDeposits.listByAddress(db, owner, id, {
|
|
609
|
+
...(decoded
|
|
610
|
+
? { cursor: { createdAt: String(decoded[0]), id: String(decoded[1]) } }
|
|
611
|
+
: {}),
|
|
612
|
+
limit: limit + 1,
|
|
613
|
+
});
|
|
614
|
+
const page = Cursor.paginate({
|
|
615
|
+
key: (record) => [record.createdAt, record.id],
|
|
616
|
+
limit,
|
|
617
|
+
rows,
|
|
618
|
+
});
|
|
619
|
+
const data = page.rows.map(FundingDeposit.toPublic);
|
|
620
|
+
c.header('Cache-Control', Cache.policies.noStore);
|
|
621
|
+
c.header('Vary', 'Accept-Encoding, Authorization, Tempo-API-Key, X-API-Key');
|
|
622
|
+
return c.json(Response.validated(schema.listFundingDepositAddressDeposits.Response, {
|
|
623
|
+
data,
|
|
624
|
+
nextCursor: page.nextCursor,
|
|
625
|
+
}), 200);
|
|
626
|
+
}
|
|
627
|
+
catch (cause) {
|
|
628
|
+
return Response.upstream(c, cause);
|
|
629
|
+
}
|
|
630
|
+
})
|
|
631
|
+
.get('/v1/funding/deposit-addresses/:id{fda_[A-Za-z0-9_-]+}', Auth.policy({ apiKey: { scopes: ['funding:read'] } }), OpenApi.validate('param', schema.getFundingDepositAddress.Params, {
|
|
632
|
+
code: 'param_invalid',
|
|
633
|
+
message: 'Check the path parameters and try again.',
|
|
634
|
+
}), OpenApi.describeRoute({
|
|
635
|
+
description: 'Returns one reusable funding deposit address.',
|
|
636
|
+
operationId: 'getFundingDepositAddress',
|
|
637
|
+
responses: OpenApi.responses({
|
|
638
|
+
errors: {
|
|
639
|
+
400: { codes: ['param_invalid'] },
|
|
640
|
+
404: {
|
|
641
|
+
codes: ['funding_deposit_address_not_found'],
|
|
642
|
+
description: 'No visible funding deposit address exists for the identifier.',
|
|
643
|
+
},
|
|
644
|
+
},
|
|
645
|
+
success: {
|
|
646
|
+
description: 'The requested funding deposit address.',
|
|
647
|
+
example: fundingDepositAddressExample,
|
|
648
|
+
schema: schema.getFundingDepositAddress.Response,
|
|
649
|
+
},
|
|
650
|
+
}),
|
|
651
|
+
summary: 'Get deposit address',
|
|
652
|
+
tags: ['Deposit Addresses'],
|
|
653
|
+
}), async (c) => {
|
|
654
|
+
if (Auth.narrowAccess)
|
|
655
|
+
return Auth.accessError(c);
|
|
656
|
+
if (OpenApi.narrowValidation)
|
|
657
|
+
return OpenApi.validationError(c, {
|
|
658
|
+
code: 'param_invalid',
|
|
659
|
+
message: 'Check the path parameters and try again.',
|
|
660
|
+
});
|
|
661
|
+
const owner = Access.owner(c);
|
|
662
|
+
if (!owner)
|
|
663
|
+
return Access.unauthorized(c);
|
|
664
|
+
const db = Db.get(c.get('db'));
|
|
665
|
+
const { id } = c.req.valid('param');
|
|
666
|
+
try {
|
|
667
|
+
const record = await FundingDepositAddresses.getOwned(db, owner, id);
|
|
668
|
+
if (!record)
|
|
669
|
+
return Response.error(c, {
|
|
670
|
+
code: 'funding_deposit_address_not_found',
|
|
671
|
+
message: 'Funding deposit address not found',
|
|
672
|
+
status: 404,
|
|
673
|
+
});
|
|
674
|
+
c.header('Cache-Control', Cache.policies.noStore);
|
|
675
|
+
c.header('Vary', 'Accept-Encoding, Authorization, Tempo-API-Key, X-API-Key');
|
|
676
|
+
return c.json(Response.validated(schema.getFundingDepositAddress.Response, FundingDepositAddress.toPublic(record)), 200);
|
|
677
|
+
}
|
|
678
|
+
catch (cause) {
|
|
679
|
+
return Response.upstream(c, cause);
|
|
680
|
+
}
|
|
681
|
+
})
|
|
682
|
+
.post('/v1/funding/deposit-addresses/:id{fda_[A-Za-z0-9_-]+}/reconcile', Auth.policy({
|
|
683
|
+
apiKey: { rateLimit: { perMinute: 10 }, scopes: ['funding:write'] },
|
|
684
|
+
}), OpenApi.validate('param', schema.reconcileFundingDepositAddress.Params, {
|
|
685
|
+
code: 'param_invalid',
|
|
686
|
+
message: 'Check the path parameters and try again.',
|
|
687
|
+
}), OpenApi.describeRoute({
|
|
688
|
+
description: 'Queues an immediate provider reconciliation for one reusable funding deposit address.',
|
|
689
|
+
operationId: 'reconcileFundingDepositAddress',
|
|
690
|
+
responses: reconcileFundingDepositAddressResponses,
|
|
691
|
+
summary: 'Reconcile deposit address',
|
|
692
|
+
tags: ['Deposit Addresses'],
|
|
693
|
+
}), async (c) => {
|
|
694
|
+
if (Auth.narrowAccess)
|
|
695
|
+
return Auth.accessError(c);
|
|
696
|
+
if (OpenApi.narrowValidation)
|
|
697
|
+
return OpenApi.validationError(c, {
|
|
698
|
+
code: 'param_invalid',
|
|
699
|
+
message: 'Check the path parameters and try again.',
|
|
700
|
+
});
|
|
701
|
+
const owner = Access.owner(c);
|
|
702
|
+
if (!owner)
|
|
703
|
+
return Access.unauthorized(c);
|
|
704
|
+
c.header('Cache-Control', Cache.policies.noStore);
|
|
705
|
+
c.header('Vary', 'Accept-Encoding, Authorization, Tempo-API-Key');
|
|
706
|
+
const db = Db.get(c.get('db'));
|
|
707
|
+
const { id } = c.req.valid('param');
|
|
708
|
+
try {
|
|
709
|
+
const record = await FundingDepositAddresses.getOwned(db, owner, id);
|
|
710
|
+
if (!record)
|
|
711
|
+
return Response.error(c, {
|
|
712
|
+
code: 'funding_deposit_address_not_found',
|
|
713
|
+
message: 'Funding deposit address not found',
|
|
714
|
+
status: 404,
|
|
715
|
+
});
|
|
716
|
+
if (record.status !== 'active')
|
|
717
|
+
return Response.error(c, {
|
|
718
|
+
code: 'funding_deposit_address_not_active',
|
|
719
|
+
message: 'Funding deposit address is not active',
|
|
720
|
+
status: 409,
|
|
721
|
+
});
|
|
722
|
+
const dispatch = options.dispatchReconciliation;
|
|
723
|
+
if (!dispatch)
|
|
724
|
+
return Response.error(c, {
|
|
725
|
+
code: 'not_implemented',
|
|
726
|
+
message: 'Manual funding reconciliation is not configured',
|
|
727
|
+
status: 501,
|
|
728
|
+
});
|
|
729
|
+
await dispatch({
|
|
730
|
+
addressId: record.id,
|
|
731
|
+
trigger: 'manual',
|
|
732
|
+
type: 'funding:deposit-address:reconcile',
|
|
733
|
+
});
|
|
734
|
+
return c.body(null, 202);
|
|
735
|
+
}
|
|
736
|
+
catch (cause) {
|
|
737
|
+
return Response.upstream(c, cause);
|
|
738
|
+
}
|
|
739
|
+
})
|
|
740
|
+
.get('/v1/funding/deposits', Auth.policy({ apiKey: { scopes: ['funding:read'] } }), OpenApi.validate('query', schema.listFundingDeposits.Query, {
|
|
741
|
+
code: 'query_invalid',
|
|
742
|
+
message: 'Check the query parameters and try again.',
|
|
743
|
+
}), OpenApi.describeRoute({
|
|
744
|
+
description: 'Lists deposits received by one Tempo account, newest first.',
|
|
745
|
+
operationId: 'listFundingDeposits',
|
|
746
|
+
responses: OpenApi.responses({
|
|
747
|
+
errors: {
|
|
748
|
+
400: { codes: ['query_invalid'] },
|
|
749
|
+
},
|
|
750
|
+
success: {
|
|
751
|
+
description: 'A page of funding deposits received by the account.',
|
|
752
|
+
example: fundingDepositsExample,
|
|
753
|
+
schema: schema.listFundingDeposits.Response,
|
|
754
|
+
},
|
|
755
|
+
}),
|
|
756
|
+
summary: 'List deposits by recipient',
|
|
757
|
+
tags: ['Deposit Addresses'],
|
|
758
|
+
}), async (c) => {
|
|
759
|
+
if (Auth.narrowAccess)
|
|
760
|
+
return Auth.accessError(c);
|
|
761
|
+
if (OpenApi.narrowValidation)
|
|
762
|
+
return OpenApi.validationError(c, {
|
|
763
|
+
code: 'query_invalid',
|
|
764
|
+
message: 'Check the query parameters and try again.',
|
|
765
|
+
});
|
|
766
|
+
const owner = Access.owner(c);
|
|
767
|
+
if (!owner)
|
|
768
|
+
return Access.unauthorized(c);
|
|
769
|
+
const db = Db.get(c.get('db'));
|
|
770
|
+
const { cursor, limit, recipient } = c.req.valid('query');
|
|
771
|
+
const decoded = cursor ? Cursor.decode(cursor, ['timestamp', 'id']) : undefined;
|
|
772
|
+
try {
|
|
773
|
+
const rows = await FundingDeposits.listByRecipient(db, {
|
|
774
|
+
...(decoded
|
|
775
|
+
? { cursor: { createdAt: String(decoded[0]), id: String(decoded[1]) } }
|
|
776
|
+
: {}),
|
|
777
|
+
limit: limit + 1,
|
|
778
|
+
owner,
|
|
779
|
+
recipient,
|
|
780
|
+
});
|
|
781
|
+
const page = Cursor.paginate({
|
|
782
|
+
key: (record) => [record.createdAt, record.id],
|
|
783
|
+
limit,
|
|
784
|
+
rows,
|
|
785
|
+
});
|
|
786
|
+
const data = page.rows.map(FundingDeposit.toPublic);
|
|
787
|
+
c.header('Cache-Control', Cache.policies.noStore);
|
|
788
|
+
c.header('Vary', 'Accept-Encoding, Authorization, Tempo-API-Key');
|
|
789
|
+
return c.json(Response.validated(schema.listFundingDeposits.Response, {
|
|
790
|
+
data,
|
|
791
|
+
nextCursor: page.nextCursor,
|
|
792
|
+
}), 200);
|
|
793
|
+
}
|
|
794
|
+
catch (cause) {
|
|
795
|
+
return Response.upstream(c, cause);
|
|
796
|
+
}
|
|
797
|
+
})
|
|
798
|
+
.get('/v1/funding/deposits/:id{fdp_[A-Za-z0-9_-]+}', Auth.policy({ apiKey: { scopes: ['funding:read'] } }), OpenApi.validate('param', schema.getFundingDeposit.Params, {
|
|
799
|
+
code: 'param_invalid',
|
|
800
|
+
message: 'Check the path parameters and try again.',
|
|
801
|
+
}), OpenApi.describeRoute({
|
|
802
|
+
description: 'Returns one detected deposit and its delivery status.',
|
|
803
|
+
operationId: 'getFundingDeposit',
|
|
804
|
+
responses: OpenApi.responses({
|
|
805
|
+
errors: {
|
|
806
|
+
400: { codes: ['param_invalid'] },
|
|
807
|
+
404: {
|
|
808
|
+
codes: ['funding_deposit_not_found'],
|
|
809
|
+
description: 'No visible funding deposit exists for the identifier.',
|
|
810
|
+
},
|
|
811
|
+
},
|
|
812
|
+
success: {
|
|
813
|
+
description: 'The requested funding deposit.',
|
|
814
|
+
example: fundingDepositExample,
|
|
815
|
+
schema: schema.getFundingDeposit.Response,
|
|
816
|
+
},
|
|
817
|
+
}),
|
|
818
|
+
summary: 'Get deposit',
|
|
819
|
+
tags: ['Deposit Addresses'],
|
|
820
|
+
}), fundingEtag, async (c) => {
|
|
821
|
+
if (Auth.narrowAccess)
|
|
822
|
+
return Auth.accessError(c);
|
|
823
|
+
if (OpenApi.narrowValidation)
|
|
824
|
+
return OpenApi.validationError(c, {
|
|
825
|
+
code: 'param_invalid',
|
|
826
|
+
message: 'Check the path parameters and try again.',
|
|
827
|
+
});
|
|
828
|
+
const owner = Access.owner(c);
|
|
829
|
+
if (!owner)
|
|
830
|
+
return Access.unauthorized(c);
|
|
831
|
+
const db = Db.get(c.get('db'));
|
|
832
|
+
const { id } = c.req.valid('param');
|
|
833
|
+
try {
|
|
834
|
+
const record = await FundingDeposits.getOwned(db, owner, id);
|
|
835
|
+
if (!record)
|
|
836
|
+
return Response.error(c, {
|
|
837
|
+
code: 'funding_deposit_not_found',
|
|
838
|
+
message: 'Funding deposit not found',
|
|
839
|
+
status: 404,
|
|
840
|
+
});
|
|
841
|
+
c.header('Cache-Control', Cache.policies.state);
|
|
842
|
+
c.header('Vary', 'Accept-Encoding, Authorization, Tempo-API-Key, X-API-Key');
|
|
843
|
+
return c.json(Response.validated(schema.getFundingDeposit.Response, FundingDeposit.toPublic(record)), 200);
|
|
844
|
+
}
|
|
845
|
+
catch (cause) {
|
|
846
|
+
return Response.upstream(c, cause);
|
|
847
|
+
}
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
function depositAddressNotAvailable(c) {
|
|
851
|
+
return Response.error(c, {
|
|
852
|
+
code: 'deposit_address_not_available',
|
|
853
|
+
message: 'No provider can provision the requested reusable deposit address.',
|
|
854
|
+
status: 404,
|
|
855
|
+
});
|
|
856
|
+
}
|
|
857
|
+
class IdempotencyLeaseLostError extends Error {
|
|
858
|
+
name = 'Funding.IdempotencyLeaseLostError';
|
|
859
|
+
}
|
|
860
|
+
//# sourceMappingURL=deposit-addresses.js.map
|