@@ -65,64 +65,67 @@ type defiPositionsResponse struct {
6565}
6666
6767type defiAggregations struct {
68- TotalUSDValue float64 `json:"total_usd_value "`
68+ TotalValueUSD float64 `json:"total_value_usd "`
6969 TotalByChain map [string ]float64 `json:"total_by_chain,omitempty"`
7070}
7171
72- // defiPosition is a flat struct matching the polymorphic DefiPosition schema.
72+ // defiTokenInfo represents a token object returned by the API with address,
73+ // name, symbol, and optional numeric fields depending on position type.
74+ type defiTokenInfo struct {
75+ Address string `json:"address,omitempty"`
76+ Name string `json:"name,omitempty"`
77+ Symbol string `json:"symbol,omitempty"`
78+ Decimals int `json:"decimals,omitempty"`
79+ Holdings float64 `json:"holdings,omitempty"`
80+ PriceUSD float64 `json:"price_usd,omitempty"`
81+ }
82+
83+ // nftTokenDetails holds per-token data inside an NFT concentrated-liquidity position.
84+ type nftTokenDetails struct {
85+ PriceUSD float64 `json:"price_usd"`
86+ Holdings float64 `json:"holdings,omitempty"`
87+ Rewards float64 `json:"rewards,omitempty"`
88+ }
89+
90+ // defiPosition matches the polymorphic DefiPosition schema returned by the API.
7391// Fields are optional depending on the `type` discriminator.
7492type defiPosition struct {
75- Type string `json:"type"`
76- ChainID int64 `json:"chain_id"`
77- USDVal float64 `json:"usd_value"`
78- Logo * string `json:"logo,omitempty"`
93+ Type string `json:"type"`
94+ Chain string `json:"chain,omitempty"`
95+ ChainID int64 `json:"chain_id"`
96+ ValueUSD float64 `json:"value_usd"`
97+ Logo * string `json:"logo,omitempty"`
7998
8099 // Erc4626 / Tokenized fields
81- TokenType string `json:"token_type,omitempty"`
82- Token string `json:"token,omitempty"`
83- TokenName string `json:"token_name,omitempty"`
84- TokenSymbol string `json:"token_symbol,omitempty"`
85- UnderlyingToken string `json:"underlying_token,omitempty"`
86- UnderlyingTokenName string `json:"underlying_token_name,omitempty"`
87- UnderlyingTokenSymbol string `json:"underlying_token_symbol,omitempty"`
88- UnderlyingTokenDecimals int `json:"underlying_token_decimals,omitempty"`
100+ TokenType string `json:"token_type,omitempty"`
101+ Token * defiTokenInfo `json:"token,omitempty"`
102+ UnderlyingToken * defiTokenInfo `json:"underlying_token,omitempty"`
103+ LendingPool string `json:"lending_pool,omitempty"`
89104
90105 // Erc4626 / Tokenized / UniswapV2 fields
91- CalculatedBalance float64 `json:"calculated_balance ,omitempty"`
92- PriceInUSD float64 `json:"price_in_usd ,omitempty"`
106+ Balance float64 `json:"balance ,omitempty"`
107+ PriceUSD float64 `json:"price_usd ,omitempty"`
93108
94109 // UniswapV2 / Nft / NftV4 fields
95- Protocol string `json:"protocol,omitempty"`
96- Pool string `json:"pool,omitempty"`
97- PoolID []int `json:"pool_id,omitempty"`
98- PoolManager string `json:"pool_manager,omitempty"`
99- Salt []int `json:"salt,omitempty"`
100- Token0 string `json:"token0,omitempty"`
101- Token0Name string `json:"token0_name,omitempty"`
102- Token0Symbol string `json:"token0_symbol,omitempty"`
103- Token0Decimals int `json:"token0_decimals,omitempty"`
104- Token1 string `json:"token1,omitempty"`
105- Token1Name string `json:"token1_name,omitempty"`
106- Token1Symbol string `json:"token1_symbol,omitempty"`
107- Token1Decimals int `json:"token1_decimals,omitempty"`
108- LPBalance string `json:"lp_balance,omitempty"`
109- Token0Price float64 `json:"token0_price,omitempty"`
110- Token1Price float64 `json:"token1_price,omitempty"`
110+ Protocol string `json:"protocol,omitempty"`
111+ Pool string `json:"pool,omitempty"`
112+ PoolID string `json:"pool_id,omitempty"`
113+ PoolManager string `json:"pool_manager,omitempty"`
114+ Salt string `json:"salt,omitempty"`
115+ Token0 * defiTokenInfo `json:"token0,omitempty"`
116+ Token1 * defiTokenInfo `json:"token1,omitempty"`
117+ LPBalance string `json:"lp_balance,omitempty"`
111118
112119 // Nft / NftV4 concentrated liquidity positions
113120 Positions []nftPositionDetails `json:"positions,omitempty"`
114121}
115122
116123type nftPositionDetails struct {
117- TickLower int `json:"tick_lower"`
118- TickUpper int `json:"tick_upper"`
119- TokenID string `json:"token_id"`
120- Token0Price float64 `json:"token0_price"`
121- Token0Holdings float64 `json:"token0_holdings,omitempty"`
122- Token0Rewards float64 `json:"token0_rewards,omitempty"`
123- Token1Price float64 `json:"token1_price"`
124- Token1Holdings float64 `json:"token1_holdings,omitempty"`
125- Token1Rewards float64 `json:"token1_rewards,omitempty"`
124+ TickLower int `json:"tick_lower"`
125+ TickUpper int `json:"tick_upper"`
126+ TokenID string `json:"token_id"`
127+ Token0 * nftTokenDetails `json:"token0,omitempty"`
128+ Token1 * nftTokenDetails `json:"token1,omitempty"`
126129}
127130
128131func runDefiPositions (cmd * cobra.Command , args []string ) error {
@@ -169,7 +172,7 @@ func runDefiPositions(cmd *cobra.Command, args []string) error {
169172 p .Type ,
170173 fmt .Sprintf ("%d" , p .ChainID ),
171174 p .Protocol ,
172- output .FormatUSD (p .USDVal ),
175+ output .FormatUSD (p .ValueUSD ),
173176 positionDetails (p ),
174177 }
175178 }
@@ -184,18 +187,26 @@ func runDefiPositions(cmd *cobra.Command, args []string) error {
184187
185188// positionDetails returns a human-readable summary for a DeFi position,
186189// varying by position type.
190+ // tokenSymbol safely extracts the symbol from a token info pointer.
191+ func tokenSymbol (t * defiTokenInfo ) string {
192+ if t == nil {
193+ return ""
194+ }
195+ return t .Symbol
196+ }
197+
187198func positionDetails (p defiPosition ) string {
188199 switch p .Type {
189200 case "Erc4626" :
190201 parts := []string {}
191- if p . TokenSymbol != "" {
192- parts = append (parts , p . TokenSymbol )
202+ if sym := tokenSymbol ( p . Token ); sym != "" {
203+ parts = append (parts , sym )
193204 }
194- if p . UnderlyingTokenSymbol != "" {
195- parts = append (parts , fmt .Sprintf ("-> %s" , p . UnderlyingTokenSymbol ))
205+ if sym := tokenSymbol ( p . UnderlyingToken ); sym != "" {
206+ parts = append (parts , fmt .Sprintf ("-> %s" , sym ))
196207 }
197- if p .CalculatedBalance != 0 {
198- parts = append (parts , fmt .Sprintf ("bal=%.6g" , p .CalculatedBalance ))
208+ if p .Balance != 0 {
209+ parts = append (parts , fmt .Sprintf ("bal=%.6g" , p .Balance ))
199210 }
200211 return strings .Join (parts , " " )
201212
@@ -204,23 +215,23 @@ func positionDetails(p defiPosition) string {
204215 if p .TokenType != "" {
205216 parts = append (parts , p .TokenType )
206217 }
207- if p . TokenSymbol != "" {
208- parts = append (parts , p . TokenSymbol )
218+ if sym := tokenSymbol ( p . Token ); sym != "" {
219+ parts = append (parts , sym )
209220 }
210- if p .CalculatedBalance != 0 {
211- parts = append (parts , fmt .Sprintf ("bal=%.6g" , p .CalculatedBalance ))
221+ if p .Balance != 0 {
222+ parts = append (parts , fmt .Sprintf ("bal=%.6g" , p .Balance ))
212223 }
213224 return strings .Join (parts , " " )
214225
215226 case "UniswapV2" :
216- pair := formatPair (p . Token0Symbol , p . Token1Symbol )
217- if p .CalculatedBalance != 0 {
218- return fmt .Sprintf ("%s bal=%.6g" , pair , p .CalculatedBalance )
227+ pair := formatPair (tokenSymbol ( p . Token0 ), tokenSymbol ( p . Token1 ) )
228+ if p .Balance != 0 {
229+ return fmt .Sprintf ("%s bal=%.6g" , pair , p .Balance )
219230 }
220231 return pair
221232
222233 case "Nft" , "NftV4" :
223- pair := formatPair (p . Token0Symbol , p . Token1Symbol )
234+ pair := formatPair (tokenSymbol ( p . Token0 ), tokenSymbol ( p . Token1 ) )
224235 nPos := len (p .Positions )
225236 if nPos == 1 {
226237 return fmt .Sprintf ("%s (1 position)" , pair )
@@ -252,7 +263,7 @@ func printAggregations(w io.Writer, agg *defiAggregations) {
252263 return
253264 }
254265
255- fmt .Fprintf (w , "\n Total USD Value: %s\n " , output .FormatUSD (agg .TotalUSDValue ))
266+ fmt .Fprintf (w , "\n Total USD Value: %s\n " , output .FormatUSD (agg .TotalValueUSD ))
256267
257268 if len (agg .TotalByChain ) > 0 {
258269 fmt .Fprintln (w , "Breakdown by chain:" )
0 commit comments