From a11d7482966468b08d1c58ab56c425935342c71d Mon Sep 17 00:00:00 2001 From: Sigri Date: Wed, 18 Feb 2026 22:11:34 +0100 Subject: [PATCH] Develop to preprod: Fix PR#135 (#136) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: unchecked asset filters (#95) * merge master in develop (#103) * Revert "Master" * merge preprod <> master (#67) * add dropdown * simplify selector and add all token option * feat: change allPage value to Infinity * feat: estimate the fully rented rent * feat: add fully rented estimation to asset cards * refactor: move hook calls to top of the page * fix: propInfo definition * fix: last rent condition * feat: add estimation for property with not fully rented history * feat: get fully rented APR * apply max APR method * rename variable and functions * feat: add RWA token * feat: re-enable property onClick * feat: add rwa valuation on the rwa card * fix: missing property * feat: add rwa to summary card * define useRWA * take into account user currency * feat: add RWA value to net value calculation * remove comment * refactor: clean imports * feat: include RWA on Ethereum * fix: en communs * feat: update filter to support RWA token * fix: prettier * fix: other prettier errors * let prettier add strange semi-column * fix: imports * use hook * add fallback * switch for a useMemo * feat: add real time fully rented APR * feat: add gloabl metric fully rented APR * feat: add disclaimer * feat: add disclaimer * feat: update disclaimer message * fix: disclaimer message * improve message * feat: create yam statics stics page * feat: add yam statistics for all RealT Tokens on Gnosis (who have Gnosis chain contract prop) * feat: mask tokens with no volume * fix: add token name * feat: add pagination * feat: improve style * feat: change token per page to 100 * feat: add fully rented APR to asset grid * refactor: remove logs * feat: add fully rented APR to property details * fix: reset current page when tokens changed * fix: reset current page when user change page size * feat: add translation for YAM statistics hearder label * fix: yamStatistics: use selected currency for token price * feat: yamStatistics: add owned | all filter * feat: yamStatistics: add subsidized, fullySubsidized and notSubsidized filters * add additional fallbacks RPC URLs * fix: RPC initialization on currencies file --------- Co-authored-by: Nandy Bâ * merge preprod <> master (#77) * add dropdown * simplify selector and add all token option * feat: change allPage value to Infinity * feat: estimate the fully rented rent * feat: add fully rented estimation to asset cards * refactor: move hook calls to top of the page * fix: propInfo definition * fix: last rent condition * feat: add estimation for property with not fully rented history * feat: get fully rented APR * apply max APR method * rename variable and functions * feat: add RWA token * feat: re-enable property onClick * feat: add rwa valuation on the rwa card * fix: missing property * feat: add rwa to summary card * define useRWA * take into account user currency * feat: add RWA value to net value calculation * remove comment * refactor: clean imports * feat: include RWA on Ethereum * fix: en communs * feat: update filter to support RWA token * fix: prettier * fix: other prettier errors * let prettier add strange semi-column * fix: imports * use hook * add fallback * switch for a useMemo * feat: add real time fully rented APR * feat: add gloabl metric fully rented APR * feat: add disclaimer * feat: add disclaimer * feat: update disclaimer message * fix: disclaimer message * improve message * feat: create yam statics stics page * feat: add yam statistics for all RealT Tokens on Gnosis (who have Gnosis chain contract prop) * feat: mask tokens with no volume * fix: add token name * feat: add pagination * feat: improve style * feat: change token per page to 100 * feat: add fully rented APR to asset grid * refactor: remove logs * feat: add fully rented APR to property details * fix: reset current page when tokens changed * fix: reset current page when user change page size * feat: add translation for YAM statistics hearder label * fix: yamStatistics: use selected currency for token price * feat: yamStatistics: add owned | all filter * feat: yamStatistics: add subsidized, fullySubsidized and notSubsidized filters * add additional fallbacks RPC URLs * fix: RPC initialization on currencies file * feat: YamStatistic: add Yamp Volume's number of days * fix: fullyRentedAPR: fix french disclaimer text * fix: second disclaimer text small error * feat: fullyRentedAPR: manage VEFA properties * refactore: improve comment * feat: show VEFA properties forced fully rented APR only if property do not have tenants * fix: RWA table view * feat: add bridge link on header (#74) * fix: VEFA Realtime APR --------- Co-authored-by: Nandy Bâ Co-authored-by: Yohann Durand * Revert "merge preprod <> master (#77)" This reverts commit 68daad55434377c91e0224a580528e9a501e392f. * fix: APY fully rented for properties before start rent day (#85) Co-authored-by: alex <123092072+AlexRLT@users.noreply.github.com> * Change NS for RealToken (#89) (#90) --------- Co-authored-by: Sigri Co-authored-by: Kurtisone <104103601+Kurtisone@users.noreply.github.com> Co-authored-by: jycssu-com <110905167+jycssu-com@users.noreply.github.com> Co-authored-by: alex <123092072+AlexRLT@users.noreply.github.com> Co-authored-by: Jycssu Co-authored-by: Yohann Durand * feat: restrict merges to preprod (#104) * merge develop into master (#102) (#107) * Change NS for RealToken (#89) * chore: install required sharp dependency for production * chore(deps): bump body-parser from 1.20.2 to 1.20.3 (#87) Bumps [body-parser](https://github.com/expressjs/body-parser) from 1.20.2 to 1.20.3. - [Release notes](https://github.com/expressjs/body-parser/releases) - [Changelog](https://github.com/expressjs/body-parser/blob/master/HISTORY.md) - [Commits](https://github.com/expressjs/body-parser/compare/1.20.2...1.20.3) --- updated-dependencies: - dependency-name: body-parser dependency-type: indirect ... * feat: add new assets REG, REG Vote Power (#91) * first version : Assets as rows * 'other' assets display switch; 'other' assets shown in Summary 'other' assets = tokens others than RealTokens = RWA, REG, ... - Display switch for hiding 'other' assets in table rows - 'other' assets figures added to Summary section * contracts utils * use contract utils for fetching balances * Eth provider removed: REG Voting Power is not deployed on Eth * batchCallOneFunction not fully tested * error args * comments * wrong 'inheritance' * smol fix: properly throw 'User not found' error * style/comments * settings labels and icons * unitIcon: optional + alignment with other units * Filter 'other' assets in Grid view * Settings: switch icons, section names * fix: lint warning * fix: lint warning * fix: build error ? * fix: build error #2 ? * fix: build error #3 ? * fix: build error #4 ? * fix: build error #5 ? * Fix: existing filter error when filtering on "Owned on" * fix: avoid fetching others assets balances if user wallet was not set * feat: add other token price on cards and fix decimals rounds for total invest * fix: improve subsudy filter for exclude other realtokens * fix: fix last changes filter and use correct typing in filters for avoid futurs errors * feat: set initial launch date for RWA token * feat: initialize rpc provider only once * feat: include reg tokens locked inside the incentive vault * fix: fix eslint warnings and add missing translations * Update .gitignore exclude pnpm lockfile * Create .nvmrc add .nvmrc for switching between node versions * Update contract.ts optional parameters for customizing batch calls: batch max and min size, number of attempts before giving up * Regvotingpower: icon orange, size grows depending on amount * unused import * Usdc currency/rate * Voting power size & fill color depending on power amount * Voting power size & fill color depending on power amount * Fixes: token prices in USD, avoid fetching REG vault balances on Eth - Tokens values fix: Updated assets prices by using currenciesRates and user currency rate (previously considering usdc = usd and xdai = usd as well), now assets prices are converted from their respective currencies (e.g. usdc, xdai) to usd, then total value is priced in user choosen currency. - Smol fix: getAddressesLockedBalances was fetching vault balances on Eth, throwing errors as there is not such vault on Eth. Added parameters for fetching any number of vault by provider(s). * comment * Check Providers ability to handle requests - Check Providers ability to handle requests - Additionnal providers https://rpc.ankr.com/eth and https://eth-pokt.nodies.app currently fail to handle concurrent requests * Assets hooks moved in index for avoiding duplicate requests - Both SummaryCard and AssetsView use the same hooks for getting their data, leading to duplicate web3 requests Moving hooks in parent components prevents this form happening. * prettier * Avoid divide by zero in case asset.totalUnits is unknown/zero * "Clean" scripts * Assets filtering moved to the right place filter has to be in assetsView/AssetsView.tsx * Paging translation labels: placeholder, All * simplify pageSize handling * Assets filtering fix (causing pagination issues) OtherAssets filtering was improperly implemented into the map loops by skipping non "others assets" value, causing holes in display (missing cards). Filtering is now done in AssetsView where it should have been in the first place * Revert changes: realtime/global labels for rents calculation switch --------- * fix: API URLs issue #97 (#98) * addresses issue #97 Moves api urls to (.)ENV set utls to new api.realtoken.community domain (previously api.realt.community) * Update .env.sample missing update of sample .env * Update .env.sample * NEXT_PUBLIC_ prefix removed for REALTOKENAPI ENV vars * feat: update yarn lock (#99) * feat: add new API urls to deployment workflow (#100) * feat: pass new env variables to docker env (#101) * fix: unchecked asset filters (#95) * merge master in develop (#103) * Revert "Master" * merge preprod <> master (#67) * add dropdown * simplify selector and add all token option * feat: change allPage value to Infinity * feat: estimate the fully rented rent * feat: add fully rented estimation to asset cards * refactor: move hook calls to top of the page * fix: propInfo definition * fix: last rent condition * feat: add estimation for property with not fully rented history * feat: get fully rented APR * apply max APR method * rename variable and functions * feat: add RWA token * feat: re-enable property onClick * feat: add rwa valuation on the rwa card * fix: missing property * feat: add rwa to summary card * define useRWA * take into account user currency * feat: add RWA value to net value calculation * remove comment * refactor: clean imports * feat: include RWA on Ethereum * fix: en communs * feat: update filter to support RWA token * fix: prettier * fix: other prettier errors * let prettier add strange semi-column * fix: imports * use hook * add fallback * switch for a useMemo * feat: add real time fully rented APR * feat: add gloabl metric fully rented APR * feat: add disclaimer * feat: add disclaimer * feat: update disclaimer message * fix: disclaimer message * improve message * feat: create yam statics stics page * feat: add yam statistics for all RealT Tokens on Gnosis (who have Gnosis chain contract prop) * feat: mask tokens with no volume * fix: add token name * feat: add pagination * feat: improve style * feat: change token per page to 100 * feat: add fully rented APR to asset grid * refactor: remove logs * feat: add fully rented APR to property details * fix: reset current page when tokens changed * fix: reset current page when user change page size * feat: add translation for YAM statistics hearder label * fix: yamStatistics: use selected currency for token price * feat: yamStatistics: add owned | all filter * feat: yamStatistics: add subsidized, fullySubsidized and notSubsidized filters * add additional fallbacks RPC URLs * fix: RPC initialization on currencies file --------- * merge preprod <> master (#77) * add dropdown * simplify selector and add all token option * feat: change allPage value to Infinity * feat: estimate the fully rented rent * feat: add fully rented estimation to asset cards * refactor: move hook calls to top of the page * fix: propInfo definition * fix: last rent condition * feat: add estimation for property with not fully rented history * feat: get fully rented APR * apply max APR method * rename variable and functions * feat: add RWA token * feat: re-enable property onClick * feat: add rwa valuation on the rwa card * fix: missing property * feat: add rwa to summary card * define useRWA * take into account user currency * feat: add RWA value to net value calculation * remove comment * refactor: clean imports * feat: include RWA on Ethereum * fix: en communs * feat: update filter to support RWA token * fix: prettier * fix: other prettier errors * let prettier add strange semi-column * fix: imports * use hook * add fallback * switch for a useMemo * feat: add real time fully rented APR * feat: add gloabl metric fully rented APR * feat: add disclaimer * feat: add disclaimer * feat: update disclaimer message * fix: disclaimer message * improve message * feat: create yam statics stics page * feat: add yam statistics for all RealT Tokens on Gnosis (who have Gnosis chain contract prop) * feat: mask tokens with no volume * fix: add token name * feat: add pagination * feat: improve style * feat: change token per page to 100 * feat: add fully rented APR to asset grid * refactor: remove logs * feat: add fully rented APR to property details * fix: reset current page when tokens changed * fix: reset current page when user change page size * feat: add translation for YAM statistics hearder label * fix: yamStatistics: use selected currency for token price * feat: yamStatistics: add owned | all filter * feat: yamStatistics: add subsidized, fullySubsidized and notSubsidized filters * add additional fallbacks RPC URLs * fix: RPC initialization on currencies file * feat: YamStatistic: add Yamp Volume's number of days * fix: fullyRentedAPR: fix french disclaimer text * fix: second disclaimer text small error * feat: fullyRentedAPR: manage VEFA properties * refactore: improve comment * feat: show VEFA properties forced fully rented APR only if property do not have tenants * fix: RWA table view * feat: add bridge link on header (#74) * fix: VEFA Realtime APR --------- * Revert "merge preprod <> master (#77)" This reverts commit 68daad55434377c91e0224a580528e9a501e392f. * fix: APY fully rented for properties before start rent day (#85) * Change NS for RealToken (#89) (#90) --------- --------- Signed-off-by: dependabot[bot] Co-authored-by: Sigri Co-authored-by: Jycssu Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: BenoistP <75934369+BenoistP@users.noreply.github.com> Co-authored-by: Kurtisone <104103601+Kurtisone@users.noreply.github.com> Co-authored-by: jycssu-com <110905167+jycssu-com@users.noreply.github.com> Co-authored-by: alex <123092072+AlexRLT@users.noreply.github.com> Co-authored-by: Yohann Durand * feat: add workflow to restrict merges to master (#108) * feat: add product type filter (#94) * feat: add product type filter * refactor: change filter file name * feat: add product type i18n translations * feat: update REG and RWA type to support new filter * feat: automatically activate other assets fetch it equity token filter is selected * feat: add default value for product type filter if user un-checked it * Prevents settings menu from closing while clicking on calendar (#113) * Prevents settings menu from closing while clicking on calendar calendar is considered as "outside" of the settings menu - State added for watching calendar "display" state - Allow menu closing (on any click) when calendar is not opened and prevents it from closing when calendar is open * prettier * fix: other asset balance by wallet (#112) * tmp/test * debug removed * detailed balances by wallet type todo: - check amounts with vault - remove debug log - check types / interfaces * console.log debug removed * getChainId returns Provider's chain id used with getWalletChainName (wallets\walletsSelector.ts) * getWalletChainName returns "wallet" chain used with getChainId (repositories\RpcProvider.ts) * Sum's wallets token balances by chain (wallet type) returns addresses balances by "wallets" (chain) and sums all balances * prettier * feat: optimize RPC calls (#114) * feat: add address book lib * feat: add aave core lib * feat: add wallet balance provider provider ABI * feat: optimize getAddressesBalances to minimize RPC calls * fix: remove un-used lib * fix: erc20 abi * small enhancement fix: Balances details (by chain) missing #111 * fix: merge issue * useless console.dir --------- Co-authored-by: BenoistP Co-authored-by: BenoistP <75934369+BenoistP@users.noreply.github.com> * 73 feature add matomo analytics 2 (#110) * merge develop into master (#102) * Change NS for RealToken (#89) * chore: install required sharp dependency for production * chore(deps): bump body-parser from 1.20.2 to 1.20.3 (#87) Bumps [body-parser](https://github.com/expressjs/body-parser) from 1.20.2 to 1.20.3. - [Release notes](https://github.com/expressjs/body-parser/releases) - [Changelog](https://github.com/expressjs/body-parser/blob/master/HISTORY.md) - [Commits](https://github.com/expressjs/body-parser/compare/1.20.2...1.20.3) --- updated-dependencies: - dependency-name: body-parser dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat: add new assets REG, REG Vote Power (#91) * first version : Assets as rows * 'other' assets display switch; 'other' assets shown in Summary 'other' assets = tokens others than RealTokens = RWA, REG, ... - Display switch for hiding 'other' assets in table rows - 'other' assets figures added to Summary section * contracts utils * use contract utils for fetching balances * Eth provider removed: REG Voting Power is not deployed on Eth * batchCallOneFunction not fully tested * error args * comments * wrong 'inheritance' * smol fix: properly throw 'User not found' error * style/comments * settings labels and icons * unitIcon: optional + alignment with other units * Filter 'other' assets in Grid view * Settings: switch icons, section names * fix: lint warning * fix: lint warning * fix: build error ? * fix: build error #2 ? * fix: build error #3 ? * fix: build error #4 ? * fix: build error #5 ? * Fix: existing filter error when filtering on "Owned on" * fix: avoid fetching others assets balances if user wallet was not set * feat: add other token price on cards and fix decimals rounds for total invest * fix: improve subsudy filter for exclude other realtokens * fix: fix last changes filter and use correct typing in filters for avoid futurs errors * feat: set initial launch date for RWA token * feat: initialize rpc provider only once * feat: include reg tokens locked inside the incentive vault * fix: fix eslint warnings and add missing translations * Update .gitignore exclude pnpm lockfile * Create .nvmrc add .nvmrc for switching between node versions * Update contract.ts optional parameters for customizing batch calls: batch max and min size, number of attempts before giving up * Regvotingpower: icon orange, size grows depending on amount * unused import * Usdc currency/rate * Voting power size & fill color depending on power amount * Voting power size & fill color depending on power amount * Fixes: token prices in USD, avoid fetching REG vault balances on Eth - Tokens values fix: Updated assets prices by using currenciesRates and user currency rate (previously considering usdc = usd and xdai = usd as well), now assets prices are converted from their respective currencies (e.g. usdc, xdai) to usd, then total value is priced in user choosen currency. - Smol fix: getAddressesLockedBalances was fetching vault balances on Eth, throwing errors as there is not such vault on Eth. Added parameters for fetching any number of vault by provider(s). * comment * Check Providers ability to handle requests - Check Providers ability to handle requests - Additionnal providers https://rpc.ankr.com/eth and https://eth-pokt.nodies.app currently fail to handle concurrent requests * Assets hooks moved in index for avoiding duplicate requests - Both SummaryCard and AssetsView use the same hooks for getting their data, leading to duplicate web3 requests Moving hooks in parent components prevents this form happening. * prettier * Avoid divide by zero in case asset.totalUnits is unknown/zero * "Clean" scripts * Assets filtering moved to the right place filter has to be in assetsView/AssetsView.tsx * Paging translation labels: placeholder, All * simplify pageSize handling * Assets filtering fix (causing pagination issues) OtherAssets filtering was improperly implemented into the map loops by skipping non "others assets" value, causing holes in display (missing cards). Filtering is now done in AssetsView where it should have been in the first place * Revert changes: realtime/global labels for rents calculation switch --------- Co-authored-by: Jycssu * fix: API URLs issue #97 (#98) * addresses issue #97 Moves api urls to (.)ENV set utls to new api.realtoken.community domain (previously api.realt.community) * Update .env.sample missing update of sample .env * Update .env.sample * NEXT_PUBLIC_ prefix removed for REALTOKENAPI ENV vars * feat: update yarn lock (#99) * feat: add new API urls to deployment workflow (#100) * feat: pass new env variables to docker env (#101) * fix: unchecked asset filters (#95) * merge master in develop (#103) * Revert "Master" * merge preprod <> master (#67) * add dropdown * simplify selector and add all token option * feat: change allPage value to Infinity * feat: estimate the fully rented rent * feat: add fully rented estimation to asset cards * refactor: move hook calls to top of the page * fix: propInfo definition * fix: last rent condition * feat: add estimation for property with not fully rented history * feat: get fully rented APR * apply max APR method * rename variable and functions * feat: add RWA token * feat: re-enable property onClick * feat: add rwa valuation on the rwa card * fix: missing property * feat: add rwa to summary card * define useRWA * take into account user currency * feat: add RWA value to net value calculation * remove comment * refactor: clean imports * feat: include RWA on Ethereum * fix: en communs * feat: update filter to support RWA token * fix: prettier * fix: other prettier errors * let prettier add strange semi-column * fix: imports * use hook * add fallback * switch for a useMemo * feat: add real time fully rented APR * feat: add gloabl metric fully rented APR * feat: add disclaimer * feat: add disclaimer * feat: update disclaimer message * fix: disclaimer message * improve message * feat: create yam statics stics page * feat: add yam statistics for all RealT Tokens on Gnosis (who have Gnosis chain contract prop) * feat: mask tokens with no volume * fix: add token name * feat: add pagination * feat: improve style * feat: change token per page to 100 * feat: add fully rented APR to asset grid * refactor: remove logs * feat: add fully rented APR to property details * fix: reset current page when tokens changed * fix: reset current page when user change page size * feat: add translation for YAM statistics hearder label * fix: yamStatistics: use selected currency for token price * feat: yamStatistics: add owned | all filter * feat: yamStatistics: add subsidized, fullySubsidized and notSubsidized filters * add additional fallbacks RPC URLs * fix: RPC initialization on currencies file --------- Co-authored-by: Nandy Bâ * merge preprod <> master (#77) * add dropdown * simplify selector and add all token option * feat: change allPage value to Infinity * feat: estimate the fully rented rent * feat: add fully rented estimation to asset cards * refactor: move hook calls to top of the page * fix: propInfo definition * fix: last rent condition * feat: add estimation for property with not fully rented history * feat: get fully rented APR * apply max APR method * rename variable and functions * feat: add RWA token * feat: re-enable property onClick * feat: add rwa valuation on the rwa card * fix: missing property * feat: add rwa to summary card * define useRWA * take into account user currency * feat: add RWA value to net value calculation * remove comment * refactor: clean imports * feat: include RWA on Ethereum * fix: en communs * feat: update filter to support RWA token * fix: prettier * fix: other prettier errors * let prettier add strange semi-column * fix: imports * use hook * add fallback * switch for a useMemo * feat: add real time fully rented APR * feat: add gloabl metric fully rented APR * feat: add disclaimer * feat: add disclaimer * feat: update disclaimer message * fix: disclaimer message * improve message * feat: create yam statics stics page * feat: add yam statistics for all RealT Tokens on Gnosis (who have Gnosis chain contract prop) * feat: mask tokens with no volume * fix: add token name * feat: add pagination * feat: improve style * feat: change token per page to 100 * feat: add fully rented APR to asset grid * refactor: remove logs * feat: add fully rented APR to property details * fix: reset current page when tokens changed * fix: reset current page when user change page size * feat: add translation for YAM statistics hearder label * fix: yamStatistics: use selected currency for token price * feat: yamStatistics: add owned | all filter * feat: yamStatistics: add subsidized, fullySubsidized and notSubsidized filters * add additional fallbacks RPC URLs * fix: RPC initialization on currencies file * feat: YamStatistic: add Yamp Volume's number of days * fix: fullyRentedAPR: fix french disclaimer text * fix: second disclaimer text small error * feat: fullyRentedAPR: manage VEFA properties * refactore: improve comment * feat: show VEFA properties forced fully rented APR only if property do not have tenants * fix: RWA table view * feat: add bridge link on header (#74) * fix: VEFA Realtime APR --------- Co-authored-by: Nandy Bâ Co-authored-by: Yohann Durand * Revert "merge preprod <> master (#77)" This reverts commit 68daad55434377c91e0224a580528e9a501e392f. * fix: APY fully rented for properties before start rent day (#85) Co-authored-by: alex <123092072+AlexRLT@users.noreply.github.com> * Change NS for RealToken (#89) (#90) --------- Co-authored-by: Sigri Co-authored-by: Kurtisone <104103601+Kurtisone@users.noreply.github.com> Co-authored-by: jycssu-com <110905167+jycssu-com@users.noreply.github.com> Co-authored-by: alex <123092072+AlexRLT@users.noreply.github.com> Co-authored-by: Jycssu Co-authored-by: Yohann Durand --------- Signed-off-by: dependabot[bot] Co-authored-by: Sigri Co-authored-by: Jycssu Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: BenoistP <75934369+BenoistP@users.noreply.github.com> Co-authored-by: Kurtisone <104103601+Kurtisone@users.noreply.github.com> Co-authored-by: jycssu-com <110905167+jycssu-com@users.noreply.github.com> Co-authored-by: alex <123092072+AlexRLT@users.noreply.github.com> Co-authored-by: Yohann Durand * Matomo analytics * should work way better with this * fix: pushed old/wrong version * disable cookie set to true --------- Signed-off-by: dependabot[bot] Co-authored-by: Nandy Bâ Co-authored-by: Sigri Co-authored-by: Jycssu Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Kurtisone <104103601+Kurtisone@users.noreply.github.com> Co-authored-by: jycssu-com <110905167+jycssu-com@users.noreply.github.com> Co-authored-by: alex <123092072+AlexRLT@users.noreply.github.com> Co-authored-by: Yohann Durand * deployment fix ERROR: failed to solve: process "/bin/sh -c yarn --frozen-lockfile" did not complete successfully: exit code: 1 https://github.com/RealToken-Community/dashboard-v2/actions/runs/14044504299/job/39322225393 * Clean env * Update branch.yml * Fix spellcheck * Fix spellcheck Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Fix spellcheck * Clean README * Fix spellcheck * Add id site for matomo * Fix matomo id * Factoring tokens added (#121) - prefilter - advanced filter - labels * 117 read only not working (#122) * Upgrade to latest realt-commons * Explicitly set Gnosis as the default chain ... so current bug/freeze due to error on ETH won't show up (rpc error, bug in real-token\realt-interface-commons\packages\realt-commons\src\components\modals\WalletModal\WalletModal.tsx ?) when choosing read-only * Update _app.tsx * Packages upgrades & downgrades @mantine/* downgraded to ^7.13.4 for matching realt-interface-commons versions * Update package.json Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/pages/_app.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Make yarn lockfile great again ... again * Squashed commit of the following: commit 511597e79ace97981419772db180dc197d8647ed Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Sat May 24 21:40:03 2025 +0200 typo commit 58c4546b0eed59aa672f701feeb63dcaddef2cdd Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Sat May 24 21:37:57 2025 +0200 useless import removed commit db039ff418c7998458b55229e66c1b77e5308281 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Sat May 24 21:34:57 2025 +0200 add realt-commons default rpc to dashboard default rpcs list commit 2c7a38fdd71ee29ee2caff2660d9cf6ab6dce106 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Sat May 24 21:28:51 2025 +0200 comment commit 9a94f4f04daf6af17cb01f71cb276b33fe5ff2ed Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Sat May 24 17:32:05 2025 +0200 Squashed commit of the following: commit 57552411ab2851c9ad13f52accfbd3614846a20e Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Sat May 24 17:12:45 2025 +0200 prettier commit 2b3703fd1ce717d097862422d3c894a3f4213171 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Sat May 24 17:10:06 2025 +0200 RPC urls Additionnal rpc urls (codes defines default urls, thes additional ones (if setted) will prefix defaults) Env value is a string, urls are separated by commas. Duplicates, empty, nulls values are merged and removed valid values: RPC_URLS_ETH_MAINNET = https://rpc.eth.gateway.fm RPC_URLS_ETH_MAINNET = ,,https://rpc.eth.gateway.fm, commit 197dd98f97658fc53b82b1edd84fc77cecc26738 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Sat May 24 17:03:59 2025 +0200 Read-only fix Redefine realt-commons chain config using RpcProviders config commit 1759ad998d19243a3fdc03ed87637219ae1adf95 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Sat May 24 14:49:43 2025 +0200 temporary commit commit 422ca846e6135ca1a11d08abb418d3897052c1b3 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Sat May 24 12:43:10 2025 +0200 restore previous version commit 24dbc43b0372c382230f5d02396051aef9d9772e Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Sat May 24 12:39:01 2025 +0200 restore previous version commit 35ecebff78c68860bf870d57082bcbd5a5398330 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Fri May 23 19:36:31 2025 +0200 Update RpcProvider.ts commit 2726a2b18697c92cff674966d7293c07a1279a2a Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Fri May 23 11:13:45 2025 +0200 temp commit for backup commit 22022e67668c1a1202699df04497d6fe59691692 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Wed May 21 21:01:28 2025 +0200 Make yarn lockfile great again commit b00269800130d2e912e071b3425f2d8fdc706d74 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Wed May 21 19:23:15 2025 +0200 Update src/pages/_app.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> commit 159ef858febb0e2b538c3fefd0f377ac4546918c Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Wed May 21 19:21:34 2025 +0200 Update package.json Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> commit 022c8b908cb9e87a3b4cba310e2d9f5eb64d32ba Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Wed May 21 19:15:37 2025 +0200 Packages upgrades & downgrades @mantine/* downgraded to ^7.13.4 for matching realt-interface-commons versions commit a2ec82042d5b439b825dede4676b667b05b8853c Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Wed May 21 18:03:46 2025 +0200 Update _app.tsx commit 70f02ec5a469d0f39f71491a263c56aeaffa410e Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Wed May 21 17:58:22 2025 +0200 Explicitly set Gnosis as the default chain ... so current bug/freeze due to error on ETH won't show up (rpc error, bug in real-token\realt-interface-commons\packages\realt-commons\src\components\modals\WalletModal\WalletModal.tsx ?) when choosing read-only commit a09a9164e829807272daafe0cb601e7bb8f10e3c Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Tue May 20 15:35:09 2025 +0200 Upgrade to latest realt-commons * Fix docker local build error => ERROR [app builder 4/5] COPY . . 14.2s ------ > [app builder 4/5] COPY . .: ------ failed to solve: cannot replace to directory /var/lib/docker/overlay2/np0icniix570dylxjdxatwyl7/merged/app/node_modules/@apollo/client with file  ELIFECYCLE  Command failed with exit code 1. * Docker: fixes docker-compose: version is obsolete upgrade build to node 18 * Fix CI/CD * v2.4.2 * Clean .env.sample * fix: consume initializeProvidersQueue and add timout (#125) * fix: consume initializeProvidersQueue and add timout * fix: resolve linting issues using Prettier * fix: fix type issue (GnosisRpcProvider property on unknown type) * fix: resolve type error in RpcProvider (assigning 'unknown' to 'ProvidersWithUrls') * feat: add page test-rpc for debug * Include alternative apis (PitsBI actually) (#129) * Prevent missing rentStartDate property from crashing app Set date to null when rentStartDate(.)date is not defined: avoid "Cannot read properties of null" RWA asset is missing this field * ENV updates: REALTOKEN_API, PITSBI REALTOKENAPI, REALTOKENAPI_HISTORY renamed/splited into REALTOKEN_API_BASE REALTOKEN_API_VERSION REALTOKEN_API_GET_ALLTOKENS REALTOKEN_API_HISTORY_BASE plus PITSBI_API env vars * Squashed commit of the following: commit b89ddda809b63ecd1dbf622c7bc3c660b5b3d62d Author: BenoistP Date: Fri Jul 25 16:00:07 2025 +0200 Prettier commit d9b976fc839618aa210ce0cca1ba17681f9e6d08 Author: BenoistP Date: Fri Jul 25 15:58:54 2025 +0200 Update index.ts stupido commit d3aab8dd692f895129005c47f073015c98a150c3 Author: BenoistP Date: Fri Jul 25 15:58:24 2025 +0200 Clean debug removed display fetch error (status) in console commit 0aeabe9eea18edcb0930aa02837fbaadd163bbc9 Author: BenoistP Date: Fri Jul 25 15:17:43 2025 +0200 Build warnings fix commit d55fdbcd5abd8742c1a874b6270c8511bbb87cf1 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Wed Jul 23 23:11:26 2025 +0200 Clean Todo: display response error reason in logs commit 3799702cc2c48e2b52a83e01cc84462564af6408 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Wed Jul 23 23:05:29 2025 +0200 Update index.ts commit 18fb791e456e9a8c087fd0aa1ecd79e9d4a4503d Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Wed Jul 23 23:04:48 2025 +0200 Skip fetching Pitsbi data twice commit c50593cffcd7a0f502b0c669c30d6816fd1649e0 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Wed Jul 23 22:53:32 2025 +0200 Use Pisbi API as fallback commit 869d804d4037cbf4c46dbf61985be10d146843ef Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Wed Jul 23 22:38:31 2025 +0200 debug removed commit 413f05db48c3dc4cb6936994173c87d0c7c4df62 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Wed Jul 23 22:02:31 2025 +0200 Setting: set DisplayAdditionalData initial value commit d7add133e57b463f95297508843a78c872e643da Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Wed Jul 23 21:36:44 2025 +0200 clean commit 5b8db348eabd97e2a1dbb44f8b00790c6bddb5b6 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Wed Jul 23 21:23:57 2025 +0200 Package update: ethers commit 2c9ad413a8d0a085eafd636c7cb8198530323284 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Wed Jul 23 21:17:55 2025 +0200 Package update: @tabler/icons -> @tabler/icons-react commit f2fe3fb72189e6b1249b7f2c4e1dff11f1fac947 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Wed Jul 23 19:36:35 2025 +0200 Priority icon size commit a00e48e110cb4ce6d2e30488f9bf3bdb937be272 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Wed Jul 23 19:35:48 2025 +0200 Show/Hide additional data Setting commit ab80d664b1b6b32670e3b95fa4161af58190d7a3 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Wed Jul 23 19:07:32 2025 +0200 Asset Issues commit 024a632e05bb7fc7330d962b568f6576733f7b0b Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Wed Jul 23 14:34:22 2025 +0200 Style every icons have the same color commit 0076c5bb8daa38c5dc7a284fd70db01700a2b329 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Wed Jul 23 14:17:44 2025 +0200 Styling Style: Loader icon replaced by Skeleton fading, Badge style removed selectRealtokensIsLoadingExtraData commit 041b973655b59d16b5a6f1db77a30ecd7e9d8665 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Wed Jul 23 12:40:58 2025 +0200 fetchWithRetry commit d76b6ffae6c64c9a864dcf47327ed0898ccb069e Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Wed Jul 23 12:40:26 2025 +0200 Update general.ts commit cd627fe5fe7779c4d3d8a590a1c8065ebd012f4d Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Wed Jul 23 12:39:34 2025 +0200 fetchWithRetry commit c1f21bcdf18b724331275035272a8165af447b58 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Tue Jul 22 23:24:14 2025 +0200 AssetCard commit 49dbc253743f31e62f5bb5a5ea22731bd1f41382 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Tue Jul 22 23:18:09 2025 +0200 AssetCard commit 6e2ce8f803a8f1d67a39ee0c92dfb22297dc4d01 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Tue Jul 22 22:41:13 2025 +0200 Tags moved to IssuesStatusTags.tsx, AssetCard commit 7819ca649a5bba15f9462acb4d52c3cc60421205 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Tue Jul 22 21:29:39 2025 +0200 Undefined rentStartDate_date Display warning commit 54ccacdcb223d2cc55f06cafd276d9292e424c9c Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Tue Jul 22 20:21:11 2025 +0200 Icon Loader commit 2e34a8f2cb810ce9716966c612a25229eaffd745 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Tue Jul 22 20:03:16 2025 +0200 Icon loader commit ea4f09ddb835ed00b2dd8a97b9ae316edbe2ad51 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Tue Jul 22 19:43:15 2025 +0200 Icons, Badge, "Unknown" state (loading) commit 5fb41280ccee9bec1d8c80e6bbd24ecdd84d4adf Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Tue Jul 22 15:20:26 2025 +0200 Icons + Tooltips commit 13f0cb765f79177faa4c6217ad11ee2a6ff3d870 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Mon Jul 21 23:26:47 2025 +0200 WIP Icons todo: - use Pitsbi as main source of data if realt community api fails - add hover on priority/status - add priority/status data on cards view - fix asset detail (crash on card click / forward) commit cfb4d9728a378bedd76bcc064da0a66446f22d97 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Mon Jul 21 18:07:36 2025 +0200 ENV updates: REALTOKEN_API commit 30d79d112fb705c2c26b6160975b81fb6e2b6711 Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Mon Jul 21 18:06:23 2025 +0200 Set date to null when rentStartDate(.)date is not defined Set date to null when rentStartDate(.)date is not defined, avoid "Cannot read properties of null". Typically for RWA asset commit 6ad40ac70e980e48758540d278c6f49bb144720f Author: BenoistP <75934369+BenoistP@users.noreply.github.com> Date: Mon Jul 21 14:43:56 2025 +0200 WIP * Api uris, only load extra data (Pitsbi) when setting is enabled * Typo/wrong label * Asset issues status filter * Priority filter * 130 clicking on an asset and/or getting back to list/card view view causes a crash (#131) * Fix connector prop changing on every page change using memo() Error: The connectors prop passed to Web3ReactProvider must be referentially static. If connectors is changing, try providing a key prop to Web3ReactProvider that changes every time connectors changes. * use contracts addresses as IDs * prettier * Feat reusd sushiv3 (#134) * First implementation using Honeyswap pools for price Todo: use Sushi v3 for asset pricing * Squashed commit of the following: commit 9f53ea56206b0ccd130b53d558da001fffddf72d Author: BenoistP Date: Sat Aug 2 10:01:46 2025 +0200 Clean debug removed, tests commit 5a61f555eeaeb3e9879a257df386a05ef3fa2af4 Author: BenoistP Date: Thu Jul 31 14:22:44 2025 +0200 result decimals fixed todo: clean & test commit 011bff67da9e4446d00d5084bb8ccb414d678352 Author: BenoistP Date: Wed Jul 30 18:35:43 2025 +0200 todo: fix getUniV3AssetPrice final price commit 781628cef97e0a4c212563e552b611325dcad5f7 Author: BenoistP Date: Sun Jul 27 16:18:24 2025 +0200 tmp * Update yarn.lock / fix for failed build ERROR: failed to build: failed to solve: process "/bin/sh -c yarn --frozen-lockfile" did not complete successfully: exit code: *** * Revert "Update yarn.lock / fix for failed build" This reverts commit 03560ac5fb3d32f0b4fe24a52dc2144c54a01856. * yarn: fix for failed build ERROR: failed to build: failed to solve: process "/bin/sh -c yarn --frozen-lockfile" did not complete successfully: exit code: *** * workflow: vercel Deploying to Vercel for the time being * Update vercel.json * Revert "Update vercel.json" This reverts commit 7a1bcda4e8fc3abcf2b6012d963a9654fc8884f9. * Revert "workflow: vercel" This reverts commit 949a457ddd02fe9b623d33cb440460181bd5e76f. * merge develop into master (#102) (#107) * Change NS for RealToken (#89) * chore: install required sharp dependency for production * chore(deps): bump body-parser from 1.20.2 to 1.20.3 (#87) Bumps [body-parser](https://github.com/expressjs/body-parser) from 1.20.2 to 1.20.3. - [Release notes](https://github.com/expressjs/body-parser/releases) - [Changelog](https://github.com/expressjs/body-parser/blob/master/HISTORY.md) - [Commits](https://github.com/expressjs/body-parser/compare/1.20.2...1.20.3) --- updated-dependencies: - dependency-name: body-parser dependency-type: indirect ... * feat: add new assets REG, REG Vote Power (#91) * first version : Assets as rows * 'other' assets display switch; 'other' assets shown in Summary 'other' assets = tokens others than RealTokens = RWA, REG, ... - Display switch for hiding 'other' assets in table rows - 'other' assets figures added to Summary section * contracts utils * use contract utils for fetching balances * Eth provider removed: REG Voting Power is not deployed on Eth * batchCallOneFunction not fully tested * error args * comments * wrong 'inheritance' * smol fix: properly throw 'User not found' error * style/comments * settings labels and icons * unitIcon: optional + alignment with other units * Filter 'other' assets in Grid view * Settings: switch icons, section names * fix: lint warning * fix: lint warning * fix: build error ? * fix: build error #2 ? * fix: build error #3 ? * fix: build error #4 ? * fix: build error #5 ? * Fix: existing filter error when filtering on "Owned on" * fix: avoid fetching others assets balances if user wallet was not set * feat: add other token price on cards and fix decimals rounds for total invest * fix: improve subsudy filter for exclude other realtokens * fix: fix last changes filter and use correct typing in filters for avoid futurs errors * feat: set initial launch date for RWA token * feat: initialize rpc provider only once * feat: include reg tokens locked inside the incentive vault * fix: fix eslint warnings and add missing translations * Update .gitignore exclude pnpm lockfile * Create .nvmrc add .nvmrc for switching between node versions * Update contract.ts optional parameters for customizing batch calls: batch max and min size, number of attempts before giving up * Regvotingpower: icon orange, size grows depending on amount * unused import * Usdc currency/rate * Voting power size & fill color depending on power amount * Voting power size & fill color depending on power amount * Fixes: token prices in USD, avoid fetching REG vault balances on Eth - Tokens values fix: Updated assets prices by using currenciesRates and user currency rate (previously considering usdc = usd and xdai = usd as well), now assets prices are converted from their respective currencies (e.g. usdc, xdai) to usd, then total value is priced in user choosen currency. - Smol fix: getAddressesLockedBalances was fetching vault balances on Eth, throwing errors as there is not such vault on Eth. Added parameters for fetching any number of vault by provider(s). * comment * Check Providers ability to handle requests - Check Providers ability to handle requests - Additionnal providers https://rpc.ankr.com/eth and https://eth-pokt.nodies.app currently fail to handle concurrent requests * Assets hooks moved in index for avoiding duplicate requests - Both SummaryCard and AssetsView use the same hooks for getting their data, leading to duplicate web3 requests Moving hooks in parent components prevents this form happening. * prettier * Avoid divide by zero in case asset.totalUnits is unknown/zero * "Clean" scripts * Assets filtering moved to the right place filter has to be in assetsView/AssetsView.tsx * Paging translation labels: placeholder, All * simplify pageSize handling * Assets filtering fix (causing pagination issues) OtherAssets filtering was improperly implemented into the map loops by skipping non "others assets" value, causing holes in display (missing cards). Filtering is now done in AssetsView where it should have been in the first place * Revert changes: realtime/global labels for rents calculation switch --------- * fix: API URLs issue #97 (#98) * addresses issue #97 Moves api urls to (.)ENV set utls to new api.realtoken.community domain (previously api.realt.community) * Update .env.sample missing update of sample .env * Update .env.sample * NEXT_PUBLIC_ prefix removed for REALTOKENAPI ENV vars * feat: update yarn lock (#99) * feat: add new API urls to deployment workflow (#100) * feat: pass new env variables to docker env (#101) * fix: unchecked asset filters (#95) * merge master in develop (#103) * Revert "Master" * merge preprod <> master (#67) * add dropdown * simplify selector and add all token option * feat: change allPage value to Infinity * feat: estimate the fully rented rent * feat: add fully rented estimation to asset cards * refactor: move hook calls to top of the page * fix: propInfo definition * fix: last rent condition * feat: add estimation for property with not fully rented history * feat: get fully rented APR * apply max APR method * rename variable and functions * feat: add RWA token * feat: re-enable property onClick * feat: add rwa valuation on the rwa card * fix: missing property * feat: add rwa to summary card * define useRWA * take into account user currency * feat: add RWA value to net value calculation * remove comment * refactor: clean imports * feat: include RWA on Ethereum * fix: en communs * feat: update filter to support RWA token * fix: prettier * fix: other prettier errors * let prettier add strange semi-column * fix: imports * use hook * add fallback * switch for a useMemo * feat: add real time fully rented APR * feat: add gloabl metric fully rented APR * feat: add disclaimer * feat: add disclaimer * feat: update disclaimer message * fix: disclaimer message * improve message * feat: create yam statics stics page * feat: add yam statistics for all RealT Tokens on Gnosis (who have Gnosis chain contract prop) * feat: mask tokens with no volume * fix: add token name * feat: add pagination * feat: improve style * feat: change token per page to 100 * feat: add fully rented APR to asset grid * refactor: remove logs * feat: add fully rented APR to property details * fix: reset current page when tokens changed * fix: reset current page when user change page size * feat: add translation for YAM statistics hearder label * fix: yamStatistics: use selected currency for token price * feat: yamStatistics: add owned | all filter * feat: yamStatistics: add subsidized, fullySubsidized and notSubsidized filters * add additional fallbacks RPC URLs * fix: RPC initialization on currencies file --------- * merge preprod <> master (#77) * add dropdown * simplify selector and add all token option * feat: change allPage value to Infinity * feat: estimate the fully rented rent * feat: add fully rented estimation to asset cards * refactor: move hook calls to top of the page * fix: propInfo definition * fix: last rent condition * feat: add estimation for property with not fully rented history * feat: get fully rented APR * apply max APR method * rename variable and functions * feat: add RWA token * feat: re-enable property onClick * feat: add rwa valuation on the rwa card * fix: missing property * feat: add rwa to summary card * define useRWA * take into account user currency * feat: add RWA value to net value calculation * remove comment * refactor: clean imports * feat: include RWA on Ethereum * fix: en communs * feat: update filter to support RWA token * fix: prettier * fix: other prettier errors * let prettier add strange semi-column * fix: imports * use hook * add fallback * switch for a useMemo * feat: add real time fully rented APR * feat: add gloabl metric fully rented APR * feat: add disclaimer * feat: add disclaimer * feat: update disclaimer message * fix: disclaimer message * improve message * feat: create yam statics stics page * feat: add yam statistics for all RealT Tokens on Gnosis (who have Gnosis chain contract prop) * feat: mask tokens with no volume * fix: add token name * feat: add pagination * feat: improve style * feat: change token per page to 100 * feat: add fully rented APR to asset grid * refactor: remove logs * feat: add fully rented APR to property details * fix: reset current page when tokens changed * fix: reset current page when user change page size * feat: add translation for YAM statistics hearder label * fix: yamStatistics: use selected currency for token price * feat: yamStatistics: add owned | all filter * feat: yamStatistics: add subsidized, fullySubsidized and notSubsidized filters * add additional fallbacks RPC URLs * fix: RPC initialization on currencies file * feat: YamStatistic: add Yamp Volume's number of days * fix: fullyRentedAPR: fix french disclaimer text * fix: second disclaimer text small error * feat: fullyRentedAPR: manage VEFA properties * refactore: improve comment * feat: show VEFA properties forced fully rented APR only if property do not have tenants * fix: RWA table view * feat: add bridge link on header (#74) * fix: VEFA Realtime APR --------- * Revert "merge preprod <> master (#77)" This reverts commit 68daad55434377c91e0224a580528e9a501e392f. * fix: APY fully rented for properties before start rent day (#85) * Change NS for RealToken (#89) (#90) --------- --------- Signed-off-by: dependabot[bot] Co-authored-by: Sigri Co-authored-by: Jycssu Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: BenoistP <75934369+BenoistP@users.noreply.github.com> Co-authored-by: Kurtisone <104103601+Kurtisone@users.noreply.github.com> Co-authored-by: jycssu-com <110905167+jycssu-com@users.noreply.github.com> Co-authored-by: alex <123092072+AlexRLT@users.noreply.github.com> Co-authored-by: Yohann Durand * Revert "Update yarn.lock / fix for failed build" This reverts commit 03560ac5fb3d32f0b4fe24a52dc2144c54a01856. * yarn: fix for failed build ERROR: failed to build: failed to solve: process "/bin/sh -c yarn --frozen-lockfile" did not complete successfully: exit code: *** * Fix prettier --------- Signed-off-by: dependabot[bot] Co-authored-by: Nandy Bâ Co-authored-by: Kurtisone <104103601+Kurtisone@users.noreply.github.com> Co-authored-by: jycssu-com <110905167+jycssu-com@users.noreply.github.com> Co-authored-by: alex <123092072+AlexRLT@users.noreply.github.com> Co-authored-by: Jycssu Co-authored-by: Yohann Durand Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: BenoistP <75934369+BenoistP@users.noreply.github.com> Co-authored-by: BenoistP Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .env.sample | 15 +- .github/workflows/branch.yml | 14 +- README.md | 3 +- docker-compose-branch.yml | 13 +- package.json | 6 +- .../assetPage/assetPageIssuesTab.tsx | 62 ++ .../filters/AssetsViewFilterModal.tsx | 20 + .../filters/AssetsViewIssuePriorityFilter.tsx | 106 +++ .../filters/AssetsViewIssuesStatusFilter.tsx | 97 +++ .../assetsView/filters/useFilters.ts | 21 + .../types/assetIssuePriority.type.ts | 7 + .../assetsView/types/assetIssueStatus.type.ts | 6 + src/components/assetsView/types/index.ts | 2 + .../assetsView/views/AssetTable.tsx | 68 +- src/components/cards/AssetCard.module.sass | 3 + src/components/cards/AssetCard.tsx | 67 +- src/components/cards/main/SummaryCard.tsx | 6 +- .../commons/assets/IssuesStatusTags.tsx | 273 ++++++++ src/components/commons/assets/index.ts | 1 + src/components/layouts/Footer.tsx | 2 +- src/components/layouts/Header.tsx | 2 +- src/components/layouts/SettingsMenu.tsx | 19 +- src/components/layouts/WalletMenu.tsx | 2 +- src/hooks/useFullyRentedAPR.ts | 14 +- src/hooks/useInitStore.ts | 13 +- src/hooks/useREG.ts | 8 +- src/hooks/useREUSD.ts | 234 +++++++ src/hooks/useRWA.ts | 4 +- src/i18next/locales/en/common.json | 81 ++- src/i18next/locales/fr/common.json | 84 ++- src/pages/_app.tsx | 79 ++- src/pages/api/history/index.ts | 27 +- src/pages/api/pitsBiExtraProperties/index.ts | 52 ++ src/pages/api/properties/index.ts | 76 ++- src/pages/api/uris.ts | 11 + src/pages/asset/[assetId].tsx | 26 +- src/pages/index.tsx | 8 +- src/pages/test-rpc.tsx | 23 +- src/repositories/RpcProvider.ts | 25 +- src/repositories/realtoken.repository.ts | 23 + src/states/index.ts | 6 + .../features/realtokens/realtokensSelector.ts | 5 + .../features/realtokens/realtokensSlice.ts | 133 +++- .../features/settings/settingsSelector.ts | 6 + src/store/features/settings/settingsSlice.ts | 21 + src/store/features/wallets/walletsSelector.ts | 19 +- src/types/APIPitsBI.ts | 57 ++ src/types/APIRealToken.ts | 9 + src/types/RealToken.ts | 7 + src/utils/blockchain/abi/UniswapV3PoolABI.ts | 644 ++++++++++++++++++ .../blockchain/abi/UniswapV3QuoterV2ABI.ts | 152 +++++ src/utils/blockchain/consts/misc.ts | 115 ++++ src/utils/blockchain/consts/otherTokens.ts | 57 +- src/utils/blockchain/poolPrice.ts | 157 ++++- src/utils/general.ts | 64 +- yarn.lock | 240 ++++++- 56 files changed, 3085 insertions(+), 210 deletions(-) create mode 100644 src/components/assetPage/assetPageIssuesTab.tsx create mode 100644 src/components/assetsView/filters/AssetsViewIssuePriorityFilter.tsx create mode 100644 src/components/assetsView/filters/AssetsViewIssuesStatusFilter.tsx create mode 100644 src/components/assetsView/types/assetIssuePriority.type.ts create mode 100644 src/components/assetsView/types/assetIssueStatus.type.ts create mode 100644 src/components/commons/assets/IssuesStatusTags.tsx create mode 100644 src/hooks/useREUSD.ts create mode 100644 src/pages/api/pitsBiExtraProperties/index.ts create mode 100644 src/pages/api/uris.ts create mode 100644 src/types/APIPitsBI.ts create mode 100644 src/utils/blockchain/abi/UniswapV3PoolABI.ts create mode 100644 src/utils/blockchain/abi/UniswapV3QuoterV2ABI.ts create mode 100644 src/utils/blockchain/consts/misc.ts diff --git a/.env.sample b/.env.sample index 9f68f428..be551d0d 100644 --- a/.env.sample +++ b/.env.sample @@ -1,8 +1,17 @@ BUILD_ENV= -COMMUNITY_API_KEY= +REALTOKEN_COMMUNITY_API_KEY= THEGRAPH_API_KEY= -REALTOKENAPI='https://api.realtoken.community/v1/token' -REALTOKENAPI_HISTORY='https://history.api.realtoken.community/' + +REALTOKEN_COMMUNITY_API_BASE='https://api.realtoken.community/' +REALTOKEN_COMMUNITY_API_VERSION='v1' +REALTOKEN_COMMUNITY_API_GET_ALLTOKENS='token' +REALTOKEN_COMMUNITY_API_HISTORY_BASE='https://history.api.realtoken.community/' + +PITSBI_API_BASE='https://api.pitsbi.io/' +PITSBI_API_VERSION='api' +PITSBI_API_GET_LASTUPDATE='last_get_realTokens_communityDashboard' +PITSBI_API_GET_ALLTOKENS='realTokens_communityDashboard' + MATOMO_URL='https://data.realtoken.community/' MATOMO_SITE_ID= diff --git a/.github/workflows/branch.yml b/.github/workflows/branch.yml index 3b725645..1cd380dd 100644 --- a/.github/workflows/branch.yml +++ b/.github/workflows/branch.yml @@ -77,7 +77,7 @@ jobs: DOCKER_PASSWD: ${{ secrets.DOCKER_PASSWD }} DOMAIN_URL: ${{ secrets.DOMAIN_URL }} THEGRAPH_API_KEY: ${{ secrets.THEGRAPH_API_KEY }} - COMMUNITY_API_KEY: ${{ secrets.COMMUNITY_API_KEY }} + REALTOKEN_COMMUNITY_API_KEY: ${{ secrets.REALTOKEN_COMMUNITY_API_KEY }} RPC_URLS_ETH_MAINNET: ${{ secrets.RPC_URLS_ETH_MAINNET }} RPC_URLS_GNOSIS_MAINNET: ${{ secrets.RPC_URLS_GNOSIS_MAINNET }} MATOMO_ID_PROD: ${{ secrets.MATOMO_ID_PROD }} @@ -106,9 +106,15 @@ jobs: docker login -u "$DOCKER_LOGIN" -p "$DOCKER_PASSWD" \$DOCKER_REGISTRY THEGRAPH_API_KEY="$THEGRAPH_API_KEY" \ - COMMUNITY_API_KEY="$COMMUNITY_API_KEY" \ - REALTOKENAPI="https://api.$DOMAIN_URL/v1/token" \ - REALTOKENAPI_HISTORY="https://history.api.$DOMAIN_URL/" \ + REALTOKEN_COMMUNITY_API_KEY="$REALTOKEN_COMMUNITY_API_KEY" \ + REALTOKEN_COMMUNITY_API_BASE="https://api.$DOMAIN_URL" \ + REALTOKEN_COMMUNITY_API_VERSION='v1' \ + REALTOKEN_COMMUNITY_API_GET_ALLTOKENS='token' \ + REALTOKEN_COMMUNITY_API_HISTORY_BASE="https://history.api.$DOMAIN_URL/" \ + PITSBI_API_BASE="https://api.pitsbi.io/" \ + PITSBI_API_VERSION='api' \ + PITSBI_API_GET_LASTUPDATE='last_get_realTokens_communityDashboard' \ + PITSBI_API_GET_ALLTOKENS='realTokens_communityDashboard' \ MATOMO_URL="https://data.$DOMAIN_URL/" \ MATOMO_SITE_ID="$MATOMO_SITE_ID" \ RPC_URLS_ETH_MAINNET="$RPC_URLS_ETH_MAINNET" \ diff --git a/README.md b/README.md index cc787905..037827db 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ ## TODO -- [x] Use Community API to get RealTokens data (and remove public/mock/realt.min.json) - [x] Display assets in a table - [x] Allow to sort assets - [ ] Create Readme @@ -16,7 +15,7 @@ To run the project you will need to set-up a `.env` file in the root folder, by copy/paste `.env.sample` file. -To get a `COMMUNITY_API_KEY`, join the dedicated [telegram dev channel](https://t.me/+XQyoaFfmN61yk7X0) then ask for. +To get a `REALTOKEN_COMMUNITY_API_KEY`, join the dedicated [telegram dev channel](https://t.me/+XQyoaFfmN61yk7X0) then ask for. To get a `THEGRAPH_API_KEY`, you need to create an account on [thegraph](https://thegraph.com/docs/en/querying/managing-api-keys/) diff --git a/docker-compose-branch.yml b/docker-compose-branch.yml index 91712164..dc1df0c7 100644 --- a/docker-compose-branch.yml +++ b/docker-compose-branch.yml @@ -4,9 +4,16 @@ services: container_name: ${DOCKER_BRANCH}-dashboard environment: - THEGRAPH_API_KEY=$THEGRAPH_API_KEY - - COMMUNITY_API_KEY=$COMMUNITY_API_KEY - - REALTOKENAPI=$REALTOKENAPI - - REALTOKENAPI_HISTORY=$REALTOKENAPI_HISTORY + - REALTOKEN_COMMUNITY_API_KEY=$REALTOKEN_COMMUNITY_API_KEY + + - REALTOKEN_COMMUNITY_API_BASE=$REALTOKEN_COMMUNITY_API_BASE + - REALTOKEN_COMMUNITY_API_VERSION=$REALTOKEN_COMMUNITY_API_VERSION + - REALTOKEN_COMMUNITY_API_GET_ALLTOKENS=$REALTOKEN_COMMUNITY_API_GET_ALLTOKENS + - REALTOKEN_COMMUNITY_API_HISTORY_BASE=$REALTOKEN_COMMUNITY_API_HISTORY_BASE + - PITSBI_API_BASE + - PITSBI_API_VERSION + - PITSBI_API_GET_LASTUPDATE + - PITSBI_API_GET_ALLTOKENS - MATOMO_URL=$MATOMO_URL - MATOMO_SITE_ID=$MATOMO_SITE_ID - RPC_URLS_ETH_MAINNET=$RPC_URLS_ETH_MAINNET diff --git a/package.json b/package.json index 33a4b78d..8f066ce7 100644 --- a/package.json +++ b/package.json @@ -28,14 +28,16 @@ "@realtoken/realt-commons": "1.4.20", "@reduxjs/toolkit": "^2.8.2", "@socialgouv/matomo-next": "^1.9.2", - "@tabler/icons": "^1.119.0", + "@uniswap/sdk-core": "^7.7.2", + "@uniswap/v3-sdk": "^3.25.2", + "@tabler/icons-react": "^3.34.1", "@web3-react/core": "^8.2.3", "cookies-next": "^2.1.2", "date-fns": "^3.6.0", "dayjs": "^1.11.13", "del-cli": "^6.0.0", "dexie": "^3.2.7", - "ethers": "^6.14.1", + "ethers": "^6.15.0", "graphql": "^16.11.0", "i18next": "^22.5.1", "jotai": "^1.13.1", diff --git a/src/components/assetPage/assetPageIssuesTab.tsx b/src/components/assetPage/assetPageIssuesTab.tsx new file mode 100644 index 00000000..61aa4d41 --- /dev/null +++ b/src/components/assetPage/assetPageIssuesTab.tsx @@ -0,0 +1,62 @@ +import { FC } from 'react' +import { useTranslation } from 'react-i18next' + +import { UserRealtoken } from 'src/store/features/wallets/walletsSelector' +import { RealTokenToBeFixedStatus } from 'src/types/APIPitsBI' + +import { AssetPageTable } from './assetPageTable' + +export const AssetPageIssuesTab: FC<{ + realtoken: UserRealtoken +}> = ({ realtoken }) => { + const { t } = useTranslation('common', { keyPrefix: 'assetPage.issues' }) + const { t: tAssetIssues } = useTranslation('common', { + keyPrefix: 'assetIssues', + }) + + const statusLabel = () => { + switch (realtoken.extraData?.pitsBI?.actions?.realt_status) { + case RealTokenToBeFixedStatus.NoExhibit: + return tAssetIssues('status.noExhibit') + case RealTokenToBeFixedStatus.Scheduled: + return tAssetIssues('status.scheduled') + case RealTokenToBeFixedStatus.UpgradedAndReady: + return tAssetIssues('status.upgradedReady') + case undefined: + default: + return tAssetIssues('status.unknown') + } + } + + return ( + <> + + + ) +} + +AssetPageIssuesTab.displayName = 'AssetPageIssuesTab' diff --git a/src/components/assetsView/filters/AssetsViewFilterModal.tsx b/src/components/assetsView/filters/AssetsViewFilterModal.tsx index 5bcd75fd..4e6e6b74 100644 --- a/src/components/assetsView/filters/AssetsViewFilterModal.tsx +++ b/src/components/assetsView/filters/AssetsViewFilterModal.tsx @@ -14,10 +14,13 @@ import { assetsViewFilterAtom, } from 'src/states' import { selectUserIncludesOtherAssets } from 'src/store/features/settings/settingsSelector' +import { selectUserDisplayAdditionalData } from 'src/store/features/settings/settingsSelector' import { userIncludesOtherAssetsChanged } from 'src/store/features/settings/settingsSlice' import { AssetProductType } from '../types' import { AssetsViewProductTypeFilter } from './AssetsViewFilterProductType' +import { AssetsViewIssuePriorityFilter } from './AssetsViewIssuePriorityFilter' +import { AssetsViewIssueStatusFilter } from './AssetsViewIssuesStatusFilter' import { AssetsViewRentStatusFilter } from './AssetsViewRentStatusFilter' import { AssetsViewRmmStatusFilter } from './AssetsViewRmmStatusFilter' import { AssetsViewSort } from './AssetsViewSort' @@ -59,6 +62,7 @@ export const AssetsViewFilterModal: FC = ({ const dispatch = useDispatch() const setUserIncludesOtherAssets = (value: boolean) => dispatch(userIncludesOtherAssetsChanged(value)) + const userDisplayAdditionalData = useSelector(selectUserDisplayAdditionalData) const onClose = useCallback(() => { context.closeModal(id) @@ -123,6 +127,22 @@ export const AssetsViewFilterModal: FC = ({ setFilterModel({ ...filterModel, ...value }) }} /> + {userDisplayAdditionalData && ( + <> + { + setFilterModel({ ...filterModel, ...value }) + }} + /> + { + setFilterModel({ ...filterModel, ...value }) + }} + /> + + )} diff --git a/src/components/assetsView/filters/AssetsViewIssuePriorityFilter.tsx b/src/components/assetsView/filters/AssetsViewIssuePriorityFilter.tsx new file mode 100644 index 00000000..6c71a78b --- /dev/null +++ b/src/components/assetsView/filters/AssetsViewIssuePriorityFilter.tsx @@ -0,0 +1,106 @@ +import { FC } from 'react' +import { useTranslation } from 'react-i18next' + +import { Select } from '@mantine/core' + +import { assetsViewDefaultFilter } from 'src/states' +import { + OtherRealtoken, + UserRealtoken, +} from 'src/store/features/wallets/walletsSelector' +import { RealTokenToBeRepairedPriority } from 'src/types/APIPitsBI' + +import { useInputStyles } from '../../inputs/useInputStyles' +import { AssetIssuePriorityType } from '../types' + +interface AssetsViewIssuePriorityFilterModel { + issuePriority: AssetIssuePriorityType +} + +interface AssetsViewIssuePriorityFilterProps { + filter: AssetsViewIssuePriorityFilterModel + onChange: (value: AssetsViewIssuePriorityFilterModel) => void +} +export const AssetsViewIssuePriorityFilter: FC< + AssetsViewIssuePriorityFilterProps +> = ({ filter, onChange }) => { + const { t } = useTranslation('common', { keyPrefix: 'assetIssuePriority' }) + const { classes: inputClasses } = useInputStyles() + + const viewOptions = [ + { + value: AssetIssuePriorityType.ALL, + label: t('options.all'), + }, + { + value: AssetIssuePriorityType.NONE, + label: t('options.none'), + }, + { + value: AssetIssuePriorityType.HIGH, + label: t('options.high'), + }, + { + value: AssetIssuePriorityType.MEDIUM, + label: t('options.medium'), + }, + { + value: AssetIssuePriorityType.LOW, + label: t('options.low'), + }, + ] + + return ( + + onChange({ + issueStatus: + (value as AssetIssueStatusType) ?? + assetsViewDefaultFilter.issueStatus, + }) + } + classNames={inputClasses} + /> + ) +} +AssetsViewIssueStatusFilter.displayName = 'AssetsViewIssueStatusFilter' + +export function useAssetsViewIssueStatusFilter( + filter: AssetsViewIssueStatusFilterModel, +) { + function assetIssueStatusFilterFunction( + asset: UserRealtoken | OtherRealtoken, + ) { + const Asset = asset as UserRealtoken + switch (filter.issueStatus) { + case AssetIssueStatusType.ALL: + return true + case AssetIssueStatusType.NOEXHIBIT: + return ( + Asset.extraData?.pitsBI?.actions?.realt_status === undefined || + Asset.extraData?.pitsBI?.actions?.realt_status === + RealTokenToBeFixedStatus.NoExhibit + ) + case AssetIssueStatusType.UPGRADEDANDREADY: + return ( + Asset.extraData?.pitsBI?.actions?.realt_status === + RealTokenToBeFixedStatus.UpgradedAndReady + ) + case AssetIssueStatusType.SCHEDULED: + return ( + Asset.extraData?.pitsBI?.actions?.realt_status === + RealTokenToBeFixedStatus.Scheduled + ) + } + } + + return { assetIssueStatusFilterFunction } +} diff --git a/src/components/assetsView/filters/useFilters.ts b/src/components/assetsView/filters/useFilters.ts index d5276a7f..3790068d 100644 --- a/src/components/assetsView/filters/useFilters.ts +++ b/src/components/assetsView/filters/useFilters.ts @@ -1,12 +1,17 @@ +import { useSelector } from 'react-redux' + import { useAtom } from 'jotai' import { assetsViewDefaultFilter, assetsViewFilterAtom } from 'src/states' +import { selectUserDisplayAdditionalData } from 'src/store/features/settings/settingsSelector' import { OtherRealtoken, UserRealtoken, } from 'src/store/features/wallets/walletsSelector' import { useAssetsViewProductTypeFilter } from './AssetsViewFilterProductType' +import { useAssetsViewIssuePriorityFilter } from './AssetsViewIssuePriorityFilter' +import { useAssetsViewIssueStatusFilter } from './AssetsViewIssuesStatusFilter' import { useAssetsViewRentStatusFilter } from './AssetsViewRentStatusFilter' import { useAssetsViewRmmStatusFilter } from './AssetsViewRmmStatusFilter' import { useAssetsViewSort } from './AssetsViewSort' @@ -17,6 +22,7 @@ import { useAssetsViewUserStatusFilter } from './AssetsViewUserStatusFilter' export function useAssetsViewFilters() { const [currentFilter] = useAtom(assetsViewFilterAtom) const activeFilter = Object.assign({}, assetsViewDefaultFilter, currentFilter) + const userDisplayAdditionalData = useSelector(selectUserDisplayAdditionalData) const { assetSortFunction } = useAssetsViewSort(activeFilter) const { assetSubsidyFilterFunction } = @@ -31,6 +37,10 @@ export function useAssetsViewFilters() { useAssetsViewRmmStatusFilter(activeFilter) const { assetUserProtocolFilterFunction } = useAssetsViewUserProtocolFilter(activeFilter) + const { assetIssueStatusFilterFunction } = + useAssetsViewIssueStatusFilter(activeFilter) + const { assetIssuePriorityFilterFunction } = + useAssetsViewIssuePriorityFilter(activeFilter) function assetsViewFilterFunction( tokenList: (UserRealtoken | OtherRealtoken)[], @@ -42,6 +52,17 @@ export function useAssetsViewFilters() { .filter(assetRentStatusFilterFunction) .filter(assetSubsidyFilterFunction) .filter(assetRmmStatusFilterFunction) + .filter( + userDisplayAdditionalData ? assetIssueStatusFilterFunction : () => true, + ) + .filter( + userDisplayAdditionalData ? assetIssueStatusFilterFunction : () => true, + ) + .filter( + userDisplayAdditionalData + ? assetIssuePriorityFilterFunction + : () => true, + ) .sort(assetSortFunction) } diff --git a/src/components/assetsView/types/assetIssuePriority.type.ts b/src/components/assetsView/types/assetIssuePriority.type.ts new file mode 100644 index 00000000..57e2218f --- /dev/null +++ b/src/components/assetsView/types/assetIssuePriority.type.ts @@ -0,0 +1,7 @@ +export enum AssetIssuePriorityType { + ALL = 'all', + NONE = 'none', + HIGH = 'high', + MEDIUM = 'medium', + LOW = 'low', +} diff --git a/src/components/assetsView/types/assetIssueStatus.type.ts b/src/components/assetsView/types/assetIssueStatus.type.ts new file mode 100644 index 00000000..b5a9f603 --- /dev/null +++ b/src/components/assetsView/types/assetIssueStatus.type.ts @@ -0,0 +1,6 @@ +export enum AssetIssueStatusType { + ALL = 'all', + NOEXHIBIT = 'no_exhibit', + UPGRADEDANDREADY = 'upgraded_and_ready', + SCHEDULED = 'scheduled', +} diff --git a/src/components/assetsView/types/index.ts b/src/components/assetsView/types/index.ts index 74dbbcbe..23981803 100644 --- a/src/components/assetsView/types/index.ts +++ b/src/components/assetsView/types/index.ts @@ -6,3 +6,5 @@ export * from './assetRentStatus.type' export * from './assetRmmStatus.type' export * from './assetUserProtocol.type' export * from './assetProduct.type' +export * from './assetIssueStatus.type' +export * from './assetIssuePriority.type' diff --git a/src/components/assetsView/views/AssetTable.tsx b/src/components/assetsView/views/AssetTable.tsx index 546055d9..da7e25e4 100644 --- a/src/components/assetsView/views/AssetTable.tsx +++ b/src/components/assetsView/views/AssetTable.tsx @@ -10,12 +10,19 @@ import moment from 'moment' import { useCurrencyValue } from 'src/hooks/useCurrencyValue' import { useFullyRentedAPR } from 'src/hooks/useFullyRentedAPR' +import { selectUserDisplayAdditionalData } from 'src/store/features/settings/settingsSelector' import { selectTransfersIsLoaded } from 'src/store/features/transfers/transfersSelector' import { OtherRealtoken, UserRealtoken, } from 'src/store/features/wallets/walletsSelector' +import { + ExhibitStatusTag, + IssueStatusTag, + PriorityStatusTag, +} from '../../commons/assets' + export const AssetTable: FC<{ realtokens: (UserRealtoken | OtherRealtoken)[] }> = (props) => { @@ -49,7 +56,7 @@ AssetTable.displayName = 'AssetTable' const AssetTableHeader: FC = () => { const { t } = useTranslation('common', { keyPrefix: 'assetTable' }) const transfersIsLoaded = useSelector(selectTransfersIsLoaded) - + const userDisplayAdditionalData = useSelector(selectUserDisplayAdditionalData) return ( {t('property')} @@ -74,6 +81,19 @@ const AssetTableHeader: FC = () => { {t('rentedUnits')} {t('propertyValue')} {t('lastChange')} + {userDisplayAdditionalData && ( + <> + + {t('status.header')} + + + {t('priority.header')} + + + {t('lawsuit.header')} + + + )} ) } @@ -82,6 +102,7 @@ AssetTableHeader.displayName = 'AssetTableHeader' const AssetTableRow: FC<{ value: UserRealtoken }> = (props) => { const { t } = useTranslation('common', { keyPrefix: 'numbers' }) const transfersIsLoaded = useSelector(selectTransfersIsLoaded) + const userDisplayAdditionalData = useSelector(selectUserDisplayAdditionalData) const router = useRouter() const value = props.value.value @@ -161,6 +182,30 @@ const AssetTableRow: FC<{ value: UserRealtoken }> = (props) => { .toDate() .toLocaleDateString()} + {userDisplayAdditionalData && ( + <> + + + + + + + + + + + )} ) } @@ -168,7 +213,7 @@ const AssetTableRow: FC<{ value: UserRealtoken }> = (props) => { const OtherTableRow: FC<{ value: OtherRealtoken }> = (props) => { const { t } = useTranslation('common', { keyPrefix: 'numbers' }) const transfersIsLoaded = useSelector(selectTransfersIsLoaded) - + const userDisplayAdditionalData = useSelector(selectUserDisplayAdditionalData) const { shortName, value, unitPriceCost, amount, totalInvestment } = props.value @@ -218,6 +263,25 @@ const OtherTableRow: FC<{ value: OtherRealtoken }> = (props) => { {'-'} + {userDisplayAdditionalData && ( + <> + +
+ {'-'} +
+
+ +
+ {'-'} +
+
+ +
+ {'-'} +
+
+ + )} ) } diff --git a/src/components/cards/AssetCard.module.sass b/src/components/cards/AssetCard.module.sass index 8f96a520..55c09342 100644 --- a/src/components/cards/AssetCard.module.sass +++ b/src/components/cards/AssetCard.module.sass @@ -36,6 +36,9 @@ .textSm font-size: 14px +.textXs + font-size: 12px + .textLocation font-size: 12px text-align: center diff --git a/src/components/cards/AssetCard.tsx b/src/components/cards/AssetCard.tsx index 24fb609c..d234dc1c 100644 --- a/src/components/cards/AssetCard.tsx +++ b/src/components/cards/AssetCard.tsx @@ -4,13 +4,16 @@ import { useSelector } from 'react-redux' import Image from 'next/image' -import { Badge, Card, Group } from '@mantine/core' +import { Badge, Card, Grid, Group } from '@mantine/core' import moment from 'moment' import { useCurrencyValue } from 'src/hooks/useCurrencyValue' import { useFullyRentedAPR } from 'src/hooks/useFullyRentedAPR' -import { selectUserRentCalculation } from 'src/store/features/settings/settingsSelector' +import { + selectUserDisplayAdditionalData, + selectUserRentCalculation, +} from 'src/store/features/settings/settingsSelector' import { OtherRealtoken, UserRealtoken, @@ -19,6 +22,9 @@ import { RentCalculationState } from 'src/types/RentCalculation' import { Divider, + ExhibitStatusTag, + IssueStatusTag, + PriorityStatusTag, RentStatusTag, RmmStatusTag, SubsidyStatusTag, @@ -39,14 +45,19 @@ interface PropertyCardProps { const PropertyCardComponent: FC = (props) => { const { t: tNumbers } = useTranslation('common', { keyPrefix: 'numbers' }) const { t } = useTranslation('common', { keyPrefix: 'assetCard' }) - const rentCalculation = useSelector(selectUserRentCalculation) + const userDisplayAdditionalData = useSelector(selectUserDisplayAdditionalData) const realtimeDate = moment(new Date(rentCalculation.date)) - const rentStartDate = new Date(props.value.rentStartDate.date) + const rentStartDate_date = props.value.rentStartDate?.date ?? null + if (!rentStartDate_date) { + console.warn(`Rent start date is not defined for ${props.value.uuid}`) + } + const rentStartDate = new Date(rentStartDate_date) const isDisabled = - rentCalculation.state === RentCalculationState.Realtime && - rentStartDate > realtimeDate.toDate() + !rentStartDate_date || + (rentCalculation.state === RentCalculationState.Realtime && + rentStartDate > realtimeDate.toDate()) const rentNotStarted = t('rentNotStarted') const isSubsidized = @@ -174,6 +185,50 @@ const PropertyCardComponent: FC = (props) => { + {userDisplayAdditionalData && ( +
+
{t('assetIssues.title')}
+ + + + {t('assetIssues.status')} + + + + + + + + {t('assetIssues.priority')} + + + + + + + + {t('assetIssues.lawsuit')} + + {/* center element */} + + + + + +
+ )} +
diff --git a/src/components/cards/main/SummaryCard.tsx b/src/components/cards/main/SummaryCard.tsx index 6f1c97e2..9a44e90f 100644 --- a/src/components/cards/main/SummaryCard.tsx +++ b/src/components/cards/main/SummaryCard.tsx @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' import { Box, Card, Text, Title } from '@mantine/core' -import { IconArchive, IconBolt, IconBoltOff } from '@tabler/icons' +import { IconArchive, IconBolt, IconBoltOff } from '@tabler/icons-react' import { selectTransfersIsLoaded } from 'src/store/features/transfers/transfersSelector' import { OtherRealtoken } from 'src/store/features/wallets/walletsSelector' @@ -18,6 +18,7 @@ interface SummaryCardProps { otherAssetsData: { rwa: OtherRealtoken | null reg: OtherRealtoken | null + reUsd: OtherRealtoken | null regVotingPower: OtherRealtoken | null } } @@ -33,6 +34,7 @@ export const SummaryCard: FC = ({ otherAssetsData }) => { const rwaValue = otherAssetsData?.rwa?.value ?? 0 const regValue = otherAssetsData?.reg?.value ?? 0 + const reUsdValue = otherAssetsData?.reUsd?.value ?? 0 const regVotingPowerAmount = otherAssetsData?.regVotingPower?.amount ?? 0 // Calculate the power logo size of the voting power depending on the amount const additionnalPowerSize = Math.floor(Math.log10(regVotingPowerAmount)) @@ -43,6 +45,7 @@ export const SummaryCard: FC = ({ otherAssetsData }) => { realtokensValue.total + stableDepositValue + rwaValue + + reUsdValue + regValue - stableDebtValue @@ -66,6 +69,7 @@ export const SummaryCard: FC = ({ otherAssetsData }) => { + ((props, ref) => { + const { value, priority, ...rest } = props + const iconColor = + !value || value === 'na' + ? 'gray' + : value === RealTokenToBeFixedStatus.NoExhibit + ? 'green' + : value === RealTokenToBeFixedStatus.Scheduled + ? priority === 1 + ? 'red' + : priority === 2 + ? 'orange' + : priority === 3 + ? 'yellow' + : !priority + ? 'gray' + : 'purple' + : value === RealTokenToBeFixedStatus.UpgradedAndReady + ? 'green' + : 'purple' + const icon = + value === RealTokenToBeFixedStatus.NoExhibit || value == 'na' ? ( + + ) : value === RealTokenToBeFixedStatus.Scheduled ? ( + + ) : value === RealTokenToBeFixedStatus.UpgradedAndReady ? ( + + ) : ( + + ) + return ( +
+
+ {icon} +
+
+ ) +}) + +/* Status Component + * Displays a status icon with a tooltip + */ +export const IssueStatusTag: FC<{ + value: string | undefined + priority: number | undefined +}> = ({ value, priority }) => { + const { t } = useTranslation('common', { keyPrefix: 'assetIssues' }) + const isLoadingExtraData = useSelector(selectRealtokensIsLoadingExtraData) + const toolTipText = !value + ? t(`status.unknown`) + : value === 'na' + ? t('status.na') + : value === RealTokenToBeFixedStatus.NoExhibit + ? t('status.noExhibit') + : value === RealTokenToBeFixedStatus.Scheduled + ? t('status.scheduled') + : value === RealTokenToBeFixedStatus.UpgradedAndReady + ? t('status.upgradedReady') + : t('status.unknown') + return ( + <> + {isLoadingExtraData ? ( + + ) : ( + + + + )} + + ) +} + +IssueStatusTag.displayName = 'IssueStatusTag' + +/* Priority Icons Component + * Displays icons based on priority + * @param value: number representing the priority level + * use forwardRef to allow the component to be used as with a tooltip + */ +// eslint-disable-next-line react/display-name +const PriorityIcons = forwardRef( + (props, ref) => { + const { value, ...rest } = props + const iconColor = + !value || value === 0 || value === -1 + ? 'gray' + : value === 1 + ? 'red' + : value === 2 + ? 'orange' + : value === 3 + ? 'yellow' + : 'purple' + const piorityColor = iconColor + const prioritySize = !value ? 16 : 22 + const iconSize = 16 + const iconPriority = !value ? ( + value === 0 ? ( + + ) : ( + <> + ) + ) : value === 1 ? ( + + ) : value === 2 ? ( + + ) : ( + + ) + const icon = + value === 0 ? ( + <> + ) : value === 1 ? ( + + ) : value === 2 ? ( + + ) : ( + + ) + + return ( +
+
+ {iconPriority} + {icon} +
+
+ ) + }, +) + +/* Priority Badge Component + * Displays a badge with an icon representing the priority level, as well as a tooltip with the priority level text. + */ +export const PriorityStatusTag: FC<{ value: number | undefined }> = ({ + value, +}) => { + const { t } = useTranslation('common', { keyPrefix: 'assetIssues' }) + const isLoadingExtraData = useSelector(selectRealtokensIsLoadingExtraData) + const toolTipText = !value + ? value === 0 + ? t('priority.na') + : t('priority.unknown') + : value === -1 + ? t('priority.na') + : t(`priority.${value}`) + return isLoadingExtraData ? ( + + ) : ( + + + + ) +} + +PriorityStatusTag.displayName = 'PriorityStatusTag' + +/* Exhibit Icons Component + * Displays an icon based on the exhibit number and volume, with a tooltip showing the exhibit number + * use forwardRef to allow the component to be used as with a tooltip + */ +// eslint-disable-next-line react/display-name +const ExhibitIcons = forwardRef< + HTMLDivElement, + { + exhibitNumber: number | undefined + exhibitVolume: number | undefined + priority: number | undefined + } +>((props, ref) => { + const { exhibitNumber, exhibitVolume, priority, ...rest } = props + const noExhibit = + (!exhibitNumber && !exhibitVolume) || + (exhibitNumber === -1 && exhibitVolume === -1) + const iconColor = noExhibit + ? 'gray' + : priority === 1 + ? 'red' + : priority === 2 + ? 'orange' + : priority === 3 + ? 'yellow' + : !priority + ? 'gray' + : 'purple' + const iconSize = 16 + const icon = + !exhibitNumber && !exhibitVolume ? ( + exhibitNumber === 0 && exhibitVolume === 0 ? ( + + ) : ( + + ) + ) : ( + + ) + return ( +
+
+ {icon} +
+
+ ) +}) + +/* Exhibit Component + * Displays an exhibit icon with a tooltip + */ +export const ExhibitStatusTag: FC<{ + exhibitNumber: number | undefined + exhibitVolume: number | undefined + priority: number | undefined +}> = ({ exhibitNumber, exhibitVolume, priority }) => { + const { t } = useTranslation('common', { keyPrefix: 'assetIssues' }) + const isLoadingExtraData = useSelector(selectRealtokensIsLoadingExtraData) + const toolTipText = + exhibitNumber === 0 && exhibitVolume === 0 + ? t('lawsuit.na') + : !exhibitNumber && !exhibitVolume + ? t('lawsuit.unknown') + : exhibitNumber === -1 && exhibitVolume === -1 + ? t('lawsuit.na') + : t('lawsuit.exhibit') + ` # ${exhibitNumber} volume ${exhibitVolume}` + return ( + <> + {isLoadingExtraData ? ( + + ) : ( + + + + )} + + ) +} + +ExhibitStatusTag.displayName = 'ExhibitStatusTag' diff --git a/src/components/commons/assets/index.ts b/src/components/commons/assets/index.ts index 4369f854..2fe36d27 100644 --- a/src/components/commons/assets/index.ts +++ b/src/components/commons/assets/index.ts @@ -1,3 +1,4 @@ export * from './RentStatusTag' export * from './RmmStatusTag' export * from './SubsidyStatusTag' +export * from './IssuesStatusTags' diff --git a/src/components/layouts/Footer.tsx b/src/components/layouts/Footer.tsx index 2e904d30..e7b36e16 100644 --- a/src/components/layouts/Footer.tsx +++ b/src/components/layouts/Footer.tsx @@ -5,7 +5,7 @@ import { IconBrandDiscord, IconBrandMedium, IconBrandTelegram, -} from '@tabler/icons' +} from '@tabler/icons-react' import { Logo } from 'src/assets' diff --git a/src/components/layouts/Header.tsx b/src/components/layouts/Header.tsx index 4c1a4653..a8bd83dd 100644 --- a/src/components/layouts/Header.tsx +++ b/src/components/layouts/Header.tsx @@ -19,7 +19,7 @@ import { IconFilePencil, IconHome2, IconReceipt, -} from '@tabler/icons' +} from '@tabler/icons-react' import { Logo } from 'src/assets' import { selectTransfersIsLoaded } from 'src/store/features/transfers/transfersSelector' diff --git a/src/components/layouts/SettingsMenu.tsx b/src/components/layouts/SettingsMenu.tsx index ee12916e..abb5753e 100644 --- a/src/components/layouts/SettingsMenu.tsx +++ b/src/components/layouts/SettingsMenu.tsx @@ -31,13 +31,16 @@ import { IconMoon, IconSettings, IconSun, -} from '@tabler/icons' + IconTableMinus, + IconTablePlus, +} from '@tabler/icons-react' import { setCookie } from 'cookies-next' import { TransferDatabaseService } from 'src/repositories/transfers/TransferDatabase' import { selectUserCurrency, + selectUserDisplayAdditionalData, selectUserIncludesEth, selectUserIncludesLevinSwap, selectUserIncludesOtherAssets, @@ -47,6 +50,7 @@ import { } from 'src/store/features/settings/settingsSelector' import { userCurrencyChanged, + userDisplayAdditionalDataChanged, userIncludesEthChanged, userIncludesLevinSwapChanged, userIncludesOtherAssetsChanged, @@ -269,6 +273,7 @@ const FetchDataSettings: FC = () => { const userIncludesLevinSwap = useSelector(selectUserIncludesLevinSwap) const userIncludesRmmV2 = useSelector(selectUserIncludesRmmV2) const userIncludesOtherAssets = useSelector(selectUserIncludesOtherAssets) + const userDisplayAdditionalData = useSelector(selectUserDisplayAdditionalData) const setUserIncludesEth = (value: boolean) => dispatch(userIncludesEthChanged(value)) @@ -278,6 +283,8 @@ const FetchDataSettings: FC = () => { dispatch(userIncludesRmmV2Changed(value)) const setUserIncludesOtherAssets = (value: boolean) => dispatch(userIncludesOtherAssetsChanged(value)) + const setUserDisplayAdditionalData = (value: boolean) => + dispatch(userDisplayAdditionalDataChanged(value)) return ( <> @@ -318,6 +325,16 @@ const FetchDataSettings: FC = () => { offLabel={} style={{ margin: '4px 8px' }} /> + + setUserDisplayAdditionalData(event.currentTarget.checked) + } + label={t('displayAdditionalData')} + onLabel={} + offLabel={} + style={{ margin: '4px 8px' }} + /> ) } diff --git a/src/components/layouts/WalletMenu.tsx b/src/components/layouts/WalletMenu.tsx index c5ee351c..05da396e 100644 --- a/src/components/layouts/WalletMenu.tsx +++ b/src/components/layouts/WalletMenu.tsx @@ -5,7 +5,7 @@ import { useSelector } from 'react-redux' import { ActionIcon, Badge, Box, Button, Flex, Menu } from '@mantine/core' import { useDisclosure } from '@mantine/hooks' import { useModals } from '@mantine/modals' -import { IconWallet } from '@tabler/icons' +import { IconWallet } from '@tabler/icons-react' import { useWeb3React } from '@web3-react/core' import { ethers } from 'ethers' diff --git a/src/hooks/useFullyRentedAPR.ts b/src/hooks/useFullyRentedAPR.ts index d303399e..4222d290 100644 --- a/src/hooks/useFullyRentedAPR.ts +++ b/src/hooks/useFullyRentedAPR.ts @@ -100,10 +100,18 @@ const APRDisabled = ( token: UserRealtoken, ) => { const realtimeDate = moment(new Date(rentCalculation.date)) - const rentStartDate = new Date(token.rentStartDate.date) + // Set date to null when rentStartDate(.)date is not defined, + // avoid "Cannot read properties of null". + // Typically for RWA asset + const rentStartDate_date = token.rentStartDate?.date ?? null + if (!rentStartDate_date) { + console.warn(`Rent start date is not defined for ${token.uuid}`) + } + const rentStartDate = new Date(rentStartDate_date) const isDisabled = - rentCalculation.state === RentCalculationState.Realtime && - rentStartDate > realtimeDate.toDate() + !rentStartDate_date || + (rentCalculation.state === RentCalculationState.Realtime && + rentStartDate > realtimeDate.toDate()) return isDisabled } diff --git a/src/hooks/useInitStore.ts b/src/hooks/useInitStore.ts index 041019d4..d7a3130a 100644 --- a/src/hooks/useInitStore.ts +++ b/src/hooks/useInitStore.ts @@ -5,10 +5,14 @@ import { useWeb3React } from '@web3-react/core' import { fetchCurrenciesRates } from 'src/store/features/currencies/currenciesSlice' import { selectRealtokens } from 'src/store/features/realtokens/realtokensSelector' -import { fetchRealtokens } from 'src/store/features/realtokens/realtokensSlice' +import { + fetchRealtokens, + fetchRealtokensExtraData, +} from 'src/store/features/realtokens/realtokensSlice' import { selectAllUserAddressList, selectUserAddressList, + selectUserDisplayAdditionalData, } from 'src/store/features/settings/settingsSelector' import { initializeSettings, @@ -32,6 +36,7 @@ export default function useInitStore() { const addressList = useSelector(selectUserAddressList) const realtokens = useSelector(selectRealtokens) const { account } = useWeb3React() + const userDisplayAdditionalData = useSelector(selectUserDisplayAdditionalData) useEffect(() => { const accountAddress = account?.toLowerCase() @@ -59,4 +64,10 @@ export default function useInitStore() { dispatch(resetTransfers()) } }, [realtokens, addresses, dispatch]) + + useEffect(() => { + if (realtokens.length && userDisplayAdditionalData) { + dispatch(fetchRealtokensExtraData()) + } + }, [realtokens, userDisplayAdditionalData]) } diff --git a/src/hooks/useREG.ts b/src/hooks/useREG.ts index b3a8dc29..f92165db 100644 --- a/src/hooks/useREG.ts +++ b/src/hooks/useREG.ts @@ -21,13 +21,15 @@ import { import { APIRealTokenProductType } from 'src/types/APIRealToken' import { Currency } from 'src/types/Currencies' import { ERC20ABI } from 'src/utils/blockchain/abi/ERC20ABI' +import { + HoneySwapFactory_Address, + REG_Vault_Gnosis_ContractAddress, +} from 'src/utils/blockchain/consts/misc' import { DEFAULT_REG_PRICE, DEFAULT_USDC_USD_RATE, DEFAULT_XDAI_USD_RATE, - HoneySwapFactory_Address, REG_ContractAddress, - REG_Vault_Gnosis_ContractAddress, REG_asset_ID, REGtokenDecimals, USDConXdai_ContractAddress, @@ -104,7 +106,7 @@ const getREG = async ( ) availableBalance += balance[WalletType.Ethereum].amount } - + const regVaultAbiGetUserGlobalStateOnly = getRegVaultAbiGetUserGlobalStateOnly() diff --git a/src/hooks/useREUSD.ts b/src/hooks/useREUSD.ts new file mode 100644 index 00000000..e2c049af --- /dev/null +++ b/src/hooks/useREUSD.ts @@ -0,0 +1,234 @@ +import { useEffect, useState } from 'react' +import { useSelector } from 'react-redux' + +import { Contract } from 'ethers' +import test from 'node:test' + +import { WalletType } from 'src/repositories' +import { initializeProviders } from 'src/repositories/RpcProvider' +import { + selectCurrencyRates, + selectUserCurrency, +} from 'src/store/features/currencies/currenciesSelector' +import { + selectUserAddressList, + selectUserIncludesEth, +} from 'src/store/features/settings/settingsSelector' +import { + BalanceByWalletType, + REUSDGRealtoken, + updateBalanceValues, +} from 'src/store/features/wallets/walletsSelector' +import { APIRealTokenProductType } from 'src/types/APIRealToken' +import { Currency } from 'src/types/Currencies' +import { ERC20ABI } from 'src/utils/blockchain/abi/ERC20ABI' +import { + CHAIN_ID__GNOSIS_XDAI, + HoneySwapFactory_Address, + SUSHISWAP_DEPLOYMENTS, +} from 'src/utils/blockchain/consts/misc' +import { + DEFAULT_REUSD_PRICE, + DEFAULT_USDC_USD_RATE, + DEFAULT_XDAI_USD_RATE, + REUSD_ContractAddress, + REUSD_asset_ID, + REGtokenDecimals as REUSDtokenDecimals, + USDConXdai_ContractAddress, + USDCtokenDecimals, + WXDAI_ContractAddress, + WXDAItokenDecimals, +} from 'src/utils/blockchain/consts/otherTokens' +import { getAddressesBalances } from 'src/utils/blockchain/erc20Infos' +import { + AssetPrice, + FeeAmounts, + averageValues, + getUniV2AssetPrice, + getUniV3AssetPrice, +} from 'src/utils/blockchain/poolPrice' + +/** + * + * @param addressList : user addresses list + * @param userRate : user selected currency rate + * @param currenciesRates : currencies rates + * @param includeETH : include balances on ETH in the calculation + * @returns + */ +const getREUSD = async ( + addressList: string[], + userRate: number, + currenciesRates: Record, + includeETH = false, +): Promise => { + const { GnosisRpcProvider, EthereumRpcProvider } = await initializeProviders() + const providers = [GnosisRpcProvider] + if (includeETH) { + providers.push(EthereumRpcProvider) + } + const ReusdContract_Gnosis = new Contract( + REUSD_ContractAddress, + ERC20ABI, + GnosisRpcProvider, + ) + const balance: BalanceByWalletType = { + [WalletType.Gnosis]: { + amount: 0, + value: 0, + }, + [WalletType.Ethereum]: { + amount: 0, + value: 0, + }, + [WalletType.RMM]: { + amount: 0, + value: 0, + }, + [WalletType.LevinSwap]: { + amount: 0, + value: 0, + }, + } + let availableBalance = await getAddressesBalances( + REUSD_ContractAddress, + addressList, + GnosisRpcProvider, + ) + + balance[WalletType.Gnosis].amount = availableBalance + + if (includeETH) { + balance[WalletType.Ethereum].amount = await getAddressesBalances( + REUSD_ContractAddress, + addressList, + EthereumRpcProvider, + ) + availableBalance += balance[WalletType.Ethereum].amount + } + + const totalAmount = availableBalance + const contractReusdTotalSupply = await ReusdContract_Gnosis.totalSupply() + const totalTokens = + Number(contractReusdTotalSupply) / 10 ** REUSDtokenDecimals + const amount = totalAmount / 10 ** REUSDtokenDecimals + + // Get REG token prices in USDC and WXDAI from LPs + const reusdPriceUsdcHoneyswap = await getUniV2AssetPrice( + HoneySwapFactory_Address, + REUSD_ContractAddress, + USDConXdai_ContractAddress, + REUSDtokenDecimals, + USDCtokenDecimals, + GnosisRpcProvider, + ) + // const reusdPriceWxdaiHoneyswap = await getUniV2AssetPrice( HoneySwapFactory_Address, REUSD_ContractAddress, WXDAI_ContractAddress, REUSDtokenDecimals, WXDAItokenDecimals, GnosisRpcProvider ) + const reusdPriceWxdaiHoneyswap = null // Honeyswap does not have WXDAI pool (yet) + + // Main pool on Gnosis: Sushiswap @ 0.01% fee + const reusdPriceUsdcSushiv3 = await getUniV3AssetPrice( + SUSHISWAP_DEPLOYMENTS, + REUSD_ContractAddress, + USDConXdai_ContractAddress, + REUSDtokenDecimals, + USDCtokenDecimals, + GnosisRpcProvider, + CHAIN_ID__GNOSIS_XDAI, + FeeAmounts.LOWEST, // 0.01% fee + AssetPrice.TokenA, // REUSD is token0, USDC is token1 + 20, // Amount of token to quote without decimals + ) + // const reusdPriceWxdaiSushiv3 = await getUniV3AssetPrice( SUSHISWAP_DEPLOYMENTS, REUSD_ContractAddress, WXDAI_ContractAddress, REUSDtokenDecimals, WXDAItokenDecimals, GnosisRpcProvider, CHAIN_ID__GNOSIS_XDAI, FeeAmounts.LOWEST ) // 0.01% fee AssetPrice.TokenA, // REUSD is token0, WXDAI is token1 + const reusdPriceWxdaiSushiv3 = null // Sushiswap does not have WXDAI pool (yet) + + // Get rates for XDAI and USDC against USD + const rateXdaiUSD = currenciesRates?.XDAI + ? currenciesRates.XDAI + : DEFAULT_XDAI_USD_RATE + const rateUsdcUSD = currenciesRates?.USDC + ? currenciesRates.USDC + : DEFAULT_USDC_USD_RATE + // Convert Honeyswap token prices to USD + const assetPriceUsd1 = reusdPriceUsdcHoneyswap + ? reusdPriceUsdcHoneyswap * rateUsdcUSD + : null + const assetPriceUsd2 = reusdPriceWxdaiHoneyswap + ? reusdPriceWxdaiHoneyswap * rateXdaiUSD + : null + // Get Honeyswap average token prices in USD + const assetAveragePriceOnHoneyswapInUSD = averageValues([ + assetPriceUsd1, + assetPriceUsd2, + ]) + // Convert Sushiswap token price to USD + const assetPriceUsd3 = reusdPriceUsdcSushiv3 + ? reusdPriceUsdcSushiv3 * rateUsdcUSD + : null + + const assetPriceUsd4 = reusdPriceWxdaiSushiv3 + ? reusdPriceWxdaiSushiv3 * rateXdaiUSD + : null + + // Get Sushiswap average token prices in USD + const assetAveragePriceOnSushiswapInUSD = averageValues([ + assetPriceUsd3, + assetPriceUsd4, + ]) + + // Apply a 20 / 80 ratio between Honeyswap and Sushiswap prices + // we SHOULD COMPARE LIQUIDITY instead (todo) + const assetPriceInUSD = + 0.2 * + (assetAveragePriceOnHoneyswapInUSD + ? assetAveragePriceOnHoneyswapInUSD + : DEFAULT_REUSD_PRICE) + + 0.8 * + (assetAveragePriceOnSushiswapInUSD + ? assetAveragePriceOnSushiswapInUSD + : DEFAULT_REUSD_PRICE) + + // Convert prices in Currency by applying rate + const tokenPrice = assetPriceInUSD + ? assetPriceInUSD / userRate + : DEFAULT_REUSD_PRICE / userRate + const value = tokenPrice * amount + const totalInvestment = totalTokens * tokenPrice + // Update all balance values with token price + updateBalanceValues(balance, tokenPrice) + + return { + id: `${REUSD_asset_ID}`, + fullName: 'Realtoken Ecosystem USD', + shortName: 'REUSD', + productType: APIRealTokenProductType.EquityToken, + amount, + tokenPrice, + totalTokens, + imageLink: [ + 'https://static.debank.com/image/project/logo_url/xdai_realtrmm/05ce107c2155971276a46b920053f704.png', + ], + isRmmAvailable: false, + value, + totalInvestment, + unitPriceCost: tokenPrice, + balance, + } +} + +export const useREUSD = () => { + const [reusd, setReusd] = useState(null) + const addressList = useSelector(selectUserAddressList) + const { rate: userRate } = useSelector(selectUserCurrency) + const includeETH = useSelector(selectUserIncludesEth) + const currenciesRates = useSelector(selectCurrencyRates) + + useEffect(() => { + if (addressList.length) { + getREUSD(addressList, userRate, currenciesRates, includeETH).then( + setReusd, + ) + } + }, [addressList, userRate, currenciesRates, includeETH]) + + return reusd +} diff --git a/src/hooks/useRWA.ts b/src/hooks/useRWA.ts index c87b4980..568c7d31 100644 --- a/src/hooks/useRWA.ts +++ b/src/hooks/useRWA.ts @@ -20,11 +20,11 @@ import { import { APIRealTokenProductType } from 'src/types/APIRealToken' import { Currency } from 'src/types/Currencies' import { ERC20ABI } from 'src/utils/blockchain/abi/ERC20ABI' +import { HoneySwapFactory_Address } from 'src/utils/blockchain/consts/misc' import { DEFAULT_RWA_PRICE, DEFAULT_USDC_USD_RATE, DEFAULT_XDAI_USD_RATE, - HoneySwapFactory_Address, RWA_ContractAddress, RWA_asset_ID, RWAtokenDecimals, @@ -115,7 +115,7 @@ const getRWA = async ( const tokenPrice = (assetAveragePriceUSD ?? DEFAULT_RWA_PRICE) / userRate const value = tokenPrice * amount const totalInvestment = totalTokens * tokenPrice - + // Update all balance values with token price updateBalanceValues(balance, tokenPrice) diff --git a/src/i18next/locales/en/common.json b/src/i18next/locales/en/common.json index 55668c19..fbdf265c 100644 --- a/src/i18next/locales/en/common.json +++ b/src/i18next/locales/en/common.json @@ -32,7 +32,8 @@ "includesEth": "Includes Ethereum", "includesLevinSwap": "Includes LevinSwap", "includesRmmV2": "Includes RMM V2", - "includesOtherAssets": "Includes other assets" + "includesOtherAssets": "Includes other assets", + "displayAdditionalData": "Display additional data" }, "walletButton": { "connectWallet": "Connect wallet", @@ -77,6 +78,7 @@ "stableBorrow": "RMM borrow", "rwa": "RWA", "reg": "REG", + "reUsd": "REUSD", "regVote": "REG Vote Power" }, "worthCard": { @@ -146,7 +148,7 @@ "resetFilter": "Reset" }, "assetSubsidy": { - "label": "All", + "label": "Subsidized rents", "options": { "all": "No filtered", "subsidized": "Subsidized", @@ -196,6 +198,25 @@ "notAvailable": "No" } }, + "assetIssueStatus": { + "label": "Issue Status", + "options": { + "all": "All", + "noExhibit": "No exhibit", + "scheduled": "Scheduled", + "upgradedAndReady": "Upgraded and ready" + } + }, + "assetIssuePriority": { + "label": "Issue Priority", + "options": { + "all": "All", + "none": "No priority", + "high": "High", + "medium": "Medium", + "low": "Low" + } + }, "assetUserProtocol": { "label": "Owned on", "options": { @@ -227,7 +248,36 @@ "full": "Subsidized", "partial": "Partially subsidized" }, - "subsidy": "Subsidy" + "subsidy": "Subsidy", + "assetIssues": { + "title": "Asset issues", + "status": "Status", + "priority": "Priority", + "lawsuit": "Lawsuit" + } + }, + "assetIssues": { + "status": { + "noExhibit": "Not applicable", + "scheduled": "Scheduled", + "upgradedReady": "Upgraded and ready", + "unknown": "Unknown", + "na": "Not applicable" + }, + "priority": { + "1": "Priority 1 (High)", + "2": "Priority 2 (Medium)", + "3": "Priority 3 (Low)", + "na": "Not applicable", + "unknown": "Unknown" + }, + "lawsuit": { + "na": "Not applicable", + "exhibit": "exhibit", + "unknown": "Unknown", + "exhibitNumber": "number", + "volumeNumber": "volume number" + } }, "assetTable": { "property": "Property", @@ -238,18 +288,28 @@ "unitPriceCost": "Unit price cost", "unrealizedCapitalGain": "Capital gain", "apr": "Yield", - "fullyRentedAPR":"Fully rented APR *", + "fullyRentedAPR": "Fully rented APR *", "weeklyRents": "Weekly rents", "yearlyRents": "Yearly rents", "rentedUnits": "Rented units", "propertyValue": "Property value", - "lastChange": "Last change" + "lastChange": "Last change", + "status": { + "header": "Status" + }, + "priority": { + "header": "Priority" + }, + "lawsuit": { + "header": "Lawsuit" + } }, "assetPage": { "home": "Home", "viewOnRealt": "View on RealT", "tabs": { "main": "Main informations", + "issues": "Issues", "property": "Property details", "history": "Property changes", "transfers": "My transactions", @@ -272,6 +332,11 @@ "rentYear": "Yearly rent", "rentedUnits": "Rented units" }, + "issues": { + "status": "Status", + "priority": "Priority", + "lawsuit": "Lawsuit" + }, "property": { "initialLaunchDate": "Launch date", "totalValue": "Investment value", @@ -422,10 +487,10 @@ "title": "Initial data loading", "description": "Retrieving your transactions in progress. This loading can take some time depending on the number of transactions performed (10-20 seconds / 1000 transactions). On your next visits, only new transactions will be retrieved." }, - "disclaimer":{ + "disclaimer": { "fullyRentedAPR": "This is a beta estimation done by RealT community. Please report any issues. Please note that is an indicative value and not a guarantee. RealT community or RealT does not take any responsibility for user actions based on this value." }, - "errors" : { + "errors": { "userNotFound": "User not found" } -} +} \ No newline at end of file diff --git a/src/i18next/locales/fr/common.json b/src/i18next/locales/fr/common.json index 70c34a87..ef5d27d1 100644 --- a/src/i18next/locales/fr/common.json +++ b/src/i18next/locales/fr/common.json @@ -32,7 +32,8 @@ "includesEth": "Inclure Ethereum", "includesLevinSwap": "Inclure LevinSwap", "includesRmmV2": "Inclure RMM V2", - "includesOtherAssets": "Inclure d'autres actifs" + "includesOtherAssets": "Inclure d'autres actifs", + "displayAdditionalData": "Afficher des données supplémentaires" }, "walletButton": { "connectWallet": "Connecter mon portefeuille", @@ -77,6 +78,7 @@ "stableBorrow": "Emprunt RMM", "rwa": "RWA", "reg": "REG", + "reUsd": "REUSD", "regVote": "Pouvoir de vote" }, "worthCard": { @@ -196,6 +198,25 @@ "notAvailable": "Non" } }, + "assetIssueStatus": { + "label": "Statut des problèmes", + "options": { + "all": "Tous", + "noExhibit": "Non concerné", + "scheduled": "Planifié", + "upgradedAndReady": "Réparé, prêt" + } + }, + "assetIssuePriority": { + "label": "Priorité des problèmes", + "options": { + "all": "Tous", + "none": "Sans problème", + "high": "Priorité 1 (Haute)", + "medium": "Priorité 2 (Moyenne)", + "low": "Priorité 3 (Basse)" + } + }, "assetUserProtocol": { "label": "Possédées sur", "options": { @@ -212,7 +233,7 @@ "weekly": "Loyers hebdomadaires", "yearly": "Loyers annuels", "rentedUnits": "Logements loués", - "tokenPrice":"Prix d'un token", + "tokenPrice": "Prix d'un token", "propertyValue": "Valeur de la propriété", "rentStartDate": "Date du premier loyer", "fullyRentedEstimation": "Rendement 100% loué", @@ -227,7 +248,36 @@ "full": "Subventionnée", "partial": "Partiellement subventionnée" }, - "subsidy": "Loyer subventioné" + "subsidy": "Loyer subventioné", + "assetIssues": { + "title": "Problèmes", + "status": "Etat", + "priority": "Priorité", + "lawsuit": "Poursuite" + } + }, + "assetIssues": { + "status": { + "noExhibit": "Non concerné", + "scheduled": "Planifié", + "upgradedReady": "Réparé, prêt", + "unknown": "Inconnu", + "na": "Non concerné" + }, + "priority": { + "1": "Priorité 1 (Haute)", + "2": "Priorité 2 (Moyenne)", + "3": "Priorité 3 (Basse)", + "na": "Non concerné", + "unknown": "Inconnu" + }, + "lawsuit": { + "na": "Sans objet", + "exhibit": "plainte", + "unknown": "Inconnu", + "exhibitNumber": "numéro", + "volumeNumber": "volume" + } }, "assetTable": { "property": "Propriété", @@ -238,18 +288,28 @@ "tokenPrice": "Prix du token", "unitPriceCost": "Prix de revient", "apr": "Rendement annuel", - "fullyRentedAPR":"Rendement 100% loué *", + "fullyRentedAPR": "Rendement 100% loué *", "weeklyRents": "Loyer hebdo", "yearlyRents": "Loyer annuel", "rentedUnits": "Logements loués", "propertyValue": "Valeur de la propriété", - "lastChange": "Dernier changement" + "lastChange": "Dernier changement", + "status": { + "header": "Statut" + }, + "priority": { + "header": "Priorité" + }, + "lawsuit": { + "header": "Poursuite" + } }, "assetPage": { "home": "Accueil", "viewOnRealt": "Voir sur RealT", "tabs": { "main": "Informations générales", + "issues": "Problèmes", "property": "Détail du bien", "history": "Changements du bien", "transfers": "Mes transactions", @@ -272,6 +332,11 @@ "rentYear": "Loyer annuel", "rentedUnits": "Logements loués" }, + "issues": { + "status": "Statut", + "priority": "Priorité", + "lawsuit": "Poursuite" + }, "property": { "initialLaunchDate": "Mise en vente", "totalValue": "Valeur de l'investissement", @@ -423,11 +488,10 @@ "title": "Chargement initial des données", "description": "Récupération de vos transactions en cours. Ce chargement peut prendre un certain temps en fonction du nombre de transactions effecutées (10-20 secondes / 1000 transactions). Lors de vos prochaines visites, seul les nouvelles transactions seront récupérées." }, - "disclaimer":{ + "disclaimer": { "fullyRentedAPR": "Cette estimation est en phase bêta et a été développée par la communauté RealT. Nous vous invitons à signaler tout problème éventuel. Les informations fournies sont à titre indicatif uniquement. La communauté RealT ou RealT ne peut être tenue responsable en cas de décision prise à partir de données inexactes." - } - , - "errors" : { + }, + "errors": { "userNotFound": "User not found" } -} +} \ No newline at end of file diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 1ede46ef..73e66d6e 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,4 +1,4 @@ -import { useEffect } from 'react' +import { useEffect, useMemo } from 'react' import { QueryClient, QueryClientProvider } from 'react-query' import { Provider } from 'react-redux' @@ -116,45 +116,54 @@ const App = ({ // Customize chains config for Gnosis and Ethereum // using rpc urls from props - const CustomChainsConfig = { - // Keep Goerli as testnet else an error will arise at init - [ChainsID.Goerli]: RealtCommonsDefaultChainsConfig[ChainsID.Goerli], - [ChainsID.Gnosis]: { - ...RealtCommonsDefaultChainsConfig[ChainsID.Gnosis], - rpcUrl: - GnosisRpcUrl || RealtCommonsDefaultChainsConfig[ChainsID.Gnosis].rpcUrl, - }, - [ChainsID.Ethereum]: { - ...RealtCommonsDefaultChainsConfig[ChainsID.Ethereum], - rpcUrl: - EthereumRpcUrl || - RealtCommonsDefaultChainsConfig[ChainsID.Ethereum].rpcUrl, - }, - // TODO: add Polygon - } + const CustomChainsConfig = useMemo( + () => ({ + // Keep Goerli as testnet else an error will arise at init + [ChainsID.Goerli]: RealtCommonsDefaultChainsConfig[ChainsID.Goerli], + [ChainsID.Gnosis]: { + ...RealtCommonsDefaultChainsConfig[ChainsID.Gnosis], + rpcUrl: + GnosisRpcUrl || + RealtCommonsDefaultChainsConfig[ChainsID.Gnosis].rpcUrl, + }, + [ChainsID.Ethereum]: { + ...RealtCommonsDefaultChainsConfig[ChainsID.Ethereum], + rpcUrl: + EthereumRpcUrl || + RealtCommonsDefaultChainsConfig[ChainsID.Ethereum].rpcUrl, + }, + // TODO: add Polygon + }), + [GnosisRpcUrl, EthereumRpcUrl], + ) - const dashbordChains: ChainSelectConfig = { - allowedChains: parseAllowedChain(ChainsID), - chainsConfig: CustomChainsConfig, - defaultChainId: ChainsID.Gnosis, // Explicitly setting Gnosis as the defaultChainId - } + const dashbordChains: ChainSelectConfig = useMemo( + () => ({ + allowedChains: parseAllowedChain(ChainsID), + chainsConfig: CustomChainsConfig, + defaultChainId: ChainsID.Gnosis, // Explicitly setting Gnosis as the defaultChainId + }), + [CustomChainsConfig], + ) const envName = process.env.NEXT_PUBLIC_ENV ?? 'development' const walletConnectKey = process.env.NEXT_PUBLIC_WALLET_CONNECT_KEY ?? '' - const readOnly = getReadOnlyConnector(dashbordChains) - const walletConnect = getWalletConnectV2( - dashbordChains, - envName, - walletConnectKey, - false, - ) - - const libraryConnectors = getConnectors({ - readOnly: readOnly, - metamask: [metaMask, metaMaskHooks], - walletConnectV2: walletConnect, - } as unknown as ConnectorsAvailable) + const libraryConnectors = useMemo(() => { + const readOnly = getReadOnlyConnector(dashbordChains) + const walletConnect = getWalletConnectV2( + dashbordChains, + envName, + walletConnectKey, + false, + ) + + return getConnectors({ + readOnly: readOnly, + metamask: [metaMask, metaMaskHooks], + walletConnectV2: walletConnect, + } as unknown as ConnectorsAvailable) + }, [dashbordChains, envName, walletConnectKey]) return ( diff --git a/src/pages/api/history/index.ts b/src/pages/api/history/index.ts index 7305ad8e..65ebd34a 100644 --- a/src/pages/api/history/index.ts +++ b/src/pages/api/history/index.ts @@ -1,20 +1,31 @@ import { NextApiHandler } from 'next' +import { APIRealTokenCommunityEnv } from 'src/types/APIRealToken' import { APIRealTokenHistory } from 'src/types/APIRealTokenHistory' import { useCache } from 'src/utils/useCache' const getRealTokenHistory = useCache( async (): Promise => { - if (!process.env.COMMUNITY_API_KEY) { - throw new Error('Missing COMMUNITY_API_KEY env variable') + if (!process.env[APIRealTokenCommunityEnv.API_KEY]) { + throw new Error( + `Missing ${APIRealTokenCommunityEnv.API_KEY} env variable`, + ) } - if (!process.env.REALTOKENAPI_HISTORY) { - throw new Error('Missing REALTOKENAPI_HISTORY env variable') + if (!process.env[APIRealTokenCommunityEnv.API_HISTORY]) { + throw new Error( + `Missing ${APIRealTokenCommunityEnv.API_HISTORY} env variable`, + ) } - const response = await fetch(process.env.REALTOKENAPI_HISTORY, { - method: 'GET', - headers: { 'X-AUTH-REALT-TOKEN': process.env.COMMUNITY_API_KEY }, - }) + const response = await fetch( + process.env[APIRealTokenCommunityEnv.API_HISTORY], + { + method: 'GET', + headers: { + [APIRealTokenCommunityEnv.AUTH]: + process.env[APIRealTokenCommunityEnv.API_KEY], + }, + }, + ) if (!response.ok) { throw new Error('Failed to fetch properties history') diff --git a/src/pages/api/pitsBiExtraProperties/index.ts b/src/pages/api/pitsBiExtraProperties/index.ts new file mode 100644 index 00000000..5f5ffec9 --- /dev/null +++ b/src/pages/api/pitsBiExtraProperties/index.ts @@ -0,0 +1,52 @@ +import { NextApiHandler } from 'next' + +import { APIPitsBiEnv } from 'src/types/APIPitsBI' +import { APIRealToken } from 'src/types/APIRealToken' +import { fetchWithRetry } from 'src/utils/general' +import { useCache } from 'src/utils/useCache' + +import { URIS } from '../uris' + +const getRealTokenListExtraData = useCache( + async (): Promise => { + let APIPitsBi_Env_available = true + for (const envVar of [ + APIPitsBiEnv.VERSION, + APIPitsBiEnv.BASE, + APIPitsBiEnv.GET_LASTUPDATE, + APIPitsBiEnv.GET_ALLTOKENS, + ]) { + if (!process.env[envVar]) { + APIPitsBi_Env_available = false + console.warn( + `extraProperties: Missing PitsBi API ${envVar} env variable`, + ) + } + } + if (!APIPitsBi_Env_available) { + console.warn('extraProperties: PitsBi API is not available') + // Return empty response + return [] + } + const pitsBiApiResponse = await fetchWithRetry( + URIS.PITSBI_API_GET_ALLTOKENS, + { method: 'GET' }, + 2, + 5_000, + ) + return pitsBiApiResponse.json() + }, + { duration: 1000 * 60 * 60 }, +) + +const handler: NextApiHandler = async (req, res) => { + try { + res.status(200).json(await getRealTokenListExtraData()) + } catch (error) { + const message = error instanceof Error ? error.message : 'Unknown error' + console.log(message) + res.status(500).json(message) + } +} + +export default handler diff --git a/src/pages/api/properties/index.ts b/src/pages/api/properties/index.ts index 45f8c7be..11d8c327 100644 --- a/src/pages/api/properties/index.ts +++ b/src/pages/api/properties/index.ts @@ -1,26 +1,74 @@ import { NextApiHandler } from 'next' -import { APIRealToken } from 'src/types/APIRealToken' +import { APIPitsBiEnv } from 'src/types/APIPitsBI' +import { APIRealToken, APIRealTokenCommunityEnv } from 'src/types/APIRealToken' +import { fetchWithRetry } from 'src/utils/general' import { useCache } from 'src/utils/useCache' +import { URIS } from '../uris' + const getRealTokenList = useCache( async (): Promise => { - if (!process.env.COMMUNITY_API_KEY) { - throw new Error('Missing COMMUNITY_API_KEY env variable') + for (const envVar of [ + APIRealTokenCommunityEnv.API_KEY, + APIRealTokenCommunityEnv.API_BASE, + APIRealTokenCommunityEnv.VERSION, + APIRealTokenCommunityEnv.GET_ALLTOKENS, + ]) { + if (!process.env[envVar]) { + throw new Error( + `Missing RealToken Community API ${envVar} env variable`, + ) + } } - if (!process.env.REALTOKENAPI) { - throw new Error('Missing REALTOKENAPI env variable') + try { + const realTokenApiResponse = await fetchWithRetry( + URIS.REALTOKEN_COMMUNITY_API_GET_ALLTOKENS, + { + method: 'GET', + headers: { + [APIRealTokenCommunityEnv.AUTH]: process.env[ + APIRealTokenCommunityEnv.API_KEY + ] as string, + }, + }, + 2, + 1_000, + ) + return realTokenApiResponse.json() + } catch (error) { + console.error(`Failed to fetch RealToken API: ${error}`) } - const response = await fetch(process.env.REALTOKENAPI, { - method: 'GET', - headers: { 'X-AUTH-REALT-TOKEN': process.env.COMMUNITY_API_KEY }, - }) - - if (!response.ok) { - throw new Error('Failed to fetch properties : ' + (await response.text())) + // Use Pitsbi API as fallback datasource if RealToken API is not available + // Pitsbi is 100% compatible with RealToken API + let APIPitsbi_Env_available = true + for (const envVar of [ + APIPitsBiEnv.VERSION, + APIPitsBiEnv.BASE, + APIPitsBiEnv.GET_LASTUPDATE, + APIPitsBiEnv.GET_ALLTOKENS, + ]) { + if (!process.env[envVar]) { + APIPitsbi_Env_available = false + console.warn(`Missing Pitsbi API ${envVar} env variable`) + } } - - return response.json() + if (!APIPitsbi_Env_available) { + throw new Error( + `Failed to fetch properties from RealToken API ; PitsBI API environment variables are not set, unable to fetch data.`, + ) + } + const pitsbiApiResponse = await fetchWithRetry( + URIS.PITSBI_API_GET_ALLTOKENS, + { + method: 'GET', + }, + 2, + 10_000, + ) + // Return Pitsbi API response if RealToken API is not available + console.warn(`Pitsbi API used as fallback`) + return pitsbiApiResponse.json() }, { duration: 1000 * 60 * 60 }, ) diff --git a/src/pages/api/uris.ts b/src/pages/api/uris.ts new file mode 100644 index 00000000..eadf5d73 --- /dev/null +++ b/src/pages/api/uris.ts @@ -0,0 +1,11 @@ +// Define API Uris +import { APIPitsBiEnv } from 'src/types/APIPitsBI' +import { APIRealTokenCommunityEnv } from 'src/types/APIRealToken' + +const REALTOKEN_COMMUNITY_API_GET_ALLTOKENS = `${process.env[APIRealTokenCommunityEnv.API_BASE]}${process.env[APIRealTokenCommunityEnv.VERSION]}/${process.env[APIRealTokenCommunityEnv.GET_ALLTOKENS]}` +const PITSBI_API_GET_ALLTOKENS = `${process.env[APIPitsBiEnv.BASE]}${process.env[APIPitsBiEnv.VERSION]}/${process.env[APIPitsBiEnv.GET_ALLTOKENS]}` + +export const URIS = { + REALTOKEN_COMMUNITY_API_GET_ALLTOKENS, + PITSBI_API_GET_ALLTOKENS, +} diff --git a/src/pages/asset/[assetId].tsx b/src/pages/asset/[assetId].tsx index 9c93d541..cbd283dc 100644 --- a/src/pages/asset/[assetId].tsx +++ b/src/pages/asset/[assetId].tsx @@ -7,15 +7,19 @@ import Image from 'next/image' import { useRouter } from 'next/router' import { Anchor, Breadcrumbs, Button, Flex } from '@mantine/core' -import { IconExternalLink } from '@tabler/icons' +import { IconExternalLink } from '@tabler/icons-react' import { AssetPageHistoryTab } from 'src/components/assetPage/assetPageHistoryTab' +import { AssetPageIssuesTab } from 'src/components/assetPage/assetPageIssuesTab' import { AssetPageMainTab } from 'src/components/assetPage/assetPageMainTab' import { AssetPagePropertyTab } from 'src/components/assetPage/assetPagePropertyTab' import { AssetPageTransfersTab } from 'src/components/assetPage/assetPageTransfersTab' import { AssetPageYamStatisticsTab } from 'src/components/assetPage/assetPageYamStatisticsTab' import FullyRentedAPRDisclaimer from 'src/components/commons/others/FullyRentedAPRDisclaimer' -import { selectIsLoading } from 'src/store/features/settings/settingsSelector' +import { + selectIsLoading, + selectUserDisplayAdditionalData, +} from 'src/store/features/settings/settingsSelector' import { selectTransfersIsLoaded } from 'src/store/features/transfers/transfersSelector' import { selectAllUserRealtokens } from 'src/store/features/wallets/walletsSelector' @@ -23,6 +27,7 @@ import styles from './AssetPage.module.sass' enum Tabs { Main = 'main', + Issues = 'issues', Property = 'property', Transfers = 'transfers', History = 'history', @@ -51,7 +56,7 @@ const AssetPage: NextPage = () => { const { t } = useTranslation('common', { keyPrefix: 'assetPage' }) const realtokens = useSelector(selectAllUserRealtokens) const transfersIsLoaded = useSelector(selectTransfersIsLoaded) - + const userDisplayAdditionalData = useSelector(selectUserDisplayAdditionalData) const isLoading = useSelector(selectIsLoading) const router = useRouter() const { assetId } = router.query @@ -97,6 +102,21 @@ const AssetPage: NextPage = () => {
) : null} + {userDisplayAdditionalData && ( + <> + setActiveTab(Tabs.Issues)} + /> + {activeTab === Tabs.Issues ? ( +
+ +
+ ) : null} + + )} + { const realtokens = useSelector(selectUserRealtokens) const rwa = useRWA() const reg = useREG() + const reUsd = useREUSD() const regVotingPower = useRegVotingPower() const allAssetsData = useMemo(() => { @@ -33,19 +35,21 @@ const HomePage: NextPage = () => { ...realtokens, rwa, reg, + reUsd, regVotingPower, ].filter((asset) => !!asset) return assets as (UserRealtoken | OtherRealtoken)[] - }, [realtokens, rwa, reg, regVotingPower]) + }, [realtokens, rwa, reg, reUsd, regVotingPower]) const otherAssetsData = useMemo(() => { const assets = { rwa, reg, + reUsd, regVotingPower, } return assets - }, [rwa, reg, regVotingPower]) + }, [rwa, reg, reUsd, regVotingPower]) return ( diff --git a/src/pages/test-rpc.tsx b/src/pages/test-rpc.tsx index cff97bca..b6d3c1c9 100644 --- a/src/pages/test-rpc.tsx +++ b/src/pages/test-rpc.tsx @@ -59,8 +59,8 @@ export default function TestRpcPage() { if (loading) { return (
-

Test RPC - Chargement...

-

Test de initializeProviders en cours...

+

{'Test RPC - Chargement...'}

+

{'Test de initializeProviders en cours...'}

) } @@ -68,13 +68,13 @@ export default function TestRpcPage() { if (!results?.success) { return (
-

Test RPC - Erreur

-

❌ initializeProviders a échoué

+

{'Test RPC - Erreur'}

+

{'❌ initializeProviders a échoué'}

- Message: {results?.error?.message} + {'Message:'} {results?.error?.message}

- Stack trace + {'Stack trace'}
@@ -87,16 +87,17 @@ export default function TestRpcPage() {
 
   return (
     
-

Test RPC - Succès

-

✅ initializeProviders réussi

+

{'Test RPC - Succès'}

+

{'✅ initializeProviders réussi'}

- Durée: {results.duration}ms + {'Durée:'} {results.duration} + {'ms\r'}

- Gnosis RPC: {results.providers?.gnosisUrl} + {'Gnosis RPC:'} {results.providers?.gnosisUrl}

- Ethereum RPC: {results.providers?.ethereumUrl} + {'Ethereum RPC:'} {results.providers?.ethereumUrl}

) diff --git a/src/repositories/RpcProvider.ts b/src/repositories/RpcProvider.ts index 9e6168b6..f09a602d 100644 --- a/src/repositories/RpcProvider.ts +++ b/src/repositories/RpcProvider.ts @@ -1,4 +1,5 @@ import { CHAINS as RealtCommonsDefaultChainsConfig } from '@realtoken/realt-commons' + import { Contract, JsonRpcProvider, @@ -10,10 +11,10 @@ import { import { ERC20ABI } from 'src/utils/blockchain/abi/ERC20ABI' import { CHAINS_NAMES, - CHAIN_ID_ETHEREUM, - CHAIN_ID_GNOSIS_XDAI, - REG_ContractAddress, -} from 'src/utils/blockchain/consts/otherTokens' + CHAIN_ID__ETHEREUM, + CHAIN_ID__GNOSIS_XDAI, +} from 'src/utils/blockchain/consts/misc' +import { REG_ContractAddress } from 'src/utils/blockchain/consts/otherTokens' import { batchCallOneContractOneFunctionMultipleParams } from 'src/utils/blockchain/contract' import { wait } from 'src/utils/general' import { WaitingQueue } from 'src/utils/waitingQueue' @@ -39,17 +40,17 @@ const getRpcUrls = (chainId: number): string[] => { let defaultUrls: string[] = [] switch (chainId) { - case CHAIN_ID_ETHEREUM: + case CHAIN_ID__ETHEREUM: envVarName = 'RPC_URLS_ETH_MAINNET' // Use the default Ethereum RPC URLs from realt-commons config defaultUrls = DEFAULT_ETHEREUM_RPC_URLS.concat( - RealtCommonsDefaultChainsConfig[CHAIN_ID_ETHEREUM].rpcUrl, + RealtCommonsDefaultChainsConfig[CHAIN_ID__ETHEREUM].rpcUrl, ) break - case CHAIN_ID_GNOSIS_XDAI: + case CHAIN_ID__GNOSIS_XDAI: envVarName = 'RPC_URLS_GNOSIS_MAINNET' defaultUrls = DEFAULT_GNOSIS_RPC_URLS.concat( - RealtCommonsDefaultChainsConfig[CHAIN_ID_GNOSIS_XDAI].rpcUrl, + RealtCommonsDefaultChainsConfig[CHAIN_ID__GNOSIS_XDAI].rpcUrl, ) break // TODO: Polygon @@ -258,7 +259,7 @@ export interface ProvidersWithUrls extends Providers { } let initializeProvidersQueue: WaitingQueue | null = null -let providers: ProvidersWithUrls | undefined = undefined +// let providers: ProvidersWithUrls | undefined = undefined export const initializeProviders = async (): Promise => { if (initializeProvidersQueue) { @@ -273,7 +274,7 @@ export const initializeProviders = async (): Promise => { ]) } catch (error) { initializeProvidersQueue = null - providers = undefined + // providers = undefined // Relaunch directly without queue return await initializeProvidersDirect() } @@ -295,8 +296,8 @@ async function initializeProvidersDirect(): Promise { try { const [GnosisRpcProviderWithUrl, EthereumRpcProviderWithUrl] = await Promise.all([ - getWorkingRpc(CHAIN_ID_GNOSIS_XDAI), - getWorkingRpc(CHAIN_ID_ETHEREUM), + getWorkingRpc(CHAIN_ID__GNOSIS_XDAI), + getWorkingRpc(CHAIN_ID__ETHEREUM), ]) return { diff --git a/src/repositories/realtoken.repository.ts b/src/repositories/realtoken.repository.ts index cd683244..ee5f383a 100644 --- a/src/repositories/realtoken.repository.ts +++ b/src/repositories/realtoken.repository.ts @@ -1,5 +1,9 @@ import _sortBy from 'lodash/sortBy' +import { + APIRealTokenPitsBI, + APIRealTokenPitsBI_ExtraData, +} from 'src/types/APIPitsBI' import { APIRealToken } from 'src/types/APIRealToken' import { APIRealTokenHistory } from 'src/types/APIRealTokenHistory' import { RealToken, RealTokenRentStatus } from 'src/types/RealToken' @@ -21,6 +25,18 @@ export const RealtokenRepository = { ), })) }, + async getTokensPitsBiExtraData(): Promise { + const [tokensExtraData] = await Promise.all([ + fetchTokenListPitsBiExtraData(), + ]) + return tokensExtraData.map((tokenExtraData: APIRealTokenPitsBI) => { + return { + uuid: tokenExtraData.uuid, + actions: tokenExtraData.actions, + historic: tokenExtraData.historic, + } + }) + }, } async function fetchTokenList() { @@ -30,6 +46,13 @@ async function fetchTokenList() { : Promise.reject(response.statusText) } +async function fetchTokenListPitsBiExtraData() { + const response = await fetch('/api/pitsBiExtraProperties', { method: 'GET' }) + return response.ok + ? (response.json() as Promise) + : Promise.reject(response.statusText) +} + async function fetchHistoryList() { const response = await fetch('/api/history', { method: 'GET' }) return response.ok diff --git a/src/states/index.ts b/src/states/index.ts index 66984504..9ea46ac5 100644 --- a/src/states/index.ts +++ b/src/states/index.ts @@ -1,6 +1,8 @@ import { atomWithStorage } from 'jotai/utils' import { + AssetIssuePriorityType, + AssetIssueStatusType, AssetProductType, AssetRentStatusType, AssetRmmStatusType, @@ -25,6 +27,8 @@ export interface AssetsViewFilterType { rentStatus: AssetRentStatusType rmmStatus: AssetRmmStatusType userProtocol: AssetUserProtocolType + issueStatus: AssetIssueStatusType + issuePriority: AssetIssuePriorityType } export const assetsViewDefaultFilter: AssetsViewFilterType = { @@ -36,6 +40,8 @@ export const assetsViewDefaultFilter: AssetsViewFilterType = { rentStatus: AssetRentStatusType.ALL, rmmStatus: AssetRmmStatusType.ALL, userProtocol: AssetUserProtocolType.ALL, + issueStatus: AssetIssueStatusType.ALL, + issuePriority: AssetIssuePriorityType.ALL, } export const assetsViewFilterAtom = atomWithStorage( diff --git a/src/store/features/realtokens/realtokensSelector.ts b/src/store/features/realtokens/realtokensSelector.ts index c07eb77e..0126f665 100644 --- a/src/store/features/realtokens/realtokensSelector.ts +++ b/src/store/features/realtokens/realtokensSelector.ts @@ -7,6 +7,11 @@ export const selectRealtokensIsLoading = createSelector( (state) => state.isLoading, ) +export const selectRealtokensIsLoadingExtraData = createSelector( + (state: RootState) => state.realtokens, + (state) => state.isLoadingExtraData, +) + export const selectRealtokens = createSelector( (state: RootState) => state.realtokens, (realtokens) => realtokens.realtokens, diff --git a/src/store/features/realtokens/realtokensSlice.ts b/src/store/features/realtokens/realtokensSlice.ts index a5f43913..6f3de56a 100644 --- a/src/store/features/realtokens/realtokensSlice.ts +++ b/src/store/features/realtokens/realtokensSlice.ts @@ -1,19 +1,36 @@ +import { useSelector } from 'react-redux' + import { createAction, createReducer } from '@reduxjs/toolkit' +import { forEach } from 'lodash' + import { RealtokenRepository } from 'src/repositories' +import { selectUserDisplayAdditionalData } from 'src/store/features/settings/settingsSelector' import { AppDispatch, RootState } from 'src/store/store' -import { APIRealTokenProductType } from 'src/types/APIRealToken' +import { APIRealTokenPitsBI_ExtraData } from 'src/types/APIPitsBI' +import { APIRealToken, APIRealTokenProductType } from 'src/types/APIRealToken' import { RealToken } from 'src/types/RealToken' interface RealtokenInitialStateType { realtokens: RealToken[] isLoading: boolean + isLoadingExtraData: boolean + isExtraDataLoaded: boolean } const realtokenInitialState: RealtokenInitialStateType = { realtokens: [], isLoading: false, + isLoadingExtraData: false, + isExtraDataLoaded: false, } +// Filter function for product types +export const filterProductType = (item: APIRealToken) => + [ + APIRealTokenProductType.RealEstateRental, + APIRealTokenProductType.LoanIncome, + APIRealTokenProductType.Factoring, + ].includes(item.productType) // DISPATCH TYPE export const realtokensChangedDispatchType = 'realtokens/realtokensChanged' @@ -27,23 +44,64 @@ export const realtokensIsLoading = createAction( realtokensIsLoadingDispatchType, ) +// DISPATCH TYPE +export const realtokensExtraDataChangedDispatchType = + 'realtokens/realtokensExtraDataChanged' +export const realtokensExtraDataIsLoadingDispatchType = + 'realtokens/realtokensExtraDataIsLoading' +export const realtokensExtraDataLoadedDispatchType = + 'realtokens/realtokensExtraDataLoaded' + +// ACTIONS +export const realtokensExtraDataChanged = createAction( + realtokensExtraDataChangedDispatchType, +) +export const realtokensExtraDataIsLoading = createAction( + realtokensExtraDataIsLoadingDispatchType, +) +export const realtokensExtraDataLoaded = createAction( + realtokensExtraDataLoadedDispatchType, +) + // THUNKS export function fetchRealtokens() { return async (dispatch: AppDispatch, getState: () => RootState) => { - const isLoading = getState().realtokens.isLoading + const { isLoading } = getState().realtokens if (isLoading) return dispatch({ type: realtokensIsLoadingDispatchType, payload: true }) try { const data = await RealtokenRepository.getTokens() + // Check for exiting extraData : if PitsBI has been used as fallback datasource + // json will already contain 'actions' and 'historic' properties + const hasExtraData = data.some( + (token) => + (token as unknown as APIRealTokenPitsBI_ExtraData).actions || + (token as unknown as APIRealTokenPitsBI_ExtraData).historic, + ) + if (hasExtraData) { + // Reformat APIRealTokenPitsBI_ExtraData data + forEach(data, (token: RealToken) => { + const { actions, historic } = + token as unknown as APIRealTokenPitsBI_ExtraData + // Restructure token data: extract PitsBI "extra data" + token.extraData = { + pitsBI: { + actions: actions, + historic: historic, + }, + } + // Remove 'historic', 'actions' from token + // eslint-disable-next-line @typescript-eslint/no-explicit-any + delete (token as any).historic + // eslint-disable-next-line @typescript-eslint/no-explicit-any + delete (token as any).actions + }) + dispatch({ type: realtokensExtraDataLoadedDispatchType, payload: true }) + } + // Dispatch filtered realtokens dispatch({ type: realtokensChangedDispatchType, - payload: data.filter((item) => - [ - APIRealTokenProductType.RealEstateRental, - APIRealTokenProductType.LoanIncome, - APIRealTokenProductType.Factoring, - ].includes(item.productType), - ), + payload: data.filter(filterProductType), }) } catch (error) { console.log(error) @@ -53,6 +111,54 @@ export function fetchRealtokens() { } } +export function fetchRealtokensExtraData() { + return async (dispatch: AppDispatch, getState: () => RootState) => { + // Check if user has enabled additional + const userDisplayAdditionalData = + selectUserDisplayAdditionalData(getState()) + if (!userDisplayAdditionalData) return + const { isLoading, isLoadingExtraData, isExtraDataLoaded } = + getState().realtokens + // Wait for the initial realtokens to be loaded, skip if already loading or loaded + if (isLoading || isLoadingExtraData || isExtraDataLoaded) return + dispatch({ type: realtokensExtraDataIsLoadingDispatchType, payload: true }) + + try { + const pitsBiExtraData = + await RealtokenRepository.getTokensPitsBiExtraData() + // Build a map of tokensExtraData by uuid + const tokensExtraDataMap = new Map() + pitsBiExtraData.forEach((item) => { + tokensExtraDataMap.set(item.uuid, item) + }) + const existingTokens = await RealtokenRepository.getTokens() + + existingTokens.forEach((token) => { + const { actions, historic } = tokensExtraDataMap.get(token.uuid) || {} + // Update existing token data with PitsBI "extra data" + token.extraData = { + pitsBI: { + actions: actions, + historic: historic, + }, + } + }) + dispatch({ + type: realtokensExtraDataChangedDispatchType, + payload: existingTokens.filter(filterProductType), + }) + dispatch({ type: realtokensExtraDataLoadedDispatchType, payload: true }) + } catch (error) { + console.log(error) + } finally { + dispatch({ + type: realtokensExtraDataIsLoadingDispatchType, + payload: false, + }) + } + } +} + export const realtokensReducers = createReducer( realtokenInitialState, (builder) => { @@ -62,5 +168,14 @@ export const realtokensReducers = createReducer( builder.addCase(realtokensIsLoading, (state, action) => { state.isLoading = action.payload }) + builder.addCase(realtokensExtraDataChanged, (state, action) => { + state.realtokens = action.payload + }) + builder.addCase(realtokensExtraDataIsLoading, (state, action) => { + state.isLoadingExtraData = action.payload + }) + builder.addCase(realtokensExtraDataLoaded, (state, action) => { + state.isExtraDataLoaded = action.payload + }) }, ) diff --git a/src/store/features/settings/settingsSelector.ts b/src/store/features/settings/settingsSelector.ts index e01b65c1..f0f3a639 100644 --- a/src/store/features/settings/settingsSelector.ts +++ b/src/store/features/settings/settingsSelector.ts @@ -16,6 +16,7 @@ export const selectIsLoading = createSelector( (state, realtokens, wallets, currencies, transfers) => !state.isInitialized || realtokens.isLoading || + // realtokens.isLoadingExtraData ? wallets.isLoading || currencies.isLoading || transfers.isLoading, @@ -82,3 +83,8 @@ export const selectUserIncludesOtherAssets = createSelector( (state: RootState) => state.settings, (state) => state.includesOtherAssets, ) + +export const selectUserDisplayAdditionalData = createSelector( + (state: RootState) => state.settings, + (state) => state.displayAdditionalData, +) diff --git a/src/store/features/settings/settingsSlice.ts b/src/store/features/settings/settingsSlice.ts index 1d35913b..9e070e72 100644 --- a/src/store/features/settings/settingsSlice.ts +++ b/src/store/features/settings/settingsSlice.ts @@ -20,6 +20,8 @@ const USER_INCLUDES_ETH_LS_KEY = 'store:settings/includesEth' const USER_INCLUDES_LEVIN_SWAP_LS_KEY = 'store:settings/includesLevinSwap' const USER_INCLUDES_RMM_V2_LS_KEY = 'store:settings/includesRmmV2' const USER_INCLUDES_OTHER_ASSETS_LS_KEY = 'store:settings/includesOtherAssets' +const USER_DISPLAY_ADDITIONAL_DATA_LS_KEY = + 'store:settings/displayAdditionalData' export interface User { id: string @@ -39,6 +41,7 @@ interface SettingsInitialStateType { includesLevinSwap: boolean includesRmmV2: boolean includesOtherAssets: boolean + displayAdditionalData: boolean version?: string } @@ -54,6 +57,7 @@ const settingsInitialState: SettingsInitialStateType = { includesLevinSwap: false, includesRmmV2: false, includesOtherAssets: false, + displayAdditionalData: false, } // DISPATCH TYPE @@ -69,6 +73,8 @@ export const userIncludesRmmV2ChangedDispatchType = 'settings/includesRmmV2Changed' export const userIncludesOtherAssetsDispatchType = 'settings/includesOtherAssets' +export const userDisplayAdditionalDataDispatchType = + 'settings/displayAdditionalData' // ACTIONS export const initializeSettings = createAction(initializeSettingsDispatchType) export const userChanged = createAction(userChangedDispatchType) @@ -96,6 +102,9 @@ export const userIncludesRmmV2Changed = createAction( export const userIncludesOtherAssetsChanged = createAction( userIncludesOtherAssetsDispatchType, ) +export const userDisplayAdditionalDataChanged = createAction( + userDisplayAdditionalDataDispatchType, +) // THUNKS export function setUserAddress(address: string) { return async (dispatch: AppDispatch) => { @@ -227,6 +236,14 @@ export const settingsReducers = createReducer( action.payload.toString(), ) }) + .addCase(userDisplayAdditionalDataChanged, (state, action) => { + state.displayAdditionalData = action.payload + localStorage.setItem( + USER_DISPLAY_ADDITIONAL_DATA_LS_KEY, + action.payload.toString(), + ) + }) + .addCase(initializeSettings, (state) => { const user = localStorage.getItem(USER_LS_KEY) const userCurrency = localStorage.getItem(USER_CURRENCY_LS_KEY) @@ -243,6 +260,9 @@ export const settingsReducers = createReducer( const userIncludesOtherAssets = localStorage.getItem( USER_INCLUDES_OTHER_ASSETS_LS_KEY, ) + const userDisplayAdditionalData = localStorage.getItem( + USER_DISPLAY_ADDITIONAL_DATA_LS_KEY, + ) state.user = user ? JSON.parse(user) : undefined state.userCurrency = userCurrency @@ -262,6 +282,7 @@ export const settingsReducers = createReducer( state.includesLevinSwap = userIncludesLevinSwap === 'true' state.includesRmmV2 = userIncludesRmmV2 === 'true' state.includesOtherAssets = userIncludesOtherAssets === 'true' + state.displayAdditionalData = userDisplayAdditionalData === 'true' const { publicRuntimeConfig } = getConfig() as { publicRuntimeConfig?: { version: string } diff --git a/src/store/features/wallets/walletsSelector.ts b/src/store/features/wallets/walletsSelector.ts index 29ea0749..dffbc240 100644 --- a/src/store/features/wallets/walletsSelector.ts +++ b/src/store/features/wallets/walletsSelector.ts @@ -20,11 +20,10 @@ import { import { computeUCP } from 'src/utils/transfer/computeUCP' import { - CHAIN_ID_ETHEREUM, - CHAIN_ID_GNOSIS_XDAI, - CHAIN_NAME_ETHEREUM, - CHAIN_NAME_GNOSIS_XDAI, -} from '../../../utils/blockchain/consts/otherTokens' + CHAINS_NAMES, + CHAIN_ID__ETHEREUM, + CHAIN_ID__GNOSIS_XDAI, +} from '../../../utils/blockchain/consts/misc' import { selectRealtokens } from '../realtokens/realtokensSelector' import { selectUserRentCalculation } from '../settings/settingsSelector' @@ -88,6 +87,8 @@ export interface RWARealtoken extends OtherRealtoken { // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface REGRealtoken extends OtherRealtoken {} // eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface REUSDGRealtoken extends OtherRealtoken {} +// eslint-disable-next-line @typescript-eslint/no-empty-interface export interface REGVotingPowertoken extends OtherRealtoken {} // eslint-disable-next-line @typescript-eslint/no-empty-interface @@ -284,9 +285,9 @@ export const selectRmmDetails = createSelector( export const getWalletChainName = (chainId: number) => { switch (chainId) { - case CHAIN_ID_ETHEREUM: - return CHAIN_NAME_ETHEREUM - case CHAIN_ID_GNOSIS_XDAI: - return CHAIN_NAME_GNOSIS_XDAI + case CHAIN_ID__ETHEREUM: + return CHAINS_NAMES[CHAIN_ID__ETHEREUM] + case CHAIN_ID__GNOSIS_XDAI: + return CHAINS_NAMES[CHAIN_ID__GNOSIS_XDAI] } } diff --git a/src/types/APIPitsBI.ts b/src/types/APIPitsBI.ts new file mode 100644 index 00000000..d5492b27 --- /dev/null +++ b/src/types/APIPitsBI.ts @@ -0,0 +1,57 @@ +import { APIRealToken } from './APIRealToken' + +export enum APIPitsBiEnv { + VERSION = 'PITSBI_API_VERSION', + BASE = 'PITSBI_API_BASE', + GET_ALLTOKENS = 'PITSBI_API_GET_ALLTOKENS', + GET_LASTUPDATE = 'PITSBI_API_GET_LASTUPDATE', +} + +export enum RealTokenToBeRepairedPriority { + None = 0, + High = 1, + Medium = 2, + Low = 3, +} + +export enum RealTokenToBeFixedStatus { + NoExhibit = 'No Exhibit', + UpgradedAndReady = 'Upgraded & Ready', + Scheduled = 'Scheduled', +} + +export interface RealTokenPitsBI_Actions { + exhibit_number: number // 0 = no exhibit, 1+ = exhibit number + volume: number // Volume 0 = no exhibit, 1+ = exhibit volume number + priority: RealTokenToBeRepairedPriority // Priority of the action + realt_status: RealTokenToBeFixedStatus // Status of the action +} + +export interface RealTokenPitsBI_Historic { + yields: { + timsync: string + yield: number + days_rented: number + }[] + prices: { + timsync: string + price: number + }[] + avg_yield: number + init_yield: number + init_price: number +} + +export interface APIRealTokenPitsBI_ExtraData { + // PITS BI is 100% compliant with the APIRealToken structure + uuid: string + // ... + // PITS BI specific fields + // Only declare fields useful for us + actions: RealTokenPitsBI_Actions + historic: RealTokenPitsBI_Historic +} + +export interface APIRealTokenPitsBI + extends APIRealToken, + APIRealTokenPitsBI_ExtraData {} diff --git a/src/types/APIRealToken.ts b/src/types/APIRealToken.ts index b75a866a..4823ae27 100644 --- a/src/types/APIRealToken.ts +++ b/src/types/APIRealToken.ts @@ -14,6 +14,15 @@ export enum APIRealTokenProductType { Factoring = 'factoring_profitshare', } +export enum APIRealTokenCommunityEnv { + API_KEY = 'REALTOKEN_COMMUNITY_API_KEY', + AUTH = 'X-AUTH-REALT-TOKEN', + API_BASE = 'REALTOKEN_COMMUNITY_API_BASE', + VERSION = 'REALTOKEN_COMMUNITY_API_VERSION', + GET_ALLTOKENS = 'REALTOKEN_COMMUNITY_API_GET_ALLTOKENS', + API_HISTORY = 'REALTOKEN_COMMUNITY_API_HISTORY_BASE', +} + export interface APIRealTokenDate { date: string timezone_type: number diff --git a/src/types/RealToken.ts b/src/types/RealToken.ts index 7f3a41ff..3179a125 100644 --- a/src/types/RealToken.ts +++ b/src/types/RealToken.ts @@ -1,3 +1,4 @@ +import { RealTokenPitsBI_Actions, RealTokenPitsBI_Historic } from './APIPitsBI' import { APIRealToken } from './APIRealToken' import { RealTokenHistoryItem } from './APIRealTokenHistory' @@ -49,4 +50,10 @@ export interface RealToken extends APIRealToken { isRmmAvailable: boolean rentStatus: RealTokenRentStatus history: RealTokenHistoryItem[] + extraData?: { + pitsBI?: { + actions?: RealTokenPitsBI_Actions + historic?: RealTokenPitsBI_Historic + } + } } diff --git a/src/utils/blockchain/abi/UniswapV3PoolABI.ts b/src/utils/blockchain/abi/UniswapV3PoolABI.ts new file mode 100644 index 00000000..3ab6f908 --- /dev/null +++ b/src/utils/blockchain/abi/UniswapV3PoolABI.ts @@ -0,0 +1,644 @@ +// https://api.etherscan.io/v2/api?chainid=100&module=contract&action=getabi&address=0x17183182C4a94A895A8b31133e3B24800aF3c5e4&apikey=yourApiKey +export const UniswapV3PoolABI = [ + // Add the ABI definitions for the Uniswap V3 Quoter V2 here + { inputs: [], stateMutability: 'nonpayable', type: 'constructor' }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + indexed: true, + internalType: 'int24', + name: 'tickLower', + type: 'int24', + }, + { + indexed: true, + internalType: 'int24', + name: 'tickUpper', + type: 'int24', + }, + { + indexed: false, + internalType: 'uint128', + name: 'amount', + type: 'uint128', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount0', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount1', + type: 'uint256', + }, + ], + name: 'Burn', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + indexed: false, + internalType: 'address', + name: 'recipient', + type: 'address', + }, + { + indexed: true, + internalType: 'int24', + name: 'tickLower', + type: 'int24', + }, + { + indexed: true, + internalType: 'int24', + name: 'tickUpper', + type: 'int24', + }, + { + indexed: false, + internalType: 'uint128', + name: 'amount0', + type: 'uint128', + }, + { + indexed: false, + internalType: 'uint128', + name: 'amount1', + type: 'uint128', + }, + ], + name: 'Collect', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'sender', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'recipient', + type: 'address', + }, + { + indexed: false, + internalType: 'uint128', + name: 'amount0', + type: 'uint128', + }, + { + indexed: false, + internalType: 'uint128', + name: 'amount1', + type: 'uint128', + }, + ], + name: 'CollectProtocol', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'sender', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'recipient', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount0', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount1', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'paid0', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'paid1', + type: 'uint256', + }, + ], + name: 'Flash', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint16', + name: 'observationCardinalityNextOld', + type: 'uint16', + }, + { + indexed: false, + internalType: 'uint16', + name: 'observationCardinalityNextNew', + type: 'uint16', + }, + ], + name: 'IncreaseObservationCardinalityNext', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint160', + name: 'sqrtPriceX96', + type: 'uint160', + }, + { indexed: false, internalType: 'int24', name: 'tick', type: 'int24' }, + ], + name: 'Initialize', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'sender', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'owner', + type: 'address', + }, + { + indexed: true, + internalType: 'int24', + name: 'tickLower', + type: 'int24', + }, + { + indexed: true, + internalType: 'int24', + name: 'tickUpper', + type: 'int24', + }, + { + indexed: false, + internalType: 'uint128', + name: 'amount', + type: 'uint128', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount0', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount1', + type: 'uint256', + }, + ], + name: 'Mint', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint8', + name: 'feeProtocol0Old', + type: 'uint8', + }, + { + indexed: false, + internalType: 'uint8', + name: 'feeProtocol1Old', + type: 'uint8', + }, + { + indexed: false, + internalType: 'uint8', + name: 'feeProtocol0New', + type: 'uint8', + }, + { + indexed: false, + internalType: 'uint8', + name: 'feeProtocol1New', + type: 'uint8', + }, + ], + name: 'SetFeeProtocol', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'sender', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'recipient', + type: 'address', + }, + { + indexed: false, + internalType: 'int256', + name: 'amount0', + type: 'int256', + }, + { + indexed: false, + internalType: 'int256', + name: 'amount1', + type: 'int256', + }, + { + indexed: false, + internalType: 'uint160', + name: 'sqrtPriceX96', + type: 'uint160', + }, + { + indexed: false, + internalType: 'uint128', + name: 'liquidity', + type: 'uint128', + }, + { indexed: false, internalType: 'int24', name: 'tick', type: 'int24' }, + ], + name: 'Swap', + type: 'event', + }, + { + inputs: [ + { internalType: 'int24', name: 'tickLower', type: 'int24' }, + { internalType: 'int24', name: 'tickUpper', type: 'int24' }, + { internalType: 'uint128', name: 'amount', type: 'uint128' }, + ], + name: 'burn', + outputs: [ + { internalType: 'uint256', name: 'amount0', type: 'uint256' }, + { internalType: 'uint256', name: 'amount1', type: 'uint256' }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'int24', name: 'tickLower', type: 'int24' }, + { internalType: 'int24', name: 'tickUpper', type: 'int24' }, + { internalType: 'uint128', name: 'amount0Requested', type: 'uint128' }, + { internalType: 'uint128', name: 'amount1Requested', type: 'uint128' }, + ], + name: 'collect', + outputs: [ + { internalType: 'uint128', name: 'amount0', type: 'uint128' }, + { internalType: 'uint128', name: 'amount1', type: 'uint128' }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint128', name: 'amount0Requested', type: 'uint128' }, + { internalType: 'uint128', name: 'amount1Requested', type: 'uint128' }, + ], + name: 'collectProtocol', + outputs: [ + { internalType: 'uint128', name: 'amount0', type: 'uint128' }, + { internalType: 'uint128', name: 'amount1', type: 'uint128' }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'factory', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'fee', + outputs: [{ internalType: 'uint24', name: '', type: 'uint24' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'feeGrowthGlobal0X128', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'feeGrowthGlobal1X128', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'uint256', name: 'amount0', type: 'uint256' }, + { internalType: 'uint256', name: 'amount1', type: 'uint256' }, + { internalType: 'bytes', name: 'data', type: 'bytes' }, + ], + name: 'flash', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint16', + name: 'observationCardinalityNext', + type: 'uint16', + }, + ], + name: 'increaseObservationCardinalityNext', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint160', name: 'sqrtPriceX96', type: 'uint160' }, + ], + name: 'initialize', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'liquidity', + outputs: [{ internalType: 'uint128', name: '', type: 'uint128' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'maxLiquidityPerTick', + outputs: [{ internalType: 'uint128', name: '', type: 'uint128' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'int24', name: 'tickLower', type: 'int24' }, + { internalType: 'int24', name: 'tickUpper', type: 'int24' }, + { internalType: 'uint128', name: 'amount', type: 'uint128' }, + { internalType: 'bytes', name: 'data', type: 'bytes' }, + ], + name: 'mint', + outputs: [ + { internalType: 'uint256', name: 'amount0', type: 'uint256' }, + { internalType: 'uint256', name: 'amount1', type: 'uint256' }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + name: 'observations', + outputs: [ + { internalType: 'uint32', name: 'blockTimestamp', type: 'uint32' }, + { internalType: 'int56', name: 'tickCumulative', type: 'int56' }, + { + internalType: 'uint160', + name: 'secondsPerLiquidityCumulativeX128', + type: 'uint160', + }, + { internalType: 'bool', name: 'initialized', type: 'bool' }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint32[]', name: 'secondsAgos', type: 'uint32[]' }, + ], + name: 'observe', + outputs: [ + { internalType: 'int56[]', name: 'tickCumulatives', type: 'int56[]' }, + { + internalType: 'uint160[]', + name: 'secondsPerLiquidityCumulativeX128s', + type: 'uint160[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + name: 'positions', + outputs: [ + { internalType: 'uint128', name: 'liquidity', type: 'uint128' }, + { + internalType: 'uint256', + name: 'feeGrowthInside0LastX128', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'feeGrowthInside1LastX128', + type: 'uint256', + }, + { internalType: 'uint128', name: 'tokensOwed0', type: 'uint128' }, + { internalType: 'uint128', name: 'tokensOwed1', type: 'uint128' }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'protocolFees', + outputs: [ + { internalType: 'uint128', name: 'token0', type: 'uint128' }, + { internalType: 'uint128', name: 'token1', type: 'uint128' }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint8', name: 'feeProtocol0', type: 'uint8' }, + { internalType: 'uint8', name: 'feeProtocol1', type: 'uint8' }, + ], + name: 'setFeeProtocol', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'slot0', + outputs: [ + { internalType: 'uint160', name: 'sqrtPriceX96', type: 'uint160' }, + { internalType: 'int24', name: 'tick', type: 'int24' }, + { internalType: 'uint16', name: 'observationIndex', type: 'uint16' }, + { + internalType: 'uint16', + name: 'observationCardinality', + type: 'uint16', + }, + { + internalType: 'uint16', + name: 'observationCardinalityNext', + type: 'uint16', + }, + { internalType: 'uint8', name: 'feeProtocol', type: 'uint8' }, + { internalType: 'bool', name: 'unlocked', type: 'bool' }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'int24', name: 'tickLower', type: 'int24' }, + { internalType: 'int24', name: 'tickUpper', type: 'int24' }, + ], + name: 'snapshotCumulativesInside', + outputs: [ + { internalType: 'int56', name: 'tickCumulativeInside', type: 'int56' }, + { + internalType: 'uint160', + name: 'secondsPerLiquidityInsideX128', + type: 'uint160', + }, + { internalType: 'uint32', name: 'secondsInside', type: 'uint32' }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'recipient', type: 'address' }, + { internalType: 'bool', name: 'zeroForOne', type: 'bool' }, + { internalType: 'int256', name: 'amountSpecified', type: 'int256' }, + { internalType: 'uint160', name: 'sqrtPriceLimitX96', type: 'uint160' }, + { internalType: 'bytes', name: 'data', type: 'bytes' }, + ], + name: 'swap', + outputs: [ + { internalType: 'int256', name: 'amount0', type: 'int256' }, + { internalType: 'int256', name: 'amount1', type: 'int256' }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'int16', name: '', type: 'int16' }], + name: 'tickBitmap', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'tickSpacing', + outputs: [{ internalType: 'int24', name: '', type: 'int24' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'int24', name: '', type: 'int24' }], + name: 'ticks', + outputs: [ + { internalType: 'uint128', name: 'liquidityGross', type: 'uint128' }, + { internalType: 'int128', name: 'liquidityNet', type: 'int128' }, + { + internalType: 'uint256', + name: 'feeGrowthOutside0X128', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'feeGrowthOutside1X128', + type: 'uint256', + }, + { internalType: 'int56', name: 'tickCumulativeOutside', type: 'int56' }, + { + internalType: 'uint160', + name: 'secondsPerLiquidityOutsideX128', + type: 'uint160', + }, + { internalType: 'uint32', name: 'secondsOutside', type: 'uint32' }, + { internalType: 'bool', name: 'initialized', type: 'bool' }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'token0', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'token1', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, +] diff --git a/src/utils/blockchain/abi/UniswapV3QuoterV2ABI.ts b/src/utils/blockchain/abi/UniswapV3QuoterV2ABI.ts new file mode 100644 index 00000000..b8974a75 --- /dev/null +++ b/src/utils/blockchain/abi/UniswapV3QuoterV2ABI.ts @@ -0,0 +1,152 @@ +// https://api.etherscan.io/v2/api?chainid=100&module=contract&action=getabi&address=0xb1E835Dc2785b52265711e17fCCb0fd018226a6e&apikey=yourApiKey +export const UniswapV3QuoterV2ABI = [ + /* + { + inputs: [ + { internalType: 'address', name: '_factory', type: 'address' }, + { internalType: 'address', name: '_WETH9', type: 'address' }, + ], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { + inputs: [], + name: 'WETH9', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'factory', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + ], + name: 'quoteExactInput', + outputs: [ + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { + internalType: 'uint160[]', + name: 'sqrtPriceX96AfterList', + type: 'uint160[]', + }, + { + internalType: 'uint32[]', + name: 'initializedTicksCrossedList', + type: 'uint32[]', + }, + { internalType: 'uint256', name: 'gasEstimate', type: 'uint256' }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, +*/ + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint24', name: 'fee', type: 'uint24' }, + { + internalType: 'uint160', + name: 'sqrtPriceLimitX96', + type: 'uint160', + }, + ], + internalType: 'struct IQuoterV2.QuoteExactInputSingleParams', + name: 'params', + type: 'tuple', + }, + ], + name: 'quoteExactInputSingle', + outputs: [ + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + { internalType: 'uint160', name: 'sqrtPriceX96After', type: 'uint160' }, + { + internalType: 'uint32', + name: 'initializedTicksCrossed', + type: 'uint32', + }, + { internalType: 'uint256', name: 'gasEstimate', type: 'uint256' }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + /* + { + inputs: [ + { internalType: 'bytes', name: 'path', type: 'bytes' }, + { internalType: 'uint256', name: 'amountOut', type: 'uint256' }, + ], + name: 'quoteExactOutput', + outputs: [ + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { + internalType: 'uint160[]', + name: 'sqrtPriceX96AfterList', + type: 'uint160[]', + }, + { + internalType: 'uint32[]', + name: 'initializedTicksCrossedList', + type: 'uint32[]', + }, + { internalType: 'uint256', name: 'gasEstimate', type: 'uint256' }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'tokenIn', type: 'address' }, + { internalType: 'address', name: 'tokenOut', type: 'address' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + { internalType: 'uint24', name: 'fee', type: 'uint24' }, + { + internalType: 'uint160', + name: 'sqrtPriceLimitX96', + type: 'uint160', + }, + ], + internalType: 'struct IQuoterV2.QuoteExactOutputSingleParams', + name: 'params', + type: 'tuple', + }, + ], + name: 'quoteExactOutputSingle', + outputs: [ + { internalType: 'uint256', name: 'amountIn', type: 'uint256' }, + { internalType: 'uint160', name: 'sqrtPriceX96After', type: 'uint160' }, + { + internalType: 'uint32', + name: 'initializedTicksCrossed', + type: 'uint32', + }, + { internalType: 'uint256', name: 'gasEstimate', type: 'uint256' }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'int256', name: 'amount0Delta', type: 'int256' }, + { internalType: 'int256', name: 'amount1Delta', type: 'int256' }, + { internalType: 'bytes', name: 'path', type: 'bytes' }, + ], + name: 'uniswapV3SwapCallback', + outputs: [], + stateMutability: 'view', + type: 'function', + }, +*/ +] diff --git a/src/utils/blockchain/consts/misc.ts b/src/utils/blockchain/consts/misc.ts new file mode 100644 index 00000000..075ef896 --- /dev/null +++ b/src/utils/blockchain/consts/misc.ts @@ -0,0 +1,115 @@ +const CHAIN_ID_GNOSIS_XDAI = 100 +const CHAIN_ID_ETHEREUM = 1 +// const CHAIN_ID_POLYGON = 137 + +const CHAIN_NAME__GNOSIS_XDAI = 'gnosis' +const CHAIN_NAME__ETHEREUM = 'ethereum' +// const CHAIN_NAME__POLYGON = 'polygon' + +// Array of supported chain IDs +const SUPPORTED_CHAINS_IDS = [CHAIN_ID_GNOSIS_XDAI, CHAIN_ID_ETHEREUM] + +// Mapping chain IDs to their respective names +const CHAINS_NAMES: { [key: number]: string } = { + [CHAIN_ID_GNOSIS_XDAI]: CHAIN_NAME__GNOSIS_XDAI, + [CHAIN_ID_ETHEREUM]: CHAIN_NAME__ETHEREUM, +} + +type ChainId = number +type Address = string +type Decimals = number + +const CHAIN_ID__GNOSIS_XDAI: ChainId = 100 +const CHAIN_ID__ETHEREUM: ChainId = 1 +// const CHAIN_ID__POLYGON: ChainId = 137 + +// export declare enum ChainIds { +// GNOSIS_XDAI, +// ETHEREUM, +// // POLYGON, // Todo +// } + +// Reg Vault only deployed on Gnosis/xDai +const REG_Vault_Gnosis_ContractAddress = + '0xe1877d33471e37fe0f62d20e60c469eff83fb4a0' +// Reg Voting Power only deployed on Gnosis/xDai + +const HoneySwapFactory_Address = '0xA818b4F111Ccac7AA31D0BCc0806d64F2E0737D7' + +// SushiSwap Quoter deployed on Gnosis/xDai +const SushiSwap_Factory__GnosisXdai_Address = + '0xf78031CBCA409F2FB6876BDFDBc1b2df24cF9bEf' +const SushiSwap_Router__GnosisXdai_Address = + '0x4F54dd2F4f30347d841b7783aD08c050d8410a9d' +const SushiSwap_Quoter__GnosisXdai_Address = + '0xb1E835Dc2785b52265711e17fCCb0fd018226a6e' + +// interface UniV2DeployedAddresses { +// factory: Address +// router: Address +// } + +// interface UniV3DeployedAddresses extends UniV2DeployedAddresses { +// quoter: Address +// } + +// const SUSHISWAP_DEPLOYMENTS: { [key: ChainId]: UniV3DeployedAddresses } = { +// [CHAIN_ID__GNOSIS_XDAI]: { +// factory: SushiSwap_Factory__GnosisXdai_Address, +// router: SushiSwap_Router__GnosisXdai_Address, +// quoter: SushiSwap_Quoter__GnosisXdai_Address, +// }, +// } + +interface UniV2DeployedAddresses { + factory: Address + router: Address +} + +interface UniV3DeployedAddresses extends UniV2DeployedAddresses { + quoter: Address +} + +// Define type for deployments +// export +type UniV3Deployment = { [key: ChainId]: UniV3DeployedAddresses } + +const SUSHISWAP_DEPLOYMENTS: UniV3Deployment = { + [CHAIN_ID__GNOSIS_XDAI]: { + factory: SushiSwap_Factory__GnosisXdai_Address, + router: SushiSwap_Router__GnosisXdai_Address, + quoter: SushiSwap_Quoter__GnosisXdai_Address, + }, +} + +// console.dir(SUSHISWAP_DEPLOYMENTS) +// console.dir(ChainIds) + +// Export type for SUSHISWAP_DEPLOYMENTS +// export type SushiswapDeployments = typeof SUSHISWAP_DEPLOYMENTS + +export { + CHAIN_ID__GNOSIS_XDAI, + CHAIN_ID__ETHEREUM, + // CHAIN_ID__POLYGON, + SUPPORTED_CHAINS_IDS, + CHAINS_NAMES, + REG_Vault_Gnosis_ContractAddress, + HoneySwapFactory_Address, + SUSHISWAP_DEPLOYMENTS, +} +// export declare const SUPPORTED_CHAINS: readonly [ +// // ChainId.GNOSIS_XDAI, +// // ChainIds.ETHEREUM, +// CHAIN_ID__GNOSIS_XDAI, +// CHAIN_ID__ETHEREUM, +// ] + +export type { + Address, + ChainId, + Decimals, + UniV2DeployedAddresses, + UniV3DeployedAddresses, + UniV3Deployment, +} diff --git a/src/utils/blockchain/consts/otherTokens.ts b/src/utils/blockchain/consts/otherTokens.ts index b4b278b3..766b8616 100644 --- a/src/utils/blockchain/consts/otherTokens.ts +++ b/src/utils/blockchain/consts/otherTokens.ts @@ -1,18 +1,20 @@ -// Each asset must have a different ID (used as KEY assets view) -const RWA_asset_ID = 0 -const REG_asset_ID = 1 -const REGVotingPower_asset_ID = 2 - // Gnosis/xDai, Ethereum const RWA_ContractAddress = '0x0675e8F4A52eA6c845CB6427Af03616a2af42170' // Gnosis/xDai, Ethereum const REG_ContractAddress = '0x0AA1e96D2a46Ec6beB2923dE1E61Addf5F5f1dce' -// Reg Vault only deployed on Gnosis/xDai -const REG_Vault_Gnosis_ContractAddress = - '0xe1877d33471e37fe0f62d20e60c469eff83fb4a0' // Reg Voting Power only deployed on Gnosis/xDai const RegVotingPower_Gnosis_ContractAddress = '0x6382856a731Af535CA6aea8D364FCE67457da438' +// Gnosis/xDai, Ethereum +const REUSD_ContractAddress = '0x3390742ac0dce14ea6fcbd5ae02e2303c5d62ad9' + +const REUSD_asset_ID = REUSD_ContractAddress + +// Asset IDs for RWA, REG, and REG Voting Power +// Each asset must have a different ID (used as KEY assets view) +const RWA_asset_ID = RWA_ContractAddress +const REG_asset_ID = REG_ContractAddress +const REGVotingPower_asset_ID = RegVotingPower_Gnosis_ContractAddress // Gnosis/xDai tokens for prices calculation const WXDAI_ContractAddress = '0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d' @@ -20,6 +22,7 @@ const USDConXdai_ContractAddress = '0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83' const DEFAULT_RWA_PRICE = 50 // USD const DEFAULT_REG_PRICE = 0 // USD +const DEFAULT_REUSD_PRICE = 1 // USD const DEFAULT_REGVotingPower_PRICE = 0 // USD const DEFAULT_XDAI_USD_RATE = 1 @@ -28,30 +31,14 @@ const DEFAULT_USDC_USD_RATE = 1 const RWAtokenDecimals = 9 const REGtokenDecimals = 18 const REGVotingPowertokenDecimals = 18 +const REGUSDtokenDecimals = 18 const USDCtokenDecimals = 6 const WXDAItokenDecimals = 18 -const HoneySwapFactory_Address = '0xA818b4F111Ccac7AA31D0BCc0806d64F2E0737D7' - -const CHAIN_ID_GNOSIS_XDAI = 100 -const CHAIN_ID_ETHEREUM = 1 -const CHAIN_ID_POLYGON = 137 - -const CHAIN_NAME_GNOSIS_XDAI = 'gnosis' -const CHAIN_NAME_ETHEREUM = 'ethereum' -const CHAIN_NAME_POLYGON = 'polygon' - -// Mapping chain IDs to their respective names -const CHAINS_NAMES: { [key: number]: string } = { - [CHAIN_ID_GNOSIS_XDAI]: CHAIN_NAME_GNOSIS_XDAI, - [CHAIN_ID_ETHEREUM]: CHAIN_NAME_ETHEREUM, - [CHAIN_ID_POLYGON]: CHAIN_NAME_POLYGON, -} - export { RWA_ContractAddress, REG_ContractAddress, - REG_Vault_Gnosis_ContractAddress, + REUSD_ContractAddress, RegVotingPower_Gnosis_ContractAddress, WXDAI_ContractAddress, USDConXdai_ContractAddress, @@ -59,21 +46,23 @@ export { RWAtokenDecimals, USDCtokenDecimals, REGtokenDecimals, - HoneySwapFactory_Address, + REGUSDtokenDecimals, REGVotingPowertokenDecimals, DEFAULT_RWA_PRICE, DEFAULT_REG_PRICE, + DEFAULT_REUSD_PRICE, DEFAULT_REGVotingPower_PRICE, DEFAULT_XDAI_USD_RATE, DEFAULT_USDC_USD_RATE, RWA_asset_ID, REG_asset_ID, + REUSD_asset_ID, REGVotingPower_asset_ID, - CHAIN_ID_GNOSIS_XDAI, - CHAIN_ID_ETHEREUM, - CHAIN_ID_POLYGON, - CHAIN_NAME_GNOSIS_XDAI, - CHAIN_NAME_ETHEREUM, - CHAIN_NAME_POLYGON, - CHAINS_NAMES, + // CHAIN_ID_GNOSIS_XDAI, + // CHAIN_ID_ETHEREUM, + // CHAIN_ID_POLYGON, + // CHAIN_NAME_GNOSIS_XDAI, + // CHAIN_NAME_ETHEREUM, + // CHAIN_NAME_POLYGON, + // CHAINS_NAMES, } diff --git a/src/utils/blockchain/poolPrice.ts b/src/utils/blockchain/poolPrice.ts index 52fa70f1..9f753e40 100644 --- a/src/utils/blockchain/poolPrice.ts +++ b/src/utils/blockchain/poolPrice.ts @@ -1,7 +1,43 @@ +import { Token } from '@uniswap/sdk-core' +import { + FeeAmount, + FeeAmount as Univ3FeeAmount, + computePoolAddress, +} from '@uniswap/v3-sdk' + import { Contract, JsonRpcProvider, ethers } from 'ethers' import { LevinswapABI as UniswapV2PairABI } from './abi/LevinswapABI' import { UniswapV2FactoryABI } from './abi/UniswapV2FactoryABI' +import { UniswapV3PoolABI as UniV3PoolABI } from './abi/UniswapV3PoolABI' +import { UniswapV3QuoterV2ABI as Univ3QuoterABI } from './abi/UniswapV3QuoterV2ABI' +import { + Address, + ChainId, + Decimals, + SUPPORTED_CHAINS_IDS, + UniV3Deployment, +} from './consts/misc' + +// const FeeAmounts = { +// LOWEST: Univ3FeeAmount.LOWEST, +// LOW: Univ3FeeAmount.LOW, +// MEDIUM: Univ3FeeAmount.MEDIUM, +// HIGH: Univ3FeeAmount.HIGH, +// } + +const AssetPrice = { + TokenA: 0, + TokenB: 1, +} + +const FeeAmounts = { + // 1e6 + LOWEST: Univ3FeeAmount.LOWEST, + LOW: Univ3FeeAmount.LOW, + MEDIUM: Univ3FeeAmount.MEDIUM, + HIGH: Univ3FeeAmount.HIGH, +} const isAddressLowererThan = (address0: string, address1: string): boolean => { if (!address0 || !address1) { @@ -152,4 +188,123 @@ const averageValues = ( return average } -export { getUniV2AssetPrice, averageValues } +const getUniV3PoolAddress = ( + tokenA_Address: Address, + tokenA_Decimals: Decimals, + tokenB_Address: Address, + tokenB_Decimals: Decimals, + fee: number, + poolFactoryAddress: Address, + chainId: ChainId, +): string => { + // Validate inputs + // chainId + if (!SUPPORTED_CHAINS_IDS.includes(chainId)) { + throw new Error(`Unsupported chainId: ${chainId}`) + } + // token addresses + if (!tokenA_Address || !tokenB_Address || tokenA_Address === tokenB_Address) { + throw new Error('Invalid token addresses') + } + // token decimals + if (!tokenA_Decimals || !tokenB_Decimals) { + throw new Error('Invalid token decimals') + } + // fee + if (!Object.values(FeeAmounts).includes(fee)) { + throw new Error( + `Invalid fee: ${fee} - must be one of ${Object.values(FeeAmounts)}`, + ) + } + // pool factory address + if (!poolFactoryAddress) { + throw new Error('Invalid fee or pool factory address') + } + const tokenA = new Token(chainId, tokenA_Address, tokenA_Decimals) + const tokenB = new Token(chainId, tokenB_Address, tokenB_Decimals) + + return computePoolAddress({ + factoryAddress: poolFactoryAddress, + tokenA: tokenA, + tokenB: tokenB, + fee, + }) +} + +const getUniV3AssetPrice = async ( + uniV3Deployment: UniV3Deployment, + token0Address: Address, + token1Address: Address, + token0Decimals: Decimals, + token1Decimals: Decimals, + provider: JsonRpcProvider, + chainId: ChainId, + fee = FeeAmount.MEDIUM, // Default to 3000 bips (0.3%) + whichAssetPrice = AssetPrice.TokenA, // : 0 for token0 price, 1 for token1 price + amountIn = 10, // Amount of token to quote without decimals +): Promise => { + let price: number | null = null + try { + // Validate inputs + if (!uniV3Deployment[chainId]) { + throw new Error(`No deployment found for chainId ${chainId}`) + } + const poolAddress = getUniV3PoolAddress( + token0Address, + token0Decimals, + token1Address, + token1Decimals, + fee, + uniV3Deployment[chainId].factory, + chainId, + ) + // Check pool address result + if (!poolAddress) { + throw new Error('Failed to get pool address') + } + // Quoter + const quoterContract = new Contract( + uniV3Deployment[chainId].quoter, + Univ3QuoterABI, + provider, + ) + // Amounts + const amountInBI = + BigInt(amountIn) * + BigInt( + 10 ** + (whichAssetPrice === AssetPrice.TokenA + ? token0Decimals + : token1Decimals), + ) + + const quotedAmountOut = + await quoterContract.quoteExactInputSingle.staticCall({ + tokenIn: + whichAssetPrice === AssetPrice.TokenA ? token0Address : token1Address, + tokenOut: + whichAssetPrice === AssetPrice.TokenA ? token1Address : token0Address, + amountIn: amountInBI, + fee: fee, + sqrtPriceLimitX96: 0n, // No price limit + }) + + price = + Number(quotedAmountOut[0]) / + amountIn / + 10 ** + (whichAssetPrice === AssetPrice.TokenA + ? token1Decimals + : token0Decimals) + } catch (error) { + console.warn( + `Failed to get asset price for factoryAddress ${uniV3Deployment[chainId].factory} token0Address ${token0Address} token1Address ${token1Address} fee ${fee} chainId ${chainId} amountIn ${amountIn} whichAssetPrice ${whichAssetPrice}`, + error, + provider, + ) + } + return price +} + +export { AssetPrice, FeeAmounts } +export { getUniV2AssetPrice, averageValues, getUniV3AssetPrice } diff --git a/src/utils/general.ts b/src/utils/general.ts index 2053daf1..4c218236 100644 --- a/src/utils/general.ts +++ b/src/utils/general.ts @@ -1,3 +1,65 @@ const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) -export { wait } +const FETCH_RETRIES_DEFAULT_COUNT = 2 +const FETCH_RETRIES_DEFAULT_DELAY = 500 + +/** + * fetchWithRetry + * @param url + * @param options + * @param retries + * @param delay : number of milliseconds to wait before retrying; zero = no delay, negative = random delay, positive = fixed delay + */ +const fetchWithRetry = async ( + url: string, + options = {}, + retries = FETCH_RETRIES_DEFAULT_COUNT, + delay = FETCH_RETRIES_DEFAULT_DELAY, +): Promise => { + if (retries <= 0) { + throw new Error('Max retries reached') + } + try { + const response = await fetch(url, options) + if (response.ok) { + return response + } else { + // Handle specific error cases if needed + // if (response.status === 401) throw ... + // if (response.status === 503) throw ... + throw response + } + } catch (error) { + const errorMsg = + error instanceof Error + ? error.message + : error instanceof Response + ? `error.statusText: ${error.statusText}, error.status: ${error.status}` + : String(error) + console.error( + `Fetch error: ${errorMsg} for URL: ${url}`, + `Retries left: ${retries - 1}, Delay: ${delay}ms`, + ) + if (retries - 1 < 0) { + throw error + } + if (delay > 0) { + await wait(delay) + return fetchWithRetry(url, options, retries - 1, delay) + } else if (delay === 0) { + return fetchWithRetry(url, options, retries - 1, delay) + } else { + // Random delay for retry + const randomDelay = Math.floor(Math.random() * delay) + await wait(randomDelay) + return fetchWithRetry(url, options, retries - 1, delay) + } + } +} + +export { + wait, + fetchWithRetry, + FETCH_RETRIES_DEFAULT_COUNT, + FETCH_RETRIES_DEFAULT_DELAY, +} diff --git a/yarn.lock b/yarn.lock index 4d16f6bc..d1676505 100644 --- a/yarn.lock +++ b/yarn.lock @@ -516,7 +516,7 @@ ethereum-cryptography "^2.0.0" micro-ftch "^0.3.1" -"@ethersproject/abi@5.8.0", "@ethersproject/abi@^5.8.0": +"@ethersproject/abi@5.8.0", "@ethersproject/abi@^5.5.0", "@ethersproject/abi@^5.8.0": version "5.8.0" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.8.0.tgz#e79bb51940ac35fe6f3262d7fe2cdb25ad5f07d9" integrity sha512-b9YS/43ObplgyV6SlyQsG53/vkSal0MNA1fskSC4mbnCMi8R+NkcH8K9FPYNESf6jUefBUniE4SOKms0E/KK1Q== @@ -555,7 +555,7 @@ "@ethersproject/logger" "^5.8.0" "@ethersproject/properties" "^5.8.0" -"@ethersproject/address@5.8.0", "@ethersproject/address@^5", "@ethersproject/address@^5.8.0": +"@ethersproject/address@5.8.0", "@ethersproject/address@^5", "@ethersproject/address@^5.0.2", "@ethersproject/address@^5.8.0": version "5.8.0" resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.8.0.tgz#3007a2c352eee566ad745dca1dbbebdb50a6a983" integrity sha512-GhH/abcC46LJwshoN+uBNoKVFPxUuZm6dA257z0vZkKmU1+t8xTn8oK7B9qrj8W2rFRMch4gbJl6PmVxjxBEBA== @@ -590,14 +590,14 @@ "@ethersproject/logger" "^5.8.0" bn.js "^5.2.1" -"@ethersproject/bytes@5.8.0", "@ethersproject/bytes@^5.8.0": +"@ethersproject/bytes@5.8.0", "@ethersproject/bytes@^5.7.0", "@ethersproject/bytes@^5.8.0": version "5.8.0" resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.8.0.tgz#9074820e1cac7507a34372cadeb035461463be34" integrity sha512-vTkeohgJVCPVHu5c25XWaWQOZ4v+DkGoC42/TS2ond+PARCxTJvgTFUNDZovyQ/uAQ4EcpqqowKydcdmRKjg7A== dependencies: "@ethersproject/logger" "^5.8.0" -"@ethersproject/constants@5.8.0", "@ethersproject/constants@^5.8.0": +"@ethersproject/constants@5.8.0", "@ethersproject/constants@^5.7.0", "@ethersproject/constants@^5.8.0": version "5.8.0" resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.8.0.tgz#12f31c2f4317b113a4c19de94e50933648c90704" integrity sha512-wigX4lrf5Vu+axVTIvNsuL6YrV4O5AXl5ubcURKMEME5TnWBouUh0CDTWxZ2GpnRn1kcCgE7l8O5+VbV9QTTcg== @@ -672,6 +672,14 @@ aes-js "3.0.0" scrypt-js "3.0.1" +"@ethersproject/keccak256@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a" + integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + js-sha3 "0.8.0" + "@ethersproject/keccak256@5.8.0", "@ethersproject/keccak256@^5.8.0": version "5.8.0" resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.8.0.tgz#d2123a379567faf2d75d2aaea074ffd4df349e6a" @@ -680,7 +688,7 @@ "@ethersproject/bytes" "^5.8.0" js-sha3 "0.8.0" -"@ethersproject/logger@5.8.0", "@ethersproject/logger@^5.8.0": +"@ethersproject/logger@5.8.0", "@ethersproject/logger@^5.7.0", "@ethersproject/logger@^5.8.0": version "5.8.0" resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.8.0.tgz#f0232968a4f87d29623a0481690a2732662713d6" integrity sha512-Qe6knGmY+zPPWTC+wQrpitodgBfH7XoceCGL5bJVejmH+yCS3R8jJm8iiWuvWbG76RUmyEG53oqv6GMVWqunjA== @@ -770,7 +778,7 @@ elliptic "6.6.1" hash.js "1.1.7" -"@ethersproject/solidity@5.8.0": +"@ethersproject/solidity@5.8.0", "@ethersproject/solidity@^5.0.9": version "5.8.0" resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.8.0.tgz#429bb9fcf5521307a9448d7358c26b93695379b9" integrity sha512-4CxFeCgmIWamOHwYN9d+QWGxye9qQLilpgTU0XhYs1OahkclF+ewO+3V1U0mvpiuQxm5EHHmv8f7ClVII8EHsA== @@ -782,6 +790,15 @@ "@ethersproject/sha2" "^5.8.0" "@ethersproject/strings" "^5.8.0" +"@ethersproject/strings@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2" + integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/strings@5.8.0", "@ethersproject/strings@^5.8.0": version "5.8.0" resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.8.0.tgz#ad79fafbf0bd272d9765603215ac74fd7953908f" @@ -1334,6 +1351,16 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@openzeppelin/contracts@3.4.1-solc-0.7-2": + version "3.4.1-solc-0.7-2" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.1-solc-0.7-2.tgz#371c67ebffe50f551c3146a9eec5fe6ffe862e92" + integrity sha512-tAG9LWg8+M2CMu7hIsqHPaTyG4uDzjr6mhvH96LvOpLZZj6tgzTluBt+LsCf1/QaYrlis6pITvpIaIhE+iZB+Q== + +"@openzeppelin/contracts@3.4.2-solc-0.7": + version "3.4.2-solc-0.7" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.2-solc-0.7.tgz#38f4dbab672631034076ccdf2f3201fab1726635" + integrity sha512-W6QmqgkADuFcTLzHL8vVoNBtkwjvQRpYIAom7KiUNoLKghyx3FgH0GBjt8NRvigV1ZmMOBllvE1By1C+bi8WpA== + "@parcel/watcher-android-arm64@2.5.1": version "2.5.1" resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz#507f836d7e2042f798c7d07ad19c3546f9848ac1" @@ -1580,7 +1607,19 @@ resolved "https://registry.yarnpkg.com/@standard-schema/utils/-/utils-0.3.0.tgz#3d5e608f16c2390c10528e98e59aef6bf73cae7b" integrity sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g== -"@tabler/icons@^1.119.0", "@tabler/icons@^1.74.0": +"@tabler/icons-react@^3.34.1": + version "3.34.1" + resolved "https://registry.yarnpkg.com/@tabler/icons-react/-/icons-react-3.34.1.tgz#852b8efcab5382e44cc0f09b3de7d25ef3cba6f9" + integrity sha512-Ld6g0NqOO05kyyHsfU8h787PdHBm7cFmOycQSIrGp45XcXYDuOK2Bs0VC4T2FWSKZ6bx5g04imfzazf/nqtk1A== + dependencies: + "@tabler/icons" "3.34.1" + +"@tabler/icons@3.34.1": + version "3.34.1" + resolved "https://registry.yarnpkg.com/@tabler/icons/-/icons-3.34.1.tgz#a04a8cf79a5ebdcc44796dfa3de51d04811d2f9f" + integrity sha512-9gTnUvd7Fd/DmQgr3MKY+oJLa1RfNsQo8c/ir3TJAWghOuZXodbtbVp0QBY2DxWuuvrSZFys0HEbv1CoiI5y6A== + +"@tabler/icons@^1.74.0": version "1.119.0" resolved "https://registry.yarnpkg.com/@tabler/icons/-/icons-1.119.0.tgz#8c590bc5a563c8673a78ccd451bedabd584b376e" integrity sha512-Fk3Qq4w2SXcTjc/n1cuL5bccPkylrOMo7cYpQIf/yw6zP76LQV9dtLcHQUjFiUnaYuswR645CnURIhlafyAh9g== @@ -1817,6 +1856,87 @@ resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8" integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== +"@uniswap/lib@^4.0.1-alpha": + version "4.0.1-alpha" + resolved "https://registry.yarnpkg.com/@uniswap/lib/-/lib-4.0.1-alpha.tgz#2881008e55f075344675b3bca93f020b028fbd02" + integrity sha512-f6UIliwBbRsgVLxIaBANF6w09tYqc6Y/qXdsrbEmXHyFA7ILiKrIwRFXe1yOg8M3cksgVsO9N7yuL2DdCGQKBA== + +"@uniswap/sdk-core@^7.7.1", "@uniswap/sdk-core@^7.7.2": + version "7.7.2" + resolved "https://registry.yarnpkg.com/@uniswap/sdk-core/-/sdk-core-7.7.2.tgz#dc3a9b63b343640754860dce05d06972815eaee6" + integrity sha512-0KqXw+y0opBo6eoPAEoLHEkNpOu0NG9gEk7GAYIGok+SHX89WlykWsRYeJKTg9tOwhLpcG9oHg8xZgQ390iOrA== + dependencies: + "@ethersproject/address" "^5.0.2" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "5.7.0" + "@ethersproject/strings" "5.7.0" + big.js "^5.2.2" + decimal.js-light "^2.5.0" + jsbi "^3.1.4" + tiny-invariant "^1.1.0" + toformat "^2.0.0" + +"@uniswap/swap-router-contracts@^1.3.0": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@uniswap/swap-router-contracts/-/swap-router-contracts-1.3.1.tgz#0ebbb93eb578625618ed9489872de381f9c66fb4" + integrity sha512-mh/YNbwKb7Mut96VuEtL+Z5bRe0xVIbjjiryn+iMMrK2sFKhR4duk/86mEz0UO5gSx4pQIw9G5276P5heY/7Rg== + dependencies: + "@openzeppelin/contracts" "3.4.2-solc-0.7" + "@uniswap/v2-core" "^1.0.1" + "@uniswap/v3-core" "^1.0.0" + "@uniswap/v3-periphery" "^1.4.4" + dotenv "^14.2.0" + hardhat-watcher "^2.1.1" + +"@uniswap/v2-core@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@uniswap/v2-core/-/v2-core-1.0.1.tgz#af8f508bf183204779938969e2e54043e147d425" + integrity sha512-MtybtkUPSyysqLY2U210NBDeCHX+ltHt3oADGdjqoThZaFRDKwM6k1Nb3F0A3hk5hwuQvytFWhrWHOEq6nVJ8Q== + +"@uniswap/v3-core@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@uniswap/v3-core/-/v3-core-1.0.0.tgz#6c24adacc4c25dceee0ba3ca142b35adbd7e359d" + integrity sha512-kSC4djMGKMHj7sLMYVnn61k9nu+lHjMIxgg9CDQT+s2QYLoA56GbSK9Oxr+qJXzzygbkrmuY6cwgP6cW2JXPFA== + +"@uniswap/v3-core@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@uniswap/v3-core/-/v3-core-1.0.1.tgz#b6d2bdc6ba3c3fbd610bdc502395d86cd35264a0" + integrity sha512-7pVk4hEm00j9tc71Y9+ssYpO6ytkeI0y7WE9P6UcmNzhxPePwyAxImuhVsTqWK9YFvzgtvzJHi64pBl4jUzKMQ== + +"@uniswap/v3-periphery@^1.0.1", "@uniswap/v3-periphery@^1.1.1", "@uniswap/v3-periphery@^1.4.4": + version "1.4.4" + resolved "https://registry.yarnpkg.com/@uniswap/v3-periphery/-/v3-periphery-1.4.4.tgz#d2756c23b69718173c5874f37fd4ad57d2f021b7" + integrity sha512-S4+m+wh8HbWSO3DKk4LwUCPZJTpCugIsHrWR86m/OrUyvSqGDTXKFfc2sMuGXCZrD1ZqO3rhQsKgdWg3Hbb2Kw== + dependencies: + "@openzeppelin/contracts" "3.4.2-solc-0.7" + "@uniswap/lib" "^4.0.1-alpha" + "@uniswap/v2-core" "^1.0.1" + "@uniswap/v3-core" "^1.0.0" + base64-sol "1.0.1" + +"@uniswap/v3-sdk@^3.25.2": + version "3.25.2" + resolved "https://registry.yarnpkg.com/@uniswap/v3-sdk/-/v3-sdk-3.25.2.tgz#cb6ee174b58d86a3b3b18b3ba72f662e58c415da" + integrity sha512-0oiyJNGjUVbc958uZmAr+m4XBCjV7PfMs/OUeBv+XDl33MEYF/eH86oBhvqGDM8S/cYaK55tCXzoWkmRUByrHg== + dependencies: + "@ethersproject/abi" "^5.5.0" + "@ethersproject/solidity" "^5.0.9" + "@uniswap/sdk-core" "^7.7.1" + "@uniswap/swap-router-contracts" "^1.3.0" + "@uniswap/v3-periphery" "^1.1.1" + "@uniswap/v3-staker" "1.0.0" + tiny-invariant "^1.1.0" + tiny-warning "^1.0.3" + +"@uniswap/v3-staker@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@uniswap/v3-staker/-/v3-staker-1.0.0.tgz#9a6915ec980852479dfc903f50baf822ff8fa66e" + integrity sha512-JV0Qc46Px5alvg6YWd+UIaGH9lDuYG/Js7ngxPit1SPaIP30AlVer1UYB7BRYeUVVxE+byUyIeN5jeQ7LLDjIw== + dependencies: + "@openzeppelin/contracts" "3.4.1-solc-0.7-2" + "@uniswap/v3-core" "1.0.0" + "@uniswap/v3-periphery" "^1.0.1" + "@walletconnect/core@2.19.1": version "2.19.1" resolved "https://registry.yarnpkg.com/@walletconnect/core/-/core-2.19.1.tgz#71738940341b438326b65b3f49226decbe070bae" @@ -2230,7 +2350,7 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" -anymatch@^3.1.3: +anymatch@^3.1.3, anymatch@~3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== @@ -2405,6 +2525,11 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +base64-sol@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/base64-sol/-/base64-sol-1.0.1.tgz#91317aa341f0bc763811783c5729f1c2574600f6" + integrity sha512-ld3cCNMeXt4uJXmLZBHFGMvVpK9KsLVEhPpFRXnvSVAqABKbuNZg/+dsq3NuM+wxFLb/UrVkz7m1ciWmkMfTbg== + bech32@1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" @@ -2415,6 +2540,16 @@ big-integer@^1.6.16: resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.52.tgz#60a887f3047614a8e1bffe5d7173490a97dc8c85" integrity sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg== +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +binary-extensions@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + bn.js@^4.11.9: version "4.12.1" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.1.tgz#215741fe3c9dba2d7e12c001d0cfdbae43975ba7" @@ -2433,7 +2568,7 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@^3.0.3: +braces@^3.0.3, braces@~3.0.2: version "3.0.3" resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz" integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== @@ -2543,6 +2678,21 @@ chalk@^4.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chokidar@^3.5.3: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + chokidar@^4.0.0, chokidar@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" @@ -2749,6 +2899,11 @@ decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== +decimal.js-light@^2.5.0: + version "2.5.1" + resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934" + integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg== + decode-uri-component@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" @@ -2866,6 +3021,11 @@ dom-helpers@^5.0.1: "@babel/runtime" "^7.8.7" csstype "^3.0.2" +dotenv@^14.2.0: + version "14.3.2" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-14.3.2.tgz#7c30b3a5f777c79a3429cb2db358eef6751e8369" + integrity sha512-vwEppIphpFdvaMCaHfCEv9IgwcxMljMw2TnAQBB4VWPvzXQLTb82jwmdOKzlEVUL3gNFT4l4TPKO+Bn+sqcrVQ== + dotenv@^16.0.3: version "16.4.7" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.7.tgz#0e20c5b82950140aa99be360a8a5f52335f53c26" @@ -3401,10 +3561,10 @@ ethers@^5.6.8: "@ethersproject/web" "5.8.0" "@ethersproject/wordlists" "5.8.0" -ethers@^6.14.1: - version "6.14.1" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.14.1.tgz#96b5e967d9c3c66c6b64304d8e7669a761d6fca3" - integrity sha512-JnFiPFi3sK2Z6y7jZ3qrafDMwiXmU+6cNZ0M+kPq+mTy9skqEzwqAdFW3nb/em2xjlIVXX6Lz8ID6i3LmS4+fQ== +ethers@^6.15.0: + version "6.15.0" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.15.0.tgz#2980f2a3baf0509749b7e21f8692fa8a8349c0e3" + integrity sha512-Kf/3ZW54L4UT0pZtsY/rf+EkBU7Qi5nnhonjUb8yTXcxH3cdcWrV2cRyk0Xk/4jK6OoHhxxZHriyhje20If2hQ== dependencies: "@adraffy/ens-normalize" "1.10.1" "@noble/curves" "1.2.0" @@ -3538,6 +3698,11 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + function-bind@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" @@ -3608,7 +3773,7 @@ get-symbol-description@^1.1.0: es-errors "^1.3.0" get-intrinsic "^1.2.6" -glob-parent@^5.1.2: +glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== @@ -3727,6 +3892,13 @@ h3@^1.15.0: ufo "^1.5.4" uncrypto "^0.1.3" +hardhat-watcher@^2.1.1: + version "2.5.0" + resolved "https://registry.yarnpkg.com/hardhat-watcher/-/hardhat-watcher-2.5.0.tgz#3ee76c3cb5b99f2875b78d176207745aa484ed4a" + integrity sha512-Su2qcSMIo2YO2PrmJ0/tdkf+6pSt8zf9+4URR5edMVti6+ShI8T3xhPrwugdyTOFuyj8lKHrcTZNKUFYowYiyA== + dependencies: + chokidar "^3.5.3" + has-bigints@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.1.0.tgz#28607e965ac967e03cd2a2c70a2636a1edad49fe" @@ -3947,6 +4119,13 @@ is-bigint@^1.1.0: dependencies: has-bigints "^1.0.2" +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + is-boolean-object@^1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz#7067f47709809a393c71ff5bb3e135d8a9215d9e" @@ -4011,7 +4190,7 @@ is-generator-function@^1.0.10: has-tostringtag "^1.0.2" safe-regex-test "^1.1.0" -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== @@ -4181,6 +4360,11 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" +jsbi@^3.1.4: + version "3.2.5" + resolved "https://registry.yarnpkg.com/jsbi/-/jsbi-3.2.5.tgz#b37bb90e0e5c2814c1c2a1bcd8c729888a2e37d6" + integrity sha512-aBE4n43IPvjaddScbvWRA2YlTzKEynHzu7MqOyTipdHucf/VxS63ViCjxYRg86M8Rxwbt/GfzHl1kKERkt45fQ== + jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" @@ -4542,7 +4726,7 @@ node-releases@^2.0.19: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== -normalize-path@^3.0.0: +normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== @@ -4792,7 +4976,7 @@ picocolors@^1.0.0, picocolors@^1.1.1: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== -picomatch@^2.0.4, picomatch@^2.3.1: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -5113,6 +5297,13 @@ readdirp@^4.0.1: resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.1.2.tgz#eb85801435fbf2a7ee58f19e0921b068fc69948d" integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg== +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + real-require@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.1.0.tgz#736ac214caa20632847b7ca8c1056a0767df9381" @@ -5637,6 +5828,16 @@ thread-stream@^0.15.1: dependencies: real-require "^0.1.0" +tiny-invariant@^1.1.0: + version "1.3.3" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz#46680b7a873a0d5d10005995eb90a70d74d60127" + integrity sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg== + +tiny-warning@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" + integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== + to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" @@ -5649,6 +5850,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +toformat@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/toformat/-/toformat-2.0.0.tgz#7a043fd2dfbe9021a4e36e508835ba32056739d8" + integrity sha512-03SWBVop6nU8bpyZCx7SodpYznbZF5R4ljwNLBcTQzKOD9xuihRo/psX58llS1BMFhhAI08H3luot5GoXJz2pQ== + tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"