@raac/rpc 2.0.0-beta.20 → 2.0.0-beta.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -101,6 +101,7 @@ const findContract_1 = require("./contracts/findContract");
101
101
  const decodeCall_1 = require("./contracts/decodeCall");
102
102
  const resolveName_1 = require("./contracts/resolveName");
103
103
  const getTokenMetadata_1 = require("./contracts/getTokenMetadata");
104
+ const resolveTokenInfo_1 = require("./contracts/resolveTokenInfo");
104
105
  // House Prices
105
106
  const getLatestPrice_1 = __importDefault(require("./contracts/housePrices/getLatestPrice"));
106
107
  const setOracle_1 = __importDefault(require("./contracts/housePrices/setOracle"));
@@ -558,6 +559,7 @@ class RPCLibrary {
558
559
  resolveName: resolveName_1.resolveName,
559
560
  resolveNames: resolveName_1.resolveNames,
560
561
  getTokenMetadata: getTokenMetadata_1.getTokenMetadata,
562
+ resolveTokenInfo: resolveTokenInfo_1.resolveTokenInfo,
561
563
  feeCollector: {
562
564
  claimRewardFromPool: claimRewardFromPool_1.default,
563
565
  getState: getState_2.default,
@@ -8,21 +8,22 @@ const ethers_1 = require("ethers");
8
8
  const chains_1 = __importDefault(require("../../../configs/chains"));
9
9
  const _helpers_1 = require("../gaugeSystems/_helpers");
10
10
  const amount_1 = require("../../../utils/amount");
11
+ const resolveTokenInfo_1 = require("../../resolveTokenInfo");
11
12
  const _tokens_1 = require("./_tokens");
12
13
  const RAW_BALANCE_OF_ABI = ["function rawBalanceOf(address account) view returns (uint256)"];
13
14
  const tokenState = async (chainId, token, user, spender, provider) => {
14
15
  const contract = new ethers_1.ethers.Contract(token, _tokens_1.ERC20_ABI, provider);
15
- const [balance, allowance] = await Promise.all([
16
+ const [balance, allowance, { decimals, symbol }] = await Promise.all([
16
17
  contract.balanceOf(user),
17
18
  contract.allowance(user, spender),
19
+ (0, resolveTokenInfo_1.resolveTokenInfo)(chainId, token, provider),
18
20
  ]);
19
- const balanceAmount = (0, amount_1.toTokenAmount)(chainId, token, balance);
20
21
  return {
21
22
  address: ethers_1.ethers.getAddress(token),
22
- symbol: balanceAmount.symbol,
23
- decimals: balanceAmount.decimals,
24
- balance: balanceAmount,
25
- allowance: (0, amount_1.toTokenAmount)(chainId, token, allowance),
23
+ symbol,
24
+ decimals,
25
+ balance: (0, amount_1.toAmount)(balance, decimals, symbol),
26
+ allowance: (0, amount_1.toAmount)(allowance, decimals, symbol),
26
27
  spender: ethers_1.ethers.getAddress(spender),
27
28
  };
28
29
  };
@@ -34,7 +35,7 @@ const tokenState = async (chainId, token, user, spender, provider) => {
34
35
  * @param gaugeAddressOrId - Address of the gauge contract or its config ID.
35
36
  * @param user - The wallet.
36
37
  * @param provider - Optional ethers.js Provider instance.
37
- * @returns The wallet's gauge state. Every amount carries its own token's registry decimals and symbol.
38
+ * @returns The wallet's gauge state. Every amount carries its own token's decimals and symbol.
38
39
  */
39
40
  const getUserGaugeState = async (chainId, gaugeAddressOrId, user, provider) => {
40
41
  if (!provider) {
@@ -47,6 +48,6 @@ const getUserGaugeState = async (chainId, gaugeAddressOrId, user, provider) => {
47
48
  tokenState(chainId, stakingToken, user, gauge, provider),
48
49
  underlying ? tokenState(chainId, underlying, user, stakingToken, provider) : Promise.resolve(null),
49
50
  ]);
50
- return { gauge, staked: (0, amount_1.toTokenAmount)(chainId, stakingToken, staked), stakingToken: staking, underlying: wrappable };
51
+ return { gauge, staked: (0, amount_1.toAmount)(staked, staking.decimals, staking.symbol), stakingToken: staking, underlying: wrappable };
51
52
  };
52
53
  exports.getUserGaugeState = getUserGaugeState;
@@ -65,18 +65,14 @@ async function getVaultInfo(chainId, vaultKeyOrAddress, userAddress, provider) {
65
65
  let user = null;
66
66
  if (userAddress) {
67
67
  const baseAssetContract = (0, _helpers_1.getTokenReaderContract)(baseAsset, provider);
68
- const [shareBalance, savingsTokenBalance, baseAssetBalance, redeemRequest, savingsTokenAllowance, baseAssetAllowance] = await Promise.all([
68
+ const [shareBalance, redeemRequest, savingsTokenAllowance, baseAssetAllowance] = await Promise.all([
69
69
  vault.balanceOf(userAddress),
70
- savingsTokenContract.balanceOf(userAddress),
71
- baseAssetContract.balanceOf(userAddress),
72
70
  vault.redeemRequests(userAddress),
73
71
  savingsTokenContract.allowance(userAddress, contractAddress),
74
72
  baseAssetContract.allowance(userAddress, contractAddress),
75
73
  ]);
76
74
  user = {
77
75
  shareBalance: (0, amount_1.toAmount)(shareBalance, Number(vaultDecimals), entry.id),
78
- savingsTokenBalance: (0, amount_1.toTokenAmount)(chainId, "susds", savingsTokenBalance),
79
- baseAssetBalance: (0, amount_1.toTokenAmount)(chainId, "pmusd", baseAssetBalance),
80
76
  redeemRequest: {
81
77
  shares: (0, amount_1.toAmount)(redeemRequest[0], Number(vaultDecimals), entry.id),
82
78
  exitTime: Number(redeemRequest[1]),
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.resolveTokenInfo = void 0;
7
+ const ethers_1 = require("ethers");
8
+ const chains_1 = __importDefault(require("../configs/chains"));
9
+ const amount_1 = require("../utils/amount");
10
+ const rpcError_1 = require("../utils/rpcError");
11
+ const ERC20_METADATA_ABI = ["function symbol() view returns (string)", "function decimals() view returns (uint8)"];
12
+ /** The address Curve pools (and others) list native ETH under. */
13
+ const NATIVE_ETH_PLACEHOLDER = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
14
+ /** Tokens read from the chain, keyed `chainId:address`. Holds the request, so concurrent callers share it. */
15
+ const resolved = new Map();
16
+ /**
17
+ * A token's decimals and symbol for any address: the chain config registry first, then the token itself for an address
18
+ * the registry does not list. An on-chain read happens once per chain and token; a failed read is not remembered, so
19
+ * the next call tries again.
20
+ *
21
+ * Use this wherever a method takes a token address it cannot know in advance. Methods that take an asset key keep
22
+ * using `toTokenAmount`, which reads only the registry.
23
+ *
24
+ * @param chainId The chain id.
25
+ * @param idOrAddress An asset key from the registry, or a token contract address, any casing.
26
+ * @param provider Optional ethers.js Provider instance.
27
+ * @returns The token's decimals, and its symbol when it has one: a token without a readable `symbol()` gets none.
28
+ * @throws If an asset key is not in the registry, or an unlisted token has no readable `decimals()`.
29
+ */
30
+ const resolveTokenInfo = (chainId, idOrAddress, provider) => {
31
+ const known = (0, amount_1.getTokenInfo)(chainId, idOrAddress);
32
+ if (known)
33
+ return Promise.resolve(known);
34
+ if (!ethers_1.ethers.isAddress(idOrAddress)) {
35
+ return Promise.reject(new Error(`Token ${idOrAddress} is not in the asset registry of chain ${chainId}`));
36
+ }
37
+ const address = idOrAddress.toLowerCase();
38
+ if (address === NATIVE_ETH_PLACEHOLDER)
39
+ return Promise.resolve({ decimals: amount_1.WAD_DECIMALS, symbol: "ETH" });
40
+ const key = `${chainId}:${address}`;
41
+ let info = resolved.get(key);
42
+ if (!info) {
43
+ info = readTokenInfo(chainId, address, provider);
44
+ resolved.set(key, info);
45
+ info.catch(() => resolved.delete(key));
46
+ }
47
+ return info;
48
+ };
49
+ exports.resolveTokenInfo = resolveTokenInfo;
50
+ const readTokenInfo = async (chainId, address, provider) => {
51
+ if (!provider) {
52
+ provider = new ethers_1.ethers.JsonRpcProvider(chains_1.default[chainId].rpcs[0]);
53
+ }
54
+ try {
55
+ const token = new ethers_1.ethers.Contract(address, ERC20_METADATA_ABI, provider);
56
+ const [decimals, symbol] = await Promise.all([
57
+ token.decimals(),
58
+ token.symbol().catch(() => undefined),
59
+ ]);
60
+ return symbol === undefined ? { decimals: Number(decimals) } : { decimals: Number(decimals), symbol };
61
+ }
62
+ catch (error) {
63
+ throw (0, rpcError_1.toRpcError)("contracts.resolveTokenInfo", error, { chainId, address });
64
+ }
65
+ };
@@ -6,17 +6,17 @@ const amount_1 = require("../../../utils/amount");
6
6
  const rpcError_1 = require("../../../utils/rpcError");
7
7
  const _helpers_1 = require("./_helpers");
8
8
  const ERC20_READ_ABI = [
9
- "function balanceOf(address) view returns (uint256)",
10
9
  "function allowance(address owner, address spender) view returns (uint256)",
11
10
  ];
12
11
  /**
13
- * Gets a user's position in a Yearn V3 vault, plus their vault asset balance and allowance.
12
+ * Gets a user's position in a Yearn V3 vault, plus their vault asset allowance. Read the wallet's asset balance with
13
+ * `wallet.assets.getBalance`.
14
14
  *
15
15
  * @param chainId - The chain/network to use.
16
16
  * @param vaultAddressOrId - The vault's key in the chain config's `yearn` section (e.g. "spmusd"), or its address.
17
17
  * @param account - Address of the user.
18
18
  * @param provider - Optional ethers.js Provider instance.
19
- * @returns The user's shares, their asset value, balances, allowance and limits, in base units.
19
+ * @returns The user's shares, their asset value, allowance and limits, in base units.
20
20
  */
21
21
  async function getUserInfo(chainId, vaultAddressOrId, account, provider) {
22
22
  const { entry, contract } = (0, _helpers_1.getYearnVaultContract)(chainId, vaultAddressOrId, provider);
@@ -24,9 +24,8 @@ async function getUserInfo(chainId, vaultAddressOrId, account, provider) {
24
24
  const shares = (value) => (0, amount_1.toTokenAmount)(chainId, entry.contract, value);
25
25
  try {
26
26
  const token = new ethers_1.ethers.Contract(await contract.asset(), ERC20_READ_ABI, contract.runner);
27
- const [userShares, assetBalance, assetAllowance, maxDeposit, maxWithdraw, maxRedeem] = await Promise.all([
27
+ const [userShares, assetAllowance, maxDeposit, maxWithdraw, maxRedeem] = await Promise.all([
28
28
  contract.balanceOf(account),
29
- token.balanceOf(account),
30
29
  token.allowance(account, entry.contract),
31
30
  contract.maxDeposit(account),
32
31
  contract.maxWithdraw(account),
@@ -35,7 +34,6 @@ async function getUserInfo(chainId, vaultAddressOrId, account, provider) {
35
34
  return {
36
35
  shares: shares(userShares),
37
36
  assets: asset(await contract.convertToAssets(userShares)),
38
- assetBalance: asset(assetBalance),
39
37
  assetAllowance: asset(assetAllowance),
40
38
  maxDeposit: asset(maxDeposit),
41
39
  maxWithdraw: asset(maxWithdraw),
@@ -7,6 +7,7 @@ exports.inspectCurvePool = void 0;
7
7
  const ethers_1 = require("ethers");
8
8
  const chains_1 = __importDefault(require("../../../configs/chains"));
9
9
  const rpcError_1 = require("../../../utils/rpcError");
10
+ const resolveTokenInfo_1 = require("../../resolveTokenInfo");
10
11
  const _types_1 = require("./_types");
11
12
  /**
12
13
  * The zap's Curve swap calls by pool type (TokenZapLogic.swap), for pools indexed by plain `coins(i)`. The base, A, Y,
@@ -19,7 +20,6 @@ const SWAP_SIGNATURES = [
19
20
  [_types_1.AladdinPoolType.CurveBasePool, "exchange(int128,int128,uint256,uint256)"],
20
21
  ];
21
22
  const MAX_COINS = 8;
22
- const ERC20 = ["function symbol() view returns (string)", "function decimals() view returns (uint8)"];
23
23
  /**
24
24
  * Reads a Curve pool that may not be in the chain config, so a zap route can be built through it: its coins in index
25
25
  * order with their symbols, and which zap pool types it can swap as. The types come from the swap function selectors in
@@ -53,9 +53,8 @@ const inspectCurvePool = async (chainId, pool, provider) => {
53
53
  if (!addresses.length)
54
54
  throw new Error(`${address} has no coins(0); it is not a Curve pool this can read`);
55
55
  const coins = await Promise.all(addresses.map(async (coin) => {
56
- const erc20 = new ethers_1.ethers.Contract(coin, ERC20, provider);
57
- const [symbol, decimals] = await Promise.all([erc20.symbol().catch(() => "?"), erc20.decimals().then(Number).catch(() => 18)]);
58
- return { address: coin, symbol, decimals };
56
+ const { symbol, decimals } = await (0, resolveTokenInfo_1.resolveTokenInfo)(chainId, coin, provider);
57
+ return { address: coin, symbol: symbol ?? "?", decimals };
59
58
  }));
60
59
  const poolTypes = SWAP_SIGNATURES.filter(([, sig]) => code.toLowerCase().includes(ethers_1.ethers.id(sig).slice(2, 10))).map(([type]) => type);
61
60
  return { pool: address, coins, poolTypes };
@@ -8,6 +8,7 @@ const getContractAddress_1 = __importDefault(require("../../contracts/getContrac
8
8
  const artifacts_1 = require("../../utils/artifacts");
9
9
  const amount_1 = require("../../utils/amount");
10
10
  const rpcError_1 = require("../../utils/rpcError");
11
+ const resolveTokenInfo_1 = require("../../contracts/resolveTokenInfo");
11
12
  /**
12
13
  * Walks a public address array (`adapters(i)`) until the index reverts. Any other error (network, RPC) is thrown,
13
14
  * so a flaky node never returns a shortened list as if it were complete.
@@ -30,15 +31,19 @@ async function readList(vault, method) {
30
31
  }
31
32
  return out;
32
33
  }
33
- async function readAdapter(address, provider) {
34
+ async function readAdapter(chainId, address, provider) {
34
35
  const erc20 = new ethers_1.ethers.Contract(address, (0, artifacts_1.getABI)("erc20vaultadapter"), provider);
35
36
  const [type, token] = await Promise.all([erc20.getAssetType(), erc20.getAssetToken()]);
36
37
  const erc721 = type === "ERC721";
37
38
  const adapter = erc721 ? new ethers_1.ethers.Contract(address, (0, artifacts_1.getABI)("erc721vaultadapter"), provider) : erc20;
38
- const asset = new ethers_1.ethers.Contract(token, ["function symbol() view returns (string)", "function decimals() view returns (uint8)"], provider);
39
- const [tokenSymbol, tokenDecimals, value, tokenIds] = await Promise.all([
40
- asset.symbol().catch(() => (erc721 ? "NFTs" : "tokens")),
41
- erc721 ? 0 : asset.decimals().then(Number),
39
+ // An NFT collection has no decimals, so only an ERC20 goes through resolveTokenInfo.
40
+ const assetInfo = erc721
41
+ ? new ethers_1.ethers.Contract(token, ["function symbol() view returns (string)"], provider)
42
+ .symbol()
43
+ .then((symbol) => ({ decimals: 0, symbol }), () => ({ decimals: 0, symbol: "NFTs" }))
44
+ : (0, resolveTokenInfo_1.resolveTokenInfo)(chainId, token, provider).then(({ decimals, symbol }) => ({ decimals, symbol: symbol ?? "tokens" }));
45
+ const [{ symbol: tokenSymbol, decimals: tokenDecimals }, value, tokenIds] = await Promise.all([
46
+ assetInfo,
42
47
  adapter.totalValue().catch(() => null),
43
48
  erc721 ? adapter.getDepositedTokens() : Promise.resolve([]),
44
49
  ]);
@@ -63,7 +68,7 @@ async function getRegisteredAdapters(chainId, provider) {
63
68
  return Promise.all(all.map(async (address) => {
64
69
  const flags = { address, depositable: depositable.includes(address), redeemable: redeemable.includes(address) };
65
70
  try {
66
- return { ...flags, ...(await readAdapter(address, provider)) };
71
+ return { ...flags, ...(await readAdapter(chainId, address, provider)) };
67
72
  }
68
73
  catch (error) {
69
74
  const message = error instanceof Error ? error.message : String(error);
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.getCurveLPPriceUSD = getCurveLPPriceUSD;
7
7
  const ethers_1 = require("ethers");
8
8
  const chains_1 = __importDefault(require("../configs/chains"));
9
- const getTokenMetadata_1 = require("../contracts/getTokenMetadata");
9
+ const resolveTokenInfo_1 = require("../contracts/resolveTokenInfo");
10
10
  const tokenPricesUSD_1 = require("./tokenPricesUSD");
11
11
  const CURVE_POOL_ABI = [
12
12
  "function coins(uint256) view returns (address)",
@@ -31,13 +31,13 @@ async function getCurveLPPriceUSD(chainId, token, provider) {
31
31
  ]);
32
32
  if (supply === 0n)
33
33
  return 0;
34
- const [meta0, meta1] = await Promise.all([
35
- (0, getTokenMetadata_1.getTokenMetadata)(chainId, c0, provider).catch(() => null),
36
- (0, getTokenMetadata_1.getTokenMetadata)(chainId, c1, provider).catch(() => null),
34
+ const [info0, info1] = await Promise.all([
35
+ (0, resolveTokenInfo_1.resolveTokenInfo)(chainId, c0, provider),
36
+ (0, resolveTokenInfo_1.resolveTokenInfo)(chainId, c1, provider),
37
37
  ]);
38
- const p0 = (0, tokenPricesUSD_1.getTokenPriceUSD)(meta0?.symbol);
39
- const p1 = (0, tokenPricesUSD_1.getTokenPriceUSD)(meta1?.symbol);
40
- const tvl = Number(ethers_1.ethers.formatEther(b0)) * p0 + Number(ethers_1.ethers.formatEther(b1)) * p1;
38
+ const p0 = (0, tokenPricesUSD_1.getTokenPriceUSD)(info0.symbol);
39
+ const p1 = (0, tokenPricesUSD_1.getTokenPriceUSD)(info1.symbol);
40
+ const tvl = Number(ethers_1.ethers.formatUnits(b0, info0.decimals)) * p0 + Number(ethers_1.ethers.formatUnits(b1, info1.decimals)) * p1;
41
41
  return tvl / Number(ethers_1.ethers.formatEther(supply));
42
42
  }
43
43
  catch {
@@ -47,6 +47,7 @@ import { findContract } from "./contracts/findContract";
47
47
  import { decodeCall } from "./contracts/decodeCall";
48
48
  import { resolveName, resolveNames } from "./contracts/resolveName";
49
49
  import { getTokenMetadata } from "./contracts/getTokenMetadata";
50
+ import { resolveTokenInfo } from "./contracts/resolveTokenInfo";
50
51
  import getLatestPrice from "./contracts/housePrices/getLatestPrice";
51
52
  import setOracle from "./contracts/housePrices/setOracle";
52
53
  import tick from "./minter/tick";
@@ -441,6 +442,7 @@ declare class RPCLibrary {
441
442
  resolveName: typeof resolveName;
442
443
  resolveNames: typeof resolveNames;
443
444
  getTokenMetadata: typeof getTokenMetadata;
445
+ resolveTokenInfo: typeof resolveTokenInfo;
444
446
  feeCollector: {
445
447
  claimRewardFromPool: typeof claimRewardFromPool;
446
448
  getState: typeof getFeeCollectorState;
@@ -3,9 +3,9 @@ import { ChainId } from "../../../configs/chains";
3
3
  import { TokenAmount } from "../../../utils/amount";
4
4
  export interface UserGaugeTokenState {
5
5
  address: string;
6
- /** From the token registry; undefined when the registry does not list the token. */
6
+ /** From the token registry, or read from the token when the registry does not list it; undefined if it has none. */
7
7
  symbol?: string;
8
- /** From the token registry; 18 when the registry does not list the token. */
8
+ /** From the token registry, or read from the token when the registry does not list it. */
9
9
  decimals: number;
10
10
  /** Wallet balance, in this token's base units. */
11
11
  balance: TokenAmount;
@@ -30,6 +30,6 @@ export interface UserGaugeState {
30
30
  * @param gaugeAddressOrId - Address of the gauge contract or its config ID.
31
31
  * @param user - The wallet.
32
32
  * @param provider - Optional ethers.js Provider instance.
33
- * @returns The wallet's gauge state. Every amount carries its own token's registry decimals and symbol.
33
+ * @returns The wallet's gauge state. Every amount carries its own token's decimals and symbol.
34
34
  */
35
35
  export declare const getUserGaugeState: (chainId: ChainId, gaugeAddressOrId: string, user: string, provider?: Provider) => Promise<UserGaugeState>;
@@ -3,8 +3,6 @@ import { ChainId } from "../../configs/chains";
3
3
  import { TokenAmount, ScaledValue } from "../../utils/amount";
4
4
  export interface PsmVaultUserInfo {
5
5
  shareBalance: TokenAmount;
6
- savingsTokenBalance: TokenAmount;
7
- baseAssetBalance: TokenAmount;
8
6
  redeemRequest: {
9
7
  shares: TokenAmount;
10
8
  exitTime: number;
@@ -0,0 +1,18 @@
1
+ import { Provider } from "ethers";
2
+ import { ChainId } from "../configs/chains";
3
+ import { TokenInfo } from "../utils/amount";
4
+ /**
5
+ * A token's decimals and symbol for any address: the chain config registry first, then the token itself for an address
6
+ * the registry does not list. An on-chain read happens once per chain and token; a failed read is not remembered, so
7
+ * the next call tries again.
8
+ *
9
+ * Use this wherever a method takes a token address it cannot know in advance. Methods that take an asset key keep
10
+ * using `toTokenAmount`, which reads only the registry.
11
+ *
12
+ * @param chainId The chain id.
13
+ * @param idOrAddress An asset key from the registry, or a token contract address, any casing.
14
+ * @param provider Optional ethers.js Provider instance.
15
+ * @returns The token's decimals, and its symbol when it has one: a token without a readable `symbol()` gets none.
16
+ * @throws If an asset key is not in the registry, or an unlisted token has no readable `decimals()`.
17
+ */
18
+ export declare const resolveTokenInfo: (chainId: ChainId, idOrAddress: string, provider?: Provider) => Promise<TokenInfo>;
@@ -6,8 +6,6 @@ export interface YearnVaultUserInfo {
6
6
  shares: TokenAmount;
7
7
  /** What `shares` redeem for now, in the vault asset. */
8
8
  assets: TokenAmount;
9
- /** Wallet balance of the vault asset. */
10
- assetBalance: TokenAmount;
11
9
  /** Vault asset allowance to the vault, which `deposit` and `mint` pull from. */
12
10
  assetAllowance: TokenAmount;
13
11
  /** Most the user can deposit now, in the vault asset. */
@@ -18,12 +16,13 @@ export interface YearnVaultUserInfo {
18
16
  maxRedeem: TokenAmount;
19
17
  }
20
18
  /**
21
- * Gets a user's position in a Yearn V3 vault, plus their vault asset balance and allowance.
19
+ * Gets a user's position in a Yearn V3 vault, plus their vault asset allowance. Read the wallet's asset balance with
20
+ * `wallet.assets.getBalance`.
22
21
  *
23
22
  * @param chainId - The chain/network to use.
24
23
  * @param vaultAddressOrId - The vault's key in the chain config's `yearn` section (e.g. "spmusd"), or its address.
25
24
  * @param account - Address of the user.
26
25
  * @param provider - Optional ethers.js Provider instance.
27
- * @returns The user's shares, their asset value, balances, allowance and limits, in base units.
26
+ * @returns The user's shares, their asset value, allowance and limits, in base units.
28
27
  */
29
28
  export declare function getUserInfo(chainId: ChainId, vaultAddressOrId: string, account: string, provider?: Provider): Promise<YearnVaultUserInfo>;
@@ -3,13 +3,14 @@ import { AssetType } from "../../utils/artifacts";
3
3
  import { ChainId } from "../../configs/chains/index";
4
4
  import { TokenAmount } from "../../utils/amount";
5
5
  /**
6
- * Reads a wallet's balance of a configured asset or native ETH.
6
+ * Reads a wallet's balance of a configured asset, any ERC20 by address, or native ETH.
7
7
  * @param chainId The chain id.
8
- * @param assetId The asset key in the chain config, or `"eth"` for native ETH.
8
+ * @param assetIdOrAddress The asset key in the chain config, an ERC20 contract address, or `"eth"` for native ETH.
9
9
  * @param address The wallet address.
10
10
  * @param provider The provider to read with.
11
11
  * @returns The balance in base units with decimals and symbol from the token registry (`"ETH"` and 18 for native ETH).
12
- * @throws If the asset is not deployed on the chain, or the read fails.
12
+ * A token address the registry does not list takes its decimals and symbol from the token, read once per chain.
13
+ * @throws If the asset key is not deployed on the chain, or the read fails.
13
14
  */
14
- declare function getBalance(chainId: ChainId, assetId: AssetType, address: string, provider: Provider): Promise<TokenAmount>;
15
+ declare function getBalance(chainId: ChainId, assetIdOrAddress: AssetType, address: string, provider: Provider): Promise<TokenAmount>;
15
16
  export default getBalance;
@@ -3,30 +3,41 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const ethers_1 = require("ethers");
4
4
  const contracts_1 = require("../../utils/contracts");
5
5
  const amount_1 = require("../../utils/amount");
6
+ const resolveTokenInfo_1 = require("../../contracts/resolveTokenInfo");
6
7
  const _helpers_1 = require("../../assets/_helpers");
7
8
  const rpcError_1 = require("../../utils/rpcError");
8
9
  /**
9
- * Reads a wallet's balance of a configured asset or native ETH.
10
+ * Reads a wallet's balance of a configured asset, any ERC20 by address, or native ETH.
10
11
  * @param chainId The chain id.
11
- * @param assetId The asset key in the chain config, or `"eth"` for native ETH.
12
+ * @param assetIdOrAddress The asset key in the chain config, an ERC20 contract address, or `"eth"` for native ETH.
12
13
  * @param address The wallet address.
13
14
  * @param provider The provider to read with.
14
15
  * @returns The balance in base units with decimals and symbol from the token registry (`"ETH"` and 18 for native ETH).
15
- * @throws If the asset is not deployed on the chain, or the read fails.
16
+ * A token address the registry does not list takes its decimals and symbol from the token, read once per chain.
17
+ * @throws If the asset key is not deployed on the chain, or the read fails.
16
18
  */
17
- async function getBalance(chainId, assetId, address, provider) {
19
+ async function getBalance(chainId, assetIdOrAddress, address, provider) {
18
20
  const BALANCE_OF_ABI = ["function balanceOf(address) view returns (uint256)"];
19
21
  let contractAddress;
20
22
  try {
21
- if (assetId === "eth") {
23
+ if (assetIdOrAddress === "eth") {
22
24
  const balance = await provider.getBalance(address);
23
25
  return (0, amount_1.toAmount)(balance, amount_1.WAD_DECIMALS, "ETH");
24
26
  }
25
- (0, _helpers_1.assertAssetDeployed)(chainId, assetId);
26
- contractAddress = (0, contracts_1.getContractAddress)(chainId, assetId);
27
+ if (ethers_1.ethers.isAddress(assetIdOrAddress)) {
28
+ contractAddress = assetIdOrAddress;
29
+ const contract = new ethers_1.ethers.Contract(contractAddress, BALANCE_OF_ABI, provider);
30
+ const [balance, info] = await Promise.all([
31
+ contract.balanceOf(address),
32
+ (0, resolveTokenInfo_1.resolveTokenInfo)(chainId, contractAddress, provider),
33
+ ]);
34
+ return (0, amount_1.toAmount)(balance, info.decimals, info.symbol);
35
+ }
36
+ (0, _helpers_1.assertAssetDeployed)(chainId, assetIdOrAddress);
37
+ contractAddress = (0, contracts_1.getContractAddress)(chainId, assetIdOrAddress);
27
38
  const contract = new ethers_1.ethers.Contract(contractAddress, BALANCE_OF_ABI, provider);
28
39
  const balance = await contract.balanceOf(address);
29
- return (0, amount_1.toTokenAmount)(chainId, assetId, balance);
40
+ return (0, amount_1.toTokenAmount)(chainId, assetIdOrAddress, balance);
30
41
  }
31
42
  catch (error) {
32
43
  throw (0, rpcError_1.toRpcError)("wallet.getBalance", error, { chainId, address: contractAddress });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raac/rpc",
3
- "version": "2.0.0-beta.20",
3
+ "version": "2.0.0-beta.21",
4
4
  "description": "RPC Library for RAAC",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/types/index.d.ts",