|
| 1 | +import { |
| 2 | + type ChainId, |
| 3 | + type EvmAddress, |
| 4 | + InvariantError, |
| 5 | + invariant, |
| 6 | + ok, |
| 7 | + type PercentNumber, |
| 8 | + ResultAsync, |
| 9 | + type UnexpectedError, |
| 10 | + type UserBalance, |
| 11 | + type UserBalancesRequest, |
| 12 | +} from '@aave/client'; |
| 13 | +import { userBalances } from '@aave/client/actions'; |
| 14 | + |
| 15 | +import * as common from '../../common.js'; |
| 16 | + |
| 17 | +function formatPercent(value: PercentNumber | null): string { |
| 18 | + return value ? `${value.normalized.toFixed(4)}%` : 'N/A'; |
| 19 | +} |
| 20 | + |
| 21 | +export default class UserBalanceCommand extends common.V4Command { |
| 22 | + static override description = |
| 23 | + 'List user token balances that can be used in Aave v4'; |
| 24 | + |
| 25 | + static override flags = { |
| 26 | + address: common.address({ |
| 27 | + required: false, |
| 28 | + description: 'User address (defaults to PRIVATE_KEY wallet address)', |
| 29 | + }), |
| 30 | + chain_id: common.chain({ |
| 31 | + required: true, |
| 32 | + description: 'Chain ID to query balances from', |
| 33 | + }), |
| 34 | + }; |
| 35 | + |
| 36 | + override headers = [ |
| 37 | + { value: 'Asset' }, |
| 38 | + { value: 'Symbol' }, |
| 39 | + { value: 'Balance' }, |
| 40 | + { value: 'Exchange' }, |
| 41 | + { value: 'Best Supply APY' }, |
| 42 | + { value: 'Best Borrow APY' }, |
| 43 | + ]; |
| 44 | + |
| 45 | + private getBalancesRequest(): ResultAsync< |
| 46 | + UserBalancesRequest, |
| 47 | + InvariantError | UnexpectedError |
| 48 | + > { |
| 49 | + return ResultAsync.fromPromise( |
| 50 | + this.parse(UserBalanceCommand), |
| 51 | + (error) => new InvariantError(String(error)), |
| 52 | + ).andThen(({ flags }) => { |
| 53 | + const user = common.userAddressFromFlagOrEnv( |
| 54 | + flags.address as EvmAddress | undefined, |
| 55 | + ); |
| 56 | + const chainId = flags.chain_id as ChainId; |
| 57 | + |
| 58 | + invariant(chainId, 'You must provide a chain ID'); |
| 59 | + |
| 60 | + return ok({ |
| 61 | + user, |
| 62 | + filter: { |
| 63 | + chains: { |
| 64 | + chainIds: [chainId], |
| 65 | + }, |
| 66 | + }, |
| 67 | + }); |
| 68 | + }); |
| 69 | + } |
| 70 | + |
| 71 | + async run(): Promise<UserBalance[] | InvariantError | UnexpectedError> { |
| 72 | + const result = await this.getBalancesRequest() |
| 73 | + .andThen((request) => userBalances(this.client, request)) |
| 74 | + .andThen((balances) => { |
| 75 | + if (balances.length === 0) { |
| 76 | + this.log('No balances found for this user.'); |
| 77 | + return ok(balances); |
| 78 | + } |
| 79 | + |
| 80 | + this.display( |
| 81 | + balances.map((item) => [ |
| 82 | + item.info.name, |
| 83 | + item.info.symbol, |
| 84 | + item.totalAmount.value.toFixed(6), |
| 85 | + `${item.exchange.symbol}${item.exchange.value.toFixed(2)}`, |
| 86 | + formatPercent(item.highestSupplyApy), |
| 87 | + formatPercent(item.highestBorrowApy), |
| 88 | + ]), |
| 89 | + ); |
| 90 | + |
| 91 | + return ok(balances); |
| 92 | + }); |
| 93 | + |
| 94 | + if (result.isErr()) { |
| 95 | + this.error(result.error); |
| 96 | + } |
| 97 | + |
| 98 | + return result.value; |
| 99 | + } |
| 100 | +} |
0 commit comments