tapimo 0.7.2 → 0.9.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 +20117 -19183
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +61 -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 +11 -11
- 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 +11696 -13937
- 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 +271 -271
- package/dist/apps/data/routes/addresses.d.ts +398 -167
- package/dist/apps/data/routes/addresses.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.d.ts +278 -8
- package/dist/apps/data/routes/balances.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.js +114 -2
- 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 +170 -170
- package/dist/apps/data/routes/earn.d.ts +498 -6
- package/dist/apps/data/routes/earn.d.ts.map +1 -1
- package/dist/apps/data/routes/earn.js +876 -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 +31 -31
- 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 +449 -449
- package/dist/apps/data/routes/transactions.d.ts +2 -2
- package/dist/apps/data/routes/transfers.d.ts +81 -81
- 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} +1640 -3217
- package/dist/apps/funding/App.d.ts.map +1 -0
- package/dist/apps/funding/App.js +61 -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 +1997 -0
- package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -0
- package/dist/apps/funding/routes/deposit-addresses.js +778 -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 +122 -122
- 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 +99 -99
- 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 +77 -77
- 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 +44 -44
- 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 +45 -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 +74 -1
- package/dist/db/tables/fundingDeposits.d.ts.map +1 -1
- package/dist/db/tables/fundingDeposits.js +126 -0
- package/dist/db/tables/fundingDeposits.js.map +1 -1
- package/dist/internal/Auth.d.ts +15 -11
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +52 -17
- 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 +348 -54
- 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 +438 -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 +55 -5
- package/src/App.ts +79 -5
- package/src/Client.test-d.ts +203 -28
- 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 +130 -3
- package/src/apps/data/App.test.ts +0 -12
- package/src/apps/data/App.ts +4 -8
- package/src/apps/data/routes/balances.test.ts +42 -0
- package/src/apps/data/routes/balances.ts +218 -85
- package/src/apps/data/routes/earn.test.ts +363 -0
- package/src/apps/data/routes/earn.ts +1141 -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 +78 -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 +1649 -0
- package/src/apps/funding/routes/deposit-addresses.ts +932 -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 +1236 -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 +71 -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 +239 -0
- package/src/db/tables/fundingDeposits.ts +214 -1
- package/src/internal/Auth.test.ts +205 -5
- package/src/internal/Auth.ts +63 -19
- 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 +1364 -109
- package/src/internal/funding/Reconciliation.ts +467 -88
- 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 +533 -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
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
ContractFunctionExecutionError,
|
|
6
6
|
ContractFunctionRevertedError,
|
|
7
7
|
ContractFunctionZeroDataError,
|
|
8
|
+
zeroAddress,
|
|
8
9
|
} from 'viem'
|
|
9
10
|
import * as z from 'zod/mini'
|
|
10
11
|
|
|
@@ -20,6 +21,7 @@ import * as OpenApi from '../../../internal/OpenApi.js'
|
|
|
20
21
|
import * as Response from '../../../internal/Response.js'
|
|
21
22
|
import * as Schema from '../../../internal/Schema.js'
|
|
22
23
|
import * as Store from '../../../internal/Store.js'
|
|
24
|
+
import * as Tidx from '../../../internal/Tidx.js'
|
|
23
25
|
import * as Timing from '../../../internal/Timing.js'
|
|
24
26
|
import * as Ttl from '../../../internal/Ttl.js'
|
|
25
27
|
import * as Value from '../../../internal/Value.js'
|
|
@@ -31,8 +33,31 @@ import * as Valuation from './valuation.js'
|
|
|
31
33
|
const deployedSignature =
|
|
32
34
|
'event EarnStackDeployed(address indexed earnVault,address indexed earnShare,address indexed earnFees,address engine,address asset,address owner,bytes32 deploymentId,address emergencyGuardian,address asyncJanitor,uint8 migrationMode,bytes32 earnShareSalt,bytes32 controlConfigHash,bytes32 feeConfigHash,bytes32 earnFeesSalt)'
|
|
33
35
|
|
|
36
|
+
const depositedSignature =
|
|
37
|
+
'event Deposited(address indexed caller,address indexed receiver,uint256 assets,uint256 earnShares)'
|
|
38
|
+
|
|
39
|
+
const redeemedSignature =
|
|
40
|
+
'event Redeemed(address indexed caller,address indexed receiver,uint256 earnShares,uint256 assets)'
|
|
41
|
+
|
|
42
|
+
const withdrewExactSignature =
|
|
43
|
+
'event WithdrewExact(address indexed caller,address indexed receiver,uint256 assets,uint256 earnSharesBurned)'
|
|
44
|
+
|
|
45
|
+
const redeemFinalizedSignature =
|
|
46
|
+
'event RedeemFinalized(bytes32 indexed requestId,address indexed receiver,uint256 earnShares,address asset,uint256 assets)'
|
|
47
|
+
|
|
48
|
+
const redeemCancelledSignature =
|
|
49
|
+
'event RedeemCancelled(bytes32 indexed requestId,address indexed receiver,uint256 earnShares)'
|
|
50
|
+
|
|
51
|
+
const redeemRequestedSignature =
|
|
52
|
+
'event RedeemRequested(bytes32 indexed requestId,address indexed requester,address indexed receiver,uint256 earnShares)'
|
|
53
|
+
|
|
54
|
+
const venueSharesDepositedSignature =
|
|
55
|
+
'event VenueSharesDeposited(address indexed caller,address indexed receiver,uint256 requestedVenueShares,uint256 receivedVenueShares,uint256 earnShares)'
|
|
56
|
+
|
|
34
57
|
const maxScannedDeployments = 250
|
|
35
58
|
const scanLimit = 50
|
|
59
|
+
/** Block boundary that includes all indexed history. */
|
|
60
|
+
const beforeFirstBlockNumber = -1
|
|
36
61
|
|
|
37
62
|
/** Vault TVL is reported in USD only; the API exposes no denomination parameter. */
|
|
38
63
|
const tvlDenomination = 'USD'
|
|
@@ -300,6 +325,133 @@ export namespace schema {
|
|
|
300
325
|
),
|
|
301
326
|
})
|
|
302
327
|
|
|
328
|
+
const earningsBase = z.strictObject({
|
|
329
|
+
account: Schema.Address.check(
|
|
330
|
+
z.describe('Account whose vault earnings were calculated.'),
|
|
331
|
+
z.meta({ examples: ['0x68f94cfca22e5969c0553bbeab00a2acde88939b'] }),
|
|
332
|
+
),
|
|
333
|
+
assetToken: Schema.TokenAddress.check(
|
|
334
|
+
z.describe('TIP-20 asset token accepted by the vault.'),
|
|
335
|
+
z.meta({ examples: ['0x20c000000000000000000000ff04042ee92fd449'] }),
|
|
336
|
+
),
|
|
337
|
+
currentValue: Schema.DecimalString.check(
|
|
338
|
+
z.describe(
|
|
339
|
+
'Asset value of the account’s active Earn shares at the latest indexed block, in asset base units.',
|
|
340
|
+
),
|
|
341
|
+
z.meta({ examples: ['600000'] }),
|
|
342
|
+
),
|
|
343
|
+
id: Schema.Address.check(
|
|
344
|
+
z.describe('Stable resource ID for these vault earnings, equal to the account address.'),
|
|
345
|
+
z.meta({ examples: ['0x68f94cfca22e5969c0553bbeab00a2acde88939b'] }),
|
|
346
|
+
),
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
const earningsPeriod = z
|
|
350
|
+
.enum(['30d', 'active', 'lifetime'])
|
|
351
|
+
.check(
|
|
352
|
+
z.describe(
|
|
353
|
+
'Earnings period to calculate: trailing 30 days, shares still held, or lifetime history.',
|
|
354
|
+
),
|
|
355
|
+
z.meta({ examples: ['lifetime'] }),
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
const earnings = z.union([
|
|
359
|
+
z.extend(earningsBase, {
|
|
360
|
+
lifetimeEarnings: z
|
|
361
|
+
.string()
|
|
362
|
+
.check(
|
|
363
|
+
z.regex(/^-?\d+$/),
|
|
364
|
+
z.describe(
|
|
365
|
+
'Current share value plus completed redemptions, exact withdrawals, and finalized async assets, minus asset deposits, at the latest indexed block.',
|
|
366
|
+
),
|
|
367
|
+
z.meta({ examples: ['0'] }),
|
|
368
|
+
),
|
|
369
|
+
period: z
|
|
370
|
+
.literal('lifetime')
|
|
371
|
+
.check(
|
|
372
|
+
z.describe('Returns lifetime net asset earnings.'),
|
|
373
|
+
z.meta({ examples: ['lifetime'] }),
|
|
374
|
+
),
|
|
375
|
+
status: z
|
|
376
|
+
.literal('complete')
|
|
377
|
+
.check(
|
|
378
|
+
z.describe('Earnings include a complete asset-denominated cost basis.'),
|
|
379
|
+
z.meta({ examples: ['complete'] }),
|
|
380
|
+
),
|
|
381
|
+
}),
|
|
382
|
+
z.extend(earningsBase, {
|
|
383
|
+
activeEarnings: z
|
|
384
|
+
.string()
|
|
385
|
+
.check(
|
|
386
|
+
z.regex(/^-?\d+$/),
|
|
387
|
+
z.describe(
|
|
388
|
+
'Current share value minus the weighted-average asset cost basis of shares still held, at the latest indexed block.',
|
|
389
|
+
),
|
|
390
|
+
z.meta({ examples: ['0'] }),
|
|
391
|
+
),
|
|
392
|
+
period: z
|
|
393
|
+
.literal('active')
|
|
394
|
+
.check(
|
|
395
|
+
z.describe('Returns earnings on shares still held.'),
|
|
396
|
+
z.meta({ examples: ['active'] }),
|
|
397
|
+
),
|
|
398
|
+
status: z
|
|
399
|
+
.literal('complete')
|
|
400
|
+
.check(
|
|
401
|
+
z.describe('Earnings include a complete asset-denominated cost basis.'),
|
|
402
|
+
z.meta({ examples: ['complete'] }),
|
|
403
|
+
),
|
|
404
|
+
}),
|
|
405
|
+
z.extend(earningsBase, {
|
|
406
|
+
period: z
|
|
407
|
+
.literal('30d')
|
|
408
|
+
.check(
|
|
409
|
+
z.describe('Returns cash-flow-adjusted earnings over the trailing 30 days.'),
|
|
410
|
+
z.meta({ examples: ['30d'] }),
|
|
411
|
+
),
|
|
412
|
+
status: z
|
|
413
|
+
.literal('complete')
|
|
414
|
+
.check(
|
|
415
|
+
z.describe('Earnings include a complete asset-denominated cost basis.'),
|
|
416
|
+
z.meta({ examples: ['complete'] }),
|
|
417
|
+
),
|
|
418
|
+
windowEarnings: z
|
|
419
|
+
.string()
|
|
420
|
+
.check(
|
|
421
|
+
z.regex(/^-?\d+$/),
|
|
422
|
+
z.describe(
|
|
423
|
+
'Ending share value plus realized assets, minus deposits and opening share value, over the trailing 30 days.',
|
|
424
|
+
),
|
|
425
|
+
z.meta({ examples: ['0'] }),
|
|
426
|
+
),
|
|
427
|
+
}),
|
|
428
|
+
z.extend(earningsBase, {
|
|
429
|
+
period: earningsPeriod,
|
|
430
|
+
status: z
|
|
431
|
+
.literal('incomplete_cost_basis')
|
|
432
|
+
.check(
|
|
433
|
+
z.describe(
|
|
434
|
+
'Earnings are unavailable when indexed activity cannot reconstruct the asset cost basis for the requested period.',
|
|
435
|
+
),
|
|
436
|
+
z.meta({ examples: ['incomplete_cost_basis'] }),
|
|
437
|
+
),
|
|
438
|
+
}),
|
|
439
|
+
z.extend(earningsBase, {
|
|
440
|
+
period: z
|
|
441
|
+
.enum(['30d', 'lifetime'])
|
|
442
|
+
.check(
|
|
443
|
+
z.describe('Earnings period with an unresolved async redemption.'),
|
|
444
|
+
z.meta({ examples: ['lifetime'] }),
|
|
445
|
+
),
|
|
446
|
+
status: z
|
|
447
|
+
.literal('pending_redemption')
|
|
448
|
+
.check(
|
|
449
|
+
z.describe('Earnings are unavailable while an async redemption remains open.'),
|
|
450
|
+
z.meta({ examples: ['pending_redemption'] }),
|
|
451
|
+
),
|
|
452
|
+
}),
|
|
453
|
+
])
|
|
454
|
+
|
|
303
455
|
const meta = z
|
|
304
456
|
.object({
|
|
305
457
|
valuation: z
|
|
@@ -428,6 +580,33 @@ export namespace schema {
|
|
|
428
580
|
'Current asset and Earn-share balances, allowances, and asset value for an account in one Earn vault.',
|
|
429
581
|
)
|
|
430
582
|
}
|
|
583
|
+
|
|
584
|
+
/** Schemas for the getEarnVaultEarnings operation. */
|
|
585
|
+
export namespace getEarnVaultEarnings {
|
|
586
|
+
/** Path parameters identifying the Earn vault and account. */
|
|
587
|
+
export const Params = z
|
|
588
|
+
.strictObject({
|
|
589
|
+
address: Schema.Address.check(z.describe('Account whose vault earnings to read.')),
|
|
590
|
+
vaultId: Schema.address('0xf4ae63687d6753a78e7f551d2eda1d0d31a5ea3a').check(
|
|
591
|
+
z.describe('Earn vault contract address.'),
|
|
592
|
+
),
|
|
593
|
+
})
|
|
594
|
+
.check(z.describe('Path parameters identifying an Earn vault account.'))
|
|
595
|
+
|
|
596
|
+
/** Query parameters selecting the chain containing the vault. */
|
|
597
|
+
export const Query = z
|
|
598
|
+
.strictObject({
|
|
599
|
+
chainId: Schema.ChainIdQuery,
|
|
600
|
+
period: z._default(earningsPeriod, 'lifetime'),
|
|
601
|
+
})
|
|
602
|
+
.check(z.describe('Query parameters selecting an Earn vault account.'))
|
|
603
|
+
|
|
604
|
+
/** Earnings for one account and period in one Earn vault. */
|
|
605
|
+
export const Response = Schema.describe(
|
|
606
|
+
earnings,
|
|
607
|
+
'Indexed asset value and cost-basis-aware earnings for an account in one Earn vault.',
|
|
608
|
+
)
|
|
609
|
+
}
|
|
431
610
|
}
|
|
432
611
|
|
|
433
612
|
/** Creates the public Earn-vault handlers. */
|
|
@@ -452,7 +631,7 @@ export function earn(options: earn.Options = {}) {
|
|
|
452
631
|
schema: schema.getEarnVaults.Response,
|
|
453
632
|
},
|
|
454
633
|
}),
|
|
455
|
-
summary: 'List
|
|
634
|
+
summary: 'List vaults',
|
|
456
635
|
tags: ['Earn'],
|
|
457
636
|
}),
|
|
458
637
|
// Live state and rate enrichment are embedded, so every vault read caches
|
|
@@ -560,7 +739,7 @@ export function earn(options: earn.Options = {}) {
|
|
|
560
739
|
schema: schema.getEarnVaultPosition.Response,
|
|
561
740
|
},
|
|
562
741
|
}),
|
|
563
|
-
summary: 'Get
|
|
742
|
+
summary: 'Get account position',
|
|
564
743
|
tags: ['Earn'],
|
|
565
744
|
}),
|
|
566
745
|
Cache.response<App.Environment>({
|
|
@@ -629,6 +808,101 @@ export function earn(options: earn.Options = {}) {
|
|
|
629
808
|
}
|
|
630
809
|
},
|
|
631
810
|
)
|
|
811
|
+
.get(
|
|
812
|
+
'/v1/earn/vaults/:vaultId/earnings/:address',
|
|
813
|
+
Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }),
|
|
814
|
+
OpenApi.validate('param', schema.getEarnVaultEarnings.Params, {
|
|
815
|
+
code: 'earnings_invalid',
|
|
816
|
+
message: 'Invalid vault earnings',
|
|
817
|
+
}),
|
|
818
|
+
OpenApi.validate('query', schema.getEarnVaultEarnings.Query, {
|
|
819
|
+
code: 'query_invalid',
|
|
820
|
+
message: 'Invalid query parameters',
|
|
821
|
+
}),
|
|
822
|
+
OpenApi.describeRoute({
|
|
823
|
+
description:
|
|
824
|
+
'Gets an account’s indexed asset value and earnings for lifetime history, the trailing 30 days, or shares still held.',
|
|
825
|
+
operationId: 'getEarnVaultEarnings',
|
|
826
|
+
responses: OpenApi.responses({
|
|
827
|
+
errors: {
|
|
828
|
+
400: {
|
|
829
|
+
codes: [
|
|
830
|
+
'chain_id_invalid',
|
|
831
|
+
'chain_id_unsupported',
|
|
832
|
+
'earnings_invalid',
|
|
833
|
+
'query_invalid',
|
|
834
|
+
],
|
|
835
|
+
},
|
|
836
|
+
404: {
|
|
837
|
+
codes: ['earn_vault_not_found'],
|
|
838
|
+
description: 'No compatible Earn vault was found at this address.',
|
|
839
|
+
},
|
|
840
|
+
502: 'Could not read the Earn vault earnings.',
|
|
841
|
+
},
|
|
842
|
+
success: {
|
|
843
|
+
description: 'Cost-basis-aware earnings for one account in one vault.',
|
|
844
|
+
schema: schema.getEarnVaultEarnings.Response,
|
|
845
|
+
},
|
|
846
|
+
}),
|
|
847
|
+
summary: 'Get vault earnings',
|
|
848
|
+
tags: ['Earn'],
|
|
849
|
+
}),
|
|
850
|
+
Cache.response<App.Environment>({
|
|
851
|
+
cacheControl: Cache.policies.state,
|
|
852
|
+
key: (c) => Cache.urlKey(c, schema.getEarnVaultEarnings.Query),
|
|
853
|
+
name: 'tempo-api:earn-vault-earnings:v3',
|
|
854
|
+
}),
|
|
855
|
+
async (c) => {
|
|
856
|
+
if (Auth.narrowAccess) return Auth.paidAccessError(c)
|
|
857
|
+
if (OpenApi.narrowValidation)
|
|
858
|
+
return Response.error(c, {
|
|
859
|
+
code: 'chain_id_invalid',
|
|
860
|
+
message: 'Invalid chain id',
|
|
861
|
+
status: 400,
|
|
862
|
+
})
|
|
863
|
+
if (OpenApi.narrowValidation)
|
|
864
|
+
return Response.error(c, {
|
|
865
|
+
code: 'chain_id_unsupported',
|
|
866
|
+
message: 'Unsupported chain id',
|
|
867
|
+
status: 400,
|
|
868
|
+
})
|
|
869
|
+
if (OpenApi.narrowValidation)
|
|
870
|
+
return OpenApi.validationError(c, {
|
|
871
|
+
code: 'earnings_invalid',
|
|
872
|
+
message: 'Invalid request parameters',
|
|
873
|
+
})
|
|
874
|
+
if (OpenApi.narrowValidation)
|
|
875
|
+
return OpenApi.validationError(c, {
|
|
876
|
+
code: 'query_invalid',
|
|
877
|
+
message: 'Invalid query parameters',
|
|
878
|
+
})
|
|
879
|
+
|
|
880
|
+
const { address, vaultId } = c.req.valid('param')
|
|
881
|
+
const query = c.req.valid('query')
|
|
882
|
+
const chainId = query.chainId ?? c.get('chainId')
|
|
883
|
+
try {
|
|
884
|
+
const data = await getVaultEarnings(c, {
|
|
885
|
+
account: address,
|
|
886
|
+
chainId,
|
|
887
|
+
period: query.period,
|
|
888
|
+
vault: vaultId,
|
|
889
|
+
})
|
|
890
|
+
return c.json(Response.validated(schema.getEarnVaultEarnings.Response, data), 200)
|
|
891
|
+
} catch (cause) {
|
|
892
|
+
try {
|
|
893
|
+
await resolveVaultDiscovery(c, { chainId, include: [], vaultAddress: vaultId })
|
|
894
|
+
} catch (vaultCause) {
|
|
895
|
+
if (isIncompatible(vaultCause))
|
|
896
|
+
return Response.error(c, {
|
|
897
|
+
code: 'earn_vault_not_found',
|
|
898
|
+
message: 'Earn vault not found',
|
|
899
|
+
status: 404,
|
|
900
|
+
})
|
|
901
|
+
}
|
|
902
|
+
return Response.upstream(c, cause)
|
|
903
|
+
}
|
|
904
|
+
},
|
|
905
|
+
)
|
|
632
906
|
.get(
|
|
633
907
|
'/v1/earn/vaults/:vaultId',
|
|
634
908
|
Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }),
|
|
@@ -657,7 +931,7 @@ export function earn(options: earn.Options = {}) {
|
|
|
657
931
|
schema: schema.getEarnVault.Response,
|
|
658
932
|
},
|
|
659
933
|
}),
|
|
660
|
-
summary: 'Get
|
|
934
|
+
summary: 'Get vault',
|
|
661
935
|
tags: ['Earn'],
|
|
662
936
|
}),
|
|
663
937
|
Cache.response<App.Environment>({
|
|
@@ -711,6 +985,870 @@ export declare namespace earn {
|
|
|
711
985
|
}
|
|
712
986
|
}
|
|
713
987
|
|
|
988
|
+
/** Reads one account's indexed value and returns earnings when the history is complete. */
|
|
989
|
+
export async function getVaultEarnings(
|
|
990
|
+
c: Context<App.Environment>,
|
|
991
|
+
options: getVaultEarnings.Options,
|
|
992
|
+
) {
|
|
993
|
+
const client = c.get('getClient')(options.chainId)
|
|
994
|
+
const tidx = c.get('getTidx')(options.chainId)
|
|
995
|
+
const block = await getLatestIndexedBlock(tidx)
|
|
996
|
+
const blockNumber = block.number
|
|
997
|
+
if (options.period === '30d')
|
|
998
|
+
return getWindowVaultEarnings(c, {
|
|
999
|
+
account: options.account,
|
|
1000
|
+
blockNumber,
|
|
1001
|
+
chainId: options.chainId,
|
|
1002
|
+
period: options.period,
|
|
1003
|
+
timestamp: block.timestamp,
|
|
1004
|
+
vault: options.vault,
|
|
1005
|
+
})
|
|
1006
|
+
const position = client.earn.getPosition({
|
|
1007
|
+
account: options.account,
|
|
1008
|
+
blockNumber: BigInt(blockNumber),
|
|
1009
|
+
vault: options.vault,
|
|
1010
|
+
})
|
|
1011
|
+
if (options.period === 'active') {
|
|
1012
|
+
const current = await position
|
|
1013
|
+
const base = {
|
|
1014
|
+
account: options.account,
|
|
1015
|
+
assetToken: current.assetToken,
|
|
1016
|
+
currentValue: current.value.toString(),
|
|
1017
|
+
id: options.account,
|
|
1018
|
+
period: options.period,
|
|
1019
|
+
}
|
|
1020
|
+
const activeCostBasis = await getActiveCostBasis(tidx, {
|
|
1021
|
+
account: options.account,
|
|
1022
|
+
blockNumber,
|
|
1023
|
+
shareBalance: current.shareBalance,
|
|
1024
|
+
shareToken: current.shareToken,
|
|
1025
|
+
vault: options.vault,
|
|
1026
|
+
})
|
|
1027
|
+
if (activeCostBasis === undefined) return { ...base, status: 'incomplete_cost_basis' as const }
|
|
1028
|
+
return {
|
|
1029
|
+
...base,
|
|
1030
|
+
activeEarnings: (current.value - activeCostBasis).toString(),
|
|
1031
|
+
status: 'complete' as const,
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
const [history, current] = await Promise.all([
|
|
1035
|
+
getVaultEarningsHistory(tidx, {
|
|
1036
|
+
account: options.account,
|
|
1037
|
+
blockNumber,
|
|
1038
|
+
startBlockNumber: beforeFirstBlockNumber,
|
|
1039
|
+
vault: options.vault,
|
|
1040
|
+
}),
|
|
1041
|
+
position,
|
|
1042
|
+
])
|
|
1043
|
+
const externalShareTransfer = await hasExternalShareTransfer(tidx, {
|
|
1044
|
+
account: options.account,
|
|
1045
|
+
blockNumber,
|
|
1046
|
+
shareToken: current.shareToken,
|
|
1047
|
+
startBlockNumber: beforeFirstBlockNumber,
|
|
1048
|
+
vault: options.vault,
|
|
1049
|
+
})
|
|
1050
|
+
const base = {
|
|
1051
|
+
account: options.account,
|
|
1052
|
+
assetToken: current.assetToken,
|
|
1053
|
+
currentValue: current.value.toString(),
|
|
1054
|
+
id: options.account,
|
|
1055
|
+
period: options.period,
|
|
1056
|
+
}
|
|
1057
|
+
if (history.incompleteCostBasis || externalShareTransfer)
|
|
1058
|
+
return { ...base, status: 'incomplete_cost_basis' as const }
|
|
1059
|
+
if (history.pendingRedemption) return { ...base, status: 'pending_redemption' as const }
|
|
1060
|
+
const lifetimeEarnings =
|
|
1061
|
+
current.value + history.redeemed + history.withdrewExact + history.finalized - history.deposited
|
|
1062
|
+
return { ...base, lifetimeEarnings: lifetimeEarnings.toString(), status: 'complete' as const }
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
/** Reads cash-flow-adjusted earnings over a trailing indexed window. */
|
|
1066
|
+
async function getWindowVaultEarnings(
|
|
1067
|
+
c: Context<App.Environment>,
|
|
1068
|
+
options: getWindowVaultEarnings.Options,
|
|
1069
|
+
) {
|
|
1070
|
+
const client = c.get('getClient')(options.chainId)
|
|
1071
|
+
const tidx = c.get('getTidx')(options.chainId)
|
|
1072
|
+
const [current, startBlockNumber] = await Promise.all([
|
|
1073
|
+
client.earn.getPosition({
|
|
1074
|
+
account: options.account,
|
|
1075
|
+
blockNumber: BigInt(options.blockNumber),
|
|
1076
|
+
vault: options.vault,
|
|
1077
|
+
}),
|
|
1078
|
+
getWindowStartBlockNumber(tidx, { days: 30, timestamp: options.timestamp }),
|
|
1079
|
+
])
|
|
1080
|
+
const [externalShareTransfer, history, opening] = await Promise.all([
|
|
1081
|
+
hasExternalShareTransfer(tidx, {
|
|
1082
|
+
account: options.account,
|
|
1083
|
+
blockNumber: options.blockNumber,
|
|
1084
|
+
shareToken: current.shareToken,
|
|
1085
|
+
startBlockNumber,
|
|
1086
|
+
vault: options.vault,
|
|
1087
|
+
}),
|
|
1088
|
+
getVaultEarningsHistory(tidx, {
|
|
1089
|
+
account: options.account,
|
|
1090
|
+
blockNumber: options.blockNumber,
|
|
1091
|
+
startBlockNumber,
|
|
1092
|
+
vault: options.vault,
|
|
1093
|
+
}),
|
|
1094
|
+
getWindowShareState(tidx, {
|
|
1095
|
+
account: options.account,
|
|
1096
|
+
blockNumber: options.blockNumber,
|
|
1097
|
+
shareToken: current.shareToken,
|
|
1098
|
+
startBlockNumber,
|
|
1099
|
+
vault: options.vault,
|
|
1100
|
+
}),
|
|
1101
|
+
])
|
|
1102
|
+
const base = {
|
|
1103
|
+
account: options.account,
|
|
1104
|
+
assetToken: current.assetToken,
|
|
1105
|
+
currentValue: current.value.toString(),
|
|
1106
|
+
id: options.account,
|
|
1107
|
+
period: options.period,
|
|
1108
|
+
}
|
|
1109
|
+
if (
|
|
1110
|
+
opening === undefined ||
|
|
1111
|
+
externalShareTransfer ||
|
|
1112
|
+
history.incompleteCostBasis ||
|
|
1113
|
+
opening.crossBoundaryRedemption ||
|
|
1114
|
+
opening.endShares !== current.shareBalance
|
|
1115
|
+
)
|
|
1116
|
+
return { ...base, status: 'incomplete_cost_basis' as const }
|
|
1117
|
+
if (history.pendingRedemption) return { ...base, status: 'pending_redemption' as const }
|
|
1118
|
+
const openingValue =
|
|
1119
|
+
opening.startShares === 0n
|
|
1120
|
+
? 0n
|
|
1121
|
+
: await client.earn.getRedeemQuote({
|
|
1122
|
+
blockNumber: BigInt(startBlockNumber),
|
|
1123
|
+
shareAmount: opening.startShares,
|
|
1124
|
+
vault: options.vault,
|
|
1125
|
+
})
|
|
1126
|
+
const windowEarnings = calculateWindowEarnings({
|
|
1127
|
+
deposited: history.deposited,
|
|
1128
|
+
endingValue: current.value,
|
|
1129
|
+
finalized: history.finalized,
|
|
1130
|
+
openingValue,
|
|
1131
|
+
redeemed: history.redeemed,
|
|
1132
|
+
withdrewExact: history.withdrewExact,
|
|
1133
|
+
})
|
|
1134
|
+
return { ...base, status: 'complete' as const, windowEarnings: windowEarnings.toString() }
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
declare namespace getWindowVaultEarnings {
|
|
1138
|
+
/** Inputs identifying one account, vault, and indexed window end. */
|
|
1139
|
+
type Options = {
|
|
1140
|
+
/** Account whose earnings are calculated. */
|
|
1141
|
+
account: z.output<typeof Schema.Address>
|
|
1142
|
+
/** Latest indexed block number. */
|
|
1143
|
+
blockNumber: number
|
|
1144
|
+
/** Tempo chain containing the vault. */
|
|
1145
|
+
chainId: z.output<typeof Schema.ChainId>
|
|
1146
|
+
/** Trailing earnings period. */
|
|
1147
|
+
period: '30d'
|
|
1148
|
+
/** Latest indexed block timestamp. */
|
|
1149
|
+
timestamp: string
|
|
1150
|
+
/** Earn vault contract address. */
|
|
1151
|
+
vault: z.output<typeof Schema.Address>
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
/** Calculates cash-flow-adjusted earnings between two position snapshots. */
|
|
1156
|
+
export function calculateWindowEarnings(options: calculateWindowEarnings.Options) {
|
|
1157
|
+
return (
|
|
1158
|
+
options.endingValue +
|
|
1159
|
+
options.redeemed +
|
|
1160
|
+
options.withdrewExact +
|
|
1161
|
+
options.finalized -
|
|
1162
|
+
options.deposited -
|
|
1163
|
+
options.openingValue
|
|
1164
|
+
)
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
export declare namespace calculateWindowEarnings {
|
|
1168
|
+
/** Asset-denominated values spanning one earnings window. */
|
|
1169
|
+
type Options = {
|
|
1170
|
+
/** Assets deposited during the window. */
|
|
1171
|
+
deposited: bigint
|
|
1172
|
+
/** Asset value of shares held at the ending boundary. */
|
|
1173
|
+
endingValue: bigint
|
|
1174
|
+
/** Assets received from async redemptions finalized during the window. */
|
|
1175
|
+
finalized: bigint
|
|
1176
|
+
/** Asset value of shares held at the opening boundary. */
|
|
1177
|
+
openingValue: bigint
|
|
1178
|
+
/** Assets received from redemptions during the window. */
|
|
1179
|
+
redeemed: bigint
|
|
1180
|
+
/** Assets received from exact withdrawals during the window. */
|
|
1181
|
+
withdrewExact: bigint
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
export declare namespace getVaultEarnings {
|
|
1186
|
+
/** Inputs identifying the account and vault to calculate. */
|
|
1187
|
+
type Options = {
|
|
1188
|
+
account: z.output<typeof Schema.Address>
|
|
1189
|
+
chainId: z.output<typeof Schema.ChainId>
|
|
1190
|
+
period: z.output<typeof schema.getEarnVaultEarnings.Query>['period']
|
|
1191
|
+
vault: z.output<typeof Schema.Address>
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
/** Reads the latest block whose indexed logs back the earnings calculation. */
|
|
1196
|
+
async function getLatestIndexedBlock(tidx: Tidx.Client) {
|
|
1197
|
+
const result = await tidx.fetch({
|
|
1198
|
+
engine: 'clickhouse',
|
|
1199
|
+
query: 'SELECT num, timestamp FROM blocks ORDER BY num DESC LIMIT 1' as string,
|
|
1200
|
+
})
|
|
1201
|
+
const number = Value.toNumber(result.rows[0]?.['num'])
|
|
1202
|
+
const timestamp = Value.toIsoDateTime(result.rows[0]?.['timestamp'])
|
|
1203
|
+
if (number === undefined || timestamp === undefined)
|
|
1204
|
+
throw new Error('TIDX returned no indexed block.')
|
|
1205
|
+
return { number, timestamp }
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
/** Resolves the last indexed block at or before a trailing-day boundary. */
|
|
1209
|
+
async function getWindowStartBlockNumber(
|
|
1210
|
+
tidx: Tidx.Client,
|
|
1211
|
+
options: getWindowStartBlockNumber.Options,
|
|
1212
|
+
) {
|
|
1213
|
+
const cutoff = new Date(Date.parse(options.timestamp) - Ttl.days(options.days)).toISOString()
|
|
1214
|
+
const result = await tidx.fetch({
|
|
1215
|
+
query:
|
|
1216
|
+
`SELECT num FROM blocks WHERE timestamp <= '${cutoff}' ORDER BY num DESC LIMIT 1` as string,
|
|
1217
|
+
})
|
|
1218
|
+
const row = result.rows[0]
|
|
1219
|
+
if (!row) return beforeFirstBlockNumber
|
|
1220
|
+
const blockNumber = Value.toNumber(row['num'])
|
|
1221
|
+
if (blockNumber === undefined) throw new Error('TIDX returned no Earn earnings boundary.')
|
|
1222
|
+
return blockNumber
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
declare namespace getWindowStartBlockNumber {
|
|
1226
|
+
/** Inputs defining a trailing boundary from an indexed timestamp. */
|
|
1227
|
+
type Options = {
|
|
1228
|
+
/** Number of trailing days. */
|
|
1229
|
+
days: number
|
|
1230
|
+
/** Timestamp anchoring the end of the window. */
|
|
1231
|
+
timestamp: string
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
/** Reads the indexed cash flows and exceptional states behind one earnings calculation. */
|
|
1236
|
+
async function getVaultEarningsHistory(
|
|
1237
|
+
tidx: Tidx.Client,
|
|
1238
|
+
options: getVaultEarningsHistory.Options,
|
|
1239
|
+
) {
|
|
1240
|
+
const result = await tidx.fetch({
|
|
1241
|
+
engine: 'clickhouse',
|
|
1242
|
+
query: `
|
|
1243
|
+
SELECT
|
|
1244
|
+
(
|
|
1245
|
+
SELECT toString(sum(toUInt256(assets)))
|
|
1246
|
+
FROM Deposited
|
|
1247
|
+
WHERE address = '${options.vault}'
|
|
1248
|
+
AND receiver = '${options.account}'
|
|
1249
|
+
AND block_num > ${options.startBlockNumber}
|
|
1250
|
+
AND block_num <= ${options.blockNumber}
|
|
1251
|
+
) AS deposited_assets,
|
|
1252
|
+
(
|
|
1253
|
+
SELECT toString(sum(toUInt256(finalized.assets)))
|
|
1254
|
+
FROM RedeemFinalized AS finalized
|
|
1255
|
+
INNER JOIN RedeemRequested AS requested
|
|
1256
|
+
ON requested.address = finalized.address
|
|
1257
|
+
AND requested."requestId" = finalized."requestId"
|
|
1258
|
+
WHERE finalized.address = '${options.vault}'
|
|
1259
|
+
AND requested.requester = '${options.account}'
|
|
1260
|
+
AND finalized.block_num > ${options.startBlockNumber}
|
|
1261
|
+
AND finalized.block_num <= ${options.blockNumber}
|
|
1262
|
+
AND requested.block_num <= ${options.blockNumber}
|
|
1263
|
+
) AS finalized_assets,
|
|
1264
|
+
(
|
|
1265
|
+
SELECT count()
|
|
1266
|
+
FROM RedeemRequested AS requested
|
|
1267
|
+
LEFT ANTI JOIN RedeemFinalized AS finalized
|
|
1268
|
+
ON finalized.address = requested.address
|
|
1269
|
+
AND finalized."requestId" = requested."requestId"
|
|
1270
|
+
AND finalized.block_num <= ${options.blockNumber}
|
|
1271
|
+
LEFT ANTI JOIN RedeemCancelled AS cancelled
|
|
1272
|
+
ON cancelled.address = requested.address
|
|
1273
|
+
AND cancelled."requestId" = requested."requestId"
|
|
1274
|
+
AND cancelled.block_num <= ${options.blockNumber}
|
|
1275
|
+
WHERE requested.address = '${options.vault}'
|
|
1276
|
+
AND requested.requester = '${options.account}'
|
|
1277
|
+
AND requested.block_num <= ${options.blockNumber}
|
|
1278
|
+
) AS pending_redemptions,
|
|
1279
|
+
(
|
|
1280
|
+
SELECT toString(sum(toUInt256(assets)))
|
|
1281
|
+
FROM Redeemed
|
|
1282
|
+
WHERE address = '${options.vault}'
|
|
1283
|
+
AND caller = '${options.account}'
|
|
1284
|
+
AND block_num > ${options.startBlockNumber}
|
|
1285
|
+
AND block_num <= ${options.blockNumber}
|
|
1286
|
+
) AS redeemed_assets,
|
|
1287
|
+
(
|
|
1288
|
+
SELECT count()
|
|
1289
|
+
FROM RedeemCancelled AS cancelled
|
|
1290
|
+
INNER JOIN RedeemRequested AS requested
|
|
1291
|
+
ON requested.address = cancelled.address
|
|
1292
|
+
AND requested."requestId" = cancelled."requestId"
|
|
1293
|
+
WHERE cancelled.address = '${options.vault}'
|
|
1294
|
+
AND cancelled.block_num > ${options.startBlockNumber}
|
|
1295
|
+
AND cancelled.block_num <= ${options.blockNumber}
|
|
1296
|
+
AND requested.block_num <= ${options.blockNumber}
|
|
1297
|
+
AND requested.requester != cancelled.receiver
|
|
1298
|
+
AND (
|
|
1299
|
+
requested.requester = '${options.account}'
|
|
1300
|
+
OR cancelled.receiver = '${options.account}'
|
|
1301
|
+
)
|
|
1302
|
+
) AS transferred_cancellations,
|
|
1303
|
+
(
|
|
1304
|
+
SELECT count()
|
|
1305
|
+
FROM VenueSharesDeposited
|
|
1306
|
+
WHERE address = '${options.vault}'
|
|
1307
|
+
AND receiver = '${options.account}'
|
|
1308
|
+
AND block_num > ${options.startBlockNumber}
|
|
1309
|
+
AND block_num <= ${options.blockNumber}
|
|
1310
|
+
) AS venue_deposits,
|
|
1311
|
+
(
|
|
1312
|
+
SELECT toString(sum(toUInt256(assets)))
|
|
1313
|
+
FROM WithdrewExact
|
|
1314
|
+
WHERE address = '${options.vault}'
|
|
1315
|
+
AND caller = '${options.account}'
|
|
1316
|
+
AND block_num > ${options.startBlockNumber}
|
|
1317
|
+
AND block_num <= ${options.blockNumber}
|
|
1318
|
+
) AS withdrew_exact
|
|
1319
|
+
` as string,
|
|
1320
|
+
signatures: [
|
|
1321
|
+
depositedSignature,
|
|
1322
|
+
redeemCancelledSignature,
|
|
1323
|
+
redeemFinalizedSignature,
|
|
1324
|
+
redeemRequestedSignature,
|
|
1325
|
+
redeemedSignature,
|
|
1326
|
+
venueSharesDepositedSignature,
|
|
1327
|
+
withdrewExactSignature,
|
|
1328
|
+
],
|
|
1329
|
+
})
|
|
1330
|
+
const row = result.rows[0]
|
|
1331
|
+
const deposited = Value.toIntegerString(row?.['deposited_assets'])
|
|
1332
|
+
const finalized = Value.toIntegerString(row?.['finalized_assets'])
|
|
1333
|
+
const pendingRedemptions = Value.toNumber(row?.['pending_redemptions'])
|
|
1334
|
+
const redeemed = Value.toIntegerString(row?.['redeemed_assets'])
|
|
1335
|
+
const transferredCancellations = Value.toNumber(row?.['transferred_cancellations'])
|
|
1336
|
+
const venueDeposits = Value.toNumber(row?.['venue_deposits'])
|
|
1337
|
+
const withdrewExact = Value.toIntegerString(row?.['withdrew_exact'])
|
|
1338
|
+
if (
|
|
1339
|
+
deposited === undefined ||
|
|
1340
|
+
finalized === undefined ||
|
|
1341
|
+
pendingRedemptions === undefined ||
|
|
1342
|
+
redeemed === undefined ||
|
|
1343
|
+
transferredCancellations === undefined ||
|
|
1344
|
+
venueDeposits === undefined ||
|
|
1345
|
+
withdrewExact === undefined
|
|
1346
|
+
)
|
|
1347
|
+
throw new Error('TIDX returned invalid Earn earnings history.')
|
|
1348
|
+
return {
|
|
1349
|
+
deposited: BigInt(deposited),
|
|
1350
|
+
finalized: BigInt(finalized),
|
|
1351
|
+
incompleteCostBasis: transferredCancellations > 0 || venueDeposits > 0,
|
|
1352
|
+
pendingRedemption: pendingRedemptions > 0,
|
|
1353
|
+
redeemed: BigInt(redeemed),
|
|
1354
|
+
withdrewExact: BigInt(withdrewExact),
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
declare namespace getVaultEarningsHistory {
|
|
1359
|
+
type Options = {
|
|
1360
|
+
account: z.output<typeof Schema.Address>
|
|
1361
|
+
blockNumber: number
|
|
1362
|
+
startBlockNumber: number
|
|
1363
|
+
vault: z.output<typeof Schema.Address>
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
/** Reads share balances and cross-boundary redemptions for a trailing window. */
|
|
1368
|
+
async function getWindowShareState(tidx: Tidx.Client, options: getWindowShareState.Options) {
|
|
1369
|
+
const result = await tidx.fetch({
|
|
1370
|
+
engine: 'clickhouse',
|
|
1371
|
+
query: `
|
|
1372
|
+
SELECT
|
|
1373
|
+
(
|
|
1374
|
+
SELECT toString(sum(toUInt256(amount)))
|
|
1375
|
+
FROM token_transfers
|
|
1376
|
+
WHERE token = '${options.shareToken.toLowerCase()}'
|
|
1377
|
+
AND "to" = '${options.account}'
|
|
1378
|
+
AND block_num <= ${options.startBlockNumber}
|
|
1379
|
+
) AS received_start,
|
|
1380
|
+
(
|
|
1381
|
+
SELECT toString(sum(toUInt256(amount)))
|
|
1382
|
+
FROM token_transfers
|
|
1383
|
+
WHERE token = '${options.shareToken.toLowerCase()}'
|
|
1384
|
+
AND "from" = '${options.account}'
|
|
1385
|
+
AND block_num <= ${options.startBlockNumber}
|
|
1386
|
+
) AS sent_start,
|
|
1387
|
+
(
|
|
1388
|
+
SELECT toString(sum(toUInt256(amount)))
|
|
1389
|
+
FROM token_transfers
|
|
1390
|
+
WHERE token = '${options.shareToken.toLowerCase()}'
|
|
1391
|
+
AND "to" = '${options.account}'
|
|
1392
|
+
AND block_num <= ${options.blockNumber}
|
|
1393
|
+
) AS received_end,
|
|
1394
|
+
(
|
|
1395
|
+
SELECT toString(sum(toUInt256(amount)))
|
|
1396
|
+
FROM token_transfers
|
|
1397
|
+
WHERE token = '${options.shareToken.toLowerCase()}'
|
|
1398
|
+
AND "from" = '${options.account}'
|
|
1399
|
+
AND block_num <= ${options.blockNumber}
|
|
1400
|
+
) AS sent_end,
|
|
1401
|
+
(
|
|
1402
|
+
SELECT count()
|
|
1403
|
+
FROM (
|
|
1404
|
+
SELECT finalized."requestId"
|
|
1405
|
+
FROM RedeemFinalized AS finalized
|
|
1406
|
+
INNER JOIN RedeemRequested AS requested
|
|
1407
|
+
ON requested.address = finalized.address
|
|
1408
|
+
AND requested."requestId" = finalized."requestId"
|
|
1409
|
+
WHERE finalized.address = '${options.vault}'
|
|
1410
|
+
AND requested.requester = '${options.account}'
|
|
1411
|
+
AND requested.block_num <= ${options.startBlockNumber}
|
|
1412
|
+
AND finalized.block_num > ${options.startBlockNumber}
|
|
1413
|
+
AND finalized.block_num <= ${options.blockNumber}
|
|
1414
|
+
UNION ALL
|
|
1415
|
+
SELECT cancelled."requestId"
|
|
1416
|
+
FROM RedeemCancelled AS cancelled
|
|
1417
|
+
INNER JOIN RedeemRequested AS requested
|
|
1418
|
+
ON requested.address = cancelled.address
|
|
1419
|
+
AND requested."requestId" = cancelled."requestId"
|
|
1420
|
+
WHERE cancelled.address = '${options.vault}'
|
|
1421
|
+
AND requested.requester = '${options.account}'
|
|
1422
|
+
AND requested.block_num <= ${options.startBlockNumber}
|
|
1423
|
+
AND cancelled.block_num > ${options.startBlockNumber}
|
|
1424
|
+
AND cancelled.block_num <= ${options.blockNumber}
|
|
1425
|
+
) AS cross_boundary
|
|
1426
|
+
) AS cross_boundary_redemptions
|
|
1427
|
+
` as string,
|
|
1428
|
+
signatures: [redeemCancelledSignature, redeemFinalizedSignature, redeemRequestedSignature],
|
|
1429
|
+
})
|
|
1430
|
+
const row = result.rows[0]
|
|
1431
|
+
const crossBoundaryRedemptions = Value.toNumber(row?.['cross_boundary_redemptions'])
|
|
1432
|
+
const receivedEnd = Value.toIntegerString(row?.['received_end'])
|
|
1433
|
+
const receivedStart = Value.toIntegerString(row?.['received_start'])
|
|
1434
|
+
const sentEnd = Value.toIntegerString(row?.['sent_end'])
|
|
1435
|
+
const sentStart = Value.toIntegerString(row?.['sent_start'])
|
|
1436
|
+
if (
|
|
1437
|
+
crossBoundaryRedemptions === undefined ||
|
|
1438
|
+
receivedEnd === undefined ||
|
|
1439
|
+
receivedStart === undefined ||
|
|
1440
|
+
sentEnd === undefined ||
|
|
1441
|
+
sentStart === undefined
|
|
1442
|
+
)
|
|
1443
|
+
return undefined
|
|
1444
|
+
const receivedEndValue = BigInt(receivedEnd)
|
|
1445
|
+
const receivedStartValue = BigInt(receivedStart)
|
|
1446
|
+
const sentEndValue = BigInt(sentEnd)
|
|
1447
|
+
const sentStartValue = BigInt(sentStart)
|
|
1448
|
+
if (sentEndValue > receivedEndValue || sentStartValue > receivedStartValue) return undefined
|
|
1449
|
+
return {
|
|
1450
|
+
crossBoundaryRedemption: crossBoundaryRedemptions > 0,
|
|
1451
|
+
endShares: receivedEndValue - sentEndValue,
|
|
1452
|
+
startShares: receivedStartValue - sentStartValue,
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
declare namespace getWindowShareState {
|
|
1457
|
+
/** Inputs identifying one account's share history window. */
|
|
1458
|
+
type Options = {
|
|
1459
|
+
/** Account whose share history is read. */
|
|
1460
|
+
account: z.output<typeof Schema.Address>
|
|
1461
|
+
/** Ending indexed block number. */
|
|
1462
|
+
blockNumber: number
|
|
1463
|
+
/** Earn share token contract address. */
|
|
1464
|
+
shareToken: z.output<typeof Schema.Address>
|
|
1465
|
+
/** Opening indexed block number. */
|
|
1466
|
+
startBlockNumber: number
|
|
1467
|
+
/** Earn vault contract address. */
|
|
1468
|
+
vault: z.output<typeof Schema.Address>
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
/** Returns whether an account sent or received Earn shares outside known vault flows. */
|
|
1473
|
+
async function hasExternalShareTransfer(
|
|
1474
|
+
tidx: Tidx.Client,
|
|
1475
|
+
options: hasExternalShareTransfer.Options,
|
|
1476
|
+
) {
|
|
1477
|
+
// Vault exits pull shares into the vault before burning. Reconcile counts by transaction and amount so unmatched direct sends remain external.
|
|
1478
|
+
const result = await tidx.fetch({
|
|
1479
|
+
engine: 'clickhouse',
|
|
1480
|
+
query: `
|
|
1481
|
+
SELECT
|
|
1482
|
+
(
|
|
1483
|
+
SELECT count()
|
|
1484
|
+
FROM token_transfers
|
|
1485
|
+
WHERE token = '${options.shareToken.toLowerCase()}'
|
|
1486
|
+
AND block_num > ${options.startBlockNumber}
|
|
1487
|
+
AND block_num <= ${options.blockNumber}
|
|
1488
|
+
AND (
|
|
1489
|
+
("from" = '${options.account}' AND "to" != '${options.vault}')
|
|
1490
|
+
OR (
|
|
1491
|
+
"to" = '${options.account}'
|
|
1492
|
+
AND "from" != '${zeroAddress}'
|
|
1493
|
+
AND "from" != '${options.vault}'
|
|
1494
|
+
)
|
|
1495
|
+
)
|
|
1496
|
+
) + (
|
|
1497
|
+
SELECT count()
|
|
1498
|
+
FROM (
|
|
1499
|
+
SELECT tx_hash, toUInt256(amount) AS amount, count() AS transfer_count
|
|
1500
|
+
FROM token_transfers
|
|
1501
|
+
WHERE token = '${options.shareToken.toLowerCase()}'
|
|
1502
|
+
AND "from" = '${options.account}'
|
|
1503
|
+
AND "to" = '${options.vault}'
|
|
1504
|
+
AND block_num > ${options.startBlockNumber}
|
|
1505
|
+
AND block_num <= ${options.blockNumber}
|
|
1506
|
+
GROUP BY tx_hash, amount
|
|
1507
|
+
) AS candidate
|
|
1508
|
+
LEFT JOIN (
|
|
1509
|
+
SELECT tx_hash, amount, count() AS exit_count
|
|
1510
|
+
FROM (
|
|
1511
|
+
SELECT tx_hash, toUInt256(earnShares) AS amount
|
|
1512
|
+
FROM Redeemed
|
|
1513
|
+
WHERE address = '${options.vault}'
|
|
1514
|
+
AND caller = '${options.account}'
|
|
1515
|
+
AND block_num > ${options.startBlockNumber}
|
|
1516
|
+
AND block_num <= ${options.blockNumber}
|
|
1517
|
+
UNION ALL
|
|
1518
|
+
SELECT tx_hash, toUInt256(earnShares) AS amount
|
|
1519
|
+
FROM RedeemRequested
|
|
1520
|
+
WHERE address = '${options.vault}'
|
|
1521
|
+
AND requester = '${options.account}'
|
|
1522
|
+
AND block_num > ${options.startBlockNumber}
|
|
1523
|
+
AND block_num <= ${options.blockNumber}
|
|
1524
|
+
UNION ALL
|
|
1525
|
+
SELECT tx_hash, toUInt256(earnSharesBurned) AS amount
|
|
1526
|
+
FROM WithdrewExact
|
|
1527
|
+
WHERE address = '${options.vault}'
|
|
1528
|
+
AND caller = '${options.account}'
|
|
1529
|
+
AND block_num > ${options.startBlockNumber}
|
|
1530
|
+
AND block_num <= ${options.blockNumber}
|
|
1531
|
+
) AS protocol_exit
|
|
1532
|
+
GROUP BY tx_hash, amount
|
|
1533
|
+
) AS matched
|
|
1534
|
+
ON matched.tx_hash = candidate.tx_hash AND matched.amount = candidate.amount
|
|
1535
|
+
WHERE candidate.transfer_count > coalesce(matched.exit_count, 0)
|
|
1536
|
+
) + (
|
|
1537
|
+
SELECT count()
|
|
1538
|
+
FROM (
|
|
1539
|
+
SELECT tx_hash, toUInt256(amount) AS amount, count() AS transfer_count
|
|
1540
|
+
FROM token_transfers
|
|
1541
|
+
WHERE token = '${options.shareToken.toLowerCase()}'
|
|
1542
|
+
AND "from" = '${options.vault}'
|
|
1543
|
+
AND "to" = '${options.account}'
|
|
1544
|
+
AND block_num > ${options.startBlockNumber}
|
|
1545
|
+
AND block_num <= ${options.blockNumber}
|
|
1546
|
+
GROUP BY tx_hash, amount
|
|
1547
|
+
) AS candidate
|
|
1548
|
+
LEFT JOIN (
|
|
1549
|
+
SELECT tx_hash, toUInt256(earnShares) AS amount, count() AS cancel_count
|
|
1550
|
+
FROM RedeemCancelled
|
|
1551
|
+
WHERE address = '${options.vault}'
|
|
1552
|
+
AND receiver = '${options.account}'
|
|
1553
|
+
AND block_num > ${options.startBlockNumber}
|
|
1554
|
+
AND block_num <= ${options.blockNumber}
|
|
1555
|
+
GROUP BY tx_hash, amount
|
|
1556
|
+
) AS matched
|
|
1557
|
+
ON matched.tx_hash = candidate.tx_hash AND matched.amount = candidate.amount
|
|
1558
|
+
WHERE candidate.transfer_count > coalesce(matched.cancel_count, 0)
|
|
1559
|
+
) AS count
|
|
1560
|
+
` as string,
|
|
1561
|
+
signatures: [
|
|
1562
|
+
redeemCancelledSignature,
|
|
1563
|
+
redeemedSignature,
|
|
1564
|
+
redeemRequestedSignature,
|
|
1565
|
+
withdrewExactSignature,
|
|
1566
|
+
],
|
|
1567
|
+
})
|
|
1568
|
+
const count = Value.toNumber(result.rows[0]?.['count'])
|
|
1569
|
+
if (count === undefined) throw new Error('TIDX returned an invalid Earn-share transfer count.')
|
|
1570
|
+
return count > 0
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
declare namespace hasExternalShareTransfer {
|
|
1574
|
+
type Options = {
|
|
1575
|
+
account: z.output<typeof Schema.Address>
|
|
1576
|
+
blockNumber: number
|
|
1577
|
+
shareToken: z.output<typeof Schema.Address>
|
|
1578
|
+
startBlockNumber: number
|
|
1579
|
+
vault: z.output<typeof Schema.Address>
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
/** Calculates the asset cost basis attached to an account's remaining Earn shares. */
|
|
1584
|
+
export function calculateActiveCostBasis(options: calculateActiveCostBasis.Options) {
|
|
1585
|
+
const requests = new Map<string, { costBasis: bigint | undefined; shares: bigint }>()
|
|
1586
|
+
let costBasis: bigint | undefined = 0n
|
|
1587
|
+
let shares = 0n
|
|
1588
|
+
|
|
1589
|
+
const remove = (burned: bigint) => {
|
|
1590
|
+
if (burned > shares || shares === 0n) return undefined
|
|
1591
|
+
const removed = (() => {
|
|
1592
|
+
if (costBasis === undefined) return undefined
|
|
1593
|
+
return burned === shares ? costBasis : (costBasis * burned) / shares
|
|
1594
|
+
})()
|
|
1595
|
+
if (burned === shares) costBasis = 0n
|
|
1596
|
+
else if (costBasis !== undefined && removed !== undefined) costBasis -= removed
|
|
1597
|
+
shares -= burned
|
|
1598
|
+
return { costBasis: removed }
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
for (const action of options.actions) {
|
|
1602
|
+
if (action.kind === 'deposit') {
|
|
1603
|
+
if (costBasis !== undefined) costBasis += action.assets
|
|
1604
|
+
shares += action.shares
|
|
1605
|
+
continue
|
|
1606
|
+
}
|
|
1607
|
+
if (action.kind === 'unknown') {
|
|
1608
|
+
costBasis = undefined
|
|
1609
|
+
shares += action.shares
|
|
1610
|
+
continue
|
|
1611
|
+
}
|
|
1612
|
+
if (action.kind === 'cancel') {
|
|
1613
|
+
const restored = requests.get(action.requestId)
|
|
1614
|
+
if (restored === undefined || restored.shares !== action.shares) return undefined
|
|
1615
|
+
if (costBasis === undefined || restored.costBasis === undefined) costBasis = undefined
|
|
1616
|
+
else costBasis += restored.costBasis
|
|
1617
|
+
shares += restored.shares
|
|
1618
|
+
requests.delete(action.requestId)
|
|
1619
|
+
continue
|
|
1620
|
+
}
|
|
1621
|
+
const removed = remove(action.shares)
|
|
1622
|
+
if (removed === undefined) return undefined
|
|
1623
|
+
if (action.kind === 'request')
|
|
1624
|
+
requests.set(action.requestId, { costBasis: removed.costBasis, shares: action.shares })
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
return shares === options.shareBalance && costBasis !== undefined ? costBasis : undefined
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
export declare namespace calculateActiveCostBasis {
|
|
1631
|
+
/** One indexed share or cost-basis mutation. */
|
|
1632
|
+
export type Action =
|
|
1633
|
+
| {
|
|
1634
|
+
/** Action discriminator. */
|
|
1635
|
+
kind: 'cancel'
|
|
1636
|
+
/** Async redemption request being cancelled. */
|
|
1637
|
+
requestId: string
|
|
1638
|
+
/** Shares restored to the account. */
|
|
1639
|
+
shares: bigint
|
|
1640
|
+
}
|
|
1641
|
+
| {
|
|
1642
|
+
/** Assets added to the account's cost basis. */
|
|
1643
|
+
assets: bigint
|
|
1644
|
+
/** Action discriminator. */
|
|
1645
|
+
kind: 'deposit'
|
|
1646
|
+
/** Shares issued for the deposit. */
|
|
1647
|
+
shares: bigint
|
|
1648
|
+
}
|
|
1649
|
+
| {
|
|
1650
|
+
/** Action discriminator. */
|
|
1651
|
+
kind: 'redeem'
|
|
1652
|
+
/** Shares removed from the account. */
|
|
1653
|
+
shares: bigint
|
|
1654
|
+
}
|
|
1655
|
+
| {
|
|
1656
|
+
/** Action discriminator. */
|
|
1657
|
+
kind: 'request'
|
|
1658
|
+
/** Async redemption request holding the removed basis. */
|
|
1659
|
+
requestId: string
|
|
1660
|
+
/** Shares removed from the account. */
|
|
1661
|
+
shares: bigint
|
|
1662
|
+
}
|
|
1663
|
+
| {
|
|
1664
|
+
/** Action discriminator. */
|
|
1665
|
+
kind: 'unknown'
|
|
1666
|
+
/** Shares received without an asset-denominated cost basis. */
|
|
1667
|
+
shares: bigint
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
/** Inputs for calculating the remaining cost basis. */
|
|
1671
|
+
export type Options = {
|
|
1672
|
+
/** Indexed mutations in chain order. */
|
|
1673
|
+
actions: readonly Action[]
|
|
1674
|
+
/** Share balance read at the same indexed block. */
|
|
1675
|
+
shareBalance: bigint
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
/** Reads the active asset cost basis for shares held at one indexed block. */
|
|
1680
|
+
async function getActiveCostBasis(tidx: Tidx.Client, options: getActiveCostBasis.Options) {
|
|
1681
|
+
const result = await tidx.fetch({
|
|
1682
|
+
engine: 'clickhouse',
|
|
1683
|
+
query: `
|
|
1684
|
+
SELECT kind, request_id, shares, assets
|
|
1685
|
+
FROM (
|
|
1686
|
+
SELECT
|
|
1687
|
+
'deposit' AS kind,
|
|
1688
|
+
block_num,
|
|
1689
|
+
log_idx,
|
|
1690
|
+
'' AS request_id,
|
|
1691
|
+
toString(toUInt256(earnShares)) AS shares,
|
|
1692
|
+
toString(toUInt256(assets)) AS assets
|
|
1693
|
+
FROM Deposited
|
|
1694
|
+
WHERE address = '${options.vault}'
|
|
1695
|
+
AND receiver = '${options.account}'
|
|
1696
|
+
AND block_num <= ${options.blockNumber}
|
|
1697
|
+
UNION ALL
|
|
1698
|
+
SELECT
|
|
1699
|
+
'redeem' AS kind,
|
|
1700
|
+
block_num,
|
|
1701
|
+
log_idx,
|
|
1702
|
+
'' AS request_id,
|
|
1703
|
+
toString(toUInt256(earnShares)) AS shares,
|
|
1704
|
+
'0' AS assets
|
|
1705
|
+
FROM Redeemed
|
|
1706
|
+
WHERE address = '${options.vault}'
|
|
1707
|
+
AND caller = '${options.account}'
|
|
1708
|
+
AND block_num <= ${options.blockNumber}
|
|
1709
|
+
UNION ALL
|
|
1710
|
+
SELECT
|
|
1711
|
+
'redeem' AS kind,
|
|
1712
|
+
block_num,
|
|
1713
|
+
log_idx,
|
|
1714
|
+
'' AS request_id,
|
|
1715
|
+
toString(toUInt256(earnSharesBurned)) AS shares,
|
|
1716
|
+
'0' AS assets
|
|
1717
|
+
FROM WithdrewExact
|
|
1718
|
+
WHERE address = '${options.vault}'
|
|
1719
|
+
AND caller = '${options.account}'
|
|
1720
|
+
AND block_num <= ${options.blockNumber}
|
|
1721
|
+
UNION ALL
|
|
1722
|
+
SELECT
|
|
1723
|
+
'request' AS kind,
|
|
1724
|
+
block_num,
|
|
1725
|
+
log_idx,
|
|
1726
|
+
"requestId" AS request_id,
|
|
1727
|
+
toString(toUInt256(earnShares)) AS shares,
|
|
1728
|
+
'0' AS assets
|
|
1729
|
+
FROM RedeemRequested
|
|
1730
|
+
WHERE address = '${options.vault}'
|
|
1731
|
+
AND requester = '${options.account}'
|
|
1732
|
+
AND block_num <= ${options.blockNumber}
|
|
1733
|
+
UNION ALL
|
|
1734
|
+
SELECT
|
|
1735
|
+
'cancel' AS kind,
|
|
1736
|
+
cancelled.block_num AS block_num,
|
|
1737
|
+
cancelled.log_idx AS log_idx,
|
|
1738
|
+
cancelled."requestId" AS request_id,
|
|
1739
|
+
toString(toUInt256(cancelled.earnShares)) AS shares,
|
|
1740
|
+
'0' AS assets
|
|
1741
|
+
FROM RedeemCancelled AS cancelled
|
|
1742
|
+
INNER JOIN RedeemRequested AS requested
|
|
1743
|
+
ON requested.address = cancelled.address
|
|
1744
|
+
AND requested."requestId" = cancelled."requestId"
|
|
1745
|
+
WHERE cancelled.address = '${options.vault}'
|
|
1746
|
+
AND requested.requester = '${options.account}'
|
|
1747
|
+
AND cancelled.receiver = '${options.account}'
|
|
1748
|
+
AND cancelled.block_num <= ${options.blockNumber}
|
|
1749
|
+
AND requested.block_num <= ${options.blockNumber}
|
|
1750
|
+
UNION ALL
|
|
1751
|
+
SELECT
|
|
1752
|
+
'unknown' AS kind,
|
|
1753
|
+
block_num,
|
|
1754
|
+
log_idx,
|
|
1755
|
+
'' AS request_id,
|
|
1756
|
+
toString(toUInt256(earnShares)) AS shares,
|
|
1757
|
+
'0' AS assets
|
|
1758
|
+
FROM VenueSharesDeposited
|
|
1759
|
+
WHERE address = '${options.vault}'
|
|
1760
|
+
AND receiver = '${options.account}'
|
|
1761
|
+
AND block_num <= ${options.blockNumber}
|
|
1762
|
+
UNION ALL
|
|
1763
|
+
SELECT
|
|
1764
|
+
'unknown' AS kind,
|
|
1765
|
+
block_num,
|
|
1766
|
+
log_idx,
|
|
1767
|
+
'' AS request_id,
|
|
1768
|
+
toString(toUInt256(amount)) AS shares,
|
|
1769
|
+
'0' AS assets
|
|
1770
|
+
FROM token_transfers
|
|
1771
|
+
WHERE token = '${options.shareToken.toLowerCase()}'
|
|
1772
|
+
AND "to" = '${options.account}'
|
|
1773
|
+
AND "from" != '${zeroAddress}'
|
|
1774
|
+
AND "from" != '${options.vault}'
|
|
1775
|
+
AND block_num <= ${options.blockNumber}
|
|
1776
|
+
UNION ALL
|
|
1777
|
+
SELECT
|
|
1778
|
+
'redeem' AS kind,
|
|
1779
|
+
block_num,
|
|
1780
|
+
log_idx,
|
|
1781
|
+
'' AS request_id,
|
|
1782
|
+
toString(toUInt256(amount)) AS shares,
|
|
1783
|
+
'0' AS assets
|
|
1784
|
+
FROM token_transfers
|
|
1785
|
+
WHERE token = '${options.shareToken.toLowerCase()}'
|
|
1786
|
+
AND "from" = '${options.account}'
|
|
1787
|
+
AND "to" != '${options.vault}'
|
|
1788
|
+
AND block_num <= ${options.blockNumber}
|
|
1789
|
+
) AS actions
|
|
1790
|
+
ORDER BY block_num, log_idx
|
|
1791
|
+
` as string,
|
|
1792
|
+
signatures: [
|
|
1793
|
+
depositedSignature,
|
|
1794
|
+
redeemCancelledSignature,
|
|
1795
|
+
redeemRequestedSignature,
|
|
1796
|
+
redeemedSignature,
|
|
1797
|
+
venueSharesDepositedSignature,
|
|
1798
|
+
withdrewExactSignature,
|
|
1799
|
+
],
|
|
1800
|
+
})
|
|
1801
|
+
const actions: calculateActiveCostBasis.Action[] = []
|
|
1802
|
+
for (const row of result.rows) {
|
|
1803
|
+
const kind = Value.toText(row['kind'])
|
|
1804
|
+
if (kind === 'deposit') {
|
|
1805
|
+
const assets = Value.toIntegerString(row['assets'])
|
|
1806
|
+
const shares = Value.toIntegerString(row['shares'])
|
|
1807
|
+
if (assets === undefined || shares === undefined) return undefined
|
|
1808
|
+
actions.push({ assets: BigInt(assets), kind, shares: BigInt(shares) })
|
|
1809
|
+
continue
|
|
1810
|
+
}
|
|
1811
|
+
if (kind === 'redeem') {
|
|
1812
|
+
const shares = Value.toIntegerString(row['shares'])
|
|
1813
|
+
if (shares === undefined) return undefined
|
|
1814
|
+
actions.push({ kind, shares: BigInt(shares) })
|
|
1815
|
+
continue
|
|
1816
|
+
}
|
|
1817
|
+
if (kind === 'cancel') {
|
|
1818
|
+
const requestId = Value.toText(row['request_id'])
|
|
1819
|
+
const shares = Value.toIntegerString(row['shares'])
|
|
1820
|
+
if (requestId === undefined || shares === undefined) return undefined
|
|
1821
|
+
actions.push({ kind, requestId, shares: BigInt(shares) })
|
|
1822
|
+
continue
|
|
1823
|
+
}
|
|
1824
|
+
if (kind === 'request') {
|
|
1825
|
+
const requestId = Value.toText(row['request_id'])
|
|
1826
|
+
const shares = Value.toIntegerString(row['shares'])
|
|
1827
|
+
if (requestId === undefined || shares === undefined) return undefined
|
|
1828
|
+
actions.push({ kind, requestId, shares: BigInt(shares) })
|
|
1829
|
+
continue
|
|
1830
|
+
}
|
|
1831
|
+
if (kind === 'unknown') {
|
|
1832
|
+
const shares = Value.toIntegerString(row['shares'])
|
|
1833
|
+
if (shares === undefined) return undefined
|
|
1834
|
+
actions.push({ kind, shares: BigInt(shares) })
|
|
1835
|
+
continue
|
|
1836
|
+
}
|
|
1837
|
+
return undefined
|
|
1838
|
+
}
|
|
1839
|
+
return calculateActiveCostBasis({ actions, shareBalance: options.shareBalance })
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
declare namespace getActiveCostBasis {
|
|
1843
|
+
type Options = {
|
|
1844
|
+
account: z.output<typeof Schema.Address>
|
|
1845
|
+
blockNumber: number
|
|
1846
|
+
shareBalance: bigint
|
|
1847
|
+
shareToken: z.output<typeof Schema.Address>
|
|
1848
|
+
vault: z.output<typeof Schema.Address>
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
|
|
714
1852
|
/** Lists compatible deployed Earn vaults. */
|
|
715
1853
|
export async function listVaults(c: Context<App.Environment>, options: listVaults.Options) {
|
|
716
1854
|
const db = Db.get(c.get('dbCached'))
|