@@ -46,64 +46,67 @@ type defiPositionsResponse struct {
4646}
4747
4848type defiAggregations struct {
49- TotalUSDValue float64 `json:"total_usd_value "`
49+ TotalValueUSD float64 `json:"total_value_usd "`
5050 TotalByChain map [string ]float64 `json:"total_by_chain,omitempty"`
5151}
5252
53- // defiPosition is a flat struct matching the polymorphic DefiPosition schema.
53+ // defiTokenInfo represents a token object returned by the API with address,
54+ // name, symbol, and optional numeric fields depending on position type.
55+ type defiTokenInfo struct {
56+ Address string `json:"address,omitempty"`
57+ Name string `json:"name,omitempty"`
58+ Symbol string `json:"symbol,omitempty"`
59+ Decimals int `json:"decimals,omitempty"`
60+ Holdings float64 `json:"holdings,omitempty"`
61+ PriceUSD float64 `json:"price_usd,omitempty"`
62+ }
63+
64+ // nftTokenDetails holds per-token data inside an NFT concentrated-liquidity position.
65+ type nftTokenDetails struct {
66+ PriceUSD float64 `json:"price_usd"`
67+ Holdings float64 `json:"holdings,omitempty"`
68+ Rewards float64 `json:"rewards,omitempty"`
69+ }
70+
71+ // defiPosition matches the polymorphic DefiPosition schema returned by the API.
5472// Fields are optional depending on the `type` discriminator.
5573type defiPosition struct {
56- Type string `json:"type"`
57- ChainID int64 `json:"chain_id"`
58- USDVal float64 `json:"usd_value"`
59- Logo * string `json:"logo,omitempty"`
74+ Type string `json:"type"`
75+ Chain string `json:"chain,omitempty"`
76+ ChainID int64 `json:"chain_id"`
77+ ValueUSD float64 `json:"value_usd"`
78+ Logo * string `json:"logo,omitempty"`
6079
6180 // Erc4626 / Tokenized fields
62- TokenType string `json:"token_type,omitempty"`
63- Token string `json:"token,omitempty"`
64- TokenName string `json:"token_name,omitempty"`
65- TokenSymbol string `json:"token_symbol,omitempty"`
66- UnderlyingToken string `json:"underlying_token,omitempty"`
67- UnderlyingTokenName string `json:"underlying_token_name,omitempty"`
68- UnderlyingTokenSymbol string `json:"underlying_token_symbol,omitempty"`
69- UnderlyingTokenDecimals int `json:"underlying_token_decimals,omitempty"`
81+ TokenType string `json:"token_type,omitempty"`
82+ Token * defiTokenInfo `json:"token,omitempty"`
83+ UnderlyingToken * defiTokenInfo `json:"underlying_token,omitempty"`
84+ LendingPool string `json:"lending_pool,omitempty"`
7085
7186 // Erc4626 / Tokenized / UniswapV2 fields
72- CalculatedBalance float64 `json:"calculated_balance ,omitempty"`
73- PriceInUSD float64 `json:"price_in_usd ,omitempty"`
87+ Balance float64 `json:"balance ,omitempty"`
88+ PriceUSD float64 `json:"price_usd ,omitempty"`
7489
7590 // UniswapV2 / Nft / NftV4 fields
76- Protocol string `json:"protocol,omitempty"`
77- Pool string `json:"pool,omitempty"`
78- PoolID []int `json:"pool_id,omitempty"`
79- PoolManager string `json:"pool_manager,omitempty"`
80- Salt []int `json:"salt,omitempty"`
81- Token0 string `json:"token0,omitempty"`
82- Token0Name string `json:"token0_name,omitempty"`
83- Token0Symbol string `json:"token0_symbol,omitempty"`
84- Token0Decimals int `json:"token0_decimals,omitempty"`
85- Token1 string `json:"token1,omitempty"`
86- Token1Name string `json:"token1_name,omitempty"`
87- Token1Symbol string `json:"token1_symbol,omitempty"`
88- Token1Decimals int `json:"token1_decimals,omitempty"`
89- LPBalance string `json:"lp_balance,omitempty"`
90- Token0Price float64 `json:"token0_price,omitempty"`
91- Token1Price float64 `json:"token1_price,omitempty"`
91+ Protocol string `json:"protocol,omitempty"`
92+ Pool string `json:"pool,omitempty"`
93+ PoolID string `json:"pool_id,omitempty"`
94+ PoolManager string `json:"pool_manager,omitempty"`
95+ Salt string `json:"salt,omitempty"`
96+ Token0 * defiTokenInfo `json:"token0,omitempty"`
97+ Token1 * defiTokenInfo `json:"token1,omitempty"`
98+ LPBalance string `json:"lp_balance,omitempty"`
9299
93100 // Nft / NftV4 concentrated liquidity positions
94101 Positions []nftPositionDetails `json:"positions,omitempty"`
95102}
96103
97104type nftPositionDetails struct {
98- TickLower int `json:"tick_lower"`
99- TickUpper int `json:"tick_upper"`
100- TokenID string `json:"token_id"`
101- Token0Price float64 `json:"token0_price"`
102- Token0Holdings float64 `json:"token0_holdings,omitempty"`
103- Token0Rewards float64 `json:"token0_rewards,omitempty"`
104- Token1Price float64 `json:"token1_price"`
105- Token1Holdings float64 `json:"token1_holdings,omitempty"`
106- Token1Rewards float64 `json:"token1_rewards,omitempty"`
105+ TickLower int `json:"tick_lower"`
106+ TickUpper int `json:"tick_upper"`
107+ TokenID string `json:"token_id"`
108+ Token0 * nftTokenDetails `json:"token0,omitempty"`
109+ Token1 * nftTokenDetails `json:"token1,omitempty"`
107110}
108111
109112func runDefiPositions (cmd * cobra.Command , args []string ) error {
@@ -150,7 +153,7 @@ func runDefiPositions(cmd *cobra.Command, args []string) error {
150153 p .Type ,
151154 fmt .Sprintf ("%d" , p .ChainID ),
152155 p .Protocol ,
153- output .FormatUSD (p .USDVal ),
156+ output .FormatUSD (p .ValueUSD ),
154157 positionDetails (p ),
155158 }
156159 }
@@ -165,18 +168,26 @@ func runDefiPositions(cmd *cobra.Command, args []string) error {
165168
166169// positionDetails returns a human-readable summary for a DeFi position,
167170// varying by position type.
171+ // tokenSymbol safely extracts the symbol from a token info pointer.
172+ func tokenSymbol (t * defiTokenInfo ) string {
173+ if t == nil {
174+ return ""
175+ }
176+ return t .Symbol
177+ }
178+
168179func positionDetails (p defiPosition ) string {
169180 switch p .Type {
170181 case "Erc4626" :
171182 parts := []string {}
172- if p . TokenSymbol != "" {
173- parts = append (parts , p . TokenSymbol )
183+ if sym := tokenSymbol ( p . Token ); sym != "" {
184+ parts = append (parts , sym )
174185 }
175- if p . UnderlyingTokenSymbol != "" {
176- parts = append (parts , fmt .Sprintf ("-> %s" , p . UnderlyingTokenSymbol ))
186+ if sym := tokenSymbol ( p . UnderlyingToken ); sym != "" {
187+ parts = append (parts , fmt .Sprintf ("-> %s" , sym ))
177188 }
178- if p .CalculatedBalance != 0 {
179- parts = append (parts , fmt .Sprintf ("bal=%.6g" , p .CalculatedBalance ))
189+ if p .Balance != 0 {
190+ parts = append (parts , fmt .Sprintf ("bal=%.6g" , p .Balance ))
180191 }
181192 return strings .Join (parts , " " )
182193
@@ -185,23 +196,23 @@ func positionDetails(p defiPosition) string {
185196 if p .TokenType != "" {
186197 parts = append (parts , p .TokenType )
187198 }
188- if p . TokenSymbol != "" {
189- parts = append (parts , p . TokenSymbol )
199+ if sym := tokenSymbol ( p . Token ); sym != "" {
200+ parts = append (parts , sym )
190201 }
191- if p .CalculatedBalance != 0 {
192- parts = append (parts , fmt .Sprintf ("bal=%.6g" , p .CalculatedBalance ))
202+ if p .Balance != 0 {
203+ parts = append (parts , fmt .Sprintf ("bal=%.6g" , p .Balance ))
193204 }
194205 return strings .Join (parts , " " )
195206
196207 case "UniswapV2" :
197- pair := formatPair (p . Token0Symbol , p . Token1Symbol )
198- if p .CalculatedBalance != 0 {
199- return fmt .Sprintf ("%s bal=%.6g" , pair , p .CalculatedBalance )
208+ pair := formatPair (tokenSymbol ( p . Token0 ), tokenSymbol ( p . Token1 ) )
209+ if p .Balance != 0 {
210+ return fmt .Sprintf ("%s bal=%.6g" , pair , p .Balance )
200211 }
201212 return pair
202213
203214 case "Nft" , "NftV4" :
204- pair := formatPair (p . Token0Symbol , p . Token1Symbol )
215+ pair := formatPair (tokenSymbol ( p . Token0 ), tokenSymbol ( p . Token1 ) )
205216 nPos := len (p .Positions )
206217 if nPos == 1 {
207218 return fmt .Sprintf ("%s (1 position)" , pair )
@@ -233,7 +244,7 @@ func printAggregations(w io.Writer, agg *defiAggregations) {
233244 return
234245 }
235246
236- fmt .Fprintf (w , "\n Total USD Value: %s\n " , output .FormatUSD (agg .TotalUSDValue ))
247+ fmt .Fprintf (w , "\n Total USD Value: %s\n " , output .FormatUSD (agg .TotalValueUSD ))
237248
238249 if len (agg .TotalByChain ) > 0 {
239250 fmt .Fprintln (w , "Breakdown by chain:" )
0 commit comments