tapimo 0.9.0 → 0.10.1
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 +4292 -3708
- 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 +21 -20
- package/dist/Webhooks.d.ts.map +1 -1
- package/dist/Webhooks.js +66 -23
- 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 +26 -17
- 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/apps/verified-tokens.d.ts +168 -168
- 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 +26 -0
- package/dist/analytics/Analytics.js.map +1 -1
- package/dist/apps/Zones.d.ts +4280 -84
- package/dist/apps/Zones.d.ts.map +1 -1
- package/dist/apps/Zones.js +30 -10
- package/dist/apps/Zones.js.map +1 -1
- package/dist/apps/data/App.d.ts +5799 -4929
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/FxOracle.d.ts +4 -5
- package/dist/apps/data/FxOracle.d.ts.map +1 -1
- package/dist/apps/data/FxOracle.js +19 -4
- package/dist/apps/data/FxOracle.js.map +1 -1
- package/dist/apps/data/routes/activities.d.ts +3391 -492
- package/dist/apps/data/routes/activities.d.ts.map +1 -1
- package/dist/apps/data/routes/activities.js +372 -29
- package/dist/apps/data/routes/activities.js.map +1 -1
- package/dist/apps/data/routes/addresses.d.ts +853 -339
- package/dist/apps/data/routes/addresses.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.d.ts +10 -10
- package/dist/apps/data/routes/balances.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.js +145 -83
- package/dist/apps/data/routes/balances.js.map +1 -1
- package/dist/apps/data/routes/blocks.d.ts +123 -123
- package/dist/apps/data/routes/coingecko.d.ts +169 -169
- package/dist/apps/data/routes/earn.d.ts +455 -1
- package/dist/apps/data/routes/earn.d.ts.map +1 -1
- package/dist/apps/data/routes/earn.js +369 -45
- package/dist/apps/data/routes/earn.js.map +1 -1
- package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
- package/dist/apps/data/routes/exchanges.js +114 -118
- 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 +35 -34
- 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.map +1 -1
- package/dist/apps/data/routes/receipts.js +68 -47
- package/dist/apps/data/routes/receipts.js.map +1 -1
- package/dist/apps/data/routes/rpc.d.ts +31 -31
- 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 +461 -459
- package/dist/apps/data/routes/tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/transactions.d.ts +313 -305
- package/dist/apps/data/routes/transactions.d.ts.map +1 -1
- package/dist/apps/data/routes/transactions.js +25 -23
- package/dist/apps/data/routes/transactions.js.map +1 -1
- package/dist/apps/data/routes/transfers.d.ts +108 -86
- package/dist/apps/data/routes/transfers.d.ts.map +1 -1
- package/dist/apps/data/routes/transfers.js +618 -105
- package/dist/apps/data/routes/transfers.js.map +1 -1
- package/dist/apps/data/routes/valuation.d.ts +21 -21
- package/dist/apps/data/routes/valuation.d.ts.map +1 -1
- package/dist/apps/data/routes/valuation.js +2 -2
- package/dist/apps/data/routes/valuation.js.map +1 -1
- package/dist/apps/data/routes/webhooks.d.ts +519 -868
- package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/data/routes/webhooks.js +642 -353
- package/dist/apps/data/routes/webhooks.js.map +1 -1
- package/dist/apps/data/routes/zones.d.ts +31 -31
- package/dist/apps/data/routes/zones.d.ts.map +1 -1
- package/dist/apps/data/routes/zones.js +4 -2
- package/dist/apps/data/routes/zones.js.map +1 -1
- package/dist/apps/funding/routes/deposit-addresses.js +1 -1
- package/dist/apps/funding/routes/deposit-addresses.js.map +1 -1
- package/dist/apps/funding/routes/providers.d.ts +13 -13
- package/dist/apps/funding/routes/quotes.d.ts +89 -89
- package/dist/apps/funding/routes/transfers.d.ts +36 -36
- 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 +11 -309
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +74 -74
- 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 +35 -35
- package/dist/apps/management/routes/faucet.js +2 -2
- package/dist/apps/management/routes/faucet.js.map +1 -1
- package/dist/apps/management/routes/invitations.d.ts +134 -134
- package/dist/apps/management/routes/invite-links.d.ts +80 -80
- package/dist/apps/management/routes/members.d.ts +69 -69
- package/dist/apps/management/routes/orgs.d.ts +98 -98
- package/dist/apps/management/routes/projects.d.ts +60 -60
- package/dist/apps/management/routes/usage.d.ts +72 -72
- package/dist/apps/management/routes/verified-token-requests.d.ts +83 -83
- package/dist/apps/management/routes/webhooks.d.ts +203 -618
- package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
- 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/apps/mpp/App.d.ts +56 -44
- package/dist/apps/mpp/App.d.ts.map +1 -1
- package/dist/apps/mpp/Mpp.d.ts +3 -0
- package/dist/apps/mpp/Mpp.d.ts.map +1 -1
- package/dist/apps/mpp/Mpp.js +3 -0
- package/dist/apps/mpp/Mpp.js.map +1 -1
- package/dist/apps/mpp/internal/Adapter.d.ts.map +1 -1
- package/dist/apps/mpp/internal/Adapter.js +9 -2
- package/dist/apps/mpp/internal/Adapter.js.map +1 -1
- package/dist/db/Db.d.ts +4 -2
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +75 -15
- 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/sponsoredTransactions.d.ts.map +1 -1
- package/dist/db/tables/sponsoredTransactions.js +4 -1
- package/dist/db/tables/sponsoredTransactions.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 +58 -3
- package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
- package/dist/db/tables/webhookSubscriptions.js +86 -3
- package/dist/db/tables/webhookSubscriptions.js.map +1 -1
- package/dist/internal/Auth.d.ts +3 -0
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +6 -4
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/MetricSink.d.ts +139 -3
- package/dist/internal/MetricSink.d.ts.map +1 -1
- package/dist/internal/MetricSink.js +144 -4
- 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/Schema.js +1 -1
- package/dist/internal/Schema.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 +1 -3
- package/dist/internal/Tidx.d.ts.map +1 -1
- package/dist/internal/Tidx.js +1 -3
- 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.map +1 -1
- package/dist/internal/Viem.js +2 -1
- package/dist/internal/Viem.js.map +1 -1
- package/dist/internal/WebhookDestination.d.ts +2 -0
- package/dist/internal/WebhookDestination.d.ts.map +1 -1
- package/dist/internal/WebhookDestination.js +11 -0
- package/dist/internal/WebhookDestination.js.map +1 -1
- package/dist/internal/WebhookTransfer.d.ts +75 -0
- package/dist/internal/WebhookTransfer.d.ts.map +1 -0
- package/dist/internal/WebhookTransfer.js +299 -0
- package/dist/internal/WebhookTransfer.js.map +1 -0
- package/dist/internal/Webhooks.d.ts +176 -0
- package/dist/internal/Webhooks.d.ts.map +1 -1
- package/dist/internal/Webhooks.js +216 -20
- package/dist/internal/Webhooks.js.map +1 -1
- package/dist/internal/funding/Provider.d.ts +4 -0
- package/dist/internal/funding/Provider.d.ts.map +1 -1
- package/dist/internal/funding/Provider.js +11 -7
- package/dist/internal/funding/Provider.js.map +1 -1
- package/dist/internal/funding/Reconciliation.d.ts.map +1 -1
- package/dist/internal/funding/Reconciliation.js +128 -0
- package/dist/internal/funding/Reconciliation.js.map +1 -1
- package/dist/internal/funding/providers/relay.d.ts +6 -4
- package/dist/internal/funding/providers/relay.d.ts.map +1 -1
- package/dist/internal/funding/providers/relay.js +71 -9
- package/dist/internal/funding/providers/relay.js.map +1 -1
- package/package.json +4 -4
- package/src/ApiKeys.test.ts +20 -0
- package/src/ApiKeys.ts +28 -1
- package/src/App.test.ts +10 -8
- package/src/App.ts +1 -0
- package/src/Client.test-d.ts +61 -5
- package/src/Webhooks.bench.ts +714 -0
- package/src/Webhooks.memory.bench.ts +250 -0
- package/src/Webhooks.test.ts +77 -6
- package/src/Webhooks.ts +85 -29
- 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 +8 -0
- package/src/analytics/Analytics.ts +26 -0
- package/src/apps/Zones.test.ts +23 -5
- package/src/apps/Zones.ts +31 -10
- package/src/apps/composition.test.ts +1 -0
- package/src/apps/data/App.test.ts +2 -2
- package/src/apps/data/FxOracle.test.ts +18 -0
- package/src/apps/data/FxOracle.ts +28 -5
- package/src/apps/data/routes/activities.bench.ts +41 -0
- package/src/apps/data/routes/activities.test.ts +122 -2
- package/src/apps/data/routes/activities.ts +435 -32
- package/src/apps/data/routes/balances.bench.ts +21 -0
- package/src/apps/data/routes/balances.test.ts +119 -10
- package/src/apps/data/routes/balances.ts +164 -97
- package/src/apps/data/routes/blocks.bench.ts +34 -0
- package/src/apps/data/routes/blocks.test.ts +12 -6
- 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.test.ts +197 -0
- package/src/apps/data/routes/earn.ts +487 -45
- package/src/apps/data/routes/exchanges.bench.ts +85 -0
- package/src/apps/data/routes/exchanges.test.ts +7 -6
- package/src/apps/data/routes/exchanges.ts +124 -117
- 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.test.ts +24 -7
- package/src/apps/data/routes/receipts.ts +67 -50
- 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.test.ts +51 -0
- package/src/apps/data/routes/transactions.bench.ts +40 -0
- package/src/apps/data/routes/transactions.test.ts +42 -0
- package/src/apps/data/routes/transactions.ts +44 -39
- package/src/apps/data/routes/transfers.bench.ts +28 -0
- package/src/apps/data/routes/transfers.test.ts +402 -24
- package/src/apps/data/routes/transfers.ts +708 -101
- package/src/apps/data/routes/valuation.bench.ts +14 -0
- package/src/apps/data/routes/valuation.test.ts +26 -0
- package/src/apps/data/routes/valuation.ts +6 -2
- 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 +466 -135
- package/src/apps/data/routes/webhooks.ts +820 -429
- 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 +7 -2
- package/src/apps/funding/routes/chains.test.ts +1 -1
- package/src/apps/funding/routes/deposit-addresses.test.ts +1 -1
- package/src/apps/funding/routes/deposit-addresses.ts +1 -1
- package/src/apps/funding/routes/providers.test.ts +1 -1
- package/src/apps/funding/routes/quotes.test.ts +2 -2
- package/src/apps/funding/routes/transfers.test.ts +3 -6
- 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/apps/mpp/Mpp.test.ts +13 -1
- package/src/apps/mpp/Mpp.ts +8 -0
- package/src/apps/mpp/internal/Adapter.ts +10 -2
- package/src/db/Db.test.ts +40 -6
- package/src/db/Db.ts +114 -19
- package/src/db/Schema.ts +32 -0
- package/src/db/tables/sponsoredTransactions.ts +9 -2
- package/src/db/tables/webhookQueueEvents.ts +285 -0
- package/src/db/tables/webhookSubscriptions.ts +131 -4
- package/src/internal/Auth.test.ts +61 -50
- package/src/internal/Auth.ts +9 -4
- package/src/internal/EarnVaults.test.ts +2 -2
- package/src/internal/MetricSink.test.ts +575 -0
- package/src/internal/MetricSink.ts +325 -10
- package/src/internal/RateLimit.test.ts +23 -18
- package/src/internal/RateLimit.ts +23 -15
- package/src/internal/Schema.ts +1 -1
- 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 +1 -3
- package/src/internal/Value.test.ts +25 -0
- package/src/internal/Value.ts +29 -0
- package/src/internal/Viem.test.ts +8 -0
- package/src/internal/Viem.ts +2 -1
- package/src/internal/WebhookDestination.test.ts +20 -0
- package/src/internal/WebhookDestination.ts +10 -0
- package/src/internal/WebhookTransfer.test.ts +89 -0
- package/src/internal/WebhookTransfer.ts +430 -0
- package/src/internal/Webhooks.test.ts +616 -0
- package/src/internal/Webhooks.ts +412 -24
- package/src/internal/funding/Provider.test.ts +48 -8
- package/src/internal/funding/Provider.ts +12 -7
- package/src/internal/funding/Reconciliation.local.test.ts +3 -1
- package/src/internal/funding/Reconciliation.test.ts +1240 -0
- package/src/internal/funding/Reconciliation.ts +136 -0
- package/src/internal/funding/SourceObservation.test.ts +1 -0
- package/src/internal/funding/providers/index.test.ts +73 -3
- package/src/internal/funding/providers/relay.test.ts +2 -9
- package/src/internal/funding/providers/relay.ts +72 -13
|
@@ -31,6 +31,9 @@ const redeemRequestedSignature = 'event RedeemRequested(bytes32 indexed requestI
|
|
|
31
31
|
const venueSharesDepositedSignature = 'event VenueSharesDeposited(address indexed caller,address indexed receiver,uint256 requestedVenueShares,uint256 receivedVenueShares,uint256 earnShares)';
|
|
32
32
|
const maxScannedDeployments = 250;
|
|
33
33
|
const scanLimit = 50;
|
|
34
|
+
// Concurrent metadata reads in one macrotask collapse into one deployless
|
|
35
|
+
// multicall; an await between small chunks keeps each call under the RPC limit.
|
|
36
|
+
const tokenReadChunkSize = 20;
|
|
34
37
|
/** Block boundary that includes all indexed history. */
|
|
35
38
|
const beforeFirstBlockNumber = -1;
|
|
36
39
|
/** Vault TVL is reported in USD only; the API exposes no denomination parameter. */
|
|
@@ -81,11 +84,13 @@ export var schema;
|
|
|
81
84
|
const baseIncludeQuery = Schema.includeQuery(baseInclude, 'Comma-separated vault fields to include, such as `access,zone`.');
|
|
82
85
|
const zone = z
|
|
83
86
|
.strictObject({
|
|
84
|
-
chainId: Schema.ChainId.check(z.describe('Private Zone chain id.'), z.meta({ examples: [
|
|
87
|
+
chainId: Schema.ChainId.check(z.describe('Private Zone chain id.'), z.meta({ examples: [1424310001] })),
|
|
85
88
|
inputTokens: z
|
|
86
89
|
.array(Schema.Address)
|
|
87
90
|
.check(z.describe('Tokens accepted by private deposits.'), z.meta({ examples: [['0x20c000000000000000000000ff04042ee92fd449']] })),
|
|
88
|
-
name: z
|
|
91
|
+
name: z
|
|
92
|
+
.string()
|
|
93
|
+
.check(z.describe('Private Zone name.'), z.meta({ examples: ['zone-moderato-internal'] })),
|
|
89
94
|
outputTokens: z
|
|
90
95
|
.array(Schema.Address)
|
|
91
96
|
.check(z.describe('Tokens supported by private redemptions.'), z.meta({ examples: [['0x20c000000000000000000000ff04042ee92fd449']] })),
|
|
@@ -125,7 +130,7 @@ export var schema;
|
|
|
125
130
|
.check(z.describe('Vault display label.'), z.meta({ examples: ['btPATHUSD Earn'] })),
|
|
126
131
|
sharePrice: z
|
|
127
132
|
.nullable(value)
|
|
128
|
-
.check(z.describe('Assets returned for one whole
|
|
133
|
+
.check(z.describe('Assets returned for one whole earn share, in the asset display currency, or null when the engine cannot quote an exit.')),
|
|
129
134
|
shareToken: Tokens.schema.Token.check(z.describe('Token representing vault shares.')),
|
|
130
135
|
slug: z
|
|
131
136
|
.nullable(z.string())
|
|
@@ -137,13 +142,13 @@ export var schema;
|
|
|
137
142
|
vaultAddress: Schema.Address.check(z.describe('Earn vault contract address.'), z.meta({ examples: ['0xf4ae63687d6753a78e7f551d2eda1d0d31a5ea3a'] })),
|
|
138
143
|
verified: z
|
|
139
144
|
.boolean()
|
|
140
|
-
.check(z.describe('Whether this vault is in the curated
|
|
145
|
+
.check(z.describe('Whether this vault is in the curated earn registry.'), z.meta({ examples: [true] })),
|
|
141
146
|
zone: z
|
|
142
147
|
.optional(z.nullable(zone))
|
|
143
148
|
.check(z.describe('Curated private Zone route, when requested, or null when absent.')),
|
|
144
149
|
});
|
|
145
150
|
/** One compatible Earn vault resolved from current chain state. */
|
|
146
|
-
schema.Vault = Schema.describe(vault, 'A compatible
|
|
151
|
+
schema.Vault = Schema.describe(vault, 'A compatible earn vault resolved from chain state.');
|
|
147
152
|
/** One registry-curated Earn vault. */
|
|
148
153
|
schema.VerifiedVault = Schema.describe(z.extend(vault, {
|
|
149
154
|
engine: z.extend(engine, { type: engineType }),
|
|
@@ -152,23 +157,38 @@ export var schema;
|
|
|
152
157
|
.check(z.describe('Stable curated vault slug.'), z.meta({ examples: ['btpathusd-earn-h8k2m4n6'] })),
|
|
153
158
|
verified: z
|
|
154
159
|
.literal(true)
|
|
155
|
-
.check(z.describe('This vault is in the curated
|
|
156
|
-
}), 'A registry-curated
|
|
160
|
+
.check(z.describe('This vault is in the curated earn registry.'), z.meta({ examples: [true] })),
|
|
161
|
+
}), 'A registry-curated earn vault resolved from chain state.');
|
|
157
162
|
const position = z.strictObject({
|
|
158
|
-
account: Schema.Address.check(z.describe('Account whose
|
|
163
|
+
account: Schema.Address.check(z.describe('Account whose earn position was read.'), z.meta({ examples: ['0xbe058e1c4df8a4366a387bf595b284246a93039e'] })),
|
|
159
164
|
assetAllowance: Schema.DecimalString.check(z.describe('Assets the vault may spend from this account, in asset base units.'), z.meta({ examples: ['0'] })),
|
|
160
165
|
assetBalance: Schema.DecimalString.check(z.describe('Assets this account currently holds, in asset base units.'), z.meta({ examples: ['250000000'] })),
|
|
161
166
|
assetToken: Schema.tokenAddress('0x20c000000000000000000000b9537d11c60e8b50').check(z.describe('TIP-20 asset token accepted by the vault.')),
|
|
162
167
|
id: Schema.Address.check(z.describe('Stable resource ID for this vault position, equal to the account address.'), z.meta({ examples: ['0xbe058e1c4df8a4366a387bf595b284246a93039e'] })),
|
|
163
168
|
shareAllowance: Schema.DecimalString.check(z.describe('Earn shares the vault may spend from this account, in share base units.'), z.meta({ examples: ['0'] })),
|
|
164
169
|
shareBalance: Schema.DecimalString.check(z.describe('Earn shares this account currently holds, in share base units.'), z.meta({ examples: ['99500000'] })),
|
|
165
|
-
shareToken: Schema.tokenAddress('0x20c000000000000000000000300e14ab91a10769').check(z.describe('TIP-20 token representing
|
|
166
|
-
value: Schema.DecimalString.check(z.describe('Current asset value of this account’s
|
|
170
|
+
shareToken: Schema.tokenAddress('0x20c000000000000000000000300e14ab91a10769').check(z.describe('TIP-20 token representing earn shares.')),
|
|
171
|
+
value: Schema.DecimalString.check(z.describe('Current asset value of this account’s earn shares, including fees, in asset base units.'), z.meta({ examples: ['100000000'] })),
|
|
172
|
+
});
|
|
173
|
+
const addressPosition = z.strictObject({
|
|
174
|
+
assetAmount: value.check(z.describe('Current asset value of the held shares, including fees, in `assetToken` units.')),
|
|
175
|
+
assetToken: Tokens.schema.Token.check(z.describe('Asset accepted by the vault.')),
|
|
176
|
+
id: Schema.Address.check(z.describe('Stable resource ID for this position, equal to the vault address.'), z.meta({ examples: ['0xf4ae63687d6753a78e7f551d2eda1d0d31a5ea3a'] })),
|
|
177
|
+
shareAmount: value.check(z.describe('Earn share balance this account holds, in `shareToken` units.')),
|
|
178
|
+
shareToken: Tokens.schema.Token.check(z.describe('Token representing vault shares.')),
|
|
179
|
+
valuation: z
|
|
180
|
+
.optional(z.nullable(Valuation.schema.Value))
|
|
181
|
+
.check(z.describe('The position’s nominal value when `valuation.currency` is requested, or `null` when ' +
|
|
182
|
+
'the asset token is unverified, its display currency has no rate, or rates were unavailable.')),
|
|
183
|
+
vaultAddress: Schema.Address.check(z.describe('Earn vault contract address holding this position.'), z.meta({ examples: ['0xf4ae63687d6753a78e7f551d2eda1d0d31a5ea3a'] })),
|
|
184
|
+
verified: z
|
|
185
|
+
.boolean()
|
|
186
|
+
.check(z.describe('Whether the vault is in the curated earn registry.'), z.meta({ examples: [true] })),
|
|
167
187
|
});
|
|
168
188
|
const earningsBase = z.strictObject({
|
|
169
189
|
account: Schema.Address.check(z.describe('Account whose vault earnings were calculated.'), z.meta({ examples: ['0x68f94cfca22e5969c0553bbeab00a2acde88939b'] })),
|
|
170
190
|
assetToken: Schema.TokenAddress.check(z.describe('TIP-20 asset token accepted by the vault.'), z.meta({ examples: ['0x20c000000000000000000000ff04042ee92fd449'] })),
|
|
171
|
-
currentValue: Schema.DecimalString.check(z.describe('Asset value of the account’s active
|
|
191
|
+
currentValue: Schema.DecimalString.check(z.describe('Asset value of the account’s active earn shares at the latest indexed block, in asset base units.'), z.meta({ examples: ['600000'] })),
|
|
172
192
|
id: Schema.Address.check(z.describe('Stable resource ID for these vault earnings, equal to the account address.'), z.meta({ examples: ['0x68f94cfca22e5969c0553bbeab00a2acde88939b'] })),
|
|
173
193
|
});
|
|
174
194
|
const earningsPeriod = z
|
|
@@ -245,7 +265,7 @@ export var schema;
|
|
|
245
265
|
});
|
|
246
266
|
const collectionQuery = z
|
|
247
267
|
.extend(collectionFilters, { 'apy.window': EarnRates.schema.Window })
|
|
248
|
-
.check(z.describe('Query parameters for listing
|
|
268
|
+
.check(z.describe('Query parameters for listing earn vaults.'));
|
|
249
269
|
const vaultSelector = z.strictObject({
|
|
250
270
|
chainId: Schema.ChainIdQuery,
|
|
251
271
|
include: includeQuery,
|
|
@@ -260,17 +280,17 @@ export var schema;
|
|
|
260
280
|
.strictObject({
|
|
261
281
|
data: z
|
|
262
282
|
.array(schema.Vault)
|
|
263
|
-
.check(z.describe('Compatible deployed
|
|
283
|
+
.check(z.describe('Compatible deployed earn vaults in deployment order.')),
|
|
264
284
|
meta: z.optional(meta),
|
|
265
285
|
nextCursor: Schema.NextCursor,
|
|
266
286
|
})
|
|
267
|
-
.check(z.describe('A page of compatible deployed
|
|
287
|
+
.check(z.describe('A page of compatible deployed earn vaults.'));
|
|
268
288
|
})(getEarnVaults = schema.getEarnVaults || (schema.getEarnVaults = {}));
|
|
269
289
|
/** Schemas for the getVerifiedEarnVaults operation. */
|
|
270
290
|
let getVerifiedEarnVaults;
|
|
271
291
|
(function (getVerifiedEarnVaults) {
|
|
272
292
|
/** Collection query without rates or valuation, for surfaces that cannot measure them. */
|
|
273
|
-
getVerifiedEarnVaults.BaseQuery = Schema.describe(z.extend(collectionFilters, { include: baseIncludeQuery }), 'Query parameters for listing
|
|
293
|
+
getVerifiedEarnVaults.BaseQuery = Schema.describe(z.extend(collectionFilters, { include: baseIncludeQuery }), 'Query parameters for listing earn vaults.');
|
|
274
294
|
/** Query parameters for registry-curated Earn vaults. */
|
|
275
295
|
getVerifiedEarnVaults.Query = collectionQuery;
|
|
276
296
|
/** Paginated list of registry-curated Earn vaults. */
|
|
@@ -278,29 +298,71 @@ export var schema;
|
|
|
278
298
|
.strictObject({
|
|
279
299
|
data: z
|
|
280
300
|
.array(schema.VerifiedVault)
|
|
281
|
-
.check(z.describe('Registry-curated
|
|
301
|
+
.check(z.describe('Registry-curated earn vaults ordered by vault address.')),
|
|
282
302
|
meta: z.optional(meta),
|
|
283
303
|
nextCursor: Schema.NextCursor,
|
|
284
304
|
})
|
|
285
|
-
.check(z.describe('A page of registry-curated
|
|
305
|
+
.check(z.describe('A page of registry-curated earn vaults.'));
|
|
286
306
|
})(getVerifiedEarnVaults = schema.getVerifiedEarnVaults || (schema.getVerifiedEarnVaults = {}));
|
|
307
|
+
/** Schemas for the getEarnAddressPositions operation. */
|
|
308
|
+
let getEarnAddressPositions;
|
|
309
|
+
(function (getEarnAddressPositions) {
|
|
310
|
+
/** Path parameters identifying the account. */
|
|
311
|
+
getEarnAddressPositions.Params = z
|
|
312
|
+
.strictObject({
|
|
313
|
+
address: Schema.Address.check(z.describe('Account whose earn positions to list.')),
|
|
314
|
+
})
|
|
315
|
+
.check(z.describe('Path parameters identifying an account with earn positions.'));
|
|
316
|
+
/** Query parameters for listing an account's earn positions. */
|
|
317
|
+
getEarnAddressPositions.Query = z
|
|
318
|
+
.strictObject({
|
|
319
|
+
chainId: Schema.ChainIdQuery,
|
|
320
|
+
cursor: Schema.Cursor,
|
|
321
|
+
limit: Schema.Limit,
|
|
322
|
+
'valuation.currency': Schema.Denomination.check(z.describe('When present, include each position’s nominal value in this denomination. ' +
|
|
323
|
+
'Case-insensitive and must be priced by the configured FX oracle.')),
|
|
324
|
+
verified: z
|
|
325
|
+
.optional(Schema.booleanQuery())
|
|
326
|
+
.check(z.describe('When `true`, include only positions in registry-curated (verified) earn vaults.'), z.meta({ examples: [true] })),
|
|
327
|
+
})
|
|
328
|
+
.check(z.describe('Query parameters for listing an account’s earn positions.'));
|
|
329
|
+
/** Page-level valuation rate provenance. */
|
|
330
|
+
getEarnAddressPositions.Meta = z
|
|
331
|
+
.object({
|
|
332
|
+
valuation: z
|
|
333
|
+
.optional(Valuation.schema.Pricing)
|
|
334
|
+
.check(z.describe('Rate provenance for row valuations. Present when conversion rates were ' +
|
|
335
|
+
'consulted; absent when every value was identity-valued or rates were unavailable.')),
|
|
336
|
+
})
|
|
337
|
+
.check(z.describe('Page-level valuation rate provenance.'));
|
|
338
|
+
/** Paginated list of the account's earn vault positions. */
|
|
339
|
+
getEarnAddressPositions.Response = z
|
|
340
|
+
.strictObject({
|
|
341
|
+
data: z
|
|
342
|
+
.array(addressPosition)
|
|
343
|
+
.check(z.describe('Vaults where the account holds earn shares.')),
|
|
344
|
+
meta: z.optional(getEarnAddressPositions.Meta),
|
|
345
|
+
nextCursor: Schema.NextCursor,
|
|
346
|
+
})
|
|
347
|
+
.check(z.describe('A page of the account’s earn vault positions.'));
|
|
348
|
+
})(getEarnAddressPositions = schema.getEarnAddressPositions || (schema.getEarnAddressPositions = {}));
|
|
287
349
|
/** Schemas for the getEarnVault operation. */
|
|
288
350
|
let getEarnVault;
|
|
289
351
|
(function (getEarnVault) {
|
|
290
|
-
/** Path parameters identifying one
|
|
352
|
+
/** Path parameters identifying one earn vault. */
|
|
291
353
|
getEarnVault.Params = z
|
|
292
354
|
.strictObject({
|
|
293
355
|
vaultId: Schema.Address.check(z.describe('Earn vault contract address.')),
|
|
294
356
|
})
|
|
295
|
-
.check(z.describe('Path parameters identifying one
|
|
357
|
+
.check(z.describe('Path parameters identifying one earn vault.'));
|
|
296
358
|
/** Detail query without rates or valuation, for surfaces that cannot measure them. */
|
|
297
|
-
getEarnVault.BaseQuery = Schema.describe(z.extend(vaultSelector, { include: baseIncludeQuery }), 'Query parameters selecting one
|
|
359
|
+
getEarnVault.BaseQuery = Schema.describe(z.extend(vaultSelector, { include: baseIncludeQuery }), 'Query parameters selecting one earn vault.');
|
|
298
360
|
/** Query parameters selecting the chain containing the vault. */
|
|
299
361
|
getEarnVault.Query = z
|
|
300
362
|
.extend(vaultSelector, { 'apy.window': EarnRates.schema.Window })
|
|
301
|
-
.check(z.describe('Query parameters selecting one
|
|
363
|
+
.check(z.describe('Query parameters selecting one earn vault.'));
|
|
302
364
|
/** One compatible Earn vault resolved directly from current chain state. */
|
|
303
|
-
getEarnVault.Response = Schema.describe(z.extend(vault, { meta: z.optional(meta) }), 'A compatible
|
|
365
|
+
getEarnVault.Response = Schema.describe(z.extend(vault, { meta: z.optional(meta) }), 'A compatible earn vault resolved from chain state, with rate provenance for its valued fields.');
|
|
304
366
|
})(getEarnVault = schema.getEarnVault || (schema.getEarnVault = {}));
|
|
305
367
|
/** Schemas for the getEarnVaultPosition operation. */
|
|
306
368
|
let getEarnVaultPosition;
|
|
@@ -308,16 +370,16 @@ export var schema;
|
|
|
308
370
|
/** Path parameters identifying the Earn vault and account. */
|
|
309
371
|
getEarnVaultPosition.Params = z
|
|
310
372
|
.strictObject({
|
|
311
|
-
address: Schema.Address.check(z.describe('Account whose
|
|
373
|
+
address: Schema.Address.check(z.describe('Account whose earn position to read.')),
|
|
312
374
|
vaultId: Schema.address('0xdb48b00fb28d9a8db881fcd671c3fb1bf8c486b6').check(z.describe('Earn vault contract address.')),
|
|
313
375
|
})
|
|
314
|
-
.check(z.describe('Path parameters identifying an
|
|
376
|
+
.check(z.describe('Path parameters identifying an earn vault position.'));
|
|
315
377
|
/** Query parameters selecting the chain containing the vault. */
|
|
316
378
|
getEarnVaultPosition.Query = z
|
|
317
379
|
.strictObject({ chainId: Schema.ChainIdQuery })
|
|
318
|
-
.check(z.describe('Query parameters selecting an
|
|
380
|
+
.check(z.describe('Query parameters selecting an earn vault position.'));
|
|
319
381
|
/** Current asset and Earn-share state for one account in one vault. */
|
|
320
|
-
getEarnVaultPosition.Response = Schema.describe(position, 'Current asset and
|
|
382
|
+
getEarnVaultPosition.Response = Schema.describe(position, 'Current asset and earn-share balances, allowances, and asset value for an account in one earn vault.');
|
|
321
383
|
})(getEarnVaultPosition = schema.getEarnVaultPosition || (schema.getEarnVaultPosition = {}));
|
|
322
384
|
/** Schemas for the getEarnVaultEarnings operation. */
|
|
323
385
|
let getEarnVaultEarnings;
|
|
@@ -328,16 +390,16 @@ export var schema;
|
|
|
328
390
|
address: Schema.Address.check(z.describe('Account whose vault earnings to read.')),
|
|
329
391
|
vaultId: Schema.address('0xf4ae63687d6753a78e7f551d2eda1d0d31a5ea3a').check(z.describe('Earn vault contract address.')),
|
|
330
392
|
})
|
|
331
|
-
.check(z.describe('Path parameters identifying an
|
|
393
|
+
.check(z.describe('Path parameters identifying an earn vault account.'));
|
|
332
394
|
/** Query parameters selecting the chain containing the vault. */
|
|
333
395
|
getEarnVaultEarnings.Query = z
|
|
334
396
|
.strictObject({
|
|
335
397
|
chainId: Schema.ChainIdQuery,
|
|
336
398
|
period: z._default(earningsPeriod, 'lifetime'),
|
|
337
399
|
})
|
|
338
|
-
.check(z.describe('Query parameters selecting an
|
|
400
|
+
.check(z.describe('Query parameters selecting an earn vault account.'));
|
|
339
401
|
/** Earnings for one account and period in one Earn vault. */
|
|
340
|
-
getEarnVaultEarnings.Response = Schema.describe(earnings, 'Indexed asset value and cost-basis-aware earnings for an account in one
|
|
402
|
+
getEarnVaultEarnings.Response = Schema.describe(earnings, 'Indexed asset value and cost-basis-aware earnings for an account in one earn vault.');
|
|
341
403
|
})(getEarnVaultEarnings = schema.getEarnVaultEarnings || (schema.getEarnVaultEarnings = {}));
|
|
342
404
|
})(schema || (schema = {}));
|
|
343
405
|
/** Creates the public Earn-vault handlers. */
|
|
@@ -348,12 +410,12 @@ export function earn(options = {}) {
|
|
|
348
410
|
code: 'query_invalid',
|
|
349
411
|
message: 'Invalid query parameters',
|
|
350
412
|
}), OpenApi.describeRoute({
|
|
351
|
-
description: 'Lists compatible
|
|
413
|
+
description: 'Lists compatible earn vaults discovered from deployment events and resolved from current chain state.',
|
|
352
414
|
operationId: 'getEarnVaults',
|
|
353
415
|
responses: OpenApi.responses({
|
|
354
|
-
errors: { 502: 'Could not read
|
|
416
|
+
errors: { 502: 'Could not read earn deployment or vault data.' },
|
|
355
417
|
success: {
|
|
356
|
-
description: 'A page of compatible deployed
|
|
418
|
+
description: 'A page of compatible deployed earn vaults.',
|
|
357
419
|
schema: schema.getEarnVaults.Response,
|
|
358
420
|
},
|
|
359
421
|
}),
|
|
@@ -388,12 +450,12 @@ export function earn(options = {}) {
|
|
|
388
450
|
code: 'query_invalid',
|
|
389
451
|
message: 'Invalid query parameters',
|
|
390
452
|
}), OpenApi.describeRoute({
|
|
391
|
-
description: 'Lists registry-curated
|
|
453
|
+
description: 'Lists registry-curated earn vaults after resolving their current onchain configuration.',
|
|
392
454
|
operationId: 'getVerifiedEarnVaults',
|
|
393
455
|
responses: OpenApi.responses({
|
|
394
|
-
errors: { 502: 'Could not read curated
|
|
456
|
+
errors: { 502: 'Could not read curated earn registry or vault data.' },
|
|
395
457
|
success: {
|
|
396
|
-
description: 'A page of registry-curated
|
|
458
|
+
description: 'A page of registry-curated earn vaults.',
|
|
397
459
|
schema: schema.getVerifiedEarnVaults.Response,
|
|
398
460
|
},
|
|
399
461
|
}),
|
|
@@ -420,6 +482,79 @@ export function earn(options = {}) {
|
|
|
420
482
|
catch (cause) {
|
|
421
483
|
return Response.upstream(c, cause);
|
|
422
484
|
}
|
|
485
|
+
})
|
|
486
|
+
.get('/v1/earn/addresses/:address/positions', Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }), OpenApi.validate('param', schema.getEarnAddressPositions.Params, {
|
|
487
|
+
code: 'positions_invalid',
|
|
488
|
+
message: 'Invalid address positions',
|
|
489
|
+
}), OpenApi.validate('query', schema.getEarnAddressPositions.Query, {
|
|
490
|
+
code: 'query_invalid',
|
|
491
|
+
message: 'Invalid query parameters',
|
|
492
|
+
}), OpenApi.describeRoute({
|
|
493
|
+
description: 'Lists every earn vault where an account currently holds shares, with the current balance and value of each position.',
|
|
494
|
+
operationId: 'getEarnAddressPositions',
|
|
495
|
+
responses: OpenApi.responses({
|
|
496
|
+
errors: {
|
|
497
|
+
400: {
|
|
498
|
+
codes: [
|
|
499
|
+
'chain_id_invalid',
|
|
500
|
+
'chain_id_unsupported',
|
|
501
|
+
'positions_invalid',
|
|
502
|
+
'query_invalid',
|
|
503
|
+
],
|
|
504
|
+
},
|
|
505
|
+
502: 'Could not read earn deployment or position data.',
|
|
506
|
+
},
|
|
507
|
+
success: {
|
|
508
|
+
description: 'A page of the account’s earn vault positions.',
|
|
509
|
+
schema: schema.getEarnAddressPositions.Response,
|
|
510
|
+
},
|
|
511
|
+
}),
|
|
512
|
+
summary: 'List account positions',
|
|
513
|
+
tags: ['Earn'],
|
|
514
|
+
}),
|
|
515
|
+
// `noStore` matches the address balances listing: account holdings must
|
|
516
|
+
// read fresh immediately after a deposit or redemption.
|
|
517
|
+
Cache.response({
|
|
518
|
+
cacheControl: Cache.policies.noStore,
|
|
519
|
+
key: (c) => Cache.urlKey(c, schema.getEarnAddressPositions.Query),
|
|
520
|
+
name: 'tempo-api:earn-address-positions:v1',
|
|
521
|
+
}), async (c) => {
|
|
522
|
+
if (Auth.narrowAccess)
|
|
523
|
+
return Auth.paidAccessError(c);
|
|
524
|
+
if (OpenApi.narrowValidation)
|
|
525
|
+
return Response.error(c, {
|
|
526
|
+
code: 'chain_id_invalid',
|
|
527
|
+
message: 'Invalid chain id',
|
|
528
|
+
status: 400,
|
|
529
|
+
});
|
|
530
|
+
if (OpenApi.narrowValidation)
|
|
531
|
+
return Response.error(c, {
|
|
532
|
+
code: 'chain_id_unsupported',
|
|
533
|
+
message: 'Unsupported chain id',
|
|
534
|
+
status: 400,
|
|
535
|
+
});
|
|
536
|
+
if (OpenApi.narrowValidation)
|
|
537
|
+
return OpenApi.validationError(c, {
|
|
538
|
+
code: 'positions_invalid',
|
|
539
|
+
message: 'Invalid request parameters',
|
|
540
|
+
});
|
|
541
|
+
if (OpenApi.narrowValidation)
|
|
542
|
+
return OpenApi.validationError(c, {
|
|
543
|
+
code: 'query_invalid',
|
|
544
|
+
message: 'Invalid query parameters',
|
|
545
|
+
});
|
|
546
|
+
const { address } = c.req.valid('param');
|
|
547
|
+
const query = c.req.valid('query');
|
|
548
|
+
const chainId = query.chainId ?? c.get('chainId');
|
|
549
|
+
try {
|
|
550
|
+
const data = await listPositions(c, { ...query, address, chainId, oracle });
|
|
551
|
+
return c.json(Response.validated(schema.getEarnAddressPositions.Response, data), 200);
|
|
552
|
+
}
|
|
553
|
+
catch (cause) {
|
|
554
|
+
if (cause instanceof Valuation.UnsupportedDenominationError)
|
|
555
|
+
return Response.error(c, { code: 'query_invalid', message: cause.message, status: 400 });
|
|
556
|
+
return Response.upstream(c, cause);
|
|
557
|
+
}
|
|
423
558
|
})
|
|
424
559
|
.get('/v1/earn/vaults/:vaultId/positions/:address', Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }), OpenApi.validate('param', schema.getEarnVaultPosition.Params, {
|
|
425
560
|
code: 'position_invalid',
|
|
@@ -428,7 +563,7 @@ export function earn(options = {}) {
|
|
|
428
563
|
code: 'query_invalid',
|
|
429
564
|
message: 'Invalid query parameters',
|
|
430
565
|
}), OpenApi.describeRoute({
|
|
431
|
-
description: 'Gets an account’s asset and
|
|
566
|
+
description: 'Gets an account’s asset and earn-share balances, allowances, and current asset value for one earn vault.',
|
|
432
567
|
operationId: 'getEarnVaultPosition',
|
|
433
568
|
responses: OpenApi.responses({
|
|
434
569
|
errors: {
|
|
@@ -442,12 +577,12 @@ export function earn(options = {}) {
|
|
|
442
577
|
},
|
|
443
578
|
404: {
|
|
444
579
|
codes: ['earn_vault_not_found'],
|
|
445
|
-
description: 'No compatible
|
|
580
|
+
description: 'No compatible earn vault was found at the supplied vault address.',
|
|
446
581
|
},
|
|
447
|
-
502: 'Could not read the
|
|
582
|
+
502: 'Could not read the earn vault position.',
|
|
448
583
|
},
|
|
449
584
|
success: {
|
|
450
|
-
description: 'Current
|
|
585
|
+
description: 'Current earn position for one account in one vault.',
|
|
451
586
|
schema: schema.getEarnVaultPosition.Response,
|
|
452
587
|
},
|
|
453
588
|
}),
|
|
@@ -537,9 +672,9 @@ export function earn(options = {}) {
|
|
|
537
672
|
},
|
|
538
673
|
404: {
|
|
539
674
|
codes: ['earn_vault_not_found'],
|
|
540
|
-
description: 'No compatible
|
|
675
|
+
description: 'No compatible earn vault was found at this address.',
|
|
541
676
|
},
|
|
542
|
-
502: 'Could not read the
|
|
677
|
+
502: 'Could not read the earn vault earnings.',
|
|
543
678
|
},
|
|
544
679
|
success: {
|
|
545
680
|
description: 'Cost-basis-aware earnings for one account in one vault.',
|
|
@@ -611,18 +746,18 @@ export function earn(options = {}) {
|
|
|
611
746
|
code: 'query_invalid',
|
|
612
747
|
message: 'Invalid query parameters',
|
|
613
748
|
}), OpenApi.describeRoute({
|
|
614
|
-
description: 'Resolves any compatible
|
|
749
|
+
description: 'Resolves any compatible earn vault directly from current chain state and enriches it when curated.',
|
|
615
750
|
operationId: 'getEarnVault',
|
|
616
751
|
responses: OpenApi.responses({
|
|
617
752
|
errors: {
|
|
618
753
|
404: {
|
|
619
754
|
codes: ['earn_vault_not_found'],
|
|
620
|
-
description: 'No compatible
|
|
755
|
+
description: 'No compatible earn vault was found at this address.',
|
|
621
756
|
},
|
|
622
|
-
502: 'Could not read
|
|
757
|
+
502: 'Could not read earn vault data.',
|
|
623
758
|
},
|
|
624
759
|
success: {
|
|
625
|
-
description: 'One compatible
|
|
760
|
+
description: 'One compatible earn vault.',
|
|
626
761
|
schema: schema.getEarnVault.Response,
|
|
627
762
|
},
|
|
628
763
|
}),
|
|
@@ -1501,6 +1636,195 @@ export async function listVerifiedVaults(c, options) {
|
|
|
1501
1636
|
nextCursor: matches.length > options.limit && last ? Cursor.encode([last.record.vaultAddress]) : null,
|
|
1502
1637
|
};
|
|
1503
1638
|
}
|
|
1639
|
+
/** Lists every vault where an account holds earn shares, ordered by vault address. */
|
|
1640
|
+
export async function listPositions(c, options) {
|
|
1641
|
+
const denomination = options['valuation.currency'];
|
|
1642
|
+
const db = Db.get(c.get('dbCached'));
|
|
1643
|
+
const recordsPromise = core_EarnVaults.list(db, { chainId: options.chainId });
|
|
1644
|
+
// The verified page needs no index: the registry is the candidate set.
|
|
1645
|
+
const heldPromise = options.verified
|
|
1646
|
+
? undefined
|
|
1647
|
+
: queryHeldVaults(c, { address: options.address, chainId: options.chainId });
|
|
1648
|
+
// Observing these branches prevents unhandled rejections while the
|
|
1649
|
+
// denomination validates; the later await still propagates them.
|
|
1650
|
+
void recordsPromise.catch(() => undefined);
|
|
1651
|
+
void heldPromise?.catch(() => undefined);
|
|
1652
|
+
// Validation resolves before discovery results are consumed, so a dependency
|
|
1653
|
+
// failure cannot mask a caller error.
|
|
1654
|
+
if (denomination)
|
|
1655
|
+
await Valuation.ratesFor(c, { currencies: [], denomination, oracle: options.oracle });
|
|
1656
|
+
const [records, held] = await Promise.all([recordsPromise, heldPromise]);
|
|
1657
|
+
const registry = new Map(records.map((record) => [record.vaultAddress.toLowerCase(), record]));
|
|
1658
|
+
const vaults = held ?? [...registry.keys()].sort();
|
|
1659
|
+
const decoded = options.cursor ? Cursor.decode(options.cursor, ['address']) : undefined;
|
|
1660
|
+
const cursor = decoded?.[0]?.toString().toLowerCase();
|
|
1661
|
+
const candidates = vaults.filter((vault) => !cursor || vault > cursor);
|
|
1662
|
+
const matches = [];
|
|
1663
|
+
let offset = 0;
|
|
1664
|
+
// The deployment-scan bound also caps per-request position reads, so a long
|
|
1665
|
+
// exited-position history pages through candidates instead of probing them all.
|
|
1666
|
+
while (offset < candidates.length &&
|
|
1667
|
+
matches.length <= options.limit &&
|
|
1668
|
+
offset < maxScannedDeployments) {
|
|
1669
|
+
const batchSize = discoveryBatchLimit({
|
|
1670
|
+
// Zero-share candidates are read and then rejected, so batches over-fetch.
|
|
1671
|
+
filtered: true,
|
|
1672
|
+
limit: options.limit,
|
|
1673
|
+
matches: matches.length,
|
|
1674
|
+
remaining: Math.min(candidates.length, maxScannedDeployments) - offset,
|
|
1675
|
+
});
|
|
1676
|
+
const batch = candidates.slice(offset, offset + batchSize);
|
|
1677
|
+
offset += batch.length;
|
|
1678
|
+
const read = await Timing.time(c, 'earn_positions', () => Promise.all(batch.map(async (vault) => {
|
|
1679
|
+
const record = registry.get(vault);
|
|
1680
|
+
try {
|
|
1681
|
+
const position = await readPosition(c, {
|
|
1682
|
+
address: options.address,
|
|
1683
|
+
chainId: options.chainId,
|
|
1684
|
+
vaultAddress: Schema.Address.parse(vault),
|
|
1685
|
+
});
|
|
1686
|
+
return { position, record, vaultAddress: vault };
|
|
1687
|
+
}
|
|
1688
|
+
catch (cause) {
|
|
1689
|
+
if (!record && isIncompatible(cause))
|
|
1690
|
+
return undefined;
|
|
1691
|
+
throw cause;
|
|
1692
|
+
}
|
|
1693
|
+
})));
|
|
1694
|
+
for (const match of read) {
|
|
1695
|
+
if (!match || match.position.shareBalance === 0n)
|
|
1696
|
+
continue;
|
|
1697
|
+
matches.push(match);
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
const page = matches.slice(0, options.limit);
|
|
1701
|
+
const [tokens, snapshot] = await Promise.all([
|
|
1702
|
+
(async () => {
|
|
1703
|
+
const addresses = [
|
|
1704
|
+
...new Set(page.flatMap(({ position }) => [position.assetToken, position.shareToken])),
|
|
1705
|
+
];
|
|
1706
|
+
const groups = [];
|
|
1707
|
+
for (let index = 0; index < addresses.length; index += tokenReadChunkSize)
|
|
1708
|
+
groups.push(await Tokens.resolveTokens(c, {
|
|
1709
|
+
addresses: addresses.slice(index, index + tokenReadChunkSize),
|
|
1710
|
+
chainId: options.chainId,
|
|
1711
|
+
include: ['token.verified'],
|
|
1712
|
+
}));
|
|
1713
|
+
return new Map(groups.flatMap((group) => [...group]));
|
|
1714
|
+
})(),
|
|
1715
|
+
denomination ? VerifiedTokens.snapshot(c, options.chainId) : undefined,
|
|
1716
|
+
]);
|
|
1717
|
+
const rates = denomination && snapshot
|
|
1718
|
+
? await Valuation.ratesFor(c, {
|
|
1719
|
+
currencies: page.flatMap(({ position }) => {
|
|
1720
|
+
const currency = snapshot.byAddress.get(position.assetToken.toLowerCase())?.currency;
|
|
1721
|
+
return currency === undefined ? [] : [currency];
|
|
1722
|
+
}),
|
|
1723
|
+
denomination,
|
|
1724
|
+
oracle: options.oracle,
|
|
1725
|
+
})
|
|
1726
|
+
: undefined;
|
|
1727
|
+
const last = matches[options.limit - 1];
|
|
1728
|
+
// A scan-bound exit resumes from the last probed candidate so a truncated
|
|
1729
|
+
// page never hides positions deeper in the candidate list.
|
|
1730
|
+
const next = matches.length > options.limit
|
|
1731
|
+
? last?.vaultAddress
|
|
1732
|
+
: offset < candidates.length
|
|
1733
|
+
? candidates[offset - 1]
|
|
1734
|
+
: undefined;
|
|
1735
|
+
return {
|
|
1736
|
+
data: page.map((match) => serializePosition({
|
|
1737
|
+
denomination,
|
|
1738
|
+
position: match.position,
|
|
1739
|
+
rates,
|
|
1740
|
+
snapshot,
|
|
1741
|
+
tokens,
|
|
1742
|
+
vaultAddress: match.vaultAddress,
|
|
1743
|
+
verified: match.record !== undefined,
|
|
1744
|
+
})),
|
|
1745
|
+
...(rates ? { meta: { valuation: Valuation.pricing(rates, options.oracle) } } : {}),
|
|
1746
|
+
nextCursor: next ? Cursor.encode([next]) : null,
|
|
1747
|
+
};
|
|
1748
|
+
}
|
|
1749
|
+
/** Finds vaults whose earn share token the account has ever held, by vault address. */
|
|
1750
|
+
async function queryHeldVaults(c, options) {
|
|
1751
|
+
const tidx = c.get('getTidx')(options.chainId);
|
|
1752
|
+
// Each vault's newest deployment binds its current share token; joining it
|
|
1753
|
+
// against the account's holder deltas server-side turns holdings into vault
|
|
1754
|
+
// candidates in one round trip, with no literal list to overflow the request.
|
|
1755
|
+
// Earn shares are factory-minted TIP-20s, so the `0x20c0` prefix bounds the
|
|
1756
|
+
// subquery. Stale delta rows are fine: the live read drops them.
|
|
1757
|
+
const result = await Timing.time(c, 'earn_positions_discovery', () => tidx.fetch({
|
|
1758
|
+
chainId: options.chainId,
|
|
1759
|
+
engine: 'clickhouse',
|
|
1760
|
+
query: `
|
|
1761
|
+
SELECT "earnVault"
|
|
1762
|
+
FROM (
|
|
1763
|
+
SELECT
|
|
1764
|
+
"earnVault",
|
|
1765
|
+
"earnShare",
|
|
1766
|
+
row_number() OVER (
|
|
1767
|
+
PARTITION BY lower("earnVault")
|
|
1768
|
+
ORDER BY block_num DESC, log_idx DESC
|
|
1769
|
+
) AS deployment_rank
|
|
1770
|
+
FROM EarnStackDeployed
|
|
1771
|
+
)
|
|
1772
|
+
WHERE deployment_rank = 1 AND lower("earnShare") IN (
|
|
1773
|
+
SELECT DISTINCT lower(token)
|
|
1774
|
+
FROM address_holder_deltas
|
|
1775
|
+
WHERE holder = '${options.address}' AND startsWith(token, '0x20c0')
|
|
1776
|
+
)
|
|
1777
|
+
ORDER BY lower("earnVault") ASC
|
|
1778
|
+
`,
|
|
1779
|
+
signatures: [deployedSignature],
|
|
1780
|
+
}));
|
|
1781
|
+
const rows = result.rows;
|
|
1782
|
+
return rows.flatMap((row) => {
|
|
1783
|
+
const vault = Schema.Address.safeParse(Value.toText(row['earnVault']));
|
|
1784
|
+
return vault.success ? [vault.data.toLowerCase()] : [];
|
|
1785
|
+
});
|
|
1786
|
+
}
|
|
1787
|
+
/** Reads one account's live position in one vault. */
|
|
1788
|
+
function readPosition(c, options) {
|
|
1789
|
+
return c
|
|
1790
|
+
.get('getClient')(options.chainId)
|
|
1791
|
+
.earn.getPosition({ account: options.address, vault: options.vaultAddress });
|
|
1792
|
+
}
|
|
1793
|
+
/** Serializes one live position into the address-scoped row contract. */
|
|
1794
|
+
function serializePosition(options) {
|
|
1795
|
+
const { position, tokens } = options;
|
|
1796
|
+
const assetToken = getToken(tokens, position.assetToken);
|
|
1797
|
+
const shareToken = getToken(tokens, position.shareToken);
|
|
1798
|
+
const vaultAddress = Schema.Address.parse(options.vaultAddress.toLowerCase());
|
|
1799
|
+
return {
|
|
1800
|
+
assetAmount: valueFor(position.value, assetToken),
|
|
1801
|
+
assetToken,
|
|
1802
|
+
id: vaultAddress,
|
|
1803
|
+
shareAmount: valueFor(position.shareBalance, shareToken),
|
|
1804
|
+
shareToken,
|
|
1805
|
+
...(options.denomination
|
|
1806
|
+
? {
|
|
1807
|
+
valuation: Valuation.valuationFor({
|
|
1808
|
+
amount: position.value,
|
|
1809
|
+
denomination: options.denomination,
|
|
1810
|
+
rates: options.rates,
|
|
1811
|
+
token: options.snapshot?.byAddress.get(assetToken.address),
|
|
1812
|
+
}),
|
|
1813
|
+
}
|
|
1814
|
+
: {}),
|
|
1815
|
+
vaultAddress,
|
|
1816
|
+
verified: options.verified,
|
|
1817
|
+
};
|
|
1818
|
+
}
|
|
1819
|
+
/** Renders a base-unit amount in the token's display currency and precision. */
|
|
1820
|
+
function valueFor(amount, token) {
|
|
1821
|
+
return {
|
|
1822
|
+
amount: amount.toString(),
|
|
1823
|
+
currency: token.currency,
|
|
1824
|
+
decimals: token.decimals,
|
|
1825
|
+
formatted: core_Value.format(amount, token.decimals),
|
|
1826
|
+
};
|
|
1827
|
+
}
|
|
1504
1828
|
/** Gets one compatible Earn vault. */
|
|
1505
1829
|
export async function getVault(c, options) {
|
|
1506
1830
|
const db = Db.get(c.get('dbCached'));
|