@stake-dao/reader 0.4.50 → 0.4.52
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/esm/abis/passiveVaultsMulticall.js +56 -0
- package/dist/esm/abis/passiveVaultsMulticall.js.map +1 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lockers/utils/getBribesRewardsData.js +1 -0
- package/dist/esm/lockers/utils/getBribesRewardsData.js.map +1 -1
- package/dist/esm/prices.js +7 -7
- package/dist/esm/prices.js.map +1 -1
- package/dist/esm/sdt/fetch.js +40 -40
- package/dist/esm/sdt/fetch.js.map +1 -1
- package/dist/esm/strategies/pancakeswap/fetch/pancakeswapMath.js +0 -3
- package/dist/esm/strategies/pancakeswap/fetch/pancakeswapMath.js.map +1 -1
- package/dist/esm/strategies/passive/fetch.js +73 -0
- package/dist/esm/strategies/passive/fetch.js.map +1 -0
- package/dist/esm/strategies/passive/httpCalls.js +35 -0
- package/dist/esm/strategies/passive/httpCalls.js.map +1 -0
- package/dist/esm/tsconfig.build.tsbuildinfo +1 -1
- package/dist/esm/utils/etherscan.js +7 -0
- package/dist/esm/utils/etherscan.js.map +1 -0
- package/dist/esm/utils/getAverageYearlyRewards.js +38 -0
- package/dist/esm/utils/getAverageYearlyRewards.js.map +1 -0
- package/dist/esm/utils.js +7 -7
- package/dist/esm/utils.js.map +1 -1
- package/dist/esm/votemarket/curve/config.js +1 -0
- package/dist/esm/votemarket/curve/config.js.map +1 -1
- package/dist/esm/votemarket/curve/fetchUserVlCvxClaimable.js +10 -9
- package/dist/esm/votemarket/curve/fetchUserVlCvxClaimable.js.map +1 -1
- package/dist/esm/votemarket/fetchPendingRemoteCampaigns.js +1 -1
- package/dist/esm/votemarket/fetchPendingRemoteCampaigns.js.map +1 -1
- package/dist/types/abis/passiveVaultsMulticall.d.ts +33 -0
- package/dist/types/abis/passiveVaultsMulticall.d.ts.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/lockers/utils/getBribesRewardsData.d.ts.map +1 -1
- package/dist/types/sdt/fetch.d.ts +4 -4
- package/dist/types/sdt/fetch.d.ts.map +1 -1
- package/dist/types/strategies/pancakeswap/fetch/pancakeswapMath.d.ts.map +1 -1
- package/dist/types/strategies/passive/fetch.d.ts +161 -0
- package/dist/types/strategies/passive/fetch.d.ts.map +1 -0
- package/dist/types/strategies/passive/httpCalls.d.ts +8 -0
- package/dist/types/strategies/passive/httpCalls.d.ts.map +1 -0
- package/dist/types/utils/etherscan.d.ts +14 -0
- package/dist/types/utils/etherscan.d.ts.map +1 -0
- package/dist/types/utils/getAverageYearlyRewards.d.ts +3 -0
- package/dist/types/utils/getAverageYearlyRewards.d.ts.map +1 -0
- package/dist/types/utils.d.ts +1 -1
- package/dist/types/utils.d.ts.map +1 -1
- package/dist/types/votemarket/curve/config.d.ts +1 -0
- package/dist/types/votemarket/curve/config.d.ts.map +1 -1
- package/dist/types/votemarket/curve/fetchUserVlCvxClaimable.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/abis/passiveVaultsMulticall.ts +56 -0
- package/src/index.ts +3 -0
- package/src/lockers/utils/getBribesRewardsData.ts +1 -0
- package/src/prices.ts +7 -7
- package/src/sdt/fetch.ts +40 -51
- package/src/strategies/pancakeswap/fetch/pancakeswapMath.ts +0 -3
- package/src/strategies/passive/fetch.ts +97 -0
- package/src/strategies/passive/httpCalls.ts +42 -0
- package/src/utils/etherscan.ts +21 -0
- package/src/utils/getAverageYearlyRewards.ts +53 -0
- package/src/utils.ts +22 -18
- package/src/votemarket/curve/config.ts +1 -0
- package/src/votemarket/curve/fetchUserVlCvxClaimable.ts +15 -10
- package/src/votemarket/fetchPendingRemoteCampaigns.ts +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetchUserVlCvxClaimable.d.ts","sourceRoot":"","sources":["../../../../src/votemarket/curve/fetchUserVlCvxClaimable.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,OAAO,EAAyB,MAAM,MAAM,CAAA;AAO1D,KAAK,IAAI,GAAG;IACV,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAA;CAC1B,CAAA;
|
|
1
|
+
{"version":3,"file":"fetchUserVlCvxClaimable.d.ts","sourceRoot":"","sources":["../../../../src/votemarket/curve/fetchUserVlCvxClaimable.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,OAAO,EAAyB,MAAM,MAAM,CAAA;AAO1D,KAAK,IAAI,GAAG;IACV,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAA;CAC1B,CAAA;AAuGD,eAAO,MAAM,uBAAuB,aAAoB,GAAG,OAAO,IAAI,QAAQ,MAAM;;;;;;;mBAT1C,OAAO;;iBA6EhD,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stake-dao/reader",
|
|
3
3
|
"description": "",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.52",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"graphql-request": "^6.0.0",
|
|
37
37
|
"lodash-es": "^4.17.21",
|
|
38
38
|
"viem": "^2.9.31",
|
|
39
|
-
"@stake-dao/constants": "0.4.
|
|
39
|
+
"@stake-dao/constants": "0.4.23"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@types/lodash-es": "^4.17.12"
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const passiveVaultsMulticall = [
|
|
2
|
+
{
|
|
3
|
+
constant: true,
|
|
4
|
+
inputs: [],
|
|
5
|
+
name: 'totalSupply',
|
|
6
|
+
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
|
7
|
+
payable: false,
|
|
8
|
+
stateMutability: 'view',
|
|
9
|
+
type: 'function',
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
constant: true,
|
|
13
|
+
inputs: [],
|
|
14
|
+
name: 'getPricePerFullShare',
|
|
15
|
+
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
|
16
|
+
payable: false,
|
|
17
|
+
stateMutability: 'view',
|
|
18
|
+
type: 'function',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
constant: true,
|
|
22
|
+
inputs: [{ internalType: 'address', name: 'account', type: 'address' }],
|
|
23
|
+
name: 'balanceOf',
|
|
24
|
+
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
|
25
|
+
payable: false,
|
|
26
|
+
stateMutability: 'view',
|
|
27
|
+
type: 'function',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
constant: true,
|
|
31
|
+
inputs: [],
|
|
32
|
+
name: 'withdrawalFee',
|
|
33
|
+
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
|
34
|
+
payable: false,
|
|
35
|
+
stateMutability: 'view',
|
|
36
|
+
type: 'function',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
constant: true,
|
|
40
|
+
inputs: [],
|
|
41
|
+
name: 'performanceFee',
|
|
42
|
+
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
|
|
43
|
+
payable: false,
|
|
44
|
+
stateMutability: 'view',
|
|
45
|
+
type: 'function',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'working_balances',
|
|
49
|
+
outputs: [{ type: 'uint256', name: '' }],
|
|
50
|
+
inputs: [{ type: 'address', name: 'arg0' }],
|
|
51
|
+
stateMutability: 'view',
|
|
52
|
+
type: 'function',
|
|
53
|
+
},
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
export default passiveVaultsMulticall
|
package/src/index.ts
CHANGED
|
@@ -48,6 +48,9 @@ export { buildPancakeSwapErc20Strategies } from './strategies/pancakeswap/buildE
|
|
|
48
48
|
export { computePositionData } from './strategies/pancakeswap/fetch/pancakePositions.js'
|
|
49
49
|
export { getPancakeV3Positions } from './strategies/pancakeswap/fetch/getPancakeV3Positions.js'
|
|
50
50
|
|
|
51
|
+
// Passive
|
|
52
|
+
export { fetchPassive } from './strategies/passive/fetch.js'
|
|
53
|
+
|
|
51
54
|
// Pendle
|
|
52
55
|
export { fetchPendle } from './strategies/pendle/fetch.js'
|
|
53
56
|
export { buildPendleStrategies } from './strategies/pendle/build.js'
|
|
@@ -30,6 +30,7 @@ const getBribesRewardsData = (
|
|
|
30
30
|
{ id: 'cake', value: bribesData?.[delegationWithId('cake')!] / 100 || 0 },
|
|
31
31
|
{ id: 'pendle', value: bribesData?.[delegationWithId('pendle')!] / 100 || 0 },
|
|
32
32
|
{ id: 'fxn', value: bribesData?.[delegationWithId('fxn')!] / 100 || 0 },
|
|
33
|
+
{ id: 'apw', value: bribesData?.[delegationWithId('apw')!] / 100 || 0 },
|
|
33
34
|
]
|
|
34
35
|
|
|
35
36
|
// Vote incentives APR
|
package/src/prices.ts
CHANGED
|
@@ -109,21 +109,21 @@ interface TokenToFetch {
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
export const getPrices = async (tokens: TokenToFetch[], chainId = 1): Promise<Price[]> => {
|
|
112
|
-
let
|
|
112
|
+
let pricesFromGecko: Price[] = []
|
|
113
113
|
if (tokens.length > 0) {
|
|
114
|
-
|
|
114
|
+
pricesFromGecko = pricesFromGecko.concat(await getPricesFromGeckoTerminal(tokens, chainId))
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
const
|
|
117
|
+
const pricesToFetchFromLlama = tokens
|
|
118
118
|
.map((t) => {
|
|
119
|
-
const coinPrice =
|
|
119
|
+
const coinPrice = pricesFromGecko.find((pl) => equalTlc(pl.address, t.address))?.usdPrice || 0
|
|
120
120
|
return coinPrice === 0 ? t : undefined
|
|
121
121
|
})
|
|
122
122
|
.filter(Boolean)
|
|
123
123
|
|
|
124
|
-
let
|
|
125
|
-
if (
|
|
126
|
-
|
|
124
|
+
let pricesFromLlama: Price[] = []
|
|
125
|
+
if (tokens.length > 0) {
|
|
126
|
+
pricesFromLlama = pricesFromLlama.concat(await getPricesFromLlama(pricesToFetchFromLlama, chainId))
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
return [pricesFromLlama, pricesFromGecko].flat().filter((p) => p.usdPrice > 0)
|
package/src/sdt/fetch.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { formatUnits, parseAbi, parseUnits } from 'viem'
|
|
2
2
|
|
|
3
|
-
import { ONE_DAY, type Token,
|
|
3
|
+
import { ONE_DAY, type Token, contract, tokenWithId } from '@stake-dao/constants'
|
|
4
|
+
import { mainnet } from 'viem/chains'
|
|
4
5
|
import { type Price } from '../prices.js'
|
|
5
|
-
import { multicall } from '../utils.js'
|
|
6
|
+
import { equalTlc, multicall } from '../utils.js'
|
|
7
|
+
import { getLogsFromEtherscan } from '../utils/etherscan.js'
|
|
8
|
+
import getAverageYearlyRewards from '../utils/getAverageYearlyRewards.js'
|
|
6
9
|
import veSdtAlternativeYields from './veSdtAlternativeYields.js'
|
|
7
10
|
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
explorers[1].url
|
|
11
|
-
}/api?module=logs&action=getLogs&fromBlock=${fromBlock}&address=${contracts.veSdtFeeDistributor![1]!}&topic0=0xce749457b74e10f393f2c6b1ce4261b78791376db5a3f501477a809f03f500d6&apikey=${apiKey}`
|
|
11
|
+
const NB_EVENTS_TO_USE = 5
|
|
12
|
+
const EVENT_TOPIC = '0xce749457b74e10f393f2c6b1ce4261b78791376db5a3f501477a809f03f500d6'
|
|
12
13
|
|
|
13
14
|
interface TFetchSdt {
|
|
14
15
|
provider: any
|
|
@@ -22,26 +23,41 @@ export const fetchSdt = async ({ provider, prices, explorerApiKey }: TFetchSdt)
|
|
|
22
23
|
const frax3CrvToken = tokenWithId('frax3crv') as Token
|
|
23
24
|
|
|
24
25
|
const now = Math.ceil(new Date().getTime() / 1000)
|
|
25
|
-
const lastCheck = now - ONE_DAY *
|
|
26
|
+
const lastCheck = now - ONE_DAY * 60
|
|
26
27
|
const lastCheckBlock = await (await fetch(`https://coins.llama.fi/block/ethereum/${lastCheck}`)).json()
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
let amountPerYear = 0
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const rawEventsData = await getLogsFromEtherscan({
|
|
33
|
+
chainId: mainnet.id,
|
|
34
|
+
address: contract('veSdtFeeDistributor'),
|
|
35
|
+
apikey: explorerApiKey,
|
|
36
|
+
extraQueryParams: {
|
|
37
|
+
fromBlock: `${lastCheckBlock.height}`,
|
|
38
|
+
topic0: EVENT_TOPIC,
|
|
39
|
+
},
|
|
40
|
+
})
|
|
41
|
+
amountPerYear = await getAverageYearlyRewards(rawEventsData, NB_EVENTS_TO_USE)
|
|
42
|
+
} catch {
|
|
43
|
+
amountPerYear = 0
|
|
44
|
+
}
|
|
29
45
|
|
|
30
46
|
const rawSdtData: any = await multicall(
|
|
31
47
|
provider,
|
|
32
48
|
[
|
|
33
|
-
{ address:
|
|
34
|
-
{ address: sdtToken.address, name: 'balanceOf', params: [
|
|
35
|
-
{ address: sdtToken.address, name: 'balanceOf', params: [
|
|
49
|
+
{ address: contract('sdFrx3CrvVault'), name: 'getPricePerFullShare' },
|
|
50
|
+
{ address: sdtToken.address, name: 'balanceOf', params: [contract('veSdt')] },
|
|
51
|
+
{ address: sdtToken.address, name: 'balanceOf', params: [contract('vestedSdt')] },
|
|
36
52
|
{ address: sdtToken.address, name: 'totalSupply' },
|
|
37
|
-
{ address:
|
|
53
|
+
{ address: contract('veSdt'), name: 'totalSupply' },
|
|
38
54
|
{
|
|
39
|
-
address:
|
|
55
|
+
address: contract('veSdtBoostDelegation'),
|
|
40
56
|
name: 'received_balance',
|
|
41
|
-
params: [
|
|
57
|
+
params: [contract('voteBoosterStrategy')],
|
|
42
58
|
},
|
|
43
|
-
{ address:
|
|
44
|
-
{ address:
|
|
59
|
+
{ address: contract('liquidLockersGaugeController'), name: 'get_total_weight' },
|
|
60
|
+
{ address: contract('sdGaugeController'), name: 'get_total_weight' },
|
|
45
61
|
],
|
|
46
62
|
parseAbi([
|
|
47
63
|
'function getPricePerFullShare() external view returns (uint256)',
|
|
@@ -59,55 +75,28 @@ export const fetchSdt = async ({ provider, prices, explorerApiKey }: TFetchSdt)
|
|
|
59
75
|
const veSdtTotalSupply = rawSdtData[4].result
|
|
60
76
|
const voteBoosterVeSdt = rawSdtData[5].result
|
|
61
77
|
|
|
62
|
-
const sdtPrice = prices
|
|
63
|
-
const frax3CrvPrice =
|
|
64
|
-
prices?.find((p) => p.address.toLowerCase() === frax3CrvToken.address.toLowerCase())?.usdPrice || 0
|
|
78
|
+
const sdtPrice = prices.find((p) => equalTlc(p.address, sdtToken.address))?.usdPrice || 0
|
|
79
|
+
const frax3CrvPrice = prices.find((p) => equalTlc(p.address, frax3CrvToken.address))?.usdPrice || 0
|
|
65
80
|
|
|
66
81
|
const lockedInUsd = parseUnits(`${sdtPrice}`, sdtToken.decimals) * lockedSdtBalance
|
|
67
82
|
const vestedInUsd = parseUnits(`${sdtPrice}`, sdtToken.decimals) * vestedSdtBalance
|
|
68
83
|
const percentLocked = parseUnits(formatUnits(lockedSdtBalance, 0), sdtToken.decimals) / sdtTotalSupply
|
|
69
84
|
const averageLock = (parseUnits(formatUnits(veSdtTotalSupply, 0), sdtToken.decimals) / lockedSdtBalance) * BigInt(4)
|
|
70
85
|
|
|
71
|
-
|
|
72
|
-
let checkedTimestamp = now - ONE_DAY * 7
|
|
73
|
-
|
|
74
|
-
let apr = 0
|
|
75
|
-
if (checkpoints && checkpoints.message === 'OK') {
|
|
76
|
-
const checkpointsData = checkpoints.result.map((r) => {
|
|
77
|
-
const [eventTimestamp, eventAmount] = decodeAbiParameters(
|
|
78
|
-
[
|
|
79
|
-
{ type: 'uint256', name: 'timestamp' },
|
|
80
|
-
{ type: 'uint256', name: 'amount' },
|
|
81
|
-
],
|
|
82
|
-
r.data,
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
return { ts: Number(eventTimestamp), amount: eventAmount }
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
while (lastCheck <= checkedTimestamp && rewardsAmount === Zero) {
|
|
89
|
-
const checkedWeek = checkpointsData ? checkpointsData.filter((d: any) => checkedTimestamp < d.ts) : null
|
|
90
|
-
rewardsAmount = checkedWeek ? checkedWeek.reduce((total, d) => total + d.amount, Zero) : Zero
|
|
91
|
-
checkedTimestamp = checkedTimestamp - ONE_DAY * 7
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
apr =
|
|
95
|
-
((frax3CrvPrice * Number(formatUnits(rewardsAmount, 18)) * 52) / Number(formatUnits(lockedInUsd, 18))) *
|
|
96
|
-
Number(vaultRatio)
|
|
97
|
-
}
|
|
86
|
+
const apr = ((frax3CrvPrice * amountPerYear) / Number(formatUnits(lockedInUsd, 18))) * Number(vaultRatio)
|
|
98
87
|
|
|
99
88
|
const parsedData = {
|
|
100
89
|
alternativeYields: veSdtAlternativeYields,
|
|
101
|
-
veSdtTotalSupply: formatUnits(veSdtTotalSupply,
|
|
102
|
-
locked: formatUnits(lockedSdtBalance,
|
|
90
|
+
veSdtTotalSupply: Number(formatUnits(veSdtTotalSupply, 18)),
|
|
91
|
+
locked: Number(formatUnits(lockedSdtBalance, sdtToken.decimals)),
|
|
103
92
|
lockedInUsd: Number(formatUnits(lockedInUsd, sdtToken.decimals * 2)),
|
|
104
93
|
vestedInUsd: Number(formatUnits(vestedInUsd, sdtToken.decimals * 2)),
|
|
105
94
|
percentLocked: Number(formatUnits(percentLocked, 18)) * 100,
|
|
106
95
|
averageLockInYears: Number(formatUnits(averageLock, 18)),
|
|
107
96
|
apr: apr * 100,
|
|
108
97
|
voteBoosterVeSdt: Number(formatUnits(voteBoosterVeSdt, sdtToken.decimals)),
|
|
109
|
-
lockersTotalWeight: formatUnits(rawSdtData[6].result,
|
|
110
|
-
stratsTotalWeight: formatUnits(rawSdtData[7].result,
|
|
98
|
+
lockersTotalWeight: Number(formatUnits(rawSdtData[6].result, sdtToken.decimals * 2)),
|
|
99
|
+
stratsTotalWeight: Number(formatUnits(rawSdtData[7].result, sdtToken.decimals * 2)),
|
|
111
100
|
}
|
|
112
101
|
|
|
113
102
|
return parsedData
|
|
@@ -215,7 +215,6 @@ export function mostSignificantBit(x: bigint): number {
|
|
|
215
215
|
let msb = 0
|
|
216
216
|
for (const [power, min] of POWERS_OF_2) {
|
|
217
217
|
if (x >= min) {
|
|
218
|
-
// eslint-disable-next-line operator-assignment
|
|
219
218
|
x = x >> BigInt(power)
|
|
220
219
|
msb += power
|
|
221
220
|
}
|
|
@@ -240,9 +239,7 @@ function getTickAtSqrtRatio(sqrtRatioX96: bigint): number {
|
|
|
240
239
|
for (let i = 0; i < 14; i++) {
|
|
241
240
|
r = (r * r) >> BigInt(127)
|
|
242
241
|
const f = r >> BigInt(128)
|
|
243
|
-
// eslint-disable-next-line operator-assignment
|
|
244
242
|
log_2 = log_2 | (f << BigInt(63 - i))
|
|
245
|
-
// eslint-disable-next-line operator-assignment
|
|
246
243
|
r = r >> f
|
|
247
244
|
}
|
|
248
245
|
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { formatUnits } from 'viem'
|
|
2
|
+
|
|
3
|
+
import multicallPassiveVaultsAbi from '../../abis/passiveVaultsMulticall.js'
|
|
4
|
+
import { contract, passiveStrats } from '@stake-dao/constants'
|
|
5
|
+
import { multicall } from '../../utils.js'
|
|
6
|
+
import { mainnet } from 'viem/chains'
|
|
7
|
+
import { getPassiveHttpCalls } from './httpCalls.js'
|
|
8
|
+
import { getBoost } from '../utils/boost.js'
|
|
9
|
+
|
|
10
|
+
interface TFetchPassive {
|
|
11
|
+
provider: any
|
|
12
|
+
chainId: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const vaultCalls = (vaultAddress: string, strategyAddress: string, gaugeAddress: string, chainId: number) =>
|
|
16
|
+
[
|
|
17
|
+
{ address: vaultAddress, name: 'totalSupply' },
|
|
18
|
+
{ address: vaultAddress, name: 'getPricePerFullShare' },
|
|
19
|
+
{ address: strategyAddress, name: 'performanceFee' },
|
|
20
|
+
{ address: strategyAddress, name: 'withdrawalFee' },
|
|
21
|
+
chainId === mainnet.id && { address: gaugeAddress, name: 'balanceOf', params: [contract('convexCrvLocker')] },
|
|
22
|
+
chainId === mainnet.id && {
|
|
23
|
+
address: gaugeAddress,
|
|
24
|
+
name: 'working_balances',
|
|
25
|
+
params: [contract('convexCrvLocker')],
|
|
26
|
+
},
|
|
27
|
+
].filter((c) => c && typeof c !== 'undefined')
|
|
28
|
+
|
|
29
|
+
export const fetchPassive = async ({ provider, chainId }: TFetchPassive) => {
|
|
30
|
+
const passive = passiveStrats.strats.filter((s) => s.chainId === chainId)
|
|
31
|
+
const apisData = await getPassiveHttpCalls(chainId)
|
|
32
|
+
|
|
33
|
+
const rawData: any = await multicall(
|
|
34
|
+
provider,
|
|
35
|
+
passive.flatMap((s) => vaultCalls(s.vault.address, s.vault.strategy, s.gaugeAddress, chainId)),
|
|
36
|
+
multicallPassiveVaultsAbi,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
let currentRawIndex = 0
|
|
40
|
+
const parsedStratsData = passive.map((s) => {
|
|
41
|
+
const curveApiRaw = apisData.curveApiData.map((apiResult) =>
|
|
42
|
+
apiResult.value.find((pool) => pool.lpTokenAddress.toLowerCase() === s.lpToken.address.toLowerCase()),
|
|
43
|
+
)
|
|
44
|
+
const indexOfData = curveApiRaw.findIndex((e) => e)
|
|
45
|
+
const curveApiData = curveApiRaw[indexOfData]
|
|
46
|
+
|
|
47
|
+
const subgraphData = apisData.curveSubgraphData.find((el) => el.address === curveApiData.address)
|
|
48
|
+
|
|
49
|
+
const lpPriceInUsd = curveApiData.usdTotal / Number(formatUnits(BigInt(curveApiData.totalSupply), 18))
|
|
50
|
+
|
|
51
|
+
const totalSupply = Number(formatUnits(rawData[currentRawIndex].result, s.lpToken.decimals))
|
|
52
|
+
const conversionRate = Number(formatUnits(rawData[currentRawIndex + 1].result, s.lpToken.decimals))
|
|
53
|
+
const totalSupplyInLpToken = totalSupply * conversionRate
|
|
54
|
+
const tvl = totalSupplyInLpToken * lpPriceInUsd
|
|
55
|
+
const perfFee = rawData[currentRawIndex + 2].result
|
|
56
|
+
const withdrawalFee = rawData[currentRawIndex + 3].result
|
|
57
|
+
const boost =
|
|
58
|
+
chainId === 1 ? getBoost('curve', rawData[currentRawIndex + 4].result, rawData[currentRawIndex + 5].result) : 1
|
|
59
|
+
const aprWithBoost = curveApiData.gaugeCrvApy?.[0] && boost ? curveApiData.gaugeCrvApy[0] * boost : 0
|
|
60
|
+
|
|
61
|
+
currentRawIndex += vaultCalls('', '', '', chainId).length
|
|
62
|
+
|
|
63
|
+
const apy = subgraphData.latestWeeklyApy ? Number(subgraphData.latestWeeklyApy) + aprWithBoost : 0
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
...s,
|
|
67
|
+
tradingApy: subgraphData.latestWeeklyApy,
|
|
68
|
+
minApy: curveApiData.gaugeCrvApy ? curveApiData.gaugeCrvApy[0] : 0,
|
|
69
|
+
maxApy: curveApiData.gaugeCrvApy ? curveApiData.gaugeCrvApy[1] : 0,
|
|
70
|
+
lpPriceInUsd,
|
|
71
|
+
tvl,
|
|
72
|
+
apr: {
|
|
73
|
+
boost,
|
|
74
|
+
current: {
|
|
75
|
+
total: apy,
|
|
76
|
+
details: [{ label: 'Trading Fees + Rewards (APY)', value: [apy] }],
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
stratsData: {
|
|
80
|
+
boost,
|
|
81
|
+
accumulatedFees: '0',
|
|
82
|
+
keeperFee: '0',
|
|
83
|
+
veSDTFee: '0',
|
|
84
|
+
accumulatorFee: '0',
|
|
85
|
+
claimerFee: '0',
|
|
86
|
+
perfFee: formatUnits(perfFee, 0),
|
|
87
|
+
withdrawalFee: formatUnits(withdrawalFee, 0),
|
|
88
|
+
},
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
deployed: parsedStratsData,
|
|
94
|
+
notDeployed: [],
|
|
95
|
+
fetched: true,
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { CURVE_API_URL } from '../curve/endpoints.js'
|
|
2
|
+
|
|
3
|
+
export const getPassiveHttpCalls = async (chainId: number) => {
|
|
4
|
+
switch (chainId) {
|
|
5
|
+
case 1: {
|
|
6
|
+
const ethHttpCalls = [
|
|
7
|
+
// Curve Apis
|
|
8
|
+
(await fetch(`${CURVE_API_URL}/getPools/ethereum/main`)).json(),
|
|
9
|
+
(await fetch(`${CURVE_API_URL}/getSubgraphData/ethereum`)).json(),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
const ethHttpResponses = await Promise.all(ethHttpCalls)
|
|
13
|
+
|
|
14
|
+
const ethCurveMain = ethHttpResponses[0].data.poolData
|
|
15
|
+
const ethCurveSubgraph = ethHttpResponses[1].data.poolList
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
curveSubgraphData: ethCurveSubgraph,
|
|
19
|
+
curveApiData: [{ type: 'main', value: ethCurveMain }],
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
case 137: {
|
|
23
|
+
const polygonHttpCalls = [
|
|
24
|
+
// Curve Apis
|
|
25
|
+
(await fetch(`${CURVE_API_URL}/getPools/polygon/main`)).json(),
|
|
26
|
+
(await fetch(`${CURVE_API_URL}/getSubgraphData/polygon`)).json(),
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
const polygonHttpResponses = await Promise.all(polygonHttpCalls)
|
|
30
|
+
|
|
31
|
+
const polygonCurveMain = polygonHttpResponses[0].data.poolData
|
|
32
|
+
const polygonCurveSubgraph = polygonHttpResponses[1].data.poolList
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
curveSubgraphData: polygonCurveSubgraph,
|
|
36
|
+
curveApiData: [{ type: 'main', value: polygonCurveMain }],
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return { curveSubgraphData: undefined, curveApiData: [] }
|
|
42
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
interface GetLogsFromEtherscanArgs {
|
|
2
|
+
chainId: number
|
|
3
|
+
address: string
|
|
4
|
+
apikey: string
|
|
5
|
+
rawQueryParams?: { [key: string]: string }
|
|
6
|
+
extraQueryParams?: { [key: string]: string }
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const getLogsFromEtherscan = async ({
|
|
10
|
+
chainId,
|
|
11
|
+
address,
|
|
12
|
+
apikey,
|
|
13
|
+
rawQueryParams,
|
|
14
|
+
extraQueryParams = {},
|
|
15
|
+
}: GetLogsFromEtherscanArgs) => {
|
|
16
|
+
const queryParams = rawQueryParams
|
|
17
|
+
? rawQueryParams
|
|
18
|
+
: { chainid: `${chainId}`, module: 'logs', action: 'getLogs', address, apikey, ...extraQueryParams }
|
|
19
|
+
|
|
20
|
+
return fetch(`https://api.etherscan.io/v2/api?${new URLSearchParams(queryParams)}`).then((res) => res.json())
|
|
21
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { ONE_YEAR, Zero } from '@stake-dao/constants'
|
|
2
|
+
import { decodeAbiParameters, formatUnits } from 'viem'
|
|
3
|
+
|
|
4
|
+
const decodeEvent = (rawEventsData: any) => {
|
|
5
|
+
return rawEventsData.result?.map((event) => {
|
|
6
|
+
const [eventTimestamp, eventAmount] = decodeAbiParameters(
|
|
7
|
+
[
|
|
8
|
+
{ type: 'uint256', name: 'timestamp' },
|
|
9
|
+
{ type: 'uint256', name: 'amount' },
|
|
10
|
+
],
|
|
11
|
+
event.data,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
return { eventTimestamp: Number(eventTimestamp), eventAmount }
|
|
15
|
+
})
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const decodeEventWithRewardToken = (rawEventsData: any) => {
|
|
19
|
+
return rawEventsData.result?.map((event) => {
|
|
20
|
+
const [eventTimestamp, eventRewardToken, eventAmount] = decodeAbiParameters(
|
|
21
|
+
[
|
|
22
|
+
{ type: 'uint256', name: 'timestamp' },
|
|
23
|
+
{ type: 'address', name: 'rewardToken' },
|
|
24
|
+
{ type: 'uint256', name: 'amount' },
|
|
25
|
+
],
|
|
26
|
+
event.data,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
return { eventTimestamp: Number(eventTimestamp), eventAmount, eventRewardToken }
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const getAverageYearlyRewards = async (rawEventsData: any, nbEvents: number, withRewardToken?: boolean) => {
|
|
34
|
+
const eventsData = (withRewardToken ? decodeEventWithRewardToken(rawEventsData) : decodeEvent(rawEventsData))
|
|
35
|
+
.filter((event) => event.eventAmount > Zero)
|
|
36
|
+
.sort((a, b) => b.eventTimestamp - a.eventTimestamp)
|
|
37
|
+
|
|
38
|
+
const amountPerYear =
|
|
39
|
+
eventsData
|
|
40
|
+
.slice(0, nbEvents)
|
|
41
|
+
.map((_, index) => {
|
|
42
|
+
const duration = eventsData[index].eventTimestamp - eventsData[index + 1].eventTimestamp
|
|
43
|
+
const amount = Number(formatUnits(eventsData[index].eventAmount, 18))
|
|
44
|
+
|
|
45
|
+
// Annualized Reward
|
|
46
|
+
return (amount / duration) * ONE_YEAR
|
|
47
|
+
})
|
|
48
|
+
.reduce((total, x) => (total += x), 0) / nbEvents
|
|
49
|
+
|
|
50
|
+
return amountPerYear
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export default getAverageYearlyRewards
|
package/src/utils.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { RPC, tokens } from '@stake-dao/constants'
|
|
2
2
|
import type { Token } from '@stake-dao/constants'
|
|
3
3
|
import { request } from 'graphql-request'
|
|
4
|
-
import { groupBy } from 'lodash-es'
|
|
4
|
+
import { chunk, groupBy } from 'lodash-es'
|
|
5
5
|
import {
|
|
6
6
|
http,
|
|
7
7
|
type Address,
|
|
@@ -121,23 +121,27 @@ export const rpcCall = async (rpc: string, params: RpcCallArgs[]) => {
|
|
|
121
121
|
return request
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
export const ethGetTxsReceipts = async (rpc: string, txHashes: string[]) => {
|
|
125
|
-
const
|
|
126
|
-
await fetch(rpc, {
|
|
127
|
-
method: 'POST',
|
|
128
|
-
headers: { 'Content-Type': 'application/json' },
|
|
129
|
-
body: JSON.stringify(
|
|
130
|
-
txHashes.map((txHash, index) => ({
|
|
131
|
-
id: index,
|
|
132
|
-
jsonrpc: '2.0',
|
|
133
|
-
method: 'eth_getTransactionReceipt',
|
|
134
|
-
params: [txHash],
|
|
135
|
-
})),
|
|
136
|
-
),
|
|
137
|
-
})
|
|
138
|
-
).json()
|
|
124
|
+
export const ethGetTxsReceipts = async (rpc: string, txHashes: string[], maxSize: number = 100) => {
|
|
125
|
+
const chunkedTxHashes = chunk(txHashes, maxSize)
|
|
139
126
|
|
|
140
|
-
|
|
127
|
+
const receiptsRequests = await Promise.all(
|
|
128
|
+
chunkedTxHashes.map((txs) =>
|
|
129
|
+
fetch(rpc, {
|
|
130
|
+
method: 'POST',
|
|
131
|
+
headers: { 'Content-Type': 'application/json' },
|
|
132
|
+
body: JSON.stringify(
|
|
133
|
+
txs.map((txHash, index) => ({
|
|
134
|
+
id: index,
|
|
135
|
+
jsonrpc: '2.0',
|
|
136
|
+
method: 'eth_getTransactionReceipt',
|
|
137
|
+
params: [txHash],
|
|
138
|
+
})),
|
|
139
|
+
),
|
|
140
|
+
}).then((res) => res.json()),
|
|
141
|
+
),
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
return receiptsRequests.flat()
|
|
141
145
|
}
|
|
142
146
|
|
|
143
147
|
export const ethEstimateGas = async (rpc: string, from: string, to: string, data: string) => {
|
|
@@ -289,7 +293,7 @@ export const multichainMulticall = async (calls: MultiChainCall[], abi: readonly
|
|
|
289
293
|
const callsPerChain = groupBy(calls, (call) => call.chainId)
|
|
290
294
|
|
|
291
295
|
const chains = Object.keys(callsPerChain)
|
|
292
|
-
|
|
296
|
+
|
|
293
297
|
const parsedCallsPerChain = chains.map((chainId) => callsPerChain[chainId]!.map(({ chainId, ...call }) => call))
|
|
294
298
|
|
|
295
299
|
const promises = await Promise.allSettled(
|
|
@@ -40,6 +40,7 @@ export const NOT_LP_GAUGES = {
|
|
|
40
40
|
427: '0xC91113B4Dd89dd20FDEECDAC82477Bc99A840355',
|
|
41
41
|
428: '0x7ce8aF75A9180B602445bE230860DDcb4cAc3E42',
|
|
42
42
|
473: '0x66EFd8E255B8B7Cf32961E90A5820f289402629e',
|
|
43
|
+
670: '0x5A537a46D780B1C70138aB98eDce69e7a53177ba',
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
export const NOT_LP_GAUGES_INDEX = Object.keys(NOT_LP_GAUGES).map((x) => Number(x))
|
|
@@ -19,15 +19,20 @@ type UnknownToken = {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
const getMerkleData = async (epoch: number) => {
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
const [delegators, nonDelegators] = await Promise.all([
|
|
23
|
+
fetch(
|
|
24
|
+
`${GH_STAKE_DAO_BOUNTIES_REPORT}/refs/heads/main/bounties-reports/${epoch}/vlCVX/merkle_data_delegators.json`,
|
|
25
|
+
).then((res) => res.json()),
|
|
26
|
+
fetch(
|
|
27
|
+
`${GH_STAKE_DAO_BOUNTIES_REPORT}/refs/heads/main/bounties-reports/${epoch}/vlCVX/merkle_data_non_delegators.json`,
|
|
28
|
+
).then((res) => res.json()),
|
|
29
|
+
])
|
|
30
|
+
|
|
31
|
+
return { delegators, nonDelegators }
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
const isRootMatch = (contractRoot: string, fileRoot: string) =>
|
|
30
|
-
(contractRoot === ZERO_ROOT && fileRoot === ZERO_ROOT) || contractRoot
|
|
35
|
+
(contractRoot === ZERO_ROOT && fileRoot === ZERO_ROOT) || equalTlc(contractRoot, fileRoot)
|
|
31
36
|
|
|
32
37
|
const validateMerkleRoots = (delegatorRoot: string, nonDelegatorRoot: string, merkleData: any) => {
|
|
33
38
|
const delegatorMatches = isRootMatch(delegatorRoot, merkleData.delegators.merkleRoot)
|
|
@@ -107,8 +112,8 @@ const getParsedData = (
|
|
|
107
112
|
const ZERO_ROOT = '0x0000000000000000000000000000000000000000000000000000000000000000'
|
|
108
113
|
|
|
109
114
|
export const fetchUserVlCvxClaimable = async (provider: any, rpc: Rpcs, user: string) => {
|
|
110
|
-
const nowEpoch = Math.floor(Date.now() /
|
|
111
|
-
const prevEpoch = nowEpoch -
|
|
115
|
+
const nowEpoch = Math.floor(Date.now() / 1000 / ONE_WEEK) * ONE_WEEK
|
|
116
|
+
const prevEpoch = nowEpoch - ONE_WEEK
|
|
112
117
|
|
|
113
118
|
const { delegatorRoot, nonDelegatorRoot } = await getMerkleRoots(provider)
|
|
114
119
|
|
|
@@ -140,8 +145,8 @@ export const fetchUserVlCvxClaimable = async (provider: any, rpc: Rpcs, user: st
|
|
|
140
145
|
if (!tokens.length) return []
|
|
141
146
|
|
|
142
147
|
const [delegatedClaimed, notDelegatedClaimed] = await Promise.all([
|
|
143
|
-
getClaimedData(provider, tokensDelegated, user, contract('vlCvxDelegatorMerkle'
|
|
144
|
-
getClaimedData(provider, tokensNotDelegated, user, contract('vlCvxNotDelegatorMerkle'
|
|
148
|
+
getClaimedData(provider, tokensDelegated, user, contract('vlCvxDelegatorMerkle')),
|
|
149
|
+
getClaimedData(provider, tokensNotDelegated, user, contract('vlCvxNotDelegatorMerkle')),
|
|
145
150
|
])
|
|
146
151
|
|
|
147
152
|
const unknownTokens = tokens.filter((t) => !tokenWithAddress(t))
|
|
@@ -54,7 +54,7 @@ export const fetchPendingRemoteCampaigns = async ({
|
|
|
54
54
|
const payload = decodeAbiParameters(
|
|
55
55
|
parseAbiParameters([
|
|
56
56
|
'Payload payload',
|
|
57
|
-
'struct Payload { uint256 actionType; address sender; bytes parameters }',
|
|
57
|
+
'struct Payload { uint256 actionType; address sender; address votemarket; bytes parameters }',
|
|
58
58
|
]),
|
|
59
59
|
decodedEvent.args.message.payload,
|
|
60
60
|
)
|