1
+ const sdk = require ( '@defillama/sdk' ) ;
2
+ const axios = require ( 'axios' ) ;
3
+ const utils = require ( '../utils' ) ;
4
+ const { ethers } = require ( 'ethers' ) ;
5
+
6
+ const GARDEN_STAKING_CONTRACT = '0xe2239938Ce088148b3Ab398b2b77Eedfcd9d1AfC'
7
+ const SEED_TOKEN = '0x86f65121804D2Cdbef79F9f072D4e0c2eEbABC08'
8
+ const chain = 'arbitrum'
9
+
10
+ // Event topics for staking events
11
+ const STAKED_EVENT_TOPIC = '0xa1fdccfe567643a44425efdd141171e8d992854a81e5c819c1432b0de47c9a11' ;
12
+ const STAKE_REFUNDED_EVENT_TOPIC = '0xba33cc6a4502f7f80bfe643ffa925ba7ab5e0af61c061a9aceabef0349c9dce4' ;
13
+ const MAX_UINT256 = ethers . constants . MaxUint256 ;
14
+
15
+ // ABI for the events we need
16
+ const eventAbi = [
17
+ {
18
+ "anonymous" : false ,
19
+ "inputs" : [
20
+ {
21
+ "indexed" : true ,
22
+ "internalType" : "bytes32" ,
23
+ "name" : "stakeID" ,
24
+ "type" : "bytes32"
25
+ } ,
26
+ {
27
+ "indexed" : true ,
28
+ "internalType" : "address" ,
29
+ "name" : "owner" ,
30
+ "type" : "address"
31
+ } ,
32
+ {
33
+ "indexed" : false ,
34
+ "internalType" : "uint256" ,
35
+ "name" : "stake" ,
36
+ "type" : "uint256"
37
+ } ,
38
+ {
39
+ "indexed" : false ,
40
+ "internalType" : "uint256" ,
41
+ "name" : "expiry" ,
42
+ "type" : "uint256"
43
+ }
44
+ ] ,
45
+ "name" : "Staked" ,
46
+ "type" : "event"
47
+ } ,
48
+ {
49
+ "anonymous" : false ,
50
+ "inputs" : [
51
+ {
52
+ "indexed" : true ,
53
+ "internalType" : "bytes32" ,
54
+ "name" : "stakeID" ,
55
+ "type" : "bytes32"
56
+ }
57
+ ] ,
58
+ "name" : "StakeRefunded" ,
59
+ "type" : "event"
60
+ } ,
61
+ ] ;
62
+
63
+ // TVL Calculator class (simplified for adapter use)
64
+ class TVLCalculator {
65
+ constructor ( ) {
66
+ this . stakes = new Map ( ) ; // stakeID -> stake amount
67
+ }
68
+
69
+ async fetchEvents ( ) {
70
+
71
+ try {
72
+ // Get current block number
73
+ const currentBlock = await sdk . api . util . getLatestBlock ( chain ) ;
74
+ const toBlock = currentBlock . number ;
75
+ const fromBlock = 192080606 ; // Contract deployment block
76
+
77
+ // Fetch Staked events
78
+ const stakedLogs = (
79
+ await sdk . api . util . getLogs ( {
80
+ target : GARDEN_STAKING_CONTRACT ,
81
+ topic : '' ,
82
+ toBlock,
83
+ fromBlock,
84
+ keys : [ ] ,
85
+ topics : [ STAKED_EVENT_TOPIC ] ,
86
+ chain : chain ,
87
+ } )
88
+ ) . output ;
89
+
90
+ // Fetch StakeRefunded events
91
+ const refundedLogs = (
92
+ await sdk . api . util . getLogs ( {
93
+ target : GARDEN_STAKING_CONTRACT ,
94
+ topic : '' ,
95
+ toBlock,
96
+ fromBlock,
97
+ keys : [ ] ,
98
+ topics : [ STAKE_REFUNDED_EVENT_TOPIC ] ,
99
+ chain : chain ,
100
+ } )
101
+ ) . output ;
102
+
103
+ return { stakedLogs, refundedLogs, currentBlock : currentBlock . number } ;
104
+
105
+ } catch ( error ) {
106
+ console . error ( 'Error fetching events:' , error ) ;
107
+ throw error ;
108
+ }
109
+ }
110
+
111
+ async processEvents ( stakedLogs , refundedLogs ) {
112
+
113
+ try {
114
+
115
+ const abiInterface = new ethers . utils . Interface ( eventAbi ) ;
116
+ const refundedStakeIDs = new Set ( ) ; // Track refunded stake IDs
117
+
118
+ // Process staked events first
119
+ for ( const log of stakedLogs ) {
120
+ try {
121
+ const formattedLog = {
122
+ address : log . address ,
123
+ topics : log . topics ,
124
+ data : log . data ,
125
+ blockNumber : typeof log . blockNumber === 'string' ? parseInt ( log . blockNumber , 16 ) : log . blockNumber ,
126
+ transactionHash : log . transactionHash ,
127
+ logIndex : log . logIndex
128
+ } ;
129
+
130
+ const decoded = abiInterface . parseLog ( formattedLog ) ;
131
+ const stakeID = decoded . args . stakeID ;
132
+ const stakeAmount = decoded . args . stake ;
133
+ const owner = decoded . args . owner ;
134
+ const expiry = decoded . args . expiry ;
135
+
136
+ this . stakes . set ( stakeID , {
137
+ amount : stakeAmount . toString ( ) ,
138
+ owner : owner ,
139
+ expiry : expiry . toString ( ) ,
140
+ blockNumber : formattedLog . blockNumber ,
141
+ transactionHash : log . transactionHash
142
+ } ) ;
143
+
144
+ } catch ( error ) {
145
+ console . warn ( 'Failed to decode staked event:' , error . message ) ;
146
+ }
147
+ }
148
+
149
+ // Process refunded events to remove stakes
150
+ for ( const log of refundedLogs ) {
151
+ try {
152
+ const formattedLog = {
153
+ address : log . address ,
154
+ topics : log . topics ,
155
+ data : log . data ,
156
+ blockNumber : typeof log . blockNumber === 'string' ? parseInt ( log . blockNumber , 16 ) : log . blockNumber ,
157
+ transactionHash : log . transactionHash ,
158
+ logIndex : log . logIndex
159
+ } ;
160
+
161
+ const decoded = abiInterface . parseLog ( formattedLog ) ;
162
+ const stakeID = decoded . args . stakeID ;
163
+
164
+ refundedStakeIDs . add ( stakeID ) ;
165
+ this . stakes . delete ( stakeID ) ;
166
+
167
+ } catch ( error ) {
168
+ console . warn ( 'Failed to decode refunded event:' , error . message ) ;
169
+ }
170
+ }
171
+
172
+ } catch ( error ) {
173
+ console . error ( 'Error processing events:' , error ) ;
174
+ throw error ;
175
+ }
176
+ }
177
+
178
+ calculateTVL ( ) {
179
+ try {
180
+ let totalTVLWei = BigInt ( 0 ) ;
181
+
182
+ for ( const [ stakeID , stakeInfo ] of this . stakes . entries ( ) ) {
183
+ totalTVLWei += BigInt ( stakeInfo . amount ) ;
184
+ }
185
+
186
+ const totalTVLEther = ethers . utils . formatEther ( totalTVLWei ) ;
187
+
188
+ return {
189
+ tvl : totalTVLEther ,
190
+ } ;
191
+
192
+ } catch ( error ) {
193
+ console . error ( 'Error calculating TVL:' , error ) ;
194
+ throw error ;
195
+ }
196
+ }
197
+ }
198
+
199
+ const getTvl = async ( ) => {
200
+ try {
201
+ // Get APY data
202
+ const apyData = await utils . getData ( 'https://stakingv2.garden.finance/apy' ) ;
203
+
204
+ // Calculate TVL using event-based method
205
+ const calculator = new TVLCalculator ( ) ;
206
+
207
+ // Fetch and process events
208
+ const { stakedLogs, refundedLogs } = await calculator . fetchEvents ( ) ;
209
+ await calculator . processEvents ( stakedLogs , refundedLogs ) ;
210
+
211
+ // Calculate TVL from events
212
+ const tvlResult = calculator . calculateTVL ( ) ;
213
+ const tvl = parseFloat ( tvlResult . tvl ) ;
214
+
215
+
216
+ // Get SEED price
217
+ const priceData = await axios . get (
218
+ 'https://coins.llama.fi/prices/current/arbitrum:0x86f65121804D2Cdbef79F9f072D4e0c2eEbABC08'
219
+ ) ;
220
+
221
+ const seedPrice = priceData . data . coins [ `${ chain } :${ SEED_TOKEN } ` ] ?. price || 0 ;
222
+
223
+ // Calculate TVL in USD
224
+ const tvlUsd = tvl * seedPrice ;
225
+
226
+ return [ {
227
+ pool : '0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf-base' ,
228
+ chain : utils . formatChain ( chain ) ,
229
+ project : 'garden' ,
230
+ symbol : utils . formatSymbol ( 'SEED' ) ,
231
+ tvlUsd : tvlUsd ,
232
+ apy : apyData . data ,
233
+ } ] ;
234
+
235
+ } catch ( error ) {
236
+ console . error ( 'Error in getTvl:' , error ) ;
237
+ return [ ] ;
238
+ }
239
+ } ;
240
+
241
+ module . exports = {
242
+ timetravel : false ,
243
+ apy : getTvl ,
244
+ url : 'https://app.garden.finance/stake' ,
245
+ } ;
0 commit comments