From d06bba33804b354242d3b4cc7cd278fcbaa385c1 Mon Sep 17 00:00:00 2001 From: eenvin Date: Mon, 27 Nov 2023 10:55:32 +0100 Subject: [PATCH] feat(ethPNT): support ethPNT --- public/assets/svg/ethPNT.svg | 36 +++++++++++++++++++ .../assetListModal/AssetListModal.jsx | 13 +++++-- src/hooks/use-assets.js | 34 +++++++++++++++++- src/hooks/use-swap-info.js | 3 +- src/settings/swap-assets.js | 20 +++++++++++ src/store/swap/swap.actions.js | 6 ++-- src/utils/fee.js | 9 +++-- src/utils/ptokens.js | 25 ++++++++++++- src/utils/swap-valildator.js | 1 + 9 files changed, 136 insertions(+), 11 deletions(-) create mode 100644 public/assets/svg/ethPNT.svg diff --git a/public/assets/svg/ethPNT.svg b/public/assets/svg/ethPNT.svg new file mode 100644 index 00000000..8cce3c88 --- /dev/null +++ b/public/assets/svg/ethPNT.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/components/organisms/assetListModal/AssetListModal.jsx b/src/components/organisms/assetListModal/AssetListModal.jsx index 6ee7735a..ee568732 100644 --- a/src/components/organisms/assetListModal/AssetListModal.jsx +++ b/src/components/organisms/assetListModal/AssetListModal.jsx @@ -1,9 +1,15 @@ +import PropTypes from 'prop-types' import React, { useState, useCallback, useEffect, useMemo, useRef } from 'react' import { Col, Row, Spinner } from 'react-bootstrap' -import PropTypes from 'prop-types' import styled from 'styled-components' + +import { + useAssetsWithouDefault, + useSearchAssets, + useAssetsGroupedByGivenStrategy, + useSortPntInflationToken, +} from '../../../hooks/use-assets' import { getAssetFromSymbol } from '../../../utils/maps' -import { useAssetsWithouDefault, useSearchAssets, useAssetsGroupedByGivenStrategy } from '../../../hooks/use-assets' import Icon from '../../atoms/icon/Icon' import Modal from '../../molecules/modal/Modal' @@ -166,7 +172,8 @@ const StyledSpinner = styled(Spinner)` const AssetListModal = ({ show: showModal, title, onClose, onSelect, assets: _assets, defaultAssets }) => { const [assetsWithoutDefault] = useAssetsWithouDefault(_assets) const [filteredAssets, setSearchWord] = useSearchAssets(assetsWithoutDefault) - const [assets] = useAssetsGroupedByGivenStrategy(filteredAssets) + const [groupedAssets] = useAssetsGroupedByGivenStrategy(filteredAssets) + const assets = useSortPntInflationToken(groupedAssets) const [show, setShow] = useState([]) const inputSearchRef = useRef(null) diff --git a/src/hooks/use-assets.js b/src/hooks/use-assets.js index ec25a307..07cb39b8 100644 --- a/src/hooks/use-assets.js +++ b/src/hooks/use-assets.js @@ -46,6 +46,31 @@ const useAssetsGroupedByGivenStrategy = (_assets) => { }, [_assets]) } +const useSortPntInflationToken = (_assets) => { + return useMemo(() => { + const pntExtendingTokens = Object.values(_assets) + .flatMap((array) => array) + .filter((asset) => asset.extendsPnt === true) + if (pntExtendingTokens) { + const prunedAssets = Object.fromEntries( + Object.entries(_assets).filter(([key]) => !pntExtendingTokens.some((token) => token.nativeSymbol === key)) + ) + if (pntExtendingTokens.length === 0) return prunedAssets + else if (prunedAssets.PNT && prunedAssets.PNT.length > 0 && pntExtendingTokens.length > 0) + return { + ...prunedAssets, + PNT: [prunedAssets.PNT[0], ...pntExtendingTokens, ...prunedAssets.PNT.slice(1)], + } + else + return { + ...prunedAssets, + PNT: [...pntExtendingTokens], + } + } + return _assets + }, [_assets]) +} + const useSearchAssets = (_assets) => { const [searchWord, setSearchWord] = useState('') @@ -100,4 +125,11 @@ const updateAsset = (_asset) => ({ miniImage: `./assets/svg/${_asset.miniImage || _asset.blockchain}.svg`, }) -export { useAssets, useAssetsWithouDefault, usePtoken, useAssetsGroupedByGivenStrategy, useSearchAssets } +export { + useAssets, + useAssetsWithouDefault, + usePtoken, + useAssetsGroupedByGivenStrategy, + useSearchAssets, + useSortPntInflationToken, +} diff --git a/src/hooks/use-swap-info.js b/src/hooks/use-swap-info.js index 976c236f..5a371e14 100644 --- a/src/hooks/use-swap-info.js +++ b/src/hooks/use-swap-info.js @@ -1,6 +1,7 @@ import { chainIdToTypeMap, BlockchainType } from 'ptokens-constants' import { useMemo } from 'react' +import { ETHPNT_ON_ETH_MAINNET } from '../constants' import { getAssetById } from '../store/swap/swap.selectors' import { getPeginOrPegoutMinutesEstimationByBlockchainAndEta } from '../utils/estimations' import { getFormattedFees } from '../utils/fee' @@ -63,7 +64,7 @@ const useSwapInfo = ({ from, to, amount, bpm, swappersBalances, fees }) => { const eta = getEta() const estimatedSwapTime = getPeginOrPegoutMinutesEstimationByBlockchainAndEta(from.blockchain, eta) - if (from.isNative && !to.isNative) { + if ((from.isNative && !to.isNative) || from.id === ETHPNT_ON_ETH_MAINNET) { const amounts = { ...swappersBalances } const poolAmount = to.isPseudoNative && amounts[to.swapperAddress] diff --git a/src/settings/swap-assets.js b/src/settings/swap-assets.js index 86720150..8180e307 100644 --- a/src/settings/swap-assets.js +++ b/src/settings/swap-assets.js @@ -1973,6 +1973,26 @@ const swapAssets = [ withBalanceDecimalsConversion: true, chainId: ChainId.EthereumMainnet, }, + { + address: '0xf4eA6B892853413bD9d9f1a5D3a620A0ba39c5b2', + id: 'ETHPNT_ON_ETH_MAINNET', + symbol: 'ethPNT', + name: 'eth-pNetwork', + network: 'mainnet', + blockchain: 'ETH', + decimals: 18, + nativeDecimals: 18, + withMiniImage: true, + isNative: true, + nativeSymbol: 'ethPNT', + nativeBlockchain: 'ETH', + image: 'ethPNT.svg', + withBalanceDecimalsConversion: true, + titleLabel: 'ethPNT', + chainId: ChainId.EthereumMainnet, + extendsPnt: true, + onPnetworkV2: true, + }, { address: '0x02eca910cb3a7d43ebc7e8028652ed5c6b70259b', id: 'PTERIA', diff --git a/src/store/swap/swap.actions.js b/src/store/swap/swap.actions.js index 2514feb6..197ece5c 100644 --- a/src/store/swap/swap.actions.js +++ b/src/store/swap/swap.actions.js @@ -17,7 +17,7 @@ import settings from '../../settings' import assets from '../../settings/swap-assets' import eosioTokenAbi from '../../utils/abi/eosio.token' import { parseError } from '../../utils/errors' -import { createAsset, getSwapBuilder } from '../../utils/ptokens' +import { createAsset, createEthPntAsset, getSwapBuilder } from '../../utils/ptokens' import { getReadOnlyProviderByBlockchain } from '../../utils/read-only-providers' import { updateInfoModal } from '../pages/pages.actions' import { getWallets, getWalletByBlockchain } from '../wallets/wallets.selectors' @@ -342,8 +342,8 @@ const swap = (_from, _to, _amount, _address, _opts = {}) => { if (_from.requiresCurve) { _from = getAssetById(_fromNative.pTokenId) } - - const sourceAsset = await createAsset(_from, wallets, true) + const sourceAsset = + _from.natveSymbol == 'ethPNT' ? await createAsset(_from, wallets) : await createEthPntAsset(_from, wallets) const destinationAsset = await createAsset(_to, wallets) const swapBuilder = getSwapBuilder() swapBuilder.setAmount(_amount) diff --git a/src/utils/fee.js b/src/utils/fee.js index fef8d6b3..3a778fec 100644 --- a/src/utils/fee.js +++ b/src/utils/fee.js @@ -1,8 +1,10 @@ import BigNumber from 'bignumber.js' +import _ from 'lodash' + +import { PNT_ON_ETH_MAINNET, ETHPNT_ON_ETH_MAINNET, PBTC_ON_ETH_MAINNET_V1_MIGRATION } from '../constants' + import { formatDecimalSeparator } from './amount-utils' import { createAsset } from './ptokens' -import { PNT_ON_ETH_MAINNET, ETHPNT_ON_ETH_MAINNET, PBTC_ON_ETH_MAINNET_V1_MIGRATION } from '../constants' -import _ from 'lodash' const getFeeFactor = (fee) => (_.isNil(fee) ? null : 1 - fee / 100) @@ -24,6 +26,9 @@ const getMigrationFees = (_from, _to) => { } const getSwapFees = async (_from, _to) => { + if (_from.id === ETHPNT_ON_ETH_MAINNET && _to.id === 'PNT') + return { basisPoints: 25, networkFee: 0, minProtocolFee: 0 } + if (_from.id === ETHPNT_ON_ETH_MAINNET) return { basisPoints: 10, networkFee: 0, minProtocolFee: 0 } try { const fromAsset = await createAsset(_from) const toAsset = await createAsset(_to) diff --git a/src/utils/ptokens.js b/src/utils/ptokens.js index c487da2f..f3d662f5 100644 --- a/src/utils/ptokens.js +++ b/src/utils/ptokens.js @@ -1,12 +1,13 @@ import _ from 'lodash' import { pTokensAlgorandAssetBuilder, pTokensAlgorandProvider } from 'ptokens-assets-algorand' import { pTokensEosioAssetBuilder, pTokensEosioProvider } from 'ptokens-assets-eosio' -import { pTokensEvmAssetBuilder, pTokensEvmProvider } from 'ptokens-assets-evm' +import { pTokensEvmAssetBuilder, pTokensEvmProvider, pTokensEvmAsset } from 'ptokens-assets-evm' import { pTokensUtxoAssetBuilder, pTokensBlockstreamUtxoProvider } from 'ptokens-assets-utxo' import { pTokensNode, pTokensNodeProvider } from 'ptokens-node' import { pTokensSwapBuilder } from 'ptokens-swap' import { PNETWORK_NODE_V3 } from '../constants/index' +import { getWalletByBlockchain } from '../store/wallets/wallets.selectors' import { getReadOnlyProviderByBlockchain } from './read-only-providers' @@ -72,6 +73,28 @@ export const createAsset = async (_asset, _wallets) => { return asset } +export const createEthPntAsset = async (_asset, _wallets) => { + const wallet = getWalletByBlockchain(_asset.blockchain) + const provider = new pTokensNodeProvider(PNETWORK_NODE_V3) + const node = new pTokensNode(provider) + // Here _to is used for the symbol in order to get PNT assetInfo. + // ethPNT is not directly supported and it is used as PNT only modifying the contract address. + const assetInfo = await node.getAssetInfoByChainId('PNT', _asset.chainId) + const providerInfo = new pTokensEvmProvider( + wallet.provider || getReadOnlyProviderByBlockchain(_asset.blockchain.toUpperCase()) + ) + assetInfo.tokenAddress = _asset.address + assetInfo.decimals = _asset.decimals + const config = { + node: node, + assetInfo: assetInfo, + symbol: _asset.symbol, + chainId: _asset.chainId, + provider: providerInfo, + } + return new pTokensEvmAsset(config) +} + export const getSwapBuilder = () => { const node = getNode() return new pTokensSwapBuilder(node) diff --git a/src/utils/swap-valildator.js b/src/utils/swap-valildator.js index 342e22a8..6a44fdbd 100644 --- a/src/utils/swap-valildator.js +++ b/src/utils/swap-valildator.js @@ -3,6 +3,7 @@ import _ from 'lodash' export const isValidSwap = (from, to, assets) => { if (_.isNil(from) || _.isNil(to)) return false if (to.id === from.id) return false + if (to.nativeSymbol === 'PNT' && from.nativeSymbol === 'ethPNT') return true if (!assets.find(({ id }) => from.id === id) && !assets.find(({ id }) => to.id === id)) return false if (to.isHidden) return false if (to.nativeSymbol.toLowerCase() !== from.nativeSymbol.toLowerCase()) return false