From 8d639d2f802f7af9fbe4533da02e14d37e764e04 Mon Sep 17 00:00:00 2001
From: Joseph Chalabi <100090645+chalabi2@users.noreply.github.com>
Date: Wed, 20 Dec 2023 15:40:49 -0700
Subject: [PATCH 01/40] update deploy workflow
---
.github/workflows/testuideploy.yaml | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/testuideploy.yaml b/.github/workflows/testuideploy.yaml
index a5b305d62..3ef44a048 100644
--- a/.github/workflows/testuideploy.yaml
+++ b/.github/workflows/testuideploy.yaml
@@ -8,9 +8,12 @@ on:
workflow_dispatch:
permissions:
- contents: read
+ contents: write
pages: write
id-token: write
+ actions: write
+ deployments: write
+ pull-requests: write
concurrency:
group: "pages"
From 76cd03ea1cd1036cf1e7362cf8f2159961931b19 Mon Sep 17 00:00:00 2001
From: Joseph Chalabi <100090645+chalabi2@users.noreply.github.com>
Date: Wed, 20 Dec 2023 15:45:29 -0700
Subject: [PATCH 02/40] update deploy workflow conditions
---
.github/workflows/testuideploy.yaml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/.github/workflows/testuideploy.yaml b/.github/workflows/testuideploy.yaml
index 3ef44a048..3e499ab9a 100644
--- a/.github/workflows/testuideploy.yaml
+++ b/.github/workflows/testuideploy.yaml
@@ -4,6 +4,9 @@ on:
push:
paths:
- web-ui/**
+ pull_request:
+ paths:
+ - web-ui/**
workflow_dispatch:
From d00d7b25e76e593e85960b6adf76d071314027f7 Mon Sep 17 00:00:00 2001
From: Joseph Chalabi <100090645+chalabi2@users.noreply.github.com>
Date: Wed, 20 Dec 2023 15:49:51 -0700
Subject: [PATCH 03/40] update homepage url to match pages deployment
---
web-ui/next.config.js | 4 ++--
web-ui/package.json | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/web-ui/next.config.js b/web-ui/next.config.js
index 67dede20d..36d476a96 100644
--- a/web-ui/next.config.js
+++ b/web-ui/next.config.js
@@ -3,8 +3,8 @@
module.exports = {
reactStrictMode: true,
swcMinify: true,
- basePath: '/quicksilver-app-v2',
- assetPrefix: '/quicksilver-app-v2',
+ basePath: '/quicksilver',
+ assetPrefix: '/quicksilver',
typescript: {
// !! WARN !!
// Dangerously allow production builds to successfully complete even if
diff --git a/web-ui/package.json b/web-ui/package.json
index 0dc16f181..ab33a6fd2 100644
--- a/web-ui/package.json
+++ b/web-ui/package.json
@@ -2,7 +2,7 @@
"name": "bun-qs",
"version": "0.15.3",
"private": true,
- "homepage": "https://quicksilver-zone.github.io/quicksilver-app-v2/",
+ "homepage": "https://quicksilver-zone.github.io/quicksilver/",
"scripts": {
"build": "next build",
"dev": "next dev",
From 940b6d205b40c689746d7366682b070995810e17 Mon Sep 17 00:00:00 2001
From: Joseph Chalabi <100090645+chalabi2@users.noreply.github.com>
Date: Wed, 20 Dec 2023 15:59:26 -0700
Subject: [PATCH 04/40] update deploy workflow to export proper directory
---
.github/workflows/testuideploy.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/testuideploy.yaml b/.github/workflows/testuideploy.yaml
index 3e499ab9a..66575d808 100644
--- a/.github/workflows/testuideploy.yaml
+++ b/.github/workflows/testuideploy.yaml
@@ -53,4 +53,4 @@ jobs:
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
- publish_dir: ./out
+ publish_dir: ./web-ui/out
From e5e60c4706351c797acdee298ee8f2c6a5400951 Mon Sep 17 00:00:00 2001
From: Joseph Chalabi <100090645+chalabi2@users.noreply.github.com>
Date: Wed, 20 Dec 2023 16:20:53 -0700
Subject: [PATCH 05/40] update img urls
---
web-ui/components/react/sideHeader.tsx | 14 +++++++-------
web-ui/pages/assets.tsx | 2 +-
web-ui/pages/defi.tsx | 2 +-
web-ui/pages/index.tsx | 2 +-
web-ui/pages/staking.tsx | 4 ++--
web-ui/state/chains/prod.ts | 20 ++++++++++----------
web-ui/test/Staking.test.tsx | 20 ++++++++++----------
7 files changed, 32 insertions(+), 32 deletions(-)
diff --git a/web-ui/components/react/sideHeader.tsx b/web-ui/components/react/sideHeader.tsx
index 6fa2a617b..91376e6b8 100644
--- a/web-ui/components/react/sideHeader.tsx
+++ b/web-ui/components/react/sideHeader.tsx
@@ -33,7 +33,7 @@ export const SideHeader = () => {
useEffect(() => {
const handleRouteChange = (url: string) => {
- const path = url.split('/quicksilver-app-v2/')[1];
+ const path = url.split('/quicksilver/')[1];
setSelectedPage(path);
};
@@ -93,7 +93,7 @@ export const SideHeader = () => {
h="75px"
w="75px"
borderRadius="full"
- src="/quicksilver-app-v2/img/networks/quicksilver.svg"
+ src="/quicksilver/img/networks/quicksilver.svg"
onClick={handleLogoClick}
cursor="pointer"
_hover={{
@@ -116,7 +116,7 @@ export const SideHeader = () => {
{['Staking', 'Governance', 'Defi', 'Assets'].map((item) => (
{
}}
alt="Staking"
h="55px"
- src="/quicksilver-app-v2/img/test.png"
+ src="/quicksilver/img/test.png"
/>
@@ -235,7 +235,7 @@ export const SideHeader = () => {
alt="Governance"
h="60px"
w={'60px'}
- src="/quicksilver-app-v2/img/test2.png"
+ src="/quicksilver/img/test2.png"
/>
@@ -262,7 +262,7 @@ export const SideHeader = () => {
}}
alt="Assets"
h="55px"
- src="/quicksilver-app-v2/img/test3.png"
+ src="/quicksilver/img/test3.png"
/>
@@ -289,7 +289,7 @@ export const SideHeader = () => {
}}
alt="DeFi"
h="55px"
- src="/quicksilver-app-v2/img/test4.png"
+ src="/quicksilver/img/test4.png"
/>
diff --git a/web-ui/pages/assets.tsx b/web-ui/pages/assets.tsx
index cff5f41cc..10a1ae98c 100644
--- a/web-ui/pages/assets.tsx
+++ b/web-ui/pages/assets.tsx
@@ -123,7 +123,7 @@ function Home() {
Assets
-
+
{/* Quick box */}
diff --git a/web-ui/pages/defi.tsx b/web-ui/pages/defi.tsx
index fb5e4a8a0..54312fd99 100644
--- a/web-ui/pages/defi.tsx
+++ b/web-ui/pages/defi.tsx
@@ -8,7 +8,7 @@ export default function Home() {
DeFi
-
+
>
diff --git a/web-ui/pages/index.tsx b/web-ui/pages/index.tsx
index a47cdc3f4..9c1c5c339 100644
--- a/web-ui/pages/index.tsx
+++ b/web-ui/pages/index.tsx
@@ -12,7 +12,7 @@ export default function Home() {
Quicksilver
-
+
diff --git a/web-ui/pages/staking.tsx b/web-ui/pages/staking.tsx
index d57734602..5727b89a6 100644
--- a/web-ui/pages/staking.tsx
+++ b/web-ui/pages/staking.tsx
@@ -59,13 +59,13 @@ export default function Staking() {
Staking
-
+
{/*
Date: Wed, 20 Dec 2023 21:30:01 -0700
Subject: [PATCH 06/40] complete portfolio section(queries)
---
web-ui/components/Assets/assetsGrid.tsx | 86 ++++----------------
web-ui/components/Assets/portfolio.tsx | 32 +++++---
web-ui/components/Assets/quickbox.tsx | 20 ++++-
web-ui/pages/assets.tsx | 104 ++++++++++++++++++------
4 files changed, 138 insertions(+), 104 deletions(-)
diff --git a/web-ui/components/Assets/assetsGrid.tsx b/web-ui/components/Assets/assetsGrid.tsx
index 2df225c59..e1aac53ca 100644
--- a/web-ui/components/Assets/assetsGrid.tsx
+++ b/web-ui/components/Assets/assetsGrid.tsx
@@ -1,13 +1,23 @@
+import { shiftDigits } from '@/utils';
import { Box, SimpleGrid, VStack, Text, Button, Divider, useColorModeValue, HStack, Flex, Grid, GridItem } from '@chakra-ui/react';
import React from 'react';
interface AssetCardProps {
assetName: string;
balance: string;
- apy: string;
+ apy: number;
nativeAssetName: string;
}
+interface AssetGridProps {
+ assets: Array<{
+ name: string;
+ balance: string;
+ apy: number;
+ native: string;
+ }>;
+}
+
const AssetCard: React.FC = ({ assetName, balance, apy, nativeAssetName }) => {
return (
@@ -21,11 +31,11 @@ const AssetCard: React.FC = ({ assetName, balance, apy, nativeAs
APY:
- {apy}
+ {shiftDigits(apy.toFixed(2), 2)}%
-
+
@@ -50,7 +60,7 @@ const AssetCard: React.FC = ({ assetName, balance, apy, nativeAs
-
+
-
-
-
- {nativeAssetName}
-
-
-
-
-
-
-
-
-
-
-
- ON QUICKSILVER:
-
-
-
-
- {balance}
-
-
-
-
-
-
-
);
};
-const AssetsGrid = () => {
- const assets = [
- { name: 'qATOM', balance: '0.123', apy: '12.34%', native: 'ATOM' },
- { name: 'qREGEN', balance: '0.123', apy: '12.34%', native: 'REGEN' },
- { name: 'qOSMO', balance: '0.123', apy: '12.34%', native: 'OSMO' },
- { name: 'qSTARS', balance: '0.123', apy: '12.34%', native: 'STARS' },
- { name: 'qSOMM', balance: '0.123', apy: '12.34%', native: 'SOMM' },
- ];
-
+const AssetsGrid: React.FC = ({ assets }) => {
return (
<>
- Assets (qAssets + Native Balance)
+ qAssets
diff --git a/web-ui/components/Assets/portfolio.tsx b/web-ui/components/Assets/portfolio.tsx
index c3f85a62a..c99a1ee6e 100644
--- a/web-ui/components/Assets/portfolio.tsx
+++ b/web-ui/components/Assets/portfolio.tsx
@@ -1,14 +1,22 @@
-import { Box, Flex, Text, Icon, VStack, HStack, Heading, Spinner } from '@chakra-ui/react';
+import { shiftDigits } from '@/utils';
+import { Box, Flex, Text, Icon, VStack, HStack, Heading, Spinner, Tooltip } from '@chakra-ui/react';
-import { PortfolioItemInterface } from '@/pages/assets';
+interface PortfolioItemInterface {
+ title: string;
+ percentage: string;
+ progressBarColor: string;
+ amount: string;
+ qTokenPrice: number;
+}
interface MyPortfolioProps {
portfolioItems: PortfolioItemInterface[];
isWalletConnected: boolean;
totalValue: number;
+ averageApy: number;
+ totalYearlyYield: number;
}
-
-const MyPortfolio: React.FC = ({ portfolioItems, isWalletConnected, totalValue }) => {
+const MyPortfolio: React.FC = ({ portfolioItems, isWalletConnected, totalValue, averageApy, totalYearlyYield }) => {
if (!isWalletConnected) {
return (
= ({ portfolioItems, isWalletConne
AVG APY:
- 6.56%
+ {shiftDigits(averageApy.toFixed(2), 2)}%
@@ -87,7 +95,7 @@ const MyPortfolio: React.FC = ({ portfolioItems, isWalletConne
Yearly Yield:{' '}
- $3,917
+ ${totalYearlyYield.toFixed(2)}
@@ -127,7 +135,9 @@ interface PortfolioItemProps {
const PortfolioItem: React.FC = ({ title, percentage, progressBarColor, amount, qTokenPrice }) => (
- {Number(amount).toFixed(2).toString()}
+
+ {Number(amount).toFixed(2).toString()}
+
{title}
@@ -137,9 +147,11 @@ const PortfolioItem: React.FC = ({ title, percentage, progre
-
- {`${percentage * 100}%`}
-
+
+
+ {`${percentage * 100}%`}
+
+
);
diff --git a/web-ui/components/Assets/quickbox.tsx b/web-ui/components/Assets/quickbox.tsx
index 9c619480d..2b8bf9a80 100644
--- a/web-ui/components/Assets/quickbox.tsx
+++ b/web-ui/components/Assets/quickbox.tsx
@@ -1,4 +1,4 @@
-import { Box, Flex, Text, Button, VStack, useColorModeValue, HStack, SkeletonCircle } from '@chakra-ui/react';
+import { Box, Flex, Text, Button, VStack, useColorModeValue, HStack, SkeletonCircle, Spinner } from '@chakra-ui/react';
import { useChain } from '@cosmos-kit/react';
import { defaultChainName } from '@/config';
@@ -25,6 +25,24 @@ const QuickBox = () => {
);
}
+ if (!balance) {
+ return (
+
+
+
+ );
+ }
+
return (
diff --git a/web-ui/pages/assets.tsx b/web-ui/pages/assets.tsx
index 10a1ae98c..b2199cc37 100644
--- a/web-ui/pages/assets.tsx
+++ b/web-ui/pages/assets.tsx
@@ -10,8 +10,8 @@ import StakingIntent from '@/components/Assets/intents';
import MyPortfolio from '@/components/Assets/portfolio';
import QuickBox from '@/components/Assets/quickbox';
import UnbondingAssetsTable from '@/components/Assets/unbondingTable';
-import { useIntentQuery, useQBalanceQuery, useTokenPriceQuery, useZoneQuery } from '@/hooks/useQueries';
-import { shiftDigits } from '@/utils';
+import { useAPYQuery, useBalanceQuery, useIntentQuery, useQBalanceQuery, useTokenPriceQuery, useZoneQuery } from '@/hooks/useQueries';
+import { shiftDigits, toNumber } from '@/utils';
export interface PortfolioItemInterface {
title: string;
@@ -29,32 +29,56 @@ type BalanceRates = {
[key: string]: string;
};
+type APYRates = {
+ [key: string]: Number;
+};
+
function Home() {
- const { address } = useChain('quicksilver');
const { address: qAddress, isWalletConnected } = useChain('quicksilver');
-
- // Define a function to fetch token price data
+ const { address } = useChain('quicksilver');
+ // Function to fetch token price from an API
const fetchTokenPrice = async (token: any) => {
try {
const response = await axios.get(`https://api-osmosis.imperator.co/tokens/v2/price/${token}`);
- return response.data.price; // Adjust this according to your API response structure
+ return response.data.price;
} catch (error) {
console.error('Error fetching token price:', error);
return null;
}
};
+
+ // Retrieve balance for each token
const { balance: qAtom, isLoading: qAtomIsLoading, isError: qAtomIsError } = useQBalanceQuery('quicksilver', qAddress ?? '', 'atom');
const { balance: qOsmo, isLoading: qOsmoIsLoading, isError: qOsmoIsError } = useQBalanceQuery('quicksilver', qAddress ?? '', 'osmo');
const { balance: qStars, isLoading: qStarsIsLoading, isError: qStarsIsError } = useQBalanceQuery('quicksilver', qAddress ?? '', 'stars');
const { balance: qRegen, isLoading: qRegenIsLoading, isError: qRegenIsError } = useQBalanceQuery('quicksilver', qAddress ?? '', 'regen');
const { balance: qSomm, isLoading: qSommIsLoading, isError: qSommIsError } = useQBalanceQuery('quicksilver', qAddress ?? '', 'somm');
+ // Retrieve zone data for each token
const { data: CosmosZone, isLoading: isCosmosZoneLoading, isError: isCosmosZoneError } = useZoneQuery('cosmoshub-4');
const { data: OsmoZone, isLoading: isOsmoZoneLoading, isError: isOsmoZoneError } = useZoneQuery('osmosis-1');
const { data: StarZone, isLoading: isStarZoneLoading, isError: isStarZoneError } = useZoneQuery('stargaze-1');
const { data: RegenZone, isLoading: isRegenZoneLoading, isError: isRegenZoneError } = useZoneQuery('regen-1');
const { data: SommZone, isLoading: isSommZoneLoading, isError: isSommZoneError } = useZoneQuery('sommelier-3');
-
+ // Retrieve APY data for each token
+ const { APY: cosmosAPY, isLoading: isCosmosAPYLoading, isError: isCosmosAPYError } = useAPYQuery('cosmoshub-4');
+ const { APY: osmoAPY, isLoading: isOsmoAPYLoading, isError: isOsmoAPYError } = useAPYQuery('osmosis-1');
+ const { APY: starsAPY, isLoading: isStarsAPYLoading, isError: isStarsAPYError } = useAPYQuery('stargaze-1');
+ const { APY: regenAPY, isLoading: isRegenAPYLoading, isError: isRegenAPYError } = useAPYQuery('regen-1');
+ const { APY: sommAPY, isLoading: isSommAPYLoading, isError: isSommAPYError } = useAPYQuery('sommelier-3');
+
+ // useMemo hook to cache APY data
+ const qAPYRates: APYRates = useMemo(
+ () => ({
+ qAtom: cosmosAPY,
+ qOsmo: osmoAPY,
+ qStars: starsAPY,
+ qRegen: regenAPY,
+ qSomm: sommAPY,
+ }),
+ [cosmosAPY, osmoAPY, starsAPY, regenAPY, sommAPY],
+ );
+ // useMemo hook to cache qBalance data
const qBalances: BalanceRates = useMemo(
() => ({
qAtom: shiftDigits(qAtom?.balance.amount ?? '', -6),
@@ -66,9 +90,7 @@ function Home() {
[qAtom, qOsmo, qStars, qRegen, qSomm],
);
- const [portfolioItems, setPortfolioItems] = useState([]);
- const [totalPortfolioValue, setTotalPortfolioValue] = useState(0);
-
+ // useMemo hook to cache redemption rate data
const redemptionRates: NumericRedemptionRates = useMemo(
() => ({
atom: CosmosZone?.redemptionRate ? parseFloat(CosmosZone.redemptionRate) : 1,
@@ -80,19 +102,32 @@ function Home() {
[CosmosZone, OsmoZone, StarZone, RegenZone, SommZone],
);
+ // State hooks for portfolio items, total portfolio value, and other metrics
+ const [portfolioItems, setPortfolioItems] = useState([]);
+ const [totalPortfolioValue, setTotalPortfolioValue] = useState(0);
+ const [averageApy, setAverageAPY] = useState(0);
+ const [totalYearlyYield, setTotalYearlyYield] = useState(0);
+ // useEffect hook to compute portfolio metrics when dependencies change
useEffect(() => {
const updatePortfolioItems = async () => {
let totalValue = 0;
+ let totalYearlyYield = 0;
+ let weightedAPY = 0;
let updatedItems = [];
-
+ // Loop through each token to compute value, APY, and yield
for (const token of Object.keys(qBalances)) {
const baseToken = token.replace('q', '').toLowerCase();
const price = await fetchTokenPrice(baseToken);
const qTokenPrice = price * Number(redemptionRates[baseToken]);
const qTokenBalance = qBalances[token];
-
const itemValue = Number(qTokenBalance) * qTokenPrice;
+
+ const qTokenAPY = qAPYRates[token] || 0;
+ const yearlyYield = itemValue * Number(qTokenAPY);
+ // Accumulate total values and compute weighted APY
totalValue += itemValue;
+ totalYearlyYield += yearlyYield;
+ weightedAPY += (itemValue / totalValue) * Number(qTokenAPY);
updatedItems.push({
title: token.toUpperCase(),
@@ -102,19 +137,35 @@ function Home() {
qTokenPrice: qTokenPrice || 0,
});
}
-
- // Now, calculate the percentage of each item
- updatedItems = updatedItems.map((item) => ({
- ...item,
- percentage: ((((Number(item.amount) * item.qTokenPrice) / totalValue) * 100) / 100).toFixed(2),
- }));
-
+ // Recalculate percentages for each item based on total value
+ updatedItems = updatedItems.map((item) => {
+ const itemValue = Number(item.amount) * item.qTokenPrice;
+ return {
+ ...item,
+ percentage: (((itemValue / totalValue) * 100) / 100).toFixed(2),
+ };
+ });
+ // Update state with calculated data
setPortfolioItems(updatedItems);
setTotalPortfolioValue(totalValue);
+ setAverageAPY(weightedAPY);
+ setTotalYearlyYield(totalYearlyYield);
};
updatePortfolioItems();
- }, [qBalances, CosmosZone, OsmoZone, StarZone, RegenZone, SommZone, redemptionRates]);
+ }, [qBalances, CosmosZone, OsmoZone, StarZone, RegenZone, SommZone, redemptionRates, qAPYRates]);
+
+ // useMemo hook to prepare assets data for the AssetsGrid component
+ const assetsData = useMemo(() => {
+ return Object.keys(qBalances).map((token) => {
+ return {
+ name: token.toUpperCase().replace('Q', 'q'),
+ balance: toNumber(qBalances[token], 2).toString(),
+ apy: parseFloat(qAPYRates[token]?.toFixed(2)) || 0,
+ native: token.replace('q', '').toUpperCase(),
+ };
+ });
+ }, [qBalances, qAPYRates]);
return (
<>
@@ -152,7 +203,13 @@ function Home() {
w="lg"
h="sm"
>
-
+
{/* Intent box */}
{/* Assets Grid */}
-
+
{/* Unbonding Table */}
@@ -182,10 +239,11 @@ function Home() {
>
);
}
-
+// disable ssr in order to use useQuery hooks
const DynamicAssetsPage = dynamic(() => Promise.resolve(Home), {
ssr: false,
});
+
const AssetsWrapper = () => {
return ;
};
From f896775711ba4fb33e56a433e76f4682e2188d82 Mon Sep 17 00:00:00 2001
From: Joseph Chalabi <100090645+chalabi2@users.noreply.github.com>
Date: Thu, 21 Dec 2023 15:11:08 -0700
Subject: [PATCH 07/40] add prod and dev env's for queries
---
web-ui/.env | 2 +-
web-ui/.env.development | 24 +++++
web-ui/.env.production | 13 +++
web-ui/bun.lockb | Bin 419315 -> 420225 bytes
...qckDepositModa.tsx => qckDepositModal.tsx} | 102 +++++++++++++-----
.../Assets/modals/qckWithdrawModal.tsx | 24 ++---
web-ui/components/Assets/quickbox.tsx | 2 +-
web-ui/hooks/useGrpcQueryClient.ts | 20 ++--
web-ui/hooks/useQueries.ts | 35 ++++++
web-ui/hooks/useRpcQueryClient.ts | 23 ++--
web-ui/hooks/useToaster.tsx | 15 +--
web-ui/package.json | 1 +
web-ui/pages/_app.tsx | 5 +-
web-ui/pages/assets.tsx | 2 +-
web-ui/services/zone.ts | 2 +-
web-ui/tx/ibcTransferTx.tsx | 98 +++++++++++++++++
web-ui/utils/ibc.ts | 81 ++++++++++++++
17 files changed, 374 insertions(+), 75 deletions(-)
create mode 100644 web-ui/.env.development
create mode 100644 web-ui/.env.production
rename web-ui/components/Assets/modals/{qckDepositModa.tsx => qckDepositModal.tsx} (55%)
create mode 100644 web-ui/tx/ibcTransferTx.tsx
create mode 100644 web-ui/utils/ibc.ts
diff --git a/web-ui/.env b/web-ui/.env
index ec7efd392..5677fc1e5 100644
--- a/web-ui/.env
+++ b/web-ui/.env
@@ -9,4 +9,4 @@ REACT_APP_ENABLE_CLAIMS="true"
APY_ZONES_ENDPOINT = "https://chains.cosmos.directory"
NEXT_PUBLIC_OSMOSIS_API="https://api.osmosis.zone"
NEXT_PUBLIC_WHITELISTED_DENOM="uatom,ustars,uosmo,usomm,uregen"
-NEXT_PUBLIC_WHITELISTED_ZONES="osmosis-1,stargaze-1,regen-1,cosmoshub-4,sommelier-3"
\ No newline at end of file
+NEXT_PUBLIC_WHITELISTED_ZONES="osmosis-1,stargaze-1,regen-1,cosmoshub-4,sommelier-3"
diff --git a/web-ui/.env.development b/web-ui/.env.development
new file mode 100644
index 000000000..c48665a31
--- /dev/null
+++ b/web-ui/.env.development
@@ -0,0 +1,24 @@
+NEXT_PUBLIC_CHAIN_ENV="testnet"
+NEXT_PUBLIC_TESTNET_LCD_ENDPOINT_QUICKSILVER="http://lcd.quicksilver.zone/"
+NEXT_PUBLIC_TESTNET_RPC_ENDPOINT_QUICKSILVER="http://rpc.quicksilver.zone/"
+NEXT_PUBLIC_TESTNET_LCD_ENDPOINT_COSMOSHUB=http://lcd.cosmoshub-4.quicksilver.zone
+NEXT_PUBLIC_TESTNET_RPC_ENDPOINT_COSMOSHUB=http://rpc.cosmoshub-4.quicksilver.zone
+NEXT_PUBLIC_TESTNET_LCD_ENDPOINT_OSMOSIS="http://lcd.osmosis-1.quicksilver.zone"
+NEXT_PUBLIC_TESTNET_RPC_ENDPOINT_OSMOSIS="http://rpc.osmosis-1.quicksilver.zone"
+NEXT_PUBLIC_TESTNET_LCD_ENDPOINT_STARGAZE="http://lcd.stargaze-1.quicksilver.zone"
+NEXT_PUBLIC_TESTNET_RPC_ENDPOINT_STARGAZE="http://rpc.stargaze-1.quicksilver.zone"
+NEXT_PUBLIC_TESTNET_LCD_ENDPOINT_REGEN="http://lcd.regen-1.quicksilver.zone"
+NEXT_PUBLIC_TESTNET_RPC_ENDPOINT_REGEN="http://rpc.regen-1.quicksilver.zone"
+NEXT_PUBLIC_TESTNET_LCD_ENDPOINT_SOMMELIER="http://lcd.sommelier-3.quicksilver.zone"
+NEXT_PUBLIC_TESTNET_RPC_ENDPOINT_SOMMELIER="http://rpc.sommelier-3.quicksilver.zone"
+NEXT_PUBLIC_QUICKSILVER_API="http://lcd.quicksilver.zone"
+NEXT_PUBLIC_QUICKSILVER_DATA_API="http://data.quicksilver.zone"
+ZONE_URL="quicksilver.zone"
+REACT_APP_WHITELISTED_ZONES="osmosis-1,stargaze-1,regen-1,cosmoshub-4,sommelier-3"
+REACT_APP_ENABLE_UNBONDING="true"
+REACT_APP_ENABLE_SET_INTENT="true"
+REACT_APP_ENABLE_CLAIMS="true"
+APY_ZONES_ENDPOINT = "https://chains.cosmos.directory"
+NEXT_PUBLIC_OSMOSIS_API="https://api.osmosis.zone"
+NEXT_PUBLIC_WHITELISTED_DENOM="uatom,ustars,uosmo,usomm,uregen"
+NEXT_PUBLIC_WHITELISTED_ZONES="osmosis-1,stargaze-1,regen-1,cosmoshub-4,sommelier-3"
\ No newline at end of file
diff --git a/web-ui/.env.production b/web-ui/.env.production
new file mode 100644
index 000000000..c06f0c5cf
--- /dev/null
+++ b/web-ui/.env.production
@@ -0,0 +1,13 @@
+NEXT_PUBLIC_CHAIN_ENV="mainnet"
+MAINNET_LCD_ENDPOINT_QUICKSILVER="https://lcd.quicksilver.zone"
+MAINNET_RPC_ENDPOINT_QUICKSILVER="https://rpc.quicksilver.zone"
+MAINNET_LCD_ENDPOINT_COSMOSHUB="https://lcd.cosmoshub-4.quicskilver.zone"
+MAINNET_RPC_ENDPOINT_COSMOSHUB="https://rpc.cosmoshub-4.quicskilver.zone"
+MAINNET_LCD_ENDPOINT_OSMOSIS="https://lcd.osmosis-1.quicskilver.zone"
+MAINNET_RPC_ENDPOINT_OSMOSIS="https://rpc.osmosis-1.quicskilver.zone"
+MAINNET_LCD_ENDPOINT_STARGAZE="https://lcd.stargaze-1.quicskilver.zone"
+MAINNET_RPC_ENDPOINT_STARGAZE="https://rpc.stargaze-1.quicskilver.zone"
+MAINNET_LCD_ENDPOINT_REGEN="https://lcd.regen-1.quicskilver.zone"
+MAINNET_RPC_ENDPOINT_REGEN="https://rpc.regen-1.quicskilver.zone"
+MAINNET_LCD_ENDPOINT_SOMMELIER="https://lcd.sommelier-3.quicskilver.zone"
+MAINNET_RPC_ENDPOINT_SOMMELIER="https://rpc.sommelier-3.quicskilver.zone"
\ No newline at end of file
diff --git a/web-ui/bun.lockb b/web-ui/bun.lockb
index 2c4ddd2e8b2eeb5a4e64c1e7ea49dd6590132a1a..436599ac950022588e32dc4aebfb82b5fde4a76a 100755
GIT binary patch
delta 80405
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