-
Notifications
You must be signed in to change notification settings - Fork 64
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1256 from madfish-solutions/TW-1626-deploy-add-to…
…ken-add-network-confirmations TW-1626: Add token / network from dApp confirmations
- Loading branch information
Showing
42 changed files
with
1,071 additions
and
324 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import React, { memo, useCallback, useEffect, useMemo } from 'react'; | ||
|
||
import { CaptionAlert } from 'app/atoms'; | ||
import { HashChip } from 'app/atoms/HashChip'; | ||
import { EvmNetworkLogo } from 'app/atoms/NetworkLogo'; | ||
import Spinner from 'app/atoms/Spinner/Spinner'; | ||
import { EvmAssetIconWithNetwork } from 'app/templates/AssetIcon'; | ||
import { fetchEvmTokenMetadataFromChain } from 'lib/evm/on-chain/metadata'; | ||
import { EvmAssetStandard } from 'lib/evm/types'; | ||
import { T } from 'lib/i18n'; | ||
import { EvmTokenMetadata } from 'lib/metadata/types'; | ||
import { useTypedSWR } from 'lib/swr'; | ||
import { EvmAssetToAddMetadata } from 'lib/temple/types'; | ||
import { useEvmChainByChainId } from 'temple/front/chains'; | ||
|
||
import { useAddAsset } from './context'; | ||
|
||
interface Props { | ||
metadata: EvmAssetToAddMetadata; | ||
} | ||
|
||
export const AddAssetView = memo<Props>(({ metadata }) => { | ||
const { errorMessage, setErrorMessage, setAssetMetadata } = useAddAsset(); | ||
|
||
const network = useEvmChainByChainId(metadata.chainId); | ||
|
||
const loadAssetMetadata = useCallback(() => { | ||
if (!network) { | ||
setErrorMessage('Network is not supported.'); | ||
|
||
return; | ||
} | ||
|
||
return fetchEvmTokenMetadataFromChain(network, metadata.address); | ||
}, [metadata.address, network, setErrorMessage]); | ||
|
||
const { data: metadataResponse, isValidating: isChainMetadataLoading } = useTypedSWR( | ||
['add-dApp-asset', metadata.chainId, metadata.address], | ||
loadAssetMetadata, | ||
{ | ||
shouldRetryOnError: false, | ||
focusThrottleInterval: 10_000, | ||
dedupingInterval: 10_000 | ||
} | ||
); | ||
|
||
const chainMetadata = useMemo<EvmTokenMetadata>( | ||
() => ({ ...metadataResponse, address: metadata.address, standard: EvmAssetStandard.ERC20 }), | ||
[metadataResponse, metadata.address] | ||
); | ||
|
||
useEffect(() => { | ||
if (!isChainMetadataLoading && !metadataResponse) setErrorMessage('Failed to load asset metadata.'); | ||
if (metadataResponse) { | ||
setErrorMessage(null); | ||
setAssetMetadata(chainMetadata); | ||
} | ||
}, [metadataResponse, isChainMetadataLoading, setAssetMetadata, chainMetadata, setErrorMessage]); | ||
|
||
if (!metadataResponse && isChainMetadataLoading) { | ||
return ( | ||
<div className="flex justify-center my-20"> | ||
<Spinner theme="gray" className="w-20" /> | ||
</div> | ||
); | ||
} | ||
|
||
const name = chainMetadata?.name || metadata.name || 'Unknown Asset'; | ||
const symbol = chainMetadata?.symbol || metadata.symbol || '???'; | ||
const decimals = chainMetadata?.decimals || metadata.decimals || 0; | ||
|
||
return ( | ||
<div className="flex flex-col gap-6 mb-6"> | ||
<div className="flex flex-col justify-center items-center text-center"> | ||
<EvmAssetIconWithNetwork metadata={chainMetadata} evmChainId={metadata.chainId} assetSlug={metadata.address} /> | ||
|
||
<span className="text-font-medium-bold mt-2">{symbol}</span> | ||
<span className="text-font-description text-grey-1">{name}</span> | ||
</div> | ||
|
||
<div className="flex flex-col gap-4"> | ||
{errorMessage && <CaptionAlert type="error" message={errorMessage} className="items-center" />} | ||
|
||
<div className="flex flex-col px-4 py-2 rounded-8 shadow-bottom border-0.5 border-transparent"> | ||
{network && ( | ||
<div className="py-2 flex flex-row justify-between items-center border-b-0.5 border-lines"> | ||
<p className="p-1 text-font-description text-grey-1"> | ||
<T id="network" /> | ||
</p> | ||
|
||
<div className="flex flex-row items-center"> | ||
<span className="p-1 text-font-num-bold-12">{network.name}</span> | ||
<EvmNetworkLogo chainId={network.chainId} chainName={network.name} size={16} /> | ||
</div> | ||
</div> | ||
)} | ||
|
||
<div className="py-2 flex flex-row justify-between items-center border-b-0.5 border-lines"> | ||
<p className="p-1 text-font-description text-grey-1"> | ||
<T id="contractAddress" /> | ||
</p> | ||
<HashChip hash={metadata.address} /> | ||
</div> | ||
|
||
<div className="py-2 flex flex-row justify-between items-center"> | ||
<p className="p-1 text-font-description text-grey-1"> | ||
<T id="decimals" /> | ||
</p> | ||
<p className="p-1 text-font-num-bold-12">{decimals}</p> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { useCallback, useState } from 'react'; | ||
|
||
import constate from 'constate'; | ||
|
||
import { dispatch, persistor } from 'app/store'; | ||
import { putNewEvmTokenAction } from 'app/store/evm/assets/actions'; | ||
import { putEvmTokensMetadataAction } from 'app/store/evm/tokens-metadata/actions'; | ||
import { toTokenSlug } from 'lib/assets'; | ||
import { EvmTokenMetadata } from 'lib/metadata/types'; | ||
import { useTempleClient } from 'lib/temple/front'; | ||
import { EvmAssetToAddMetadata } from 'lib/temple/types'; | ||
import { useAccountAddressForEvm } from 'temple/front'; | ||
|
||
export const [AddAssetProvider, useAddAsset] = constate(() => { | ||
const { confirmDAppEvmAssetAdding } = useTempleClient(); | ||
const accountPkh = useAccountAddressForEvm(); | ||
|
||
const [errorMessage, setErrorMessage] = useState<string | nullish>(null); | ||
const [assetMetadata, setAssetMetadata] = useState<EvmTokenMetadata | nullish>(null); | ||
|
||
const handleConfirm = useCallback( | ||
async (id: string, confirmed: boolean, dAppAssetMetadata: EvmAssetToAddMetadata) => { | ||
if (confirmed) { | ||
if (assetMetadata && accountPkh) { | ||
const assetSlug = toTokenSlug(assetMetadata.address); | ||
|
||
dispatch( | ||
putNewEvmTokenAction({ | ||
publicKeyHash: accountPkh, | ||
chainId: dAppAssetMetadata.chainId, | ||
assetSlug | ||
}) | ||
); | ||
|
||
dispatch( | ||
putEvmTokensMetadataAction({ | ||
chainId: dAppAssetMetadata.chainId, | ||
records: { [assetSlug]: assetMetadata } | ||
}) | ||
); | ||
|
||
// ensuring the last changes to the store will be persisted before window closes | ||
await persistor.flush(); | ||
|
||
confirmDAppEvmAssetAdding(id, confirmed); | ||
} else { | ||
setErrorMessage('Something’s not right. Please try again later.'); | ||
} | ||
} else { | ||
confirmDAppEvmAssetAdding(id, confirmed); | ||
} | ||
}, | ||
[accountPkh, assetMetadata, confirmDAppEvmAssetAdding] | ||
); | ||
|
||
return { errorMessage, setErrorMessage, assetMetadata, setAssetMetadata, handleConfirm }; | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import React, { memo, useCallback, useMemo } from 'react'; | ||
|
||
import clsx from 'clsx'; | ||
|
||
import { Anchor, IconBase } from 'app/atoms'; | ||
import { EvmNetworkLogo } from 'app/atoms/NetworkLogo'; | ||
import { SettingsCheckbox } from 'app/atoms/SettingsCheckbox'; | ||
import { ReactComponent as CopyIcon } from 'app/icons/base/copy.svg'; | ||
import { ReactComponent as OutLinkIcon } from 'app/icons/base/outLink.svg'; | ||
import { toastSuccess } from 'app/toaster'; | ||
import { t, T } from 'lib/i18n'; | ||
import { EvmChainToAddMetadata } from 'lib/temple/types'; | ||
|
||
import { useAddChainDataState } from './context'; | ||
|
||
interface Props { | ||
metadata: EvmChainToAddMetadata; | ||
} | ||
|
||
export const AddChainView = memo<Props>(({ metadata }) => { | ||
const chainId = Number(metadata.chainId); | ||
const { testnet, setTestnet } = useAddChainDataState(); | ||
|
||
const displayRpcUrl = useMemo(() => new URL(metadata.rpcUrl).hostname, [metadata.rpcUrl]); | ||
const displayBlockExplorerUrl = useMemo( | ||
() => metadata.blockExplorerUrl && new URL(metadata.blockExplorerUrl).hostname, | ||
[metadata.blockExplorerUrl] | ||
); | ||
|
||
const handleCopyRpcUrl = useCallback(() => { | ||
window.navigator.clipboard.writeText(metadata.rpcUrl); | ||
toastSuccess(t('copiedAddress')); | ||
}, [metadata.rpcUrl]); | ||
|
||
const handleTestnetChange = useCallback((checked: boolean) => setTestnet(checked), [setTestnet]); | ||
|
||
return ( | ||
<div className="flex flex-col gap-4 mb-6"> | ||
<div className="flex flex-col px-4 py-2 rounded-8 shadow-bottom border-0.5 border-transparent"> | ||
<div className="py-2 flex flex-row justify-between items-center border-b-0.5 border-lines"> | ||
<p className="p-1 text-font-description text-grey-1"> | ||
<T id="network" /> | ||
</p> | ||
<div className="flex flex-row items-center"> | ||
<span className="p-1 text-font-num-bold-12">{metadata.name}</span> | ||
<EvmNetworkLogo chainId={chainId} chainName={metadata.name} size={16} /> | ||
</div> | ||
</div> | ||
|
||
<div className="py-2 flex flex-row justify-between items-center border-b-0.5 border-lines"> | ||
<p className="p-1 text-font-description text-grey-1"> | ||
<T id="rpcURL" /> | ||
</p> | ||
<div className="flex flex-row px-1 py-0.5 gap-x-0.5 text-secondary cursor-pointer" onClick={handleCopyRpcUrl}> | ||
<p className="text-font-description max-w-52 truncate">{displayRpcUrl}</p> | ||
<IconBase Icon={CopyIcon} size={12} /> | ||
</div> | ||
</div> | ||
|
||
<div className="py-2 flex flex-row justify-between items-center border-b-0.5 border-lines"> | ||
<p className="p-1 text-font-description text-grey-1"> | ||
<T id="chainId" /> | ||
</p> | ||
<p className="p-1 text-font-description-bold">{chainId}</p> | ||
</div> | ||
|
||
<div | ||
className={clsx( | ||
'py-2 flex flex-row justify-between items-center', | ||
metadata.blockExplorerUrl && 'border-b-0.5 border-lines' | ||
)} | ||
> | ||
<p className="p-1 text-font-description text-grey-1"> | ||
<T id="symbol" /> | ||
</p> | ||
<p className="p-1 text-font-description-bold">{metadata.nativeCurrency.symbol}</p> | ||
</div> | ||
|
||
{metadata.blockExplorerUrl && ( | ||
<div className="py-2 flex flex-row justify-between items-center"> | ||
<p className="p-1 text-font-description text-grey-1"> | ||
<T id="blockExplorer" /> | ||
</p> | ||
<Anchor | ||
href={metadata.blockExplorerUrl} | ||
className="flex flex-row px-1 py-0.5 gap-x-0.5 text-secondary cursor-pointer" | ||
> | ||
<p className="text-font-description max-w-48 truncate">{displayBlockExplorerUrl}</p> | ||
<IconBase Icon={OutLinkIcon} size={12} /> | ||
</Anchor> | ||
</div> | ||
)} | ||
</div> | ||
|
||
<SettingsCheckbox checked={testnet} onChange={handleTestnetChange} label={<T id="testnet" />} /> | ||
</div> | ||
); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { useState } from 'react'; | ||
|
||
import constate from 'constate'; | ||
|
||
export const [AddChainDataProvider, useAddChainDataState] = constate(() => { | ||
const [testnet, setTestnet] = useState(false); | ||
|
||
return { testnet, setTestnet }; | ||
}); |
Oops, something went wrong.