Skip to content

Commit

Permalink
feat: treat all amounts as base 0 when quering (#1345)
Browse files Browse the repository at this point in the history
* feat: handle nam as denominated amount

* feat: toml file instead of env

* fix: unit tests

* chore: cleanup jest config

* feat: min denom

* chore: remove old namadaAsset.ts

* chore: dry some calls

* feat: enable localnet config in config.toml
  • Loading branch information
mateuszjasiuk authored Dec 2, 2024
1 parent 4096d39 commit 78f243f
Show file tree
Hide file tree
Showing 23 changed files with 225 additions and 73 deletions.
1 change: 1 addition & 0 deletions apps/namadillo/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
/test-results/
/playwright-report/
/playwright/.cache/
/public/localnet-config.toml
2 changes: 1 addition & 1 deletion apps/namadillo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"@chain-registry/client": "^1.53.5",
"@cosmjs/encoding": "^0.32.3",
"@keplr-wallet/types": "^0.12.136",
"@namada/indexer-client": "0.0.30",
"@namada/indexer-client": "0.0.31",
"@tailwindcss/container-queries": "^0.1.1",
"@tanstack/query-core": "^5.40.0",
"@tanstack/react-query": "^5.40.0",
Expand Down
1 change: 1 addition & 0 deletions apps/namadillo/public/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
#indexer_url = ""
#rpc_url = ""
#masp_indexer_url = ""
#localnet_enabled = false
4 changes: 2 additions & 2 deletions apps/namadillo/src/App/Ibc/IbcWithdraw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ export const IbcWithdraw: React.FC = () => {

const transactionFee = mapUndefined(
({ gasLimit, gasPrice }) => ({
originalAddress: namadaAsset.address,
asset: namadaAsset,
originalAddress: namadaAsset().address,
asset: namadaAsset(),
amount: gasPrice.multipliedBy(gasLimit),
}),
gasConfig
Expand Down
3 changes: 3 additions & 0 deletions apps/namadillo/src/App/Setup/ChainLoader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { AtomErrorBoundary } from "App/Common/AtomErrorBoundary";
import { ErrorBox } from "App/Common/ErrorBox";
import { routes } from "App/routes";
import { chainAtom } from "atoms/chain";
import { localnetConfigAtom } from "atoms/integrations";
import { useAtomValue } from "jotai";
import { ReactNode } from "react";
import { useLocation, useNavigate } from "react-router-dom";
Expand Down Expand Up @@ -31,6 +32,8 @@ export const ChainLoader = ({
children: ReactNode;
}): JSX.Element => {
const chain = useAtomValue(chainAtom);
useAtomValue(localnetConfigAtom);

const errorContainerProps = {
className: "bg-black max-w-full rounded-sm w-full text-white min-h-full",
};
Expand Down
2 changes: 1 addition & 1 deletion apps/namadillo/src/App/Transfer/AssetImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const AssetImage = ({
<img src={image} alt={altText} className="w-full" />
{isShielded !== undefined && (
<span className="absolute -bottom-1 -right-1 w-4 aspect-square">
<AssetImage asset={namadaAsset} />
<AssetImage asset={namadaAsset()} />
</span>
)}
</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ describe("Component: TransferDestination", () => {
<TransferDestination
transactionFee={{
amount: fee,
asset: namadaAsset,
originalAddress: namadaAsset.address,
asset: namadaAsset(),
originalAddress: namadaAsset().address,
}}
/>
);
Expand Down
11 changes: 3 additions & 8 deletions apps/namadillo/src/atoms/accounts/atoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { queryDependentFn } from "atoms/utils";
import BigNumber from "bignumber.js";
import { NamadaKeychain } from "hooks/useNamadaKeychain";
import { atomWithMutation, atomWithQuery } from "jotai-tanstack-query";
import { chainConfigByName } from "registry";
import {
fetchAccountBalance,
fetchAccounts,
Expand Down Expand Up @@ -89,7 +88,6 @@ export const accountBalanceAtom = atomWithQuery<BigNumber>((get) => {
const tokenAddress = get(nativeTokenAddressAtom);
const enablePolling = get(shouldUpdateBalanceAtom);
const api = get(indexerApiAtom);
const chainConfig = chainConfigByName("namada");

return {
// TODO: subscribe to indexer events when it's done
Expand All @@ -99,10 +97,7 @@ export const accountBalanceAtom = atomWithQuery<BigNumber>((get) => {
return await fetchNamAccountBalance(
api,
defaultAccount.data,
tokenAddress.data!,
// As this is a nam balance specific atom, we can safely assume that the
// first currency is the native token
chainConfig.currencies[0].coinDecimals
tokenAddress.data!
);
}, [tokenAddress, defaultAccount]),
};
Expand All @@ -111,7 +106,7 @@ export const accountBalanceAtom = atomWithQuery<BigNumber>((get) => {
// TODO combine the `accountBalanceAtom` with the `transparentBalanceAtom`
// Then execute only once the `fetchAccountBalance`, deleting the `fetchNamAccountBalance`
export const transparentBalanceAtom = atomWithQuery<
{ address: string; amount: BigNumber }[]
{ address: string; minDenomAmount: BigNumber }[]
>((get) => {
const enablePolling = get(shouldUpdateBalanceAtom);
const api = get(indexerApiAtom);
Expand All @@ -124,7 +119,7 @@ export const transparentBalanceAtom = atomWithQuery<
const response = await fetchAccountBalance(api, defaultAccountQuery.data);
return response.map((item) => ({
address: item.tokenAddress,
amount: new BigNumber(item.balance),
minDenomAmount: BigNumber(item.minDenomAmount),
}));
}, [defaultAccountQuery]),
};
Expand Down
13 changes: 5 additions & 8 deletions apps/namadillo/src/atoms/accounts/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Balance, DefaultApi } from "@namada/indexer-client";
import { Account } from "@namada/types";
import BigNumber from "bignumber.js";
import { NamadaKeychain } from "hooks/useNamadaKeychain";
import { namadaAsset, toDisplayAmount } from "utils";

export const fetchAccounts = async (): Promise<readonly Account[]> => {
const namada = await new NamadaKeychain().get();
Expand All @@ -17,26 +18,22 @@ export const fetchDefaultAccount = async (): Promise<Account | undefined> => {
export const fetchNamAccountBalance = async (
api: DefaultApi,
account: Account | undefined,
tokenAddress: string,
decimals: number
tokenAddress: string
): Promise<BigNumber> => {
if (!account) return BigNumber(0);
const balancesResponse = await api.apiV1AccountAddressGet(account.address);

const balance = balancesResponse.data
// TODO: add filter to the api call
.filter(({ tokenAddress: ta }) => ta === tokenAddress)
.map(({ tokenAddress, balance }) => {
.map(({ tokenAddress, minDenomAmount }) => {
return {
token: tokenAddress,
amount: balance,
amount: toDisplayAmount(namadaAsset(), new BigNumber(minDenomAmount)),
};
})
.at(0);

return balance ?
BigNumber(balance.amount).shiftedBy(-decimals)
: BigNumber(0);
return balance ? BigNumber(balance.amount) : BigNumber(0);
};

export const fetchAccountBalance = async (
Expand Down
8 changes: 2 additions & 6 deletions apps/namadillo/src/atoms/balance/atoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export const shieldedSyncAtom = atomWithQuery<ShieldedSyncEmitter | null>(
);

export const shieldedBalanceAtom = atomWithQuery<
{ address: string; amount: BigNumber }[]
{ address: string; minDenomAmount: BigNumber }[]
>((get) => {
const enablePolling = get(shouldUpdateBalanceAtom);
const viewingKeysQuery = get(viewingKeysAtom);
Expand Down Expand Up @@ -130,11 +130,7 @@ export const shieldedBalanceAtom = atomWithQuery<
);
const shieldedBalance = response.map(([address, amount]) => ({
address,
amount:
// Sdk returns the nam amount as `nam` instead of `namnam`
namTokenAddressQuery.data === address ?
new BigNumber(amount).shiftedBy(6)
: new BigNumber(amount),
minDenomAmount: BigNumber(amount),
}));
return shieldedBalance;
}, [viewingKeysQuery, tokenAddressesQuery, namTokenAddressQuery]),
Expand Down
6 changes: 3 additions & 3 deletions apps/namadillo/src/atoms/balance/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,13 @@ const tnamAddressToDenomTrace = (
};

export const mapNamadaAddressesToAssets = async (
balances: { address: string; amount: BigNumber }[],
balances: { address: string; minDenomAmount: BigNumber }[],
tokenAddresses: (NativeToken | IbcToken)[],
chainId: string
): Promise<AddressWithAssetAndAmountMap> => {
const coins = balances.map(({ address, amount }) => ({
const coins = balances.map(({ address, minDenomAmount }) => ({
denom: address,
amount: amount.toString(), // TODO: don't convert back to string
minDenomAmount: minDenomAmount.toString(), // TODO: don't convert back to string
}));

return await mapCoinsToAssets(coins, chainId, (tnamAddress) =>
Expand Down
6 changes: 5 additions & 1 deletion apps/namadillo/src/atoms/fees/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import BigNumber from "bignumber.js";
import invariant from "invariant";
import { GasTable } from "types";
import { txKinds } from "types/txKind";
import { namadaAsset, toDisplayAmount } from "utils";
import { txKindToIndexer } from "./functions";

export const fetchGasLimit = async (api: DefaultApi): Promise<GasTable> => {
Expand Down Expand Up @@ -33,7 +34,10 @@ export const fetchMinimumGasPrice = async (
({ token }) => token === nativeToken
);
invariant(!!nativeTokenCost, "Error querying minimum gas price");
const asBigNumber = new BigNumber(nativeTokenCost.amount);
const asBigNumber = toDisplayAmount(
namadaAsset(),
BigNumber(nativeTokenCost.minDenomAmount)
);
invariant(
!asBigNumber.isNaN(),
"Error converting minimum gas price to BigNumber"
Expand Down
29 changes: 28 additions & 1 deletion apps/namadillo/src/atoms/integrations/atoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { AssetList, Chain } from "@chain-registry/types";
import { ExtensionKey, IbcTransferProps } from "@namada/types";
import { defaultAccountAtom } from "atoms/accounts";
import { chainAtom, chainParametersAtom } from "atoms/chain";
import { settingsAtom } from "atoms/settings";
import { defaultServerConfigAtom, settingsAtom } from "atoms/settings";
import { queryDependentFn } from "atoms/utils";
import BigNumber from "bignumber.js";
import { atom } from "jotai";
Expand All @@ -20,6 +20,7 @@ import {
TransferTransactionData,
} from "types";
import {
addLocalnetToRegistry,
createIbcTx,
getIbcChannels,
getKnownChains,
Expand All @@ -28,6 +29,7 @@ import {
mapCoinsToAssets,
} from "./functions";
import {
fetchLocalnetTomlConfig,
IbcTransferParams,
queryAndStoreRpc,
queryAssetBalances,
Expand Down Expand Up @@ -198,3 +200,28 @@ export const createIbcTxAtom = atomWithMutation((get) => {
},
};
});

export const localnetConfigAtom = atomWithQuery((get) => {
const config = get(defaultServerConfigAtom);

return {
queryKey: ["localnet-config", config],
staleTime: Infinity,
retry: false,

...queryDependentFn(async () => {
try {
const localnetConfig = await fetchLocalnetTomlConfig();
addLocalnetToRegistry(localnetConfig);

return {
chainId: localnetConfig.chain_id,
tokenAddress: localnetConfig.token_address,
};
} catch (_) {
// If file not found just ignore
return null;
}
}, [Boolean(config.data?.localnet_enabled)]),
};
});
74 changes: 69 additions & 5 deletions apps/namadillo/src/atoms/integrations/functions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Asset, Chain } from "@chain-registry/types";
import { Coin } from "@cosmjs/launchpad";
import { Asset, AssetList, Chain, IBCInfo } from "@chain-registry/types";
import { QueryClient, setupIbcExtension } from "@cosmjs/stargate";
import { Tendermint34Client } from "@cosmjs/tendermint-rpc";
import { Account, IbcTransferProps } from "@namada/types";
Expand All @@ -24,7 +23,9 @@ import {
AddressWithAssetAndAmountMap,
ChainRegistryEntry,
ChainSettings,
Coin,
GasConfig,
LocalnetToml,
} from "types";
import { toBaseAmount, toDisplayAmount } from "utils";
import { getSdkInstance } from "utils/sdk";
Expand Down Expand Up @@ -230,7 +231,7 @@ export const mapCoinsToAssets = async (

const results = await Promise.allSettled(
coins.map(async (coin: Coin): Promise<AddressWithAssetAndAmount> => {
const { amount, denom } = coin;
const { minDenomAmount, denom } = coin;

const asset =
typeof chainName === "undefined" || typeof assets === "undefined" ?
Expand All @@ -243,9 +244,9 @@ export const mapCoinsToAssets = async (
)) ||
unknownAsset(denom);

const baseBalance = BigNumber(amount);
const baseBalance = BigNumber(minDenomAmount);
if (baseBalance.isNaN()) {
throw new Error(`Balance is invalid, got ${amount}`);
throw new Error(`Balance is invalid, got ${minDenomAmount}`);
}
// We always represent amounts in their display denom, so convert here
const displayBalance = toDisplayAmount(asset, baseBalance);
Expand Down Expand Up @@ -370,3 +371,66 @@ export const createIbcTx = async (
);
return transactionPair;
};

export const namadaLocal = (chainId: string): Chain => ({
...internalDevnetChain,
chain_name: "localnet",
chain_id: chainId,
});

export const namadaLocalAsset = (tokenAddress: string): AssetList => ({
...internalDevnetAssets,
chain_name: "localnet",
assets: internalDevnetAssets.assets.map((asset) =>
asset.symbol === "NAM" ?
{
...asset,
address: tokenAddress,
}
: asset
),
});

export const namadaLocalRelayer = (
chain1Channel: string,
chain2Channel: string
): IBCInfo => ({
...internalDevnetCosmosTestnetIbc,
chain_1: {
...internalDevnetCosmosTestnetIbc.chain_1,
chain_name: "localnet",
},
chain_2: {
...internalDevnetCosmosTestnetIbc.chain_2,
chain_name: "cosmosicsprovidertestnet",
},
channels: [
{
...internalDevnetCosmosTestnetIbc.channels[0],
chain_1: {
...internalDevnetCosmosTestnetIbc.channels[0].chain_1,
channel_id: chain1Channel,
},
chain_2: {
...internalDevnetCosmosTestnetIbc.channels[0].chain_2,
channel_id: chain2Channel,
},
},
],
});

export const addLocalnetToRegistry = (config: LocalnetToml): void => {
const { chain_id, token_address, chain_1_channel, chain_2_channel } = config;

const localChain: ChainRegistryEntry = {
chain: namadaLocal(chain_id),
assets: namadaLocalAsset(token_address),
ibc: [namadaLocalRelayer(chain_1_channel, chain_2_channel)],
};

cosmosRegistry.chains.push(localChain.chain);
cosmosRegistry.assets.push(localChain.assets);
cosmosRegistry.ibc.push(...localChain.ibc!);

mainnetChains.push(localChain);
};
Loading

1 comment on commit 78f243f

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.