From 526af74d1ea7590bae1ef02a1a1771a72c1688f1 Mon Sep 17 00:00:00 2001 From: Quentin Burg Date: Wed, 18 Oct 2023 15:18:56 +0200 Subject: [PATCH] :recycle: refactor components && fetch token vault informations --- batcher-ui/src/actions/events.ts | 40 +- batcher-ui/src/actions/exchange.ts | 2 +- batcher-ui/src/actions/holdings.ts | 2 +- batcher-ui/src/actions/marketholdings.ts | 41 +- batcher-ui/src/actions/wallet.ts | 3 +- batcher-ui/src/commands/events.ts | 16 +- batcher-ui/src/commands/exchange.ts | 6 +- batcher-ui/src/commands/holdings.ts | 6 +- batcher-ui/src/commands/marketholdings.ts | 40 +- batcher-ui/src/commands/wallet.ts | 4 +- batcher-ui/src/components/MMVault.tsx | 426 ------------------ .../components/{ => batcher}/BatcherInfo.tsx | 4 +- .../{ => batcher}/BatcherStepper.tsx | 4 +- .../src/components/{ => batcher}/Exchange.tsx | 10 +- .../{ => batcher}/PriceStrategy.tsx | 6 +- .../components/{ => batcher}/SelectPair.tsx | 6 +- .../src/components/{ => common}/Copy.tsx | 0 .../src/components/{ => common}/Link.tsx | 0 .../src/components/common/PrimaryButton.tsx | 11 + .../src/components/{ => common}/Toast.tsx | 4 +- .../src/components/{ => common}/Tooltip.tsx | 0 .../src/components/{ => layout}/Footer.tsx | 0 .../src/components/{ => layout}/Menu.tsx | 4 +- .../src/components/{ => layout}/NavBar.tsx | 8 +- .../src/components/{ => layout}/Root.tsx | 10 +- .../components/market-maker/GlobalVault.tsx | 57 +++ .../src/components/market-maker/MMVault.tsx | 33 ++ .../{ => market-maker}/SelectMMPair.tsx | 33 +- .../src/components/market-maker/UserVault.tsx | 336 ++++++++++++++ batcher-ui/src/config/token-vaults.ts | 7 + batcher-ui/src/contexts/events.tsx | 4 +- batcher-ui/src/contexts/tezos-toolkit.tsx | 22 +- batcher-ui/{ => src}/pages/404.tsx | 0 batcher-ui/{ => src}/pages/_app.tsx | 14 +- batcher-ui/{ => src}/pages/_document.tsx | 0 batcher-ui/{ => src}/pages/about.tsx | 0 batcher-ui/{ => src}/pages/holdings.tsx | 10 +- batcher-ui/{ => src}/pages/index.tsx | 14 +- batcher-ui/{ => src}/pages/marketmaker.tsx | 10 +- batcher-ui/{ => src}/pages/volumes.tsx | 6 +- batcher-ui/src/reducers/events.ts | 6 +- batcher-ui/src/reducers/holdings.ts | 6 +- batcher-ui/src/reducers/index.ts | 22 +- batcher-ui/src/reducers/marketholdings.ts | 40 +- batcher-ui/src/reducers/wallet.ts | 8 +- batcher-ui/src/store.ts | 4 +- batcher-ui/src/types/contracts/batcher.ts | 2 +- batcher-ui/src/types/contracts/token-vault.ts | 12 + batcher-ui/src/types/events.ts | 6 +- batcher-ui/src/types/state.ts | 16 +- batcher-ui/src/utils/market-maker.ts | 60 ++- batcher-ui/src/utils/token-manager.ts | 4 +- batcher-ui/src/utils/utils.ts | 8 +- batcher-ui/tsconfig.json | 7 +- 54 files changed, 800 insertions(+), 600 deletions(-) delete mode 100644 batcher-ui/src/components/MMVault.tsx rename batcher-ui/src/components/{ => batcher}/BatcherInfo.tsx (96%) rename batcher-ui/src/components/{ => batcher}/BatcherStepper.tsx (94%) rename batcher-ui/src/components/{ => batcher}/Exchange.tsx (98%) rename batcher-ui/src/components/{ => batcher}/PriceStrategy.tsx (96%) rename batcher-ui/src/components/{ => batcher}/SelectPair.tsx (96%) rename batcher-ui/src/components/{ => common}/Copy.tsx (100%) rename batcher-ui/src/components/{ => common}/Link.tsx (100%) create mode 100644 batcher-ui/src/components/common/PrimaryButton.tsx rename batcher-ui/src/components/{ => common}/Toast.tsx (96%) rename batcher-ui/src/components/{ => common}/Tooltip.tsx (100%) rename batcher-ui/src/components/{ => layout}/Footer.tsx (100%) rename batcher-ui/src/components/{ => layout}/Menu.tsx (92%) rename batcher-ui/src/components/{ => layout}/NavBar.tsx (92%) rename batcher-ui/src/components/{ => layout}/Root.tsx (83%) create mode 100644 batcher-ui/src/components/market-maker/GlobalVault.tsx create mode 100644 batcher-ui/src/components/market-maker/MMVault.tsx rename batcher-ui/src/components/{ => market-maker}/SelectMMPair.tsx (77%) create mode 100644 batcher-ui/src/components/market-maker/UserVault.tsx create mode 100644 batcher-ui/src/config/token-vaults.ts rename batcher-ui/{ => src}/pages/404.tsx (100%) rename batcher-ui/{ => src}/pages/_app.tsx (86%) rename batcher-ui/{ => src}/pages/_document.tsx (100%) rename batcher-ui/{ => src}/pages/about.tsx (100%) rename batcher-ui/{ => src}/pages/holdings.tsx (94%) rename batcher-ui/{ => src}/pages/index.tsx (69%) rename batcher-ui/{ => src}/pages/marketmaker.tsx (63%) rename batcher-ui/{ => src}/pages/volumes.tsx (94%) diff --git a/batcher-ui/src/actions/events.ts b/batcher-ui/src/actions/events.ts index 989387fe..b3dbbfd9 100644 --- a/batcher-ui/src/actions/events.ts +++ b/batcher-ui/src/actions/events.ts @@ -1,4 +1,4 @@ -import { BigMapEvent } from 'src/types/events'; +import type { BigMapEvent } from '@/types'; export const newEvent = (event: BigMapEvent) => ({ @@ -6,25 +6,25 @@ export const newEvent = (event: BigMapEvent) => payload: { event }, } as const); - export const closeToast = () => - ({ - type: 'CLOSE_TOAST', - } as const); +export const closeToast = () => + ({ + type: 'CLOSE_TOAST', + } as const); - export const newError = (errorContent: string) => - ({ - type: 'NEW_ERROR', - payload: { errorContent }, - } as const); +export const newError = (errorContent: string) => + ({ + type: 'NEW_ERROR', + payload: { errorContent }, + } as const); - export const newInfo = (infoContent: string) => - ({ - type: 'NEW_INFO', - payload: { infoContent }, - } as const); +export const newInfo = (infoContent: string) => + ({ + type: 'NEW_INFO', + payload: { infoContent }, + } as const); - export type EventActions = - | ReturnType - | ReturnType - | ReturnType - | ReturnType; +export type EventActions = + | ReturnType + | ReturnType + | ReturnType + | ReturnType; diff --git a/batcher-ui/src/actions/exchange.ts b/batcher-ui/src/actions/exchange.ts index 9eb7d44c..9cdaeea6 100644 --- a/batcher-ui/src/actions/exchange.ts +++ b/batcher-ui/src/actions/exchange.ts @@ -1,4 +1,4 @@ -import { BatcherStatus, CurrentSwap, PriceStrategy } from '../types'; +import { BatcherStatus, CurrentSwap, PriceStrategy } from '@/types'; export const updatePriceStrategy = (priceStrategy: PriceStrategy) => ({ diff --git a/batcher-ui/src/actions/holdings.ts b/batcher-ui/src/actions/holdings.ts index b7460078..76cf0f87 100644 --- a/batcher-ui/src/actions/holdings.ts +++ b/batcher-ui/src/actions/holdings.ts @@ -1,4 +1,4 @@ -import { HoldingsState } from 'src/types'; +import { HoldingsState } from '@/types'; export const redeem = () => ({ diff --git a/batcher-ui/src/actions/marketholdings.ts b/batcher-ui/src/actions/marketholdings.ts index badb43a4..3a974c17 100644 --- a/batcher-ui/src/actions/marketholdings.ts +++ b/batcher-ui/src/actions/marketholdings.ts @@ -1,19 +1,19 @@ -import { MarketHoldingsState } from 'src/types'; +import { MarketHoldingsState } from '@/types'; export const addLiquidity = () => ({ type: 'ADDLIQUIDITY', - }) as const; + } as const); export const removeLiquidity = () => ({ type: 'REMOVELIQUIDITY', - }) as const; + } as const); export const claimRewards = () => ({ type: 'CLAIMREWARDS', - }) as const; + } as const); export const updateMarketHoldings = ( vaults: Partial> @@ -30,13 +30,38 @@ export const getMarketHoldings = ( ({ type: 'GET_MARKET_HOLDINGS', payload: { contractAddress, userAddress }, - }) as const; + } as const); export const changeVault = (vault: string) => ({ type: 'CHANGE_VAULT', payload: { vault }, - }) as const; + } as const); + +// ---- WIP ---- // + +export const getUserVault = (userAddress: string) => + ({ + type: 'GET_USER_VAULT', + payload: { userAddress }, + } as const); + +export const updateUserVault = (vault: any) => + ({ + type: 'UPDATE_USER_VAULT', + payload: { vault }, + } as const); + +export const getGlobalVault = () => + ({ + type: 'GET_GLOBAL_VAULT', + } as const); + +export const updateGlobalVault = (vault: any) => + ({ + type: 'UPDATE_GLOBAL_VAULT', + payload: { vault }, + } as const); export type MarketHoldingsActions = | ReturnType @@ -44,4 +69,8 @@ export type MarketHoldingsActions = | ReturnType | ReturnType | ReturnType + | ReturnType + | ReturnType + | ReturnType + | ReturnType | ReturnType; diff --git a/batcher-ui/src/actions/wallet.ts b/batcher-ui/src/actions/wallet.ts index c4aa9f95..57e2831f 100644 --- a/batcher-ui/src/actions/wallet.ts +++ b/batcher-ui/src/actions/wallet.ts @@ -1,5 +1,4 @@ -// import { Option } from 'fp-ts/Option'; -import { Balances } from 'src/utils/utils'; +import { Balances } from '@/utils/utils'; const connectedWallet = ({ userAddress }: { userAddress: string }) => ({ diff --git a/batcher-ui/src/commands/events.ts b/batcher-ui/src/commands/events.ts index 973ad1c3..42a5946b 100644 --- a/batcher-ui/src/commands/events.ts +++ b/batcher-ui/src/commands/events.ts @@ -4,17 +4,21 @@ import { updateVolumes, updateOraclePrice, updateBatcherStatus, -} from 'src/actions'; -import { updateHoldings } from 'src/actions/holdings'; -import { userAddressSelector } from 'src/reducers'; -import { BatchBigmap, OrderBookBigmap, RatesCurrentBigmap } from 'src/types'; -import { BigMapEvent } from 'src/types/events'; +} from '@/actions'; +import { updateHoldings } from '@/actions/holdings'; +import { userAddressSelector } from '@/reducers'; +import type { + BatchBigmap, + OrderBookBigmap, + RatesCurrentBigmap, + BigMapEvent, +} from '@/types'; import { computeAllHoldings, computeOraclePrice, mapStatus, toVolumes, -} from 'src/utils/utils'; +} from '@/utils/utils'; export const newEventCmd = (event: BigMapEvent) => { return Cmd.run( diff --git a/batcher-ui/src/commands/exchange.ts b/batcher-ui/src/commands/exchange.ts index 07d9dd24..0eaa5f03 100644 --- a/batcher-ui/src/commands/exchange.ts +++ b/batcher-ui/src/commands/exchange.ts @@ -7,7 +7,7 @@ import { getPairsInformations, getVolumes, getTimeDifferenceInMs, -} from '../utils/utils'; +} from '@/utils/utils'; import { updateBatchNumber, updateBatcherStatus, @@ -18,8 +18,8 @@ import { updateRemainingTime, noBatchError, newError, -} from '../actions'; -import { BatcherStatus, CurrentSwap, SwapNames } from 'src/types'; +} from '@/actions'; +import { BatcherStatus, CurrentSwap, SwapNames } from '@/types'; const fetchPairInfosCmd = (pair: string) => Cmd.run( diff --git a/batcher-ui/src/commands/holdings.ts b/batcher-ui/src/commands/holdings.ts index ea3592c7..47b754d2 100644 --- a/batcher-ui/src/commands/holdings.ts +++ b/batcher-ui/src/commands/holdings.ts @@ -1,7 +1,7 @@ import { Cmd } from 'redux-loop'; -import { getOrdersBook } from '../utils/utils'; -import { updateHoldings } from 'src/actions/holdings'; -import { newError } from 'src/actions'; +import { getOrdersBook } from '@/utils/utils'; +import { updateHoldings } from '@/actions/holdings'; +import { newError } from '@/actions'; const fetchHoldingsCmd = (userAddress?: string) => { return Cmd.run( diff --git a/batcher-ui/src/commands/marketholdings.ts b/batcher-ui/src/commands/marketholdings.ts index 2136caeb..3dcdb668 100644 --- a/batcher-ui/src/commands/marketholdings.ts +++ b/batcher-ui/src/commands/marketholdings.ts @@ -1,6 +1,14 @@ import { Cmd } from 'redux-loop'; -import { getMarketHoldings } from '../utils/utils'; -import { updateMarketHoldings } from 'src/actions'; +import { + getGlobalVault, + getMarketHoldings, + getUserVault2, +} from '@/utils/market-maker'; +import { + updateGlobalVault, + updateMarketHoldings, + updateUserVault, +} from '@/actions'; const fetchMarketHoldingsCmd = ( contractAddress: string, @@ -9,7 +17,6 @@ const fetchMarketHoldingsCmd = ( return Cmd.run( async () => { const vaults = await getMarketHoldings(userAddress || ''); - return vaults; }, { @@ -18,4 +25,29 @@ const fetchMarketHoldingsCmd = ( ); }; -export { fetchMarketHoldingsCmd }; +const fetchUserVaultCmd = (userAddress: string, tokenName: string) => { + return Cmd.run( + async () => { + const userVault = await getUserVault2(userAddress, tokenName); + return userVault; + }, + { + successActionCreator: updateUserVault, + } + ); +}; + +const fetchGlobalVaultCmd = (tokenName: string) => { + return Cmd.run( + async () => { + const globalVault = await getGlobalVault(tokenName); + + return globalVault; + }, + { + successActionCreator: updateGlobalVault, + } + ); +}; + +export { fetchMarketHoldingsCmd, fetchUserVaultCmd, fetchGlobalVaultCmd }; diff --git a/batcher-ui/src/commands/wallet.ts b/batcher-ui/src/commands/wallet.ts index 2d472426..9c3bf2b0 100644 --- a/batcher-ui/src/commands/wallet.ts +++ b/batcher-ui/src/commands/wallet.ts @@ -1,6 +1,6 @@ import { Cmd } from 'redux-loop'; -import { getBalances } from '../utils/utils'; -import { gotUserBalances } from '../actions'; +import { getBalances } from '@/utils/utils'; +import { gotUserBalances } from '@/actions'; const fetchUserBalancesCmd = (userAddress?: string) => { return Cmd.run( diff --git a/batcher-ui/src/components/MMVault.tsx b/batcher-ui/src/components/MMVault.tsx deleted file mode 100644 index 8e8d6597..00000000 --- a/batcher-ui/src/components/MMVault.tsx +++ /dev/null @@ -1,426 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import * as Form from '@radix-ui/react-form'; -import { VaultToken } from '../types'; -import { scaleAmountUp } from '../utils/utils'; -import { tzip12 } from '@taquito/tzip12'; -import { tzip16 } from '@taquito/tzip16'; -import { compose, OpKind } from '@taquito/taquito'; -import SelectMMPair from './SelectMMPair'; -import { useSelector } from 'react-redux'; -import { fetchUserBalances } from '../actions'; -import { - userBalancesSelector, - userAddressSelector, - getCurrentGlobalVaultSelector, - getCurrentUserVaultSelector, -} from '../reducers'; -import { useTezosToolkit } from '../contexts/tezos-toolkit'; -import { BatchWalletOperation } from '@taquito/taquito/dist/types/wallet/batch-operation'; -import { useDispatch } from 'react-redux'; - -const MMVaultComponent = () => { - const dispatch = useDispatch(); - const marketMakerAddress = process.env.NEXT_PUBLIC_MARKETMAKER_CONTRACT_HASH; - const userAddress = useSelector(userAddressSelector); - const { tezos } = useTezosToolkit(); - const userBalances = useSelector(userBalancesSelector); - const [amountInput, setAmount] = useState('0'); - - const currentGlobalVault = useSelector(getCurrentGlobalVaultSelector); - const currentUserVault = useSelector(getCurrentUserVaultSelector); - - useEffect(() => { - if (userAddress) dispatch(fetchUserBalances()); - }, [dispatch, userAddress]); - - if (!tezos) - return ( -
-

- { - "There is an error with Tezos Tool Kit, can't swap ! Please contact \ - Marigold if problem persists." - } -

-
- ); - - const showTokenAmount = ({ vaultToken }: { vaultToken: VaultToken }) => ( -
-

- {vaultToken?.name} : {vaultToken?.amount} -

-
- ); - const showForeignAssets = (assets: Map) => ( -
- {assets.size > 0 ? ( - Object.values(assets).map(a => showTokenAmount({ vaultToken: a })) - ) : ( -
- )} -
- ); - - const addLiquidity = async ({ - tokenName, - tokenAmount, - }: { - tokenName: string; - tokenAmount: number; - }) => { - if (!userAddress) { - console.info('No user address'); - return; - } - if (!marketMakerAddress) { - console.info('No contract address'); - return; - } - - const mmContract = await tezos?.wallet.at(marketMakerAddress); - - if (!currentGlobalVault.native.address) { - return; //TODO:improve this - } - const token = currentGlobalVault.native; - const tokenContract = await tezos?.wallet.at( - token.address, - compose(tzip12, tzip16) - ); - - const scaled_amount = scaleAmountUp(tokenAmount, token.decimals); - - // This is for fa2 token standard. I.e, USDT token - const fa2_add_operator_params = [ - { - add_operator: { - owner: userAddress, - operator: marketMakerAddress, - token_id: token.id, - }, - }, - ]; - - const fa2_remove_operator_params = [ - { - remove_operator: { - owner: userAddress, - operator: marketMakerAddress, - token_id: token.id, - }, - }, - ]; - - try { - let liq_op: BatchWalletOperation | undefined = undefined; - const liq_params = { - token: { - token_id: token.id, - name: token.name, - address: token.address, - decimals: token.decimals, - standard: token.standard, - }, - amount: scaled_amount, - }; - - if (token.standard === 'FA1.2 token') { - const tokenfa12Contract = await tezos.wallet.at( - token.address, - compose(tzip12, tzip16) - ); - - liq_op = await tezos?.wallet - .batch([ - { - kind: OpKind.TRANSACTION, - ...tokenfa12Contract.methods - .approve(marketMakerAddress, scaled_amount) - .toTransferParams(), - }, - { - kind: OpKind.TRANSACTION, - ...mmContract?.methodsObject - .addLiquidity(liq_params) - .toTransferParams(), - to: marketMakerAddress, - amount: 0, - mutez: true, - }, - ]) - .send(); - } - - if (token.standard === 'FA2 token') { - liq_op = await tezos?.wallet - .batch([ - { - kind: OpKind.TRANSACTION, - ...tokenContract.methods - .update_operators(fa2_add_operator_params) - .toTransferParams(), - }, - { - kind: OpKind.TRANSACTION, - ...mmContract?.methodsObject - .addLiquidity(liq_params) - .toTransferParams(), - to: marketMakerAddress, - amount: 0, - mutez: true, - }, - { - kind: OpKind.TRANSACTION, - ...tokenContract.methods - .update_operators(fa2_remove_operator_params) - .toTransferParams(), - }, - ]) - .send(); - } - - if (!liq_op) { - console.error('Liquidity Operation is not defined...'); - throw new Error('Liquidity Operation is not defined...'); - } - - const confirm = await liq_op?.confirmation(); - - confirm?.completed - ? console.log('Successfully added liquidity !!!!!!') - : null; - - if (!confirm?.completed) { - console.error(confirm); - throw new Error(`Failed to add liquidity ${token.name} token.`); - } else { - console.info(`Successfully added liquidity ${tokenName}`); - - dispatch(fetchUserBalances()); - setAmount('0'); - } - } catch (error) { - console.log('liquidity error', error); - } - }; - - const claimRewards = async ({ tokenName }: { tokenName: string }) => { - console.log('claiming'); - try { - if (!tezos || !marketMakerAddress) { - throw new Error('Failed to initialize communication with contract.'); - } - const contractWallet = await tezos.wallet.at(marketMakerAddress); - - let claimTransaction = await contractWallet.methods - .claim(tokenName) - .send(); - - if (claimTransaction) { - const confirm = await claimTransaction.confirmation(); - if (!confirm?.completed) { - console.error('Failed to claimed rewards' + confirm); - } else { - console.info('Successfully claimed rewards'); - } - } else { - throw new Error('Failed to claimed rewards'); - } - } catch (error: any) { - console.error('Unable to claimed rewards' + error); - } - }; - - const showClaimRewards = ({ vaultToken }: { vaultToken: VaultToken }) => { - return ( -
- { - event.preventDefault(); - claimRewards({ - tokenName: vaultToken.name, - }); - }}> - - - - -
- ); - }; - - const removeLiquidity = async ({ tokenName }: { tokenName: string }) => { - console.log('removing'); - try { - if (!tezos || !marketMakerAddress) { - throw new Error('Failed to initialize communication with contract.'); - } - const contractWallet = await tezos.wallet.at(marketMakerAddress); - - let claimTransaction = await contractWallet.methods - .removeLiquidity(tokenName) - .send(); - - if (claimTransaction) { - const confirm = await claimTransaction.confirmation(); - if (!confirm?.completed) { - console.error('Failed to remove liquidity' + confirm); - } else { - console.info('Successfully removed liquidity '); - } - } else { - throw new Error('Failed to removed liquidity'); - } - } catch (error: any) { - console.error('Unable to remove liquidity' + error); - } - }; - - const showRemoveLiquidity = ({ vaultToken }: { vaultToken: VaultToken }) => { - return ( -
- { - event.preventDefault(); - removeLiquidity({ - tokenName: vaultToken.name, - }); - }}> - - - - -
- ); - }; - const showAddLiquidity = ({ - vaultToken, - userBalances, - }: { - vaultToken: VaultToken; - userBalances: Record; - }) => { - return ( -
- { - event.preventDefault(); - addLiquidity({ - tokenName: vaultToken.name, - tokenAmount: parseInt(amountInput), - }); - }}> - -
- -

- {`Balance : ${ - userBalances[vaultToken.name.toUpperCase()] || 0 - }`} -

-
-
-
- - { - (/^\d*\.{0,1}\d*$/.test(event.target.value) || - event.target.value === '') && - setAmount(event.target.value); - }} - type="text" - value={amountInput} - defaultValue={0} - /> - -
- {/* - Please input your amount - - - Invalid input. - - { - return ( - Number.parseFloat(value) > - userBalances[vaultToken.name.toUpperCase()] - ); - }}> - Greater than the balance. - */} -
- - - -
-
- ); - }; - - return ( -
-
-
-

Market Maker Vaults

-
-
-
- {currentGlobalVault && ( -
-
- - {`Total Shares: ${currentGlobalVault.total_shares}`} -
-

Native Asset

- {showTokenAmount({ - vaultToken: currentGlobalVault.native, - })} -
-
-

Foreign Assets

- {showForeignAssets(currentGlobalVault.foreign)} -
-
-
- )} - {currentUserVault && ( -
-
-

My Liquidity

-

{`Shares: ${currentUserVault.shares}`}

- {`Unclaimed Rewards: ${currentUserVault.unclaimed} TEZ`} - {showAddLiquidity({ - vaultToken: currentGlobalVault.native, - userBalances: userBalances, - })} - {showRemoveLiquidity({ - vaultToken: currentGlobalVault.native, - })} - {showClaimRewards({ - vaultToken: currentGlobalVault.native, - })} -
-
- )} -
-
- ); -}; -export default MMVaultComponent; diff --git a/batcher-ui/src/components/BatcherInfo.tsx b/batcher-ui/src/components/batcher/BatcherInfo.tsx similarity index 96% rename from batcher-ui/src/components/BatcherInfo.tsx rename to batcher-ui/src/components/batcher/BatcherInfo.tsx index 2adc7770..bc2eca59 100644 --- a/batcher-ui/src/components/BatcherInfo.tsx +++ b/batcher-ui/src/components/batcher/BatcherInfo.tsx @@ -7,8 +7,8 @@ import { currentPairSelector, oraclePriceSelector, remainingTimeSelector, -} from '../reducers'; -import { BatcherStatus } from '../types'; +} from '@/reducers'; +import { BatcherStatus } from '@/types'; const BatcherInfo = () => { const tokenPair = useSelector(currentPairSelector); diff --git a/batcher-ui/src/components/BatcherStepper.tsx b/batcher-ui/src/components/batcher/BatcherStepper.tsx similarity index 94% rename from batcher-ui/src/components/BatcherStepper.tsx rename to batcher-ui/src/components/batcher/BatcherStepper.tsx index 2fd60603..7d9e7542 100644 --- a/batcher-ui/src/components/BatcherStepper.tsx +++ b/batcher-ui/src/components/batcher/BatcherStepper.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { useSelector } from 'react-redux'; -import { batcherStatusSelector } from '../reducers'; -import { BatcherStatus } from '../types'; +import { batcherStatusSelector } from '@/reducers'; +import { BatcherStatus } from '@/types'; const Square = ({ isActive }: { isActive: boolean }) => (
( + +); + +export default PrimaryButton; diff --git a/batcher-ui/src/components/Toast.tsx b/batcher-ui/src/components/common/Toast.tsx similarity index 96% rename from batcher-ui/src/components/Toast.tsx rename to batcher-ui/src/components/common/Toast.tsx index 2c3be9cd..3b9ff17f 100644 --- a/batcher-ui/src/components/Toast.tsx +++ b/batcher-ui/src/components/common/Toast.tsx @@ -1,9 +1,9 @@ import * as React from 'react'; import * as ToastBase from '@radix-ui/react-toast'; import { useSelector } from 'react-redux'; -import { getToastInfosSelector } from 'src/reducers'; +import { getToastInfosSelector } from '@/reducers'; import { useDispatch } from 'react-redux'; -import { closeToast } from 'src/actions'; +import { closeToast } from '@/actions'; import { createPortal } from 'react-dom'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faCircleInfo, faCircleXmark } from '@fortawesome/free-solid-svg-icons'; diff --git a/batcher-ui/src/components/Tooltip.tsx b/batcher-ui/src/components/common/Tooltip.tsx similarity index 100% rename from batcher-ui/src/components/Tooltip.tsx rename to batcher-ui/src/components/common/Tooltip.tsx diff --git a/batcher-ui/src/components/Footer.tsx b/batcher-ui/src/components/layout/Footer.tsx similarity index 100% rename from batcher-ui/src/components/Footer.tsx rename to batcher-ui/src/components/layout/Footer.tsx diff --git a/batcher-ui/src/components/Menu.tsx b/batcher-ui/src/components/layout/Menu.tsx similarity index 92% rename from batcher-ui/src/components/Menu.tsx rename to batcher-ui/src/components/layout/Menu.tsx index b90d35ff..c20b613e 100644 --- a/batcher-ui/src/components/Menu.tsx +++ b/batcher-ui/src/components/layout/Menu.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import LinkComponent from './Link'; -import { useWallet } from 'src/contexts/wallet'; +import LinkComponent from '@/components/common/Link'; +import { useWallet } from '@/contexts/wallet'; interface MenuProps { setIsMenuOpen(arg: boolean): void; diff --git a/batcher-ui/src/components/NavBar.tsx b/batcher-ui/src/components/layout/NavBar.tsx similarity index 92% rename from batcher-ui/src/components/NavBar.tsx rename to batcher-ui/src/components/layout/NavBar.tsx index 4269e49f..bed11320 100644 --- a/batcher-ui/src/components/NavBar.tsx +++ b/batcher-ui/src/components/layout/NavBar.tsx @@ -1,13 +1,13 @@ import React, { useEffect } from 'react'; import { useDispatch } from 'react-redux'; import Image from 'next/image'; -import { connectedWallet, disconnectedWallet } from '../actions'; -import { useWallet } from '../contexts/wallet'; +import { connectedWallet, disconnectedWallet } from '@/actions'; +import { useWallet } from '@/contexts/wallet'; import Menu from './Menu'; -import LinkComponent from './Link'; +import LinkComponent from '@/components/common/Link'; import { faBars } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import Copy from './Copy'; +import Copy from '@/components/common/Copy'; interface NavBarProps { isMenuOpen: boolean; diff --git a/batcher-ui/src/components/Root.tsx b/batcher-ui/src/components/layout/Root.tsx similarity index 83% rename from batcher-ui/src/components/Root.tsx rename to batcher-ui/src/components/layout/Root.tsx index 176fae41..45c97148 100644 --- a/batcher-ui/src/components/Root.tsx +++ b/batcher-ui/src/components/layout/Root.tsx @@ -2,13 +2,13 @@ import { NextComponentType, NextPageContext } from 'next'; import { useEffect, useState } from 'react'; import Footer from './Footer'; import NavBar from './NavBar'; -import { batcherSetup, getCurrentBatchNumber } from 'src/actions'; +import { batcherSetup, getCurrentBatchNumber } from '@/actions'; import { useDispatch } from 'react-redux'; -import { setByKey } from 'src/utils/local-storage'; -import { getStorage } from 'src/utils/utils'; +import { setByKey } from '@/utils/local-storage'; +import { getStorage } from '@/utils/utils'; import { useSelector } from 'react-redux'; -import { batcherStatusSelector } from 'src/reducers'; -import { BatcherStatus } from 'src/types'; +import { batcherStatusSelector } from '@/reducers'; +import { BatcherStatus } from '@/types'; interface RootProps { Component: NextComponentType; diff --git a/batcher-ui/src/components/market-maker/GlobalVault.tsx b/batcher-ui/src/components/market-maker/GlobalVault.tsx new file mode 100644 index 00000000..e9f91fdf --- /dev/null +++ b/batcher-ui/src/components/market-maker/GlobalVault.tsx @@ -0,0 +1,57 @@ +import { useEffect } from 'react'; +import { useDispatch } from 'react-redux'; +import { useSelector } from 'react-redux'; +import { getGlobalVault } from '@/actions'; +import { selectCurrentVaultName, selectGlobalVault } from '@/reducers'; + +const GlobalVault = () => { + const dispatch = useDispatch(); + + const globalVault = useSelector(selectGlobalVault); + const tokenName = useSelector(selectCurrentVaultName); + + useEffect(() => { + dispatch(getGlobalVault()); + }, [dispatch]); + + //TODO + // const showForeignAssets = (assets: Map) => ( + //
+ // {assets.size > 0 ? ( + // Object.values(assets).map(a => showTokenAmount({ vaultToken: a })) + // ) : ( + //
+ // )} + //
+ // ); + + return ( +
+
+ {globalVault && ( +
+
+ {`Total Shares: ${globalVault.shares}`} +
+

Native Asset

+
+

+ {/* {vaultToken?.name} : {vaultToken?.amount} */} + {tokenName} : {globalVault.shares} +

+
+
+
+

Foreign Assets

+ + {/*TODO {showForeignAssets(currentGlobalVault.foreign)} */} +
+
+
+ )} +
+
+ ); +}; + +export default GlobalVault; diff --git a/batcher-ui/src/components/market-maker/MMVault.tsx b/batcher-ui/src/components/market-maker/MMVault.tsx new file mode 100644 index 00000000..7604f822 --- /dev/null +++ b/batcher-ui/src/components/market-maker/MMVault.tsx @@ -0,0 +1,33 @@ +import React, { useEffect } from 'react'; +import SelectMMPair from './SelectMMPair'; +import { useSelector } from 'react-redux'; +import { fetchUserBalances } from '@/actions'; +import { userAddressSelector } from '@/reducers'; +import { useDispatch } from 'react-redux'; +import GlobalVault from './GlobalVault'; +import UserVault from './UserVault'; + +const MMVaultComponent = () => { + const dispatch = useDispatch(); + const userAddress = useSelector(userAddressSelector); + + useEffect(() => { + if (userAddress) dispatch(fetchUserBalances()); + }, [dispatch, userAddress]); + + return ( +
+
+
+

Market Maker Vaults

+
+
+
+ + + +
+
+ ); +}; +export default MMVaultComponent; diff --git a/batcher-ui/src/components/SelectMMPair.tsx b/batcher-ui/src/components/market-maker/SelectMMPair.tsx similarity index 77% rename from batcher-ui/src/components/SelectMMPair.tsx rename to batcher-ui/src/components/market-maker/SelectMMPair.tsx index 0a4943d0..49b9ca6e 100644 --- a/batcher-ui/src/components/SelectMMPair.tsx +++ b/batcher-ui/src/components/market-maker/SelectMMPair.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import * as Select from '@radix-ui/react-select'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { @@ -7,14 +7,28 @@ import { faChevronUp, } from '@fortawesome/free-solid-svg-icons'; import { useDispatch, useSelector } from 'react-redux'; -import { changeVault } from 'src/actions'; -import { getCurrentVaultName, getGlobalVaults } from 'src/reducers'; +import { changeVault } from '@/actions'; +import { selectCurrentVaultName } from '@/reducers'; +import { getTokensMetadata } from '@/utils/token-manager'; const SelectMMPair = () => { const dispatch = useDispatch(); - const currentVaultName = useSelector(getCurrentVaultName); - const globalVaults = useSelector(getGlobalVaults); + const currentVaultName = useSelector(selectCurrentVaultName); + + const [tokens, setTokens] = useState< + { name: string; address: string; icon: string | undefined }[] + >([]); + + useEffect(() => { + getTokensMetadata().then( + ( + tokens: { name: string; address: string; icon: string | undefined }[] + ) => { + setTokens(tokens); + } + ); + }, []); return ( { - {Object.keys(globalVaults).map(t => ( - - {t} + {tokens.map(t => ( + + {t.name} ))} @@ -66,8 +80,7 @@ const SelectItem = React.forwardRef< disabled={disabled} className={`text-base text-dark rounded flex items-center h-[25px] pr-[35px] pl-[25px] relative select-none data-[highlighted]:outline-none data-[highlighted]:bg-hovergray disabled:cursor-not-allowed`} {...props} - ref={forwardedRef} - > + ref={forwardedRef}> {children} diff --git a/batcher-ui/src/components/market-maker/UserVault.tsx b/batcher-ui/src/components/market-maker/UserVault.tsx new file mode 100644 index 00000000..ca2b8ae6 --- /dev/null +++ b/batcher-ui/src/components/market-maker/UserVault.tsx @@ -0,0 +1,336 @@ +import { useEffect } from 'react'; +import { useDispatch } from 'react-redux'; +import { useSelector } from 'react-redux'; +import { getUserVault } from '@/actions'; +import { selectUserVault, userAddressSelector } from '@/reducers'; +import PrimaryButton from '@/components/common/PrimaryButton'; +import { Form } from '@radix-ui/react-form'; + +const UserVault = () => { + const userAddress = useSelector(userAddressSelector); + const dispatch = useDispatch(); + + const userVault = useSelector(selectUserVault); + + useEffect(() => { + if (userAddress) dispatch(getUserVault(userAddress)); + }, [userAddress, dispatch]); + + //TODO : refacto + // const addLiquidity = async ({ + // tokenName, + // tokenAmount, + // }: { + // tokenName: string; + // tokenAmount: number; + // }) => { + // if (!userAddress) { + // console.info('No user address'); + // return; + // } + // if (!marketMakerAddress) { + // console.info('No contract address'); + // return; + // } + + // const mmContract = await tezos?.wallet.at(marketMakerAddress); + + // if (!currentGlobalVault.native.address) { + // return; //TODO:improve this + // } + // const token = currentGlobalVault.native; + // const tokenContract = await tezos?.wallet.at( + // token.address, + // compose(tzip12, tzip16) + // ); + + // const scaled_amount = scaleAmountUp(tokenAmount, token.decimals); + + // // This is for fa2 token standard. I.e, USDT token + // const fa2_add_operator_params = [ + // { + // add_operator: { + // owner: userAddress, + // operator: marketMakerAddress, + // token_id: token.id, + // }, + // }, + // ]; + + // const fa2_remove_operator_params = [ + // { + // remove_operator: { + // owner: userAddress, + // operator: marketMakerAddress, + // token_id: token.id, + // }, + // }, + // ]; + + // try { + // let liq_op: BatchWalletOperation | undefined = undefined; + // const liq_params = { + // token: { + // token_id: token.id, + // name: token.name, + // address: token.address, + // decimals: token.decimals, + // standard: token.standard, + // }, + // amount: scaled_amount, + // }; + + // if (token.standard === 'FA1.2 token') { + // const tokenfa12Contract = await tezos.wallet.at( + // token.address, + // compose(tzip12, tzip16) + // ); + + // liq_op = await tezos?.wallet + // .batch([ + // { + // kind: OpKind.TRANSACTION, + // ...tokenfa12Contract.methods + // .approve(marketMakerAddress, scaled_amount) + // .toTransferParams(), + // }, + // { + // kind: OpKind.TRANSACTION, + // ...mmContract?.methodsObject + // .addLiquidity(liq_params) + // .toTransferParams(), + // to: marketMakerAddress, + // amount: 0, + // mutez: true, + // }, + // ]) + // .send(); + // } + + // if (token.standard === 'FA2 token') { + // liq_op = await tezos?.wallet + // .batch([ + // { + // kind: OpKind.TRANSACTION, + // ...tokenContract.methods + // .update_operators(fa2_add_operator_params) + // .toTransferParams(), + // }, + // { + // kind: OpKind.TRANSACTION, + // ...mmContract?.methodsObject + // .addLiquidity(liq_params) + // .toTransferParams(), + // to: marketMakerAddress, + // amount: 0, + // mutez: true, + // }, + // { + // kind: OpKind.TRANSACTION, + // ...tokenContract.methods + // .update_operators(fa2_remove_operator_params) + // .toTransferParams(), + // }, + // ]) + // .send(); + // } + + // if (!liq_op) { + // console.error('Liquidity Operation is not defined...'); + // throw new Error('Liquidity Operation is not defined...'); + // } + + // const confirm = await liq_op?.confirmation(); + + // confirm?.completed + // ? console.log('Successfully added liquidity !!!!!!') + // : null; + + // if (!confirm?.completed) { + // console.error(confirm); + // throw new Error(`Failed to add liquidity ${token.name} token.`); + // } else { + // console.info(`Successfully added liquidity ${tokenName}`); + + // dispatch(fetchUserBalances()); + // setAmount('0'); + // } + // } catch (error) { + // console.log('liquidity error', error); + // } + // }; + + // const claimRewards = async ({ tokenName }: { tokenName: string }) => { + // console.log('claiming'); + // try { + // if (!tezos || !marketMakerAddress) { + // throw new Error('Failed to initialize communication with contract.'); + // } + // const contractWallet = await tezos.wallet.at(marketMakerAddress); + + // let claimTransaction = await contractWallet.methods + // .claim(tokenName) + // .send(); + + // if (claimTransaction) { + // const confirm = await claimTransaction.confirmation(); + // if (!confirm?.completed) { + // console.error('Failed to claimed rewards' + confirm); + // } else { + // console.info('Successfully claimed rewards'); + // } + // } else { + // throw new Error('Failed to claimed rewards'); + // } + // } catch (error: any) { + // console.error('Unable to claimed rewards' + error); + // } + // }; + + // const showClaimRewards = ({ vaultToken }: { vaultToken: VaultToken }) => { + // return ( + //
+ // { + // event.preventDefault(); + // claimRewards({ + // tokenName: vaultToken.name, + // }); + // }}> + // + // + // + // + //
+ // ); + // }; + + // const removeLiquidity = async ({ tokenName }: { tokenName: string }) => { + // console.log('removing'); + // try { + // if (!tezos || !marketMakerAddress) { + // throw new Error('Failed to initialize communication with contract.'); + // } + // const contractWallet = await tezos.wallet.at(marketMakerAddress); + + // let claimTransaction = await contractWallet.methods + // .removeLiquidity(tokenName) + // .send(); + + // if (claimTransaction) { + // const confirm = await claimTransaction.confirmation(); + // if (!confirm?.completed) { + // console.error('Failed to remove liquidity' + confirm); + // } else { + // console.info('Successfully removed liquidity '); + // } + // } else { + // throw new Error('Failed to removed liquidity'); + // } + // } catch (error: any) { + // console.error('Unable to remove liquidity' + error); + // } + // }; + + // const showRemoveLiquidity = ({ vaultToken }: { vaultToken: VaultToken }) => { + // return ( + //
+ // { + // event.preventDefault(); + // removeLiquidity({ + // tokenName: vaultToken.name, + // }); + // }}> + // + // + // + // + //
+ // ); + // }; + + // const showAddLiquidity = ({ + // vaultToken, + // userBalances, + // }: { + // vaultToken: VaultToken; + // userBalances: Record; + // }) => { + // return ( + //
+ // { + // event.preventDefault(); + // addLiquidity({ + // tokenName: vaultToken.name, + // tokenAmount: parseInt(amountInput), + // }); + // }}> + // + //
+ // + //

+ // {`Balance : ${ + // userBalances[vaultToken.name.toUpperCase()] || 0 + // }`} + //

+ //
+ //
+ //
+ // + // { + // (/^\d*\.{0,1}\d*$/.test(event.target.value) || + // event.target.value === '') && + // setAmount(event.target.value); + // }} + // type="text" + // value={amountInput} + // defaultValue={0} + // /> + // + //
+ //
+ // + // + // + //
+ //
+ // ); + // }; + + return ( +
+
+ {userVault && ( +
+
+

My Liquidity

+

{`Shares: ${userVault.shares}`}

+ {`Unclaimed Rewards: ${userVault.unclaimed} TEZ`} + {/* {showAddLiquidity({ + vaultToken: currentGlobalVault.native, + userBalances: userBalances, + })} + {showRemoveLiquidity({ + vaultToken: currentGlobalVault.native, + })} + {showClaimRewards({ + vaultToken: currentGlobalVault.native, + })} */} +
+
+ )} +
+
+ ); +}; + +export default UserVault; diff --git a/batcher-ui/src/config/token-vaults.ts b/batcher-ui/src/config/token-vaults.ts new file mode 100644 index 00000000..9ed11049 --- /dev/null +++ b/batcher-ui/src/config/token-vaults.ts @@ -0,0 +1,7 @@ +export const tokenVaultsConfig: { [key: string]: string } = { + tzBTC: process.env.NEXT_PUBLIC_TZBTC_VAULT_CONTRACT_HASH, + USDT: process.env.NEXT_PUBLIC_USDT_VAULT_CONTRACT_HASH, + EURL: process.env.NEXT_PUBLIC_EURL_VAULT_CONTRACT_HASH, + BTCtz: process.env.NEXT_PUBLIC_BTCTZ_VAULT_CONTRACT_HASH, + USDtz: process.env.NEXT_PUBLIC_USDTZ_VAULT_CONTRACT_HASH, +}; diff --git a/batcher-ui/src/contexts/events.tsx b/batcher-ui/src/contexts/events.tsx index f040abc9..7ff0a8da 100644 --- a/batcher-ui/src/contexts/events.tsx +++ b/batcher-ui/src/contexts/events.tsx @@ -2,9 +2,9 @@ import { /* HubConnection */ HubConnectionBuilder } from '@microsoft/signalr'; import React, { createContext, useEffect } from 'react'; // import { useSelector } from 'react-redux'; import { useDispatch } from 'react-redux'; -import { newEvent } from 'src/actions/events'; +import { newEvent } from '@/actions/events'; // import { userAddressSelector } from 'src/reducers'; -import { setup /* subscribeTokenBalances */ } from 'src/utils/webSocketUtils'; +import { setup /* subscribeTokenBalances */ } from '@/utils/webSocketUtils'; export const EventsContext = createContext<{}>({}); diff --git a/batcher-ui/src/contexts/tezos-toolkit.tsx b/batcher-ui/src/contexts/tezos-toolkit.tsx index f6d7d5fd..ce6da160 100644 --- a/batcher-ui/src/contexts/tezos-toolkit.tsx +++ b/batcher-ui/src/contexts/tezos-toolkit.tsx @@ -1,31 +1,21 @@ import { TezosToolkit } from '@taquito/taquito'; -import React, { createContext, useEffect, useState } from 'react'; +import React, { createContext } from 'react'; type TzTkState = { - tezos?: TezosToolkit; + tezos: TezosToolkit; }; export const TezosToolkitContext = createContext({ - tezos: undefined, + tezos: new TezosToolkit(process.env.NEXT_PUBLIC_TEZOS_NODE_URI), }); -export const TezosToolkitProvider = ({ - children, -}: { - children: React.ReactNode; -}) => { - const [tezos, setConnection] = useState(); - useEffect(() => { - if (process.env.NEXT_PUBLIC_TEZOS_NODE_URI) { - setConnection(new TezosToolkit(process.env.NEXT_PUBLIC_TEZOS_NODE_URI)); - } - }, []); +export const useTezosToolkit = () => React.useContext(TezosToolkitContext); +export const TezosToolkitProvider = ({ children }: { children: React.ReactNode }) => { + const { tezos } = useTezosToolkit(); return ( {children} ); }; - -export const useTezosToolkit = () => React.useContext(TezosToolkitContext); \ No newline at end of file diff --git a/batcher-ui/pages/404.tsx b/batcher-ui/src/pages/404.tsx similarity index 100% rename from batcher-ui/pages/404.tsx rename to batcher-ui/src/pages/404.tsx diff --git a/batcher-ui/pages/_app.tsx b/batcher-ui/src/pages/_app.tsx similarity index 86% rename from batcher-ui/pages/_app.tsx rename to batcher-ui/src/pages/_app.tsx index 73ebe769..709fe80c 100644 --- a/batcher-ui/pages/_app.tsx +++ b/batcher-ui/src/pages/_app.tsx @@ -1,17 +1,17 @@ import React from 'react'; import { AppProps } from 'next/app'; -import { TezosToolkitProvider } from '../src/contexts/tezos-toolkit'; -import { WalletProvider } from '../src/contexts/wallet'; -import { EventsProvider } from '../src/contexts/events'; -import '../styles/globals.css'; +import { TezosToolkitProvider } from '@/contexts/tezos-toolkit'; +import { WalletProvider } from '@/contexts/wallet'; +import { EventsProvider } from '@/contexts/events'; +import '../../styles/globals.css'; import { Provider } from 'react-redux'; -import { store } from '../src/store'; +import { store } from '@/store'; import ReactGA from 'react-ga4'; import Head from 'next/head'; import { config } from '@fortawesome/fontawesome-svg-core'; import '@fortawesome/fontawesome-svg-core/styles.css'; -import Root from '../src/components/Root'; -import Toast from 'src/components/Toast'; +import Root from '@/components/layout/Root'; +import Toast from '@/components/common/Toast'; config.autoAddCss = false; diff --git a/batcher-ui/pages/_document.tsx b/batcher-ui/src/pages/_document.tsx similarity index 100% rename from batcher-ui/pages/_document.tsx rename to batcher-ui/src/pages/_document.tsx diff --git a/batcher-ui/pages/about.tsx b/batcher-ui/src/pages/about.tsx similarity index 100% rename from batcher-ui/pages/about.tsx rename to batcher-ui/src/pages/about.tsx diff --git a/batcher-ui/pages/holdings.tsx b/batcher-ui/src/pages/holdings.tsx similarity index 94% rename from batcher-ui/pages/holdings.tsx rename to batcher-ui/src/pages/holdings.tsx index 3297ccb8..24b21813 100644 --- a/batcher-ui/pages/holdings.tsx +++ b/batcher-ui/src/pages/holdings.tsx @@ -1,12 +1,8 @@ import React, { useCallback, useContext, useEffect } from 'react'; -import { TezosToolkitContext } from 'src/contexts/tezos-toolkit'; +import { TezosToolkitContext } from '@/contexts/tezos-toolkit'; import { useDispatch, useSelector } from 'react-redux'; -import { getHoldings, userAddressSelector } from 'src/reducers'; -import { - getHoldings as getHoldingsAction, - newError, - newInfo, -} from 'src/actions'; +import { getHoldings, userAddressSelector } from '@/reducers'; +import { getHoldings as getHoldingsAction, newError, newInfo } from '@/actions'; const Holdings = () => { const { tezos } = useContext(TezosToolkitContext); diff --git a/batcher-ui/pages/index.tsx b/batcher-ui/src/pages/index.tsx similarity index 69% rename from batcher-ui/pages/index.tsx rename to batcher-ui/src/pages/index.tsx index d556e147..752235c6 100644 --- a/batcher-ui/pages/index.tsx +++ b/batcher-ui/src/pages/index.tsx @@ -1,15 +1,11 @@ import React, { useEffect } from 'react'; -import Exchange from '../src/components/Exchange'; -import BatcherInfo from '../src/components/BatcherInfo'; -import PriceStrategy from '../src/components/PriceStrategy'; +import Exchange from '@/components/batcher/Exchange'; +import BatcherInfo from '@/components/batcher/BatcherInfo'; +import PriceStrategy from '@/components/batcher/PriceStrategy'; import { useSelector, useDispatch } from 'react-redux'; -import { currentPairSelector, userAddressSelector } from '../src/reducers'; -import { - fetchUserBalances, - batcherUnsetup, - getPairsInfos, -} from '../src/actions'; +import { currentPairSelector, userAddressSelector } from '@/reducers'; +import { fetchUserBalances, batcherUnsetup, getPairsInfos } from '@/actions'; const Swap = () => { const userAddress = useSelector(userAddressSelector); diff --git a/batcher-ui/pages/marketmaker.tsx b/batcher-ui/src/pages/marketmaker.tsx similarity index 63% rename from batcher-ui/pages/marketmaker.tsx rename to batcher-ui/src/pages/marketmaker.tsx index fac01f2f..91409dfc 100644 --- a/batcher-ui/pages/marketmaker.tsx +++ b/batcher-ui/src/pages/marketmaker.tsx @@ -1,12 +1,12 @@ -import React, { useEffect } from 'react'; +import React, { useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { userAddressSelector } from 'src/reducers'; -import { getMarketHoldings as getMarketHoldingsAction } from 'src/actions'; -import MMVault from '../src/components/MMVault'; +import { userAddressSelector } from '@/reducers'; +import { getMarketHoldings as getMarketHoldingsAction } from '@/actions'; +import MMVault from '@/components/market-maker/MMVault'; const MarketMakerHoldings = () => { const contractAddress = - process.env.NEXT_PUBLIC_MARKETMAKER_CONTRACT_HASH || ''; + process.env.NEXT_PUBLIC_MARKET_MAKER_CONTRACT_HASH || ''; const userAddress = useSelector(userAddressSelector); diff --git a/batcher-ui/pages/volumes.tsx b/batcher-ui/src/pages/volumes.tsx similarity index 94% rename from batcher-ui/pages/volumes.tsx rename to batcher-ui/src/pages/volumes.tsx index 03b5cc86..aa14ba80 100644 --- a/batcher-ui/pages/volumes.tsx +++ b/batcher-ui/src/pages/volumes.tsx @@ -1,9 +1,9 @@ import React, { useEffect } from 'react'; -import { PriceStrategy } from 'src/types'; -import { batchNumberSelector, volumesSelector } from 'src/reducers'; +import { PriceStrategy } from '@/types'; +import { batchNumberSelector, volumesSelector } from '@/reducers'; import { useSelector } from 'react-redux'; import { useDispatch } from 'react-redux'; -import { getVolumes } from 'src/actions'; +import { getVolumes } from '@/actions'; const Volume = () => { const { sell, buy } = useSelector(volumesSelector); diff --git a/batcher-ui/src/reducers/events.ts b/batcher-ui/src/reducers/events.ts index c8329ed2..0b0adf92 100644 --- a/batcher-ui/src/reducers/events.ts +++ b/batcher-ui/src/reducers/events.ts @@ -1,7 +1,7 @@ import { loop } from 'redux-loop'; -import { EventActions } from 'src/actions/events'; -import { newEventCmd } from 'src/commands/events'; -import { EventsState } from 'src/types'; +import { EventActions } from '@/actions/events'; +import { newEventCmd } from '@/commands/events'; +import { EventsState } from '@/types'; const initialState: EventsState = { toast: { diff --git a/batcher-ui/src/reducers/holdings.ts b/batcher-ui/src/reducers/holdings.ts index cc9ddabd..78b9ec0d 100644 --- a/batcher-ui/src/reducers/holdings.ts +++ b/batcher-ui/src/reducers/holdings.ts @@ -1,7 +1,7 @@ import { Cmd, loop } from 'redux-loop'; -import { HoldingsActions } from 'src/actions/holdings'; -import { fetchHoldingsCmd } from 'src/commands/holdings'; -import { HoldingsState } from 'src/types'; +import { HoldingsActions } from '@/actions/holdings'; +import { fetchHoldingsCmd } from '@/commands/holdings'; +import { HoldingsState } from '@/types'; const initialState: HoldingsState = { open: { tzBTC: 0, USDT: 0, EURL: 0 }, diff --git a/batcher-ui/src/reducers/index.ts b/batcher-ui/src/reducers/index.ts index 75c481c9..dc3a81aa 100644 --- a/batcher-ui/src/reducers/index.ts +++ b/batcher-ui/src/reducers/index.ts @@ -1,6 +1,6 @@ import { LoopReducer, combineReducers } from 'redux-loop'; -import exchangeReducer from './exchange'; -import walletReducer from './wallet'; +import exchangeReducer from '@/reducers/exchange'; +import walletReducer from '@/reducers/wallet'; import { AppState, ExchangeState, @@ -9,9 +9,9 @@ import { EventsState, HoldingsState, } from '../types'; -import { marketHoldingsReducer } from './marketholdings'; -import { eventReducer } from './events'; -import { holdingsReducer } from './holdings'; +import { marketHoldingsReducer } from '@/reducers/marketholdings'; +import { eventReducer } from '@/reducers/events'; +import { holdingsReducer } from '@/reducers/holdings'; // Wallet selectors export const userAddressSelector = (state: AppState) => { @@ -62,12 +62,18 @@ export const getCurrentUserVaultSelector = (state: AppState) => export const getCurrentGlobalVaultSelector = (state: AppState) => state.marketHoldings.globalVaults[state.marketHoldings.currentVault]; -export const getCurrentVaultName = (state: AppState) => - state.marketHoldings.currentVault; - export const getGlobalVaults = (state: AppState) => state.marketHoldings.globalVaults; +export const selectUserVault = (state: AppState) => + state.marketHoldings.currentUserVault; + +export const selectGlobalVault = (state: AppState) => + state.marketHoldings.currentGlobalVault; + +export const selectCurrentVaultName = (state: AppState) => + state.marketHoldings.currentVault; + // Events selectors export const getToastInfosSelector = (state: AppState) => state.events.toast; diff --git a/batcher-ui/src/reducers/marketholdings.ts b/batcher-ui/src/reducers/marketholdings.ts index 761f70c5..bf1a8765 100644 --- a/batcher-ui/src/reducers/marketholdings.ts +++ b/batcher-ui/src/reducers/marketholdings.ts @@ -1,12 +1,24 @@ import { Cmd, loop } from 'redux-loop'; -import { MarketHoldingsActions } from 'src/actions/marketholdings'; -import { fetchMarketHoldingsCmd } from 'src/commands/marketholdings'; -import { MarketHoldingsState } from 'src/types'; +import { MarketHoldingsActions } from '@/actions'; +import { + fetchGlobalVaultCmd, + fetchMarketHoldingsCmd, + fetchUserVaultCmd, +} from '@/commands/marketholdings'; +import { MarketHoldingsState } from '@/types'; export const initialMHState: MarketHoldingsState = { globalVaults: {}, userVaults: {}, currentVault: 'tzBTC', + + currentUserVault: { + shares: 0, + unclaimed: 0, + }, + currentGlobalVault: { + shares: 0, + }, }; export const marketHoldingsReducer = ( @@ -38,6 +50,28 @@ export const marketHoldingsReducer = ( action.payload.userAddress ) ); + case 'GET_USER_VAULT': + return loop( + state, + fetchUserVaultCmd(action.payload.userAddress, state.currentVault) + ); + case 'UPDATE_USER_VAULT': + return { + ...state, + currentUserVault: { + shares: action.payload.vault.shares, + unclaimed: action.payload.vault.unclaimed, + }, + }; + case 'GET_GLOBAL_VAULT': + return loop(state, fetchGlobalVaultCmd(state.currentVault)); + case 'UPDATE_GLOBAL_VAULT': + return { + ...state, + currentGlobalVault: { + shares: action.payload.vault.shares, + }, + }; default: return state; } diff --git a/batcher-ui/src/reducers/wallet.ts b/batcher-ui/src/reducers/wallet.ts index 597167ba..3898b6ee 100644 --- a/batcher-ui/src/reducers/wallet.ts +++ b/batcher-ui/src/reducers/wallet.ts @@ -1,8 +1,8 @@ -import { WalletActions } from 'src/actions'; +import { WalletActions } from '@/actions'; import { Loop, liftState, loop } from 'redux-loop'; -import { fetchUserBalancesCmd } from '../commands/wallet'; -import { WalletState } from 'src/types'; -import { TOKENS } from 'src/utils/utils'; +import { fetchUserBalancesCmd } from '@/commands/wallet'; +import { WalletState } from '@/types'; +import { TOKENS } from '@/utils/utils'; const initialState: WalletState = { userAddress: undefined, diff --git a/batcher-ui/src/store.ts b/batcher-ui/src/store.ts index a01ab805..8129d86d 100644 --- a/batcher-ui/src/store.ts +++ b/batcher-ui/src/store.ts @@ -5,8 +5,8 @@ import { } from 'redux'; import { install, LoopReducer, StoreCreator } from 'redux-loop'; import { createLogger } from 'redux-logger'; -import rootReducer from './reducers'; -import { AppState } from './types'; +import rootReducer from '@/reducers'; +import { AppState } from '@/types'; const enhancedStore = createStore as StoreCreator; diff --git a/batcher-ui/src/types/contracts/batcher.ts b/batcher-ui/src/types/contracts/batcher.ts index e52441d2..b0d21d74 100644 --- a/batcher-ui/src/types/contracts/batcher.ts +++ b/batcher-ui/src/types/contracts/batcher.ts @@ -1,4 +1,4 @@ -import { Token } from '../state'; +import type { Token } from '@/types/state'; export enum BatcherStatus { OPEN = 'OPEN', diff --git a/batcher-ui/src/types/contracts/token-vault.ts b/batcher-ui/src/types/contracts/token-vault.ts index f2d5187c..e1d6e8ff 100644 --- a/batcher-ui/src/types/contracts/token-vault.ts +++ b/batcher-ui/src/types/contracts/token-vault.ts @@ -35,3 +35,15 @@ export type TokenVaultStorage = { */ vault_holdings: number; }; + +export type VaultHoldingsBigmapItem = { + holder: string; + /** + * Number corresponding to shares for user + */ + shares: string; + /** + * Number corresponding to amount of tez unclaimed + */ + unclaimed: string; +}; diff --git a/batcher-ui/src/types/events.ts b/batcher-ui/src/types/events.ts index 06cf5b4f..5168b168 100644 --- a/batcher-ui/src/types/events.ts +++ b/batcher-ui/src/types/events.ts @@ -1,4 +1,8 @@ -import { BatchBigmap, OrderBookBigmap, RatesCurrentBigmap } from "./contract"; +import { + BatchBigmap, + OrderBookBigmap, + RatesCurrentBigmap, +} from '@/types/contracts/batcher'; export type BigMapEvent = { data: Array; diff --git a/batcher-ui/src/types/state.ts b/batcher-ui/src/types/state.ts index ccda5cb8..1c15db65 100644 --- a/batcher-ui/src/types/state.ts +++ b/batcher-ui/src/types/state.ts @@ -1,4 +1,8 @@ -import { BatcherStatus, PriceStrategy, SwapNames } from './contract'; +import { + BatcherStatus, + PriceStrategy, + SwapNames, +} from '@/types/contracts/batcher'; export type Token = { address: string | undefined; @@ -83,6 +87,16 @@ export type MarketHoldingsState = { // userVaults: Map; // current_vault: MVault; currentVault: string; // token name (EURL, USDT, tzBTC) + + currentUserVault: { + shares: number; + unclaimed: number; + }; + + currentGlobalVault: { + shares: number; + //TODO: fill with others informations needed + }; }; export type EventsState = { diff --git a/batcher-ui/src/utils/market-maker.ts b/batcher-ui/src/utils/market-maker.ts index a33a626e..fc25e4f0 100644 --- a/batcher-ui/src/utils/market-maker.ts +++ b/batcher-ui/src/utils/market-maker.ts @@ -1,12 +1,20 @@ // MARKET MAKER HOLDINGS -import { ContractToken, GlobalVault, UserVault, VaultToken } from 'src/types'; -import { checkStatus, scaleAmountDown } from './utils'; +import type { + ContractToken, + GlobalVault, + MarketMakerStorage, + TokenVaultStorage, + UserVault, + VaultToken, +} from '@/types'; +import { checkStatus, scaleAmountDown } from '@/utils/utils'; +import { tokenVaultsConfig } from '@/config/token-vaults'; -const getMarketMakerStorage = (): Promise => { +const getMarketMakerStorage = (): Promise => { // const getMarketMakerStorage = (): Promise => { return fetch( - `${process.env.NEXT_PUBLIC_TZKT_API_URI}/v1/contracts/${process.env.NEXT_PUBLIC_MARKETMAKER_CONTRACT_HASH}/storage` + `${process.env.NEXT_PUBLIC_TZKT_API_URI}/v1/contracts/${process.env.NEXT_PUBLIC_MARKET_MAKER_CONTRACT_HASH}/storage` ).then(checkStatus); }; @@ -85,6 +93,10 @@ const getUserVault = async ( export const getMarketHoldings = async (userAddress: string) => { const storage = await getMarketMakerStorage(); + console.log( + '🚀 ~ file: market-maker.ts:88 ~ getMarketHoldings ~ storage:', + storage + ); const userVaults = await Promise.all( Object.keys(storage.valid_tokens).map(async token => { const userVaultKey: string = `{"string":"${token}","address":"${userAddress}"}`; @@ -139,3 +151,43 @@ export const getMarketHoldings = async (userAddress: string) => { }, {}); return { globalVaults: x, userVaults: y }; }; + +// ---- WIP ---- // + +const getTokenVaultStorage = async ( + tokenName: string +): Promise => + fetch( + `${process.env.NEXT_PUBLIC_TZKT_API_URI}/v1/contracts/${tokenVaultsConfig[tokenName]}/storage` + ).then(checkStatus); + +const getUserVaultFromBigmap2 = async (bigmapId: number, userAddress: string) => + fetch( + `${process.env.NEXT_PUBLIC_TZKT_API_URI}/v1/bigmaps/${bigmapId}/keys/${userAddress}` + ).then(checkStatus).then(vault => parseUserVault(vault.value)) + +const getUserVault2 = async (userAddress: string, tokenName: string) => { + console.log("🚀 ~ file: market-maker.ts:170 ~ getUserVault2 ~ userAddress: string, tokenName: string:", userAddress, tokenName) + const storage =await getTokenVaultStorage(tokenName); + const userVault = await getUserVaultFromBigmap2(storage.vault_holdings, userAddress) + console.warn("🚀 ~ file: market-maker.ts:172 ~ getUserVault2 ~ userVault:", userVault) + return userVault +}; + +const parseUserVault = (rawUserVault: any) => ({ + holder: rawUserVault.holder, + shares: parseInt(rawUserVault.shares, 10), + unclaimed: parseInt(rawUserVault.unclaimed) +}) + +const getGlobalVault = async (tokenName: string)=> { + const storage = await getTokenVaultStorage(tokenName) + return {shares : parseInt(storage.total_shares, 10)} +} + +export {getUserVault2, getGlobalVault}; + +// getTokenVaultStorage('tzBTC').then(console.warn); +// getUserVaultFromBigmap2(375834, 'tz1R2EoKLoJuyccMesBYGewEKCKmqqyBnYLc').then( +// console.warn +// ); \ No newline at end of file diff --git a/batcher-ui/src/utils/token-manager.ts b/batcher-ui/src/utils/token-manager.ts index 673b7286..5d47fda5 100644 --- a/batcher-ui/src/utils/token-manager.ts +++ b/batcher-ui/src/utils/token-manager.ts @@ -1,5 +1,5 @@ -import { TokenManagerStorage, ValidTokensBigmapItem } from 'src/types'; -import { checkStatus } from './utils'; +import { TokenManagerStorage, ValidTokensBigmapItem } from '@/types'; +import { checkStatus } from '@/utils/utils'; const getTokenManagerStorage = (): Promise => fetch( diff --git a/batcher-ui/src/utils/utils.ts b/batcher-ui/src/utils/utils.ts index cf204276..26b89b8c 100644 --- a/batcher-ui/src/utils/utils.ts +++ b/batcher-ui/src/utils/utils.ts @@ -18,13 +18,9 @@ import { TokenNames, SwapNames, RatesCurrentBigmap, - UserVault, - GlobalVault, - VaultToken, - ContractToken, -} from '../types'; +} from '@/types'; import { NetworkType } from '@airgap/beacon-sdk'; -import { getByKey } from './local-storage'; +import { getByKey } from '@/utils/local-storage'; export const scaleAmountDown = (amount: number, decimals: number) => { const scale = 10 ** -decimals; diff --git a/batcher-ui/tsconfig.json b/batcher-ui/tsconfig.json index 5381223a..0ed5f538 100644 --- a/batcher-ui/tsconfig.json +++ b/batcher-ui/tsconfig.json @@ -25,6 +25,11 @@ "esModuleInterop": true, "isolatedModules": true, "module": "esnext", + "paths": { + "@/*": [ + "./src/*" + ] + } }, "include": [ "src/**/*", @@ -34,7 +39,7 @@ ".eslintrc.json", ".prettierrc.json", "jest.config.js", - "pages/*", + "src/pages/*", "global.d.ts" ], "exclude": [