1
1
const { sumTokens2, unwrapSlipstreamNFT } = require ( "../helper/unwrapLPs" ) ;
2
+ const utils = require ( '../helper/utils' )
2
3
3
4
const config = {
4
5
factory : "0xDa14Fdd72345c4d2511357214c5B89A919768e59" ,
@@ -8,6 +9,7 @@ const config = {
8
9
wAeroNFT : "0x17B5826382e3a5257b829cF0546A08Bd77409270" . toLowerCase ( ) ,
9
10
sAeroNFT : "0x9f42361B7602Df1A8Ae28Bf63E6cb1883CD44C27" . toLowerCase ( ) ,
10
11
sSlipNFT : "0x1Dc7A0f5336F52724B650E39174cfcbbEdD67bF1" . toLowerCase ( ) ,
12
+ wsSlipNFT : "0xD74339e0F10fcE96894916B93E5Cc7dE89C98272" . toLowerCase ( ) ,
11
13
pools : [
12
14
"0x803ea69c7e87D1d6C86adeB40CB636cC0E6B98E2" , // wethPool
13
15
"0x3ec4a293Fb906DD2Cd440c20dECB250DeF141dF1" , // usdcPool
@@ -16,7 +18,7 @@ const config = {
16
18
} ;
17
19
18
20
async function unwrapArcadiaAeroLP ( { api, ownerIds } ) {
19
- const { wAeroNFT, sAeroNFT, sSlipNFT } = config
21
+ const { wAeroNFT, sAeroNFT, sSlipNFT, wsSlipNFT } = config
20
22
const wAERONFTIds = [ ]
21
23
const sAERONFTIds = [ ]
22
24
const sSlipNftIds = [ ]
@@ -39,6 +41,9 @@ async function unwrapArcadiaAeroLP({ api, ownerIds }) {
39
41
case sSlipNFT :
40
42
sSlipNftIds . push ( ids [ i ] ) ;
41
43
break ;
44
+ case wsSlipNFT :
45
+ sSlipNftIds . push ( ids [ i ] ) ;
46
+ break ;
42
47
}
43
48
}
44
49
}
@@ -60,19 +65,79 @@ async function uwrapStakedSlipstreamLP({ api, sSlipNftIds, }) {
60
65
}
61
66
62
67
async function tvl ( api ) {
63
- const { factory, pools, uniNFT, slipNFT, wAeroNFT, sAeroNFT, sSlipNFT, alienBaseNFT } = config ;
68
+ const { factory, pools, uniNFT, slipNFT, wAeroNFT, sAeroNFT, sSlipNFT, alienBaseNFT, wsSlipNFT } = config ;
64
69
const ownerTokens = [ ]
65
70
const ownerIds = [ ]
66
71
const accs = [ ]
67
72
68
73
const uTokens = await api . multiCall ( { abi : "address:asset" , calls : pools } ) ;
69
- await api . sumTokens ( { blacklistedTokens : [ uniNFT , slipNFT , wAeroNFT , sAeroNFT , sSlipNFT , alienBaseNFT ] , tokensAndOwners2 : [ uTokens , pools ] } ) ;
74
+ await api . sumTokens ( { blacklistedTokens : [ uniNFT , slipNFT , wAeroNFT , sAeroNFT , sSlipNFT , alienBaseNFT , wsSlipNFT ] , tokensAndOwners2 : [ uTokens , pools ] } ) ;
70
75
71
76
const accounts = await api . fetchList ( { lengthAbi : 'allAccountsLength' , itemAbi : 'allAccounts' , target : factory , } )
72
- const assetDatas = await api . multiCall ( { abi : abi . generateAssetData , calls : accounts , permitFailure : true } )
73
77
74
- accounts . forEach ( ( account , i ) => {
75
- const assetData = assetDatas [ i ] ;
78
+ // Account version 1 has a stored state of all assets, and can be fetched using generateAssetData()
79
+ // Account version 2 has no such stored state, and must be fetched with external api calls.
80
+ const versions = await api . multiCall ( { abi : 'function ACCOUNT_VERSION() view returns (uint256)' , calls : accounts , } ) ;
81
+ const v1Accounts = accounts . filter ( ( _ , i ) => versions [ i ] === '1' ) ;
82
+ const v2Accounts = accounts . filter ( ( _ , i ) => versions [ i ] === '2' ) ;
83
+
84
+ // This endpoint uses the following logic:
85
+ // 1. Uses batches of all v2Accounts (to prevent rate limiting)
86
+ // 2. calls Arcadia's endpoint to fetch the asset data of a V2 account
87
+ // 3. verifies onchain that the ownership of the NFTs is indeed correct
88
+ // 4. Return format is then transformed to be identical to the format of the V1 assetData
89
+ const sleep = ( ms ) => new Promise ( resolve => setTimeout ( resolve , ms ) ) ;
90
+
91
+ const batchSize = 10 ;
92
+ for ( let i = 0 ; i < v2Accounts . length ; i += batchSize ) {
93
+ const batch = v2Accounts . slice ( i , i + batchSize ) ;
94
+ await Promise . all ( batch . map ( async ( account ) => {
95
+ try {
96
+ const assetDataCall = await utils . fetchURL ( `https://api.arcadia.finance/v1/api/accounts/spot_asset_data?chain_id=8453&account_addresses=${ account } ` ) ;
97
+ const assetData = assetDataCall . data [ 0 ] // call is made for multiple addresses, but may time out if all accounts are requested at once
98
+ if ( ! assetData || ! assetData [ 0 ] [ 0 ] || assetData [ 0 ] [ 0 ] . length < 1 ) return ;
99
+
100
+ // Since the return of the ownership data comes from a "blackbox" backend,
101
+ // we verify onchain that the ownership of the NFTs is indeed correct
102
+ // We check this for each asset where ID != 0
103
+ const verificationCalls = assetData [ 0 ] [ 1 ] . map ( ( tokenId , index ) => {
104
+ if ( tokenId === "0" ) return null ; // Skip if tokenId is 0, just an erc20, balance will be fetched through sdk
105
+ return {
106
+ target : assetData [ 0 ] [ 0 ] [ index ] ,
107
+ params : [ tokenId ]
108
+ }
109
+ } ) . filter ( call => call !== null ) ; // Remove null entries
110
+
111
+ if ( verificationCalls . length > 0 ) {
112
+ const owners = await api . multiCall ( {
113
+ abi : 'function ownerOf(uint256) view returns (address)' ,
114
+ calls : verificationCalls
115
+ } ) ;
116
+
117
+ // Verify all owners match the account
118
+ const allOwnersMatch = owners . every ( owner => owner . toLowerCase ( ) === account . toLowerCase ( ) ) ;
119
+ if ( ! allOwnersMatch ) return ; // Skip this account if any ownership verification fails
120
+ }
121
+
122
+ ownerTokens . push ( [ assetData [ 0 ] [ 0 ] , account ] )
123
+ if ( ! assetData [ 0 ] [ 0 ] . length || ! assetData [ 0 ] [ 1 ] . length || assetData [ 0 ] [ 1 ] == "0" ) return ;
124
+ ownerIds . push ( [ assetData [ 0 ] [ 0 ] , assetData [ 0 ] [ 1 ] , account ] )
125
+ accs . push ( account )
126
+ } catch ( error ) {
127
+ console . log ( `Failed to fetch/process data for account ${ account } :` , error ) ;
128
+ return ;
129
+ }
130
+ } ) ) ;
131
+
132
+ // Add small delay between batches to prevent rate limiting
133
+ if ( i + batchSize < v2Accounts . length ) { // Only sleep if there are more batches to process
134
+ await sleep ( 1000 ) ;
135
+ }
136
+ }
137
+
138
+ const assetDatasV1 = await api . multiCall ( { abi : abi . generateAssetData , calls : v1Accounts , permitFailure : true } )
139
+ v1Accounts . forEach ( ( account , i ) => {
140
+ const assetData = assetDatasV1 [ i ] ;
76
141
if ( ! assetData || ! assetData . assets || ! assetData . assets . length ) return ;
77
142
ownerTokens . push ( [ assetData . assets , account ] )
78
143
if ( ! assetData [ 0 ] . length || ! assetData [ 1 ] . length ) return ;
@@ -84,7 +149,7 @@ async function tvl (api) {
84
149
await sumTokens2 ( { api, owners : accs , uniV3ExtraConfig : { nftAddress : alienBaseNFT } } )
85
150
86
151
// add all simple ERC20s
87
- await api . sumTokens ( { ownerTokens, blacklistedTokens : [ uniNFT , slipNFT , wAeroNFT , sAeroNFT , sSlipNFT , alienBaseNFT ] , } ) ;
152
+ await api . sumTokens ( { ownerTokens, blacklistedTokens : [ uniNFT , slipNFT , wAeroNFT , sAeroNFT , sSlipNFT , alienBaseNFT , wsSlipNFT ] , } ) ;
88
153
89
154
// add all Arcadia-wrapped LP positions
90
155
await unwrapArcadiaAeroLP ( { api, ownerIds } ) ;
0 commit comments