Skip to content

Commit

Permalink
forbid um ibc out to celestia (#1629)
Browse files Browse the repository at this point in the history
* forbid um ibc out to celestia

* changeset

* filter in UI

* filter asset in component instead

* variable name

* equality order

* revert unused getter

* refactor filter w/ selector

* Adjust placeholder w/ selector

---------

Co-authored-by: turbocrime <[email protected]>
Co-authored-by: Gabe Rodriguez <[email protected]>
  • Loading branch information
3 people authored Aug 6, 2024
1 parent 33f70a1 commit b524a03
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 77 deletions.
5 changes: 5 additions & 0 deletions .changeset/polite-otters-cheer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'minifront': patch
---

forbid um ibc out to celestia
27 changes: 15 additions & 12 deletions apps/minifront/src/components/ibc/ibc-out/ibc-out-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,28 @@ import { Input } from '@repo/ui/components/ui/input';
import { ChainSelector } from './chain-selector';
import { useStore } from '../../../state';
import {
filterBalancesPerChain,
filteredIbcBalancesSelector,
ibcOutSelector,
ibcPlaceholderSelector,
ibcValidationErrors,
} from '../../../state/ibc-out';
import InputToken from '../../shared/input-token';
import { InputBlock } from '../../shared/input-block';
import { LockOpen2Icon } from '@radix-ui/react-icons';
import { useAssets, useBalancesResponses, useStakingTokenMetadata } from '../../../state/shared';

const useFilteredBalances = () => {
// Kick off requests
useStakingTokenMetadata();
useAssets();
useBalancesResponses();
// ========================
return useStore(filteredIbcBalancesSelector);
};

export const IbcOutForm = () => {
const stakingTokenMetadata = useStakingTokenMetadata();
const assets = useAssets();
const balances = useBalancesResponses();
const filteredBalances = useFilteredBalances();

const {
sendIbcWithdraw,
destinationChainAddress,
Expand All @@ -24,15 +33,9 @@ export const IbcOutForm = () => {
setAmount,
selection,
setSelection,
chain,
} = useStore(ibcOutSelector);
const filteredBalances = filterBalancesPerChain(
balances.data ?? [],
chain,
assets.data ?? [],
stakingTokenMetadata.data,
);
const validationErrors = useStore(ibcValidationErrors);
const placeholder = useStore(ibcPlaceholderSelector);

return (
<form
Expand All @@ -45,7 +48,7 @@ export const IbcOutForm = () => {
<ChainSelector />
<InputToken
label='Amount to send'
placeholder='Enter an amount'
placeholder={placeholder}
className='mb-1'
selection={selection}
setSelection={setSelection}
Expand Down
88 changes: 38 additions & 50 deletions apps/minifront/src/state/ibc-out.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AllSlices, Middleware, SliceCreator, useStore } from '.';
import { AllSlices, SliceCreator, useStore } from '.';
import {
BalancesResponse,
TransactionPlannerRequest,
Expand All @@ -23,7 +23,7 @@ import { Chain } from '@penumbra-labs/registry';
import { Metadata } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/asset/v1/asset_pb.js';
import { Channel } from '@buf/cosmos_ibc.bufbuild_es/ibc/core/channel/v1/channel_pb.js';
import { BLOCKS_PER_HOUR } from './constants';
import { ZQueryState, createZQuery } from '@penumbra-zone/zquery';
import { createZQuery, ZQueryState } from '@penumbra-zone/zquery';
import { getChains } from '../fetchers/registry';

export const { chains, useChains } = createZQuery({
Expand Down Expand Up @@ -75,6 +75,12 @@ export const createIbcOutSlice = (): SliceCreator<IbcOutSlice> => (set, get) =>
set(state => {
state.ibcOut.chain = chain;
});

// After new chain is selected, the asset selection should also be updated separately
const initialSelection = filteredIbcBalancesSelector(get())[0];
set(state => {
state.ibcOut.selection = initialSelection;
});
},
setDestinationChainAddress: addr => {
set(state => {
Expand Down Expand Up @@ -248,6 +254,9 @@ const unknownAddrIsValid = (chain: Chain | undefined, address: string): boolean
return !!words && prefix === chain.addressPrefix;
};

// These chains do not allow IBC-in transfers unless the token is native to the chain
export const NATIVE_TRANSFERS_ONLY_CHAIN_IDS = ['celestia'];

/**
* Filters the given IBC loader response balances by checking if any of the assets
* in the balance view match the staking token's asset ID or are of the same ibc channel.
Expand All @@ -272,58 +281,37 @@ export const filterBalancesPerChain = (
})
.map(m => m.penumbraAssetId!);

const assetIdsToCheck = [penumbraAssetId, ...assetsWithMatchingChannel];
const assetIdsToCheck = [...assetsWithMatchingChannel];

if (
chain?.chainId &&
penumbraAssetId &&
!NATIVE_TRANSFERS_ONLY_CHAIN_IDS.includes(chain.chainId)
) {
assetIdsToCheck.push(penumbraAssetId);
}

return allBalances.filter(({ balanceView }) => {
return assetIdsToCheck.some(assetId => assetId?.equals(getAssetIdFromValueView(balanceView)));
return assetIdsToCheck.some(assetId => assetId.equals(getAssetIdFromValueView(balanceView)));
});
};

export const ibcOutMiddleware: Middleware = f => (set, get, store) => {
const modifiedSetter: typeof set = (...args) => {
set(...args);

const initialChain = get().ibcOut.chains.data?.[0];
const shouldSetInitialChain = !!initialChain && !get().ibcOut.chain;
if (shouldSetInitialChain) {
set(state => ({
...state,
ibcOut: {
...state.ibcOut,
chain: initialChain,
},
}));
}

const assets = get().shared.assets.data;
const balancesResponses = get().shared.balancesResponses.data;
const stakingTokenMetadata = get().shared.stakingTokenMetadata.data;
const shouldSetInitialSelection =
initialChain &&
assets?.length &&
balancesResponses?.length &&
stakingTokenMetadata &&
!get().ibcOut.selection;

if (shouldSetInitialSelection) {
const initialSelection = filterBalancesPerChain(
balancesResponses,
initialChain,
assets,
stakingTokenMetadata,
)[0];

set(state => ({
...state,
ibcOut: {
...state.ibcOut,
selection: initialSelection,
},
}));
}
};

store.setState = modifiedSetter;
export const filteredIbcBalancesSelector = (state: AllSlices): BalancesResponse[] => {
return filterBalancesPerChain(
state.shared.balancesResponses.data ?? [],
state.ibcOut.chain,
state.shared.assets.data ?? [],
state.shared.stakingTokenMetadata.data,
);
};

return f(modifiedSetter, get, store);
export const ibcPlaceholderSelector = (state: AllSlices): string => {
const filteredBalances = filteredIbcBalancesSelector(state);
if (!state.ibcOut.chain) {
return 'Select a chain';
}
if (filteredBalances.length === 0) {
return 'No balances to transfer';
}
return 'Enter an amount';
};
28 changes: 13 additions & 15 deletions apps/minifront/src/state/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { create, StateCreator } from 'zustand';
import { enableMapSet } from 'immer';
import { immer } from 'zustand/middleware/immer';
import { createSwapSlice, SwapSlice } from './swap';
import { createIbcOutSlice, ibcOutMiddleware, IbcOutSlice } from './ibc-out';
import { createIbcOutSlice, IbcOutSlice } from './ibc-out';
import { createSendSlice, sendSelectionMiddleware, SendSlice } from './send';
import { createStakingSlice, StakingSlice } from './staking';
import { createStatusSlice, StatusSlice } from './status';
Expand Down Expand Up @@ -51,20 +51,18 @@ export const initializeStore = () => {
// middleware call. Thus, all other middlewares can't use immer's syntax in
// their setters.
return immer(
ibcOutMiddleware(
sendSelectionMiddleware(
swapBalancesMiddleware((setState, getState: () => AllSlices, store) => ({
ibcIn: createIbcInSlice()(setState, getState, store),
ibcOut: createIbcOutSlice()(setState, getState, store),
send: createSendSlice()(setState, getState, store),
shared: createSharedSlice()(setState, getState, store),
staking: createStakingSlice()(setState, getState, store),
status: createStatusSlice()(setState, getState, store),
swap: createSwapSlice()(setState, getState, store),
transactions: createTransactionsSlice()(setState, getState, store),
unclaimedSwaps: createUnclaimedSwapsSlice()(setState, getState, store),
})),
),
sendSelectionMiddleware(
swapBalancesMiddleware((setState, getState: () => AllSlices, store) => ({
ibcIn: createIbcInSlice()(setState, getState, store),
ibcOut: createIbcOutSlice()(setState, getState, store),
send: createSendSlice()(setState, getState, store),
shared: createSharedSlice()(setState, getState, store),
staking: createStakingSlice()(setState, getState, store),
status: createStatusSlice()(setState, getState, store),
swap: createSwapSlice()(setState, getState, store),
transactions: createTransactionsSlice()(setState, getState, store),
unclaimedSwaps: createUnclaimedSwapsSlice()(setState, getState, store),
})),
),
);
};
Expand Down

0 comments on commit b524a03

Please sign in to comment.