Skip to content

Commit fe2a2f9

Browse files
committed
Update prodigyfi methodology
1 parent 087566c commit fe2a2f9

2 files changed

Lines changed: 113 additions & 178 deletions

File tree

options/prodigy-fi/index.ts

Lines changed: 63 additions & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,17 @@
11
import { FetchOptions, FetchResultV2, SimpleAdapter } from "../../adapters/types";
2-
import { CHAIN } from "../../helpers/chains";
2+
import {
3+
BlockRange,
4+
VaultMetadata,
5+
ChainConfig,
6+
VaultQuery,
7+
config,
8+
v2Deposit,
9+
v1_1Deposit,
10+
v1Deposit,
11+
} from "./interfaces";
312

413
const WAD = 10n ** 18n;
514

6-
const vaultParams = "tuple(address owner,address vaultBatchManager,address linkedToken,address investmentToken,address aggregator,bytes32 priceFeed,uint256 expiry,uint256 linkedPrice,int256 linkedOraclePrice,uint256 yieldValue,bool isBuyLow,uint256 quantity,uint256 depositDeadline,uint64 minConfidenceRatio,address collateralPool,uint256 tradingFeeRate,address feeReceiver,int256 oraclePriceAtCreation,address signer)";
7-
const legacyVaultParams = "tuple(address owner,address vaultBatchManager,address linkedToken,address investmentToken,address aggregator,bytes32 priceFeed,uint256 expiry,uint256 linkedPrice,int256 linkedOraclePrice,uint256 yieldValue,bool isBuyLow,uint256 quantity,uint256 depositDeadline,uint64 minConfidenceRatio,address collateralPool,uint256 tradingFeeRate,address feeReceiver,int256 oraclePriceAtCreation)";
8-
9-
const eventAbis = {
10-
vaultCreated: `event VaultCreated(address indexed owner,address indexed baseToken,address indexed quoteToken,address vaultAddress,${vaultParams} vaultParams,uint8 vaultSeriesVersion)`,
11-
legacyVaultCreatedWithParams: `event VaultCreated(address indexed owner,address indexed baseToken,address indexed quoteToken,address vaultAddress,${legacyVaultParams} vaultParams)`,
12-
legacyVaultCreated: "event VaultCreated(address indexed owner,address indexed baseToken,address indexed quoteToken,address vaultAddress,uint256 expiry,uint256 linkedPrice,int256 linkedOraclePrice,uint256 yieldValue,bool isBuyLow,uint256 quantity,uint256 depositDeadline,uint256 tradingFeeRate,uint256 cancellationFeeRate,int256 oraclePriceAtCreation)",
13-
deposit: "event Deposit(address indexed user,uint256 depositAmount,int256 oraclePriceAtDeposit,uint256 yieldValue)",
14-
legacyDeposit: "event Deposit(address indexed user,uint256 depositAmount,int256 oraclePriceAtDeposit)",
15-
};
16-
17-
type ChainConfig = {
18-
factory: string;
19-
startBlock: number;
20-
// Factory upgrade boundaries (topic0 0x1fdc1341...ceed42):
21-
// [startBlock, legacyVaultCreatedWithParamsStartBlock) -> legacyVaultCreated
22-
// [legacyVaultCreatedWithParamsStartBlock, currentVaultCreatedStartBlock) -> legacyVaultCreatedWithParams
23-
// [currentVaultCreatedStartBlock, +inf) -> vaultCreated
24-
legacyVaultCreatedWithParamsStartBlock: number;
25-
currentVaultCreatedStartBlock: number;
26-
// Deposit gained the yieldValue field at currentDepositStartBlock:
27-
// [startBlock, currentDepositStartBlock) -> legacyDeposit (3-arg)
28-
// [currentDepositStartBlock, +inf) -> deposit (4-arg w/ yieldValue)
29-
currentDepositStartBlock: number;
30-
start: string;
31-
};
32-
33-
const config: Record<string, ChainConfig> = {
34-
[CHAIN.ETHEREUM]: {
35-
factory: "0xAC2a612C49f29e26858Df1a53f7623180bcc3753",
36-
startBlock: 23831175,
37-
legacyVaultCreatedWithParamsStartBlock: 23831175, // No flat legacy VaultCreated events on Ethereum
38-
currentVaultCreatedStartBlock: 24036992, // Upgrade to v8 which added vaultSeriesVersion field in VaultCreated event
39-
currentDepositStartBlock: 23831175, // No legacy deposits on Ethereum; the initial Deposit implementation already emitted yieldValue field
40-
start: "2025-11-19"
41-
},
42-
[CHAIN.BASE]: {
43-
factory: "0xFE198B51cfb1F96b56c63fe323a934BEAAA3b281",
44-
startBlock: 22133150,
45-
legacyVaultCreatedWithParamsStartBlock: 30509116, // Upgrade to v4 with vaultParams tuple in VaultCreated event
46-
currentVaultCreatedStartBlock: 39795788, // Upgrade to v8 which added vaultSeriesVersion field in VaultCreated event
47-
currentDepositStartBlock: 30509116, // Upgrade to v4 which added yieldValue field in Deposit event
48-
start: "2024-11-08"
49-
},
50-
[CHAIN.BERACHAIN]: {
51-
factory: "0x29ca87b2f744127606ada4564da8219be6498ca1",
52-
startBlock: 804138,
53-
legacyVaultCreatedWithParamsStartBlock: 5297670, // Upgrade to v4 with vaultParams tuple in VaultCreated event
54-
currentVaultCreatedStartBlock: 14682550, // Upgrade to v8 which added vaultSeriesVersion field in VaultCreated event
55-
currentDepositStartBlock: 5297670, // Upgrade to v4 which added yieldValue field in Deposit event
56-
start: "2025-02-07"
57-
},
58-
};
59-
60-
const vaultMetadataCache = new Map<string, { toBlock: number; metadata: Map<string, VaultMetadata> }>();
61-
62-
type BlockRange = { fromBlock: number; toBlock: number };
63-
64-
type VaultMetadata = {
65-
investmentToken: string;
66-
yieldValue: bigint;
67-
};
68-
6915
function intersectRange(
7016
fromBlock: number,
7117
toBlock: number,
@@ -88,92 +34,16 @@ function addVaultMetadata(
8834
vaults: Map<string, VaultMetadata>,
8935
vaultAddress: string,
9036
investmentToken: string,
37+
quantity: any,
9138
yieldValue: any,
9239
) {
93-
if (!vaultAddress || !investmentToken || yieldValue === null || yieldValue === undefined) return;
40+
if (!vaultAddress || !investmentToken || quantity === null || quantity === undefined) return;
9441

9542
vaults.set(vaultAddress.toLowerCase(), {
96-
investmentToken,
97-
yieldValue: toBigInt(yieldValue),
98-
});
99-
}
100-
101-
async function fetch(options: FetchOptions): Promise<FetchResultV2> {
102-
const chainConfig = config[options.chain];
103-
const dailyNotionalVolume = options.createBalances();
104-
const dailyPremiumVolume = options.createBalances();
105-
106-
const fetchFromBlock = await options.getFromBlock();
107-
const fetchToBlock = await options.getToBlock();
108-
const legacyDepositRange = intersectRange(fetchFromBlock, fetchToBlock, chainConfig.startBlock, chainConfig.currentDepositStartBlock - 1);
109-
const currentDepositRange = intersectRange(fetchFromBlock, fetchToBlock, chainConfig.currentDepositStartBlock, fetchToBlock);
110-
const vaultMetadata = await getVaultMetadataFromEvents(options, chainConfig, fetchToBlock);
111-
const vaultAddresses = [...vaultMetadata.keys()];
112-
113-
if (vaultAddresses.length === 0) {
114-
return { dailyNotionalVolume, dailyPremiumVolume };
115-
}
116-
117-
let combinedDepositLogs: any[] = [];
118-
119-
if (currentDepositRange) {
120-
const currentDepositLogs = await options.getLogs({
121-
noTarget: true, // there are 40K+ vaults, so no target and filter later is more optimal
122-
eventAbi: eventAbis.deposit,
123-
entireLog: true,
124-
parseLog: true,
125-
})
126-
combinedDepositLogs.push(...currentDepositLogs)
127-
}
128-
if (legacyDepositRange) {
129-
const legacyDepositLogs = await options.getLogs({
130-
noTarget: true, // there are 40K+ vaults, so no target and filter later is more optimal
131-
eventAbi: eventAbis.legacyDeposit,
132-
entireLog: true,
133-
parseLog: true,
134-
})
135-
combinedDepositLogs.push(...legacyDepositLogs)
136-
}
137-
138-
combinedDepositLogs.forEach(log => {
139-
const metadata = vaultMetadata.get(log.address.toLowerCase())
140-
if (!metadata) return;
141-
const yieldValue = log.args.yieldValue ?? metadata.yieldValue;
142-
const premiumAmount = BigInt(log.args.depositAmount) * yieldValue / WAD;
143-
dailyNotionalVolume.add(metadata.investmentToken, log.args.depositAmount);
144-
dailyPremiumVolume.add(metadata.investmentToken, premiumAmount);
145-
})
146-
147-
return {
148-
dailyNotionalVolume,
149-
dailyPremiumVolume,
150-
};
151-
}
152-
153-
async function getVaultMetadataFromEvents(
154-
options: FetchOptions,
155-
chainConfig: ChainConfig,
156-
toBlock: number,
157-
): Promise<Map<string, VaultMetadata>> {
158-
const cached = vaultMetadataCache.get(options.chain);
159-
if (cached && cached.toBlock >= toBlock) return cached.metadata;
160-
161-
const fromBlock = cached ? cached.toBlock + 1 : chainConfig.startBlock;
162-
const metadata = cached ? new Map(cached.metadata) : new Map<string, VaultMetadata>();
163-
if (fromBlock > toBlock) return metadata;
164-
165-
const freshMetadata = await fetchVaultMetadataRange(options, chainConfig, fromBlock, toBlock);
166-
freshMetadata.forEach((value, vault) => {
167-
metadata.set(vault, value);
43+
token: investmentToken.toLowerCase(),
44+
quantity: toBigInt(quantity),
45+
yieldValue: toBigInt(yieldValue ?? 0),
16846
});
169-
170-
const latestCached = vaultMetadataCache.get(options.chain);
171-
if (!latestCached || latestCached.toBlock < toBlock) {
172-
vaultMetadataCache.set(options.chain, { toBlock, metadata });
173-
return metadata;
174-
}
175-
176-
return latestCached.metadata;
17747
}
17848

17949
async function fetchVaultMetadataRange(
@@ -184,65 +54,60 @@ async function fetchVaultMetadataRange(
18454
): Promise<Map<string, VaultMetadata>> {
18555
const vaults = new Map<string, VaultMetadata>();
18656

187-
const legacyRange = intersectRange(
57+
const v2Range = intersectRange(
18858
fromBlock,
18959
toBlock,
190-
chainConfig.startBlock,
191-
chainConfig.legacyVaultCreatedWithParamsStartBlock - 1,
60+
chainConfig.v2StartBlock,
61+
toBlock,
19262
);
193-
const legacyWithParamsRange = intersectRange(
63+
const v1_1Range = intersectRange(
19464
fromBlock,
19565
toBlock,
196-
chainConfig.legacyVaultCreatedWithParamsStartBlock,
197-
chainConfig.currentVaultCreatedStartBlock - 1,
66+
chainConfig.v1_1StartBlock,
67+
chainConfig.v2StartBlock - 1,
19868
);
199-
const currentRange = intersectRange(
69+
const v1Range = intersectRange(
20070
fromBlock,
20171
toBlock,
202-
chainConfig.currentVaultCreatedStartBlock,
203-
toBlock,
72+
chainConfig.v1StartBlock,
73+
chainConfig.v1_1StartBlock - 1,
20474
);
20575

206-
type VaultQuery =
207-
| { kind: "current"; logs: Promise<any[]> }
208-
| { kind: "legacyWithParams"; logs: Promise<any[]> }
209-
| { kind: "legacy"; logs: Promise<any[]> };
210-
21176
const queries: VaultQuery[] = [];
21277

213-
if (currentRange) {
78+
if (v2Range) {
21479
queries.push({
215-
kind: "current",
80+
kind: "v2",
21681
logs: options.getLogs({
21782
target: chainConfig.factory,
218-
eventAbi: eventAbis.vaultCreated,
219-
fromBlock: currentRange.fromBlock,
83+
eventAbi: v2Deposit,
84+
fromBlock: v2Range.fromBlock,
22085
cacheInCloud: true,
22186
}),
22287
});
22388
}
22489

225-
if (legacyWithParamsRange) {
90+
if (v1_1Range) {
22691
queries.push({
227-
kind: "legacyWithParams",
92+
kind: "v1_1",
22893
logs: options.getLogs({
22994
target: chainConfig.factory,
230-
eventAbi: eventAbis.legacyVaultCreatedWithParams,
231-
fromBlock: legacyWithParamsRange.fromBlock,
232-
toBlock: legacyWithParamsRange.toBlock,
95+
eventAbi: v1_1Deposit,
96+
fromBlock: v1_1Range.fromBlock,
97+
toBlock: v1_1Range.toBlock,
23398
cacheInCloud: true,
23499
}),
235100
});
236101
}
237102

238-
if (legacyRange) {
103+
if (v1Range) {
239104
queries.push({
240-
kind: "legacy",
105+
kind: "v1",
241106
logs: options.getLogs({
242107
target: chainConfig.factory,
243-
eventAbi: eventAbis.legacyVaultCreated,
244-
fromBlock: legacyRange.fromBlock,
245-
toBlock: legacyRange.toBlock,
108+
eventAbi: v1Deposit,
109+
fromBlock: v1Range.fromBlock,
110+
toBlock: v1Range.toBlock,
246111
cacheInCloud: true,
247112
}),
248113
});
@@ -252,29 +117,49 @@ async function fetchVaultMetadataRange(
252117

253118
resolved.forEach((logs, idx) => {
254119
const kind = queries[idx].kind;
255-
if (kind === "current" || kind === "legacyWithParams") {
120+
if (kind === "v2" || kind === "v1_1") {
256121
logs.forEach((log: any) => {
257-
addVaultMetadata(vaults, log.vaultAddress, log.vaultParams.investmentToken, log.vaultParams.yieldValue);
122+
addVaultMetadata(vaults, log.vaultAddress, log.vaultParams.investmentToken, log.vaultParams.quantity, log.vaultParams.yieldValue);
258123
});
259124
} else {
260125
logs.forEach((log: any) => {
261126
const investmentToken = log.isBuyLow ? log.quoteToken : log.baseToken;
262-
addVaultMetadata(vaults, log.vaultAddress, investmentToken, log.yieldValue);
127+
addVaultMetadata(vaults, log.vaultAddress, investmentToken, log.quantity, log.yieldValue);
263128
});
264129
}
265130
});
266131

267132
return vaults;
268133
}
269134

135+
async function fetch(options: FetchOptions): Promise<FetchResultV2> {
136+
const chainConfig = config[options.chain];
137+
const dailyNotionalVolume = options.createBalances();
138+
const dailyPremiumVolume = options.createBalances();
139+
140+
const fetchFromBlock = await options.getFromBlock();
141+
const fetchToBlock = await options.getToBlock();
142+
143+
const dayVaults = await fetchVaultMetadataRange(options, chainConfig, fetchFromBlock, fetchToBlock);
144+
dayVaults.forEach((metadata) => {
145+
dailyNotionalVolume.add(metadata.token, metadata.quantity);
146+
dailyPremiumVolume.add(metadata.token, (metadata.quantity * metadata.yieldValue) / WAD);
147+
});
148+
149+
return {
150+
dailyNotionalVolume,
151+
dailyPremiumVolume,
152+
};
153+
}
154+
270155
const adapter: SimpleAdapter = {
271156
version: 2,
272157
pullHourly: true,
273158
fetch,
274159
adapter: config,
275160
methodology: {
276-
NotionalVolume: "Notional volume is the principal deposited into Prodigy.Fi DCI vaults, counted in each vault's investment token when the on-chain Deposit event is emitted.",
277-
PremiumVolume: "Premium volume is computed from each on-chain Deposit event as deposited principal multiplied by the vault yieldValue, denominated in the same investment token.",
161+
NotionalVolume: "The total value of the underlying asset in an option or derivative contract.",
162+
PremiumVolume: "The price paid up front to buy or sell option or derivative contract.",
278163
},
279164
};
280165

options/prodigy-fi/interfaces.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { CHAIN } from "../../helpers/chains";
2+
3+
export type BlockRange = { fromBlock: number; toBlock: number };
4+
5+
export type VaultMetadata = {
6+
token: string;
7+
quantity: bigint;
8+
yieldValue: bigint;
9+
};
10+
11+
export type ChainConfig = {
12+
factory: string;
13+
v1StartBlock: number;
14+
v1_1StartBlock: number;
15+
v2StartBlock: number;
16+
start: string;
17+
};
18+
19+
export type VaultQuery =
20+
| { kind: "v2"; logs: Promise<any[]> }
21+
| { kind: "v1_1"; logs: Promise<any[]> }
22+
| { kind: "v1"; logs: Promise<any[]> };
23+
24+
export const v2Deposit = `event VaultCreated(address indexed,address indexed,address indexed,address vaultAddress,tuple(address,address,address linkedToken,address investmentToken,address,bytes32,uint256,uint256 linkedPrice,int256,uint256 yieldValue,bool isBuyLow,uint256 quantity,uint256,uint64,address,uint256,address,int256,address) vaultParams,uint8)`;
25+
export const v1_1Deposit = `event VaultCreated(address indexed,address indexed,address indexed,address vaultAddress,tuple(address,address,address linkedToken,address investmentToken,address,bytes32,uint256,uint256 linkedPrice,int256,uint256 yieldValue,bool isBuyLow,uint256 quantity,uint256,uint64,address,uint256,address,int256) vaultParams)`;
26+
export const v1Deposit = `event VaultCreated(address indexed,address indexed baseToken,address indexed quoteToken,address vaultAddress,uint256,uint256 linkedPrice,int256,uint256 yieldValue,bool isBuyLow,uint256 quantity,uint256,uint256,uint256,int256)`;
27+
28+
export const config: Record<string, ChainConfig> = {
29+
[CHAIN.ETHEREUM]: {
30+
factory: "0xAC2a612C49f29e26858Df1a53f7623180bcc3753",
31+
v1StartBlock: 23831175,
32+
v1_1StartBlock: 23831175,
33+
v2StartBlock: 24036992,
34+
start: "2025-11-19"
35+
},
36+
[CHAIN.BASE]: {
37+
factory: "0xFE198B51cfb1F96b56c63fe323a934BEAAA3b281",
38+
v1StartBlock: 22133150,
39+
v1_1StartBlock: 30509116,
40+
v2StartBlock: 39795788,
41+
start: "2024-11-08"
42+
},
43+
[CHAIN.BERACHAIN]: {
44+
factory: "0x29ca87b2f744127606ada4564da8219be6498ca1",
45+
v1StartBlock: 804138,
46+
v1_1StartBlock: 5297670,
47+
v2StartBlock: 14682550,
48+
start: "2025-02-07"
49+
},
50+
};

0 commit comments

Comments
 (0)