From de5a5b71bb3dbd02d70c70fd4ba3fb1661c4d844 Mon Sep 17 00:00:00 2001
From: Noah Prince
Date: Fri, 3 May 2024 10:03:04 -0500
Subject: [PATCH 01/41] WIP: About to change to proxy assignment rename
---
README.md | 124 ++++++++++
next.config.mjs | 80 +++++--
package.json | 17 +-
src/app/[network]/proxies/[wallet]/page.tsx | 47 ++++
src/app/[network]/proxies/page.tsx | 28 +++
src/components/AssignProxyModal.tsx | 252 ++++++++++++++++++++
src/components/Header.tsx | 6 +-
src/components/Proposal.tsx | 10 +-
src/components/Proxies.tsx | 128 ++++++++++
src/components/ProxyButton.tsx | 42 ++++
src/components/ProxyProfile.tsx | 151 ++++++++++++
src/components/RevokeProxyButton.tsx | 49 ++++
src/components/RevokeProxyModal.tsx | 129 ++++++++++
src/components/SubNav.tsx | 101 ++++++++
src/components/VeTokensCallout.tsx | 2 +-
src/components/ViewPositionsButton.tsx | 23 --
src/components/VoteBreakdown.tsx | 10 +-
src/components/VoteHistory.tsx | 117 +++++++++
src/components/ui/dropdown-menu.tsx | 200 ++++++++++++++++
src/components/ui/toggle-group.tsx | 61 +++++
src/components/ui/toggle.tsx | 45 ++++
src/hooks/useMetaplexMetadata.ts | 131 ++++++++++
src/hooks/useNetwork.ts | 31 +++
src/lib/constants.ts | 9 +
src/lib/dateTools.ts | 70 ++++++
src/lib/utils.ts | 8 +
src/providers/GovernanceProvider.tsx | 5 +-
tailwind.config.ts | 1 +
yarn.lock | 246 ++++++++++++++-----
29 files changed, 1996 insertions(+), 127 deletions(-)
create mode 100644 src/app/[network]/proxies/[wallet]/page.tsx
create mode 100644 src/app/[network]/proxies/page.tsx
create mode 100644 src/components/AssignProxyModal.tsx
create mode 100644 src/components/Proxies.tsx
create mode 100644 src/components/ProxyButton.tsx
create mode 100644 src/components/ProxyProfile.tsx
create mode 100644 src/components/RevokeProxyButton.tsx
create mode 100644 src/components/RevokeProxyModal.tsx
create mode 100644 src/components/SubNav.tsx
delete mode 100644 src/components/ViewPositionsButton.tsx
create mode 100644 src/components/VoteHistory.tsx
create mode 100644 src/components/ui/dropdown-menu.tsx
create mode 100644 src/components/ui/toggle-group.tsx
create mode 100644 src/components/ui/toggle.tsx
create mode 100644 src/hooks/useMetaplexMetadata.ts
create mode 100644 src/hooks/useNetwork.ts
create mode 100644 src/lib/dateTools.ts
diff --git a/README.md b/README.md
index 3048acf..50905ac 100644
--- a/README.md
+++ b/README.md
@@ -9,3 +9,127 @@ A straw poll website that enables a simple straw-poll for Helium related initiat
* A block height deadline is set for the tally to be taken.
* Votes are tallied by the HNT voting "power" of the account. One HNT = 1 Vote.
* DCs sent to an outcome address will be flushed from the system after voting is complete.
+
+## Developing Locally (with proxies)
+
+### 1. Localnet from helium-program-library
+
+Clone helium-program-library. Run
+
+```
+anchor localnet
+```
+
+Then run
+
+```
+./scripts/bootstrap.sh
+```
+
+This will create a bunch of keypairs in packages/helium-admin-cli/keypairs. You will need to get the addresses of the HNT, IOT, and MOBILE tokens it created. Then update constants.js in `spl-utils`.
+
+### 2. Make sure modgov idls deployed
+
+Clone modular-governance.
+
+```
+./scripts/upgrade-idls.sh
+```
+
+### 3. Bootstrap the Helium DAO
+
+In this repo
+
+```
+./bin/helium-vote.ts bootstrap --name Helium --mint APqAVo5q9erS8GaXcbJuy3Gx4ikuSzXjzY4SnyppPUm1 --authority $(solana address)
+```
+
+### 4. Create a proposal
+
+```
+./bin/helium-vote.ts create-proposal --name HIP 110: Proxy Voting --proposalUri https://gist.githubusercontent.com/hiptron/4404ea1e78ed8c92ba5001df45740386/raw/88adb21a40475dd7b6673e816492357fb425c72a/HIP-110-HNT-Vote-Summary.md --orgName Helium
+```
+
+### 5. Run the account-postgres-sink for helium-vote-service
+
+Make sure you have a postgres running. I just have a script to run it via docker:
+
+```
+#!/bin/bash
+
+docker volume create -o size=10GB pgdata
+docker run --shm-size=1g -it --rm -p 5432:5432 --name postgres -e POSTGRES_PASSWORD=postgres -d -v pgdata:/var/lib/postgresql/data postgres:latest
+
+docker logs -f postgres
+```
+
+In helium-program-library, cd `packages/account-postgres-sink-service`
+
+Update the `.env` to the following:
+
+```
+SOLANA_URL=http://localhost:8899
+PGUSER=postgres
+PGPASSWORD=postgres
+USE_SUBSTREAMS=false
+PHOTON_URL=https://photon.komoot.io
+USE_KAFKA=false
+PROGRAM_ACCOUNT_CONFIGS=/path/to/helium-program-library/packages/account-postgres-sink-service/vote_service_example.json
+
+```
+
+Making sure to update the path to the `vote_service_example.json`
+
+Run
+
+```
+yarn dev
+```
+
+Then, navigate to `localhost:3000/refresh-accounts`
+
+Every time you make a change to proxies, you will need to re-hit-this endpoint.
+
+### 6. Run the helium-vote service.
+
+cd into helium-program-library/packages/helium-vote-service
+
+Update the .env to:
+
+```
+PGDATABASE=postgres
+PGUSER=postgres
+PGPASSWORD=postgres
+```
+
+Then:
+
+```
+yarn dev
+```
+
+### 7. Run helium-vote on port 3001
+
+In this repo,
+
+```
+env PORT=3001 yarn dev
+```
+
+Note that if you're using yalc, it's useful to just run:
+
+```
+yalc update && rm -rf .next/cache && env PORT=3001 yarn dev
+```
+
+
+### 8. Fund any wallets you're using for testing
+
+```
+solana transfer -u http://localhost:8899 exmrL4U6vk6VFoh3Q7fkrPbjpNLHPYFf1J8bqGypuiK 10 --allow-unfunded-recipient
+```
+
+Make sure to transfer some fake HNT to whatever wallet you plan to stake with.
+```
+spl-token transfer -u http://localhost:8899 100 --allow-unfunded-recipient --fund-recipient
+```
diff --git a/next.config.mjs b/next.config.mjs
index 3a456fa..f6b2ce7 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -15,6 +15,14 @@ const nextConfig = {
port: "",
pathname: "/**/**",
},
+ ...(process.env.NODE_ENV === "development" && [
+ {
+ protocol: "http",
+ hostname: "localhost",
+ port: "8081",
+ pathname: "/**/**",
+ },
+ ]),
],
},
logging: {
@@ -1272,122 +1280,146 @@ const nextConfig = {
},
{
source: "/147pXvGVcLKU76D7Hdi7iTTLvLw9qX8jXsCgNkKSHqo3rnfzkby",
- destination: "/legacy/147pXvGVcLKU76D7Hdi7iTTLvLw9qX8jXsCgNkKSHqo3rnfzkby",
+ destination:
+ "/legacy/147pXvGVcLKU76D7Hdi7iTTLvLw9qX8jXsCgNkKSHqo3rnfzkby",
permanent: true,
},
{
source: "/14rmHKjpZhsnrA2j24KiGzg8teA1zAMRhVGcWVUdmTAomTrJJQf",
- destination: "/legacy/14rmHKjpZhsnrA2j24KiGzg8teA1zAMRhVGcWVUdmTAomTrJJQf",
+ destination:
+ "/legacy/14rmHKjpZhsnrA2j24KiGzg8teA1zAMRhVGcWVUdmTAomTrJJQf",
permanent: true,
},
{
source: "/146pksPcH7C3Wz8hN5NxL544k1VVq6Z4W2iB14e1yJ1HFJ3Wtf3",
- destination: "/legacy/146pksPcH7C3Wz8hN5NxL544k1VVq6Z4W2iB14e1yJ1HFJ3Wtf3",
+ destination:
+ "/legacy/146pksPcH7C3Wz8hN5NxL544k1VVq6Z4W2iB14e1yJ1HFJ3Wtf3",
permanent: true,
},
{
source: "/144wSVHr4cuVSjxEC62X1bHuLsc5Hcpq6XBhfSY8BQwiyMborFZ",
- destination: "/legacy/144wSVHr4cuVSjxEC62X1bHuLsc5Hcpq6XBhfSY8BQwiyMborFZ",
+ destination:
+ "/legacy/144wSVHr4cuVSjxEC62X1bHuLsc5Hcpq6XBhfSY8BQwiyMborFZ",
permanent: true,
},
{
source: "/143vgVpLgC3LcXLZCyXYZiHCFcsH9UNz3vR8CL6SDxTLPL5tWtr",
- destination: "/legacy/143vgVpLgC3LcXLZCyXYZiHCFcsH9UNz3vR8CL6SDxTLPL5tWtr",
+ destination:
+ "/legacy/143vgVpLgC3LcXLZCyXYZiHCFcsH9UNz3vR8CL6SDxTLPL5tWtr",
permanent: true,
},
{
source: "/14jH67zhctwb3B5NmwiAjaXQuyF7jZMZCAnfyBYhSpRS3L22sQE",
- destination: "/legacy/14jH67zhctwb3B5NmwiAjaXQuyF7jZMZCAnfyBYhSpRS3L22sQE",
+ destination:
+ "/legacy/14jH67zhctwb3B5NmwiAjaXQuyF7jZMZCAnfyBYhSpRS3L22sQE",
permanent: true,
},
{
source: "/13Z3p82AX8H1EUQF74cP2qq7RmmhbKBAiGWWsyA7WLxQ9NENTVW",
- destination: "/legacy/13Z3p82AX8H1EUQF74cP2qq7RmmhbKBAiGWWsyA7WLxQ9NENTVW",
+ destination:
+ "/legacy/13Z3p82AX8H1EUQF74cP2qq7RmmhbKBAiGWWsyA7WLxQ9NENTVW",
permanent: true,
},
{
source: "/13Y79HnMVt4Epug2rbLYivMPvrcpC2wYUNbZK5Dga3tw3VBpBjk",
- destination: "/legacy/13Y79HnMVt4Epug2rbLYivMPvrcpC2wYUNbZK5Dga3tw3VBpBjk",
+ destination:
+ "/legacy/13Y79HnMVt4Epug2rbLYivMPvrcpC2wYUNbZK5Dga3tw3VBpBjk",
permanent: true,
},
{
source: "/13Y79HnMVt4Epug2rbLYivMPvrcpC2wYUNbZK5Dga3tw3VBpBjk",
- destination: "/legacy/13Y79HnMVt4Epug2rbLYivMPvrcpC2wYUNbZK5Dga3tw3VBpBjk",
+ destination:
+ "/legacy/13Y79HnMVt4Epug2rbLYivMPvrcpC2wYUNbZK5Dga3tw3VBpBjk",
permanent: true,
},
{
source: "/13KaGoC2ED8kEh2sXLZ7eGWrqDUMyFH5k48VQ3LLjU5QoQidMV4",
- destination: "/legacy/13KaGoC2ED8kEh2sXLZ7eGWrqDUMyFH5k48VQ3LLjU5QoQidMV4",
+ destination:
+ "/legacy/13KaGoC2ED8kEh2sXLZ7eGWrqDUMyFH5k48VQ3LLjU5QoQidMV4",
permanent: true,
},
{
source: "/13rA4AXq5ME5s9FEyZrE4BMjxiAF9W3kU2tnmUH8FkFGsV97jEp",
- destination: "/legacy/13rA4AXq5ME5s9FEyZrE4BMjxiAF9W3kU2tnmUH8FkFGsV97jEp",
+ destination:
+ "/legacy/13rA4AXq5ME5s9FEyZrE4BMjxiAF9W3kU2tnmUH8FkFGsV97jEp",
permanent: true,
},
{
source: "/14cXnMdXYcS7WKNh33dYUaPeo8bZmRFn5AoPA78AZtAgfFXu9jf",
- destination: "/legacy/14cXnMdXYcS7WKNh33dYUaPeo8bZmRFn5AoPA78AZtAgfFXu9jf",
+ destination:
+ "/legacy/14cXnMdXYcS7WKNh33dYUaPeo8bZmRFn5AoPA78AZtAgfFXu9jf",
permanent: true,
},
{
source: "/14KhDJUdvAXNVVP5m5cqEaLGNC859sXvpHtxWX9r999pZKC8xAs",
- destination: "/legacy/14KhDJUdvAXNVVP5m5cqEaLGNC859sXvpHtxWX9r999pZKC8xAs",
+ destination:
+ "/legacy/14KhDJUdvAXNVVP5m5cqEaLGNC859sXvpHtxWX9r999pZKC8xAs",
permanent: true,
},
{
source: "/13UrtNApGd3NbeP3NyyTejqNEPAv3NGxkGjvtwTVdaRs24NT7Wy",
- destination: "/legacy/13UrtNApGd3NbeP3NyyTejqNEPAv3NGxkGjvtwTVdaRs24NT7Wy",
+ destination:
+ "/legacy/13UrtNApGd3NbeP3NyyTejqNEPAv3NGxkGjvtwTVdaRs24NT7Wy",
permanent: true,
},
{
source: "/14Rjhhz1DXLVmSRdzappqWgD6rfgu6XYxmdaSCvWLyLH8ZWbciK",
- destination: "/legacy/14Rjhhz1DXLVmSRdzappqWgD6rfgu6XYxmdaSCvWLyLH8ZWbciK",
+ destination:
+ "/legacy/14Rjhhz1DXLVmSRdzappqWgD6rfgu6XYxmdaSCvWLyLH8ZWbciK",
permanent: true,
},
{
source: "/14me3X7jpEmn3eeFfnAkMvUoFU3cN6GAS3CDomqCikr7VQfHWrU",
- destination: "/legacy/14me3X7jpEmn3eeFfnAkMvUoFU3cN6GAS3CDomqCikr7VQfHWrU",
+ destination:
+ "/legacy/14me3X7jpEmn3eeFfnAkMvUoFU3cN6GAS3CDomqCikr7VQfHWrU",
permanent: true,
},
{
source: "/14hfi6Vs9YmwYLwVHKygqyEqwTERRrx5kfQkVoX1uqMTxiE5EgJ",
- destination: "/legacy/14hfi6Vs9YmwYLwVHKygqyEqwTERRrx5kfQkVoX1uqMTxiE5EgJ",
+ destination:
+ "/legacy/14hfi6Vs9YmwYLwVHKygqyEqwTERRrx5kfQkVoX1uqMTxiE5EgJ",
permanent: true,
},
{
source: "/14XDEkg1t398kvqvgxMMKH8qzVGNBb1mgHhTjNmc5KkC3XJxu8p",
- destination: "/legacy/14XDEkg1t398kvqvgxMMKH8qzVGNBb1mgHhTjNmc5KkC3XJxu8p",
+ destination:
+ "/legacy/14XDEkg1t398kvqvgxMMKH8qzVGNBb1mgHhTjNmc5KkC3XJxu8p",
permanent: true,
},
{
source: "/14rifUhocpzdwsrWaG5PDbdREDkzyesKe1hXuWzibv8h9DdqKLe",
- destination: "/legacy/14rifUhocpzdwsrWaG5PDbdREDkzyesKe1hXuWzibv8h9DdqKLe",
+ destination:
+ "/legacy/14rifUhocpzdwsrWaG5PDbdREDkzyesKe1hXuWzibv8h9DdqKLe",
permanent: true,
},
{
source: "/13NyqFtVKsifrh6HQ7DjSBKXRDi7qLHDoATHogoSgvBh56oZJv8",
- destination: "/legacy/13NyqFtVKsifrh6HQ7DjSBKXRDi7qLHDoATHogoSgvBh56oZJv8",
+ destination:
+ "/legacy/13NyqFtVKsifrh6HQ7DjSBKXRDi7qLHDoATHogoSgvBh56oZJv8",
permanent: true,
},
{
source: "/14MnuexopPfDg3bmq8JdCm7LMDkUBoqhqanD9QzLrUURLZxFHBx",
- destination: "/legacy/14MnuexopPfDg3bmq8JdCm7LMDkUBoqhqanD9QzLrUURLZxFHBx",
+ destination:
+ "/legacy/14MnuexopPfDg3bmq8JdCm7LMDkUBoqhqanD9QzLrUURLZxFHBx",
permanent: true,
},
{
source: "/13wCuq7XGnc4xgxPAc9n9ragKsRfmH9t9jB3c1smfKPZWSikZkd",
- destination: "/legacy/13wCuq7XGnc4xgxPAc9n9ragKsRfmH9t9jB3c1smfKPZWSikZkd",
+ destination:
+ "/legacy/13wCuq7XGnc4xgxPAc9n9ragKsRfmH9t9jB3c1smfKPZWSikZkd",
permanent: true,
},
{
source: "/14iwaexUYUe5taFgb5hx2BZw74z3TSyonRLYyZU1RbddV4bJest",
- destination: "/legacy/14iwaexUYUe5taFgb5hx2BZw74z3TSyonRLYyZU1RbddV4bJest",
+ destination:
+ "/legacy/14iwaexUYUe5taFgb5hx2BZw74z3TSyonRLYyZU1RbddV4bJest",
permanent: true,
},
{
source: "/13F5AWLhxwTjhDMnZ6ww4oJWgRkBoW9ji4JnDzwQXjsQhiT2kcX",
- destination: "/legacy/13F5AWLhxwTjhDMnZ6ww4oJWgRkBoW9ji4JnDzwQXjsQhiT2kcX",
+ destination:
+ "/legacy/13F5AWLhxwTjhDMnZ6ww4oJWgRkBoW9ji4JnDzwQXjsQhiT2kcX",
permanent: true,
},
];
diff --git a/package.json b/package.json
index ad60053..7aae9f6 100644
--- a/package.json
+++ b/package.json
@@ -16,20 +16,23 @@
"@helium/modular-governance-hooks": "^0.0.8",
"@helium/modular-governance-idls": "^0.0.8-next",
"@helium/organization-sdk": "^0.0.8",
- "@helium/spl-utils": "^0.7.11",
+ "@helium/spl-utils": "file:.yalc/@helium/spl-utils",
"@helium/state-controller-sdk": "^0.0.8",
- "@helium/voter-stake-registry-hooks": "^0.7.11",
- "@helium/voter-stake-registry-sdk": "^0.7.11",
+ "@helium/voter-stake-registry-hooks": "file:.yalc/@helium/voter-stake-registry-hooks",
+ "@helium/voter-stake-registry-sdk": "file:.yalc/@helium/voter-stake-registry-sdk",
"@hookform/resolvers": "^3.3.4",
"@metaplex-foundation/mpl-token-metadata": "2.10.0",
"@project-serum/anchor": "^0.26.0",
"@radix-ui/react-avatar": "^1.0.4",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5",
+ "@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-hover-card": "^1.0.7",
"@radix-ui/react-progress": "^1.0.3",
"@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slot": "^1.0.2",
+ "@radix-ui/react-toggle": "^1.0.3",
+ "@radix-ui/react-toggle-group": "^1.0.4",
"@solana/spl-token": "^0.4.0",
"@solana/wallet-adapter-base": "^0.9.23",
"@solana/wallet-adapter-react": "^0.15.35",
@@ -53,6 +56,7 @@
"react-countdown": "^2.3.5",
"react-dom": "^18",
"react-icons": "^5.0.1",
+ "react-infinite-scroll-component": "^6.1.0",
"react-markdown": "^9.0.1",
"sonner": "^1.4.0",
"tailwind-merge": "^2.2.1",
@@ -65,10 +69,11 @@
"@helium/account-fetch-cache": "^0.7.11",
"@helium/account-fetch-cache-hooks": "^0.7.11",
"@helium/helium-react-hooks": "^0.7.11",
- "@helium/voter-stake-registry-hooks": "^0.7.11",
- "@helium/spl-utils": "^0.7.11",
+ "@helium/voter-stake-registry-hooks": "file:.yalc/@helium/voter-stake-registry-hooks",
+ "@helium/spl-utils": "file:.yalc/@helium/spl-utils",
"@helium/modular-governance-hooks": "^0.0.8",
- "@solana/wallet-adapter-react": "^0.15.35"
+ "@solana/wallet-adapter-react": "^0.15.35",
+ "@helium/voter-stake-registry-sdk": "file:.yalc/@helium/voter-stake-registry-sdk"
},
"devDependencies": {
"@tailwindcss/typography": "^0.5.10",
diff --git a/src/app/[network]/proxies/[wallet]/page.tsx b/src/app/[network]/proxies/[wallet]/page.tsx
new file mode 100644
index 0000000..d2774a8
--- /dev/null
+++ b/src/app/[network]/proxies/[wallet]/page.tsx
@@ -0,0 +1,47 @@
+import { Header } from "@/components/Header";
+import { ProxyProfile } from "@/components/ProxyProfile";
+import { WalletBoundary } from "@/components/WalletBoundary";
+import { networksToMint } from "@/lib/constants";
+import { VoteService, getRegistrarKey } from "@helium/voter-stake-registry-sdk";
+import { PublicKey } from "@solana/web3.js";
+
+export default async function ProxyPage({
+ params: { wallet, network },
+}: {
+ params: { wallet: string; network: string };
+}) {
+ const mint = networksToMint[network];
+ const { proxy, detail, image } = await getData(
+ new PublicKey(wallet as string),
+ mint
+ );
+
+ return (
+ <>
+
+
+ >
+ );
+}
+
+export async function getData(wallet: PublicKey, mint: PublicKey) {
+ const registrar = getRegistrarKey(mint);
+
+ const voteService = new VoteService({
+ baseURL: process.env.NEXT_PUBLIC_HELIUM_VOTE_URI,
+ registrar,
+ });
+ const proxy = await voteService.getProxy(wallet.toBase58());
+ let detail = null;
+ if (proxy.detail) {
+ const res = await fetch(proxy.detail);
+ detail = await res.text();
+ }
+
+ return {
+ proxy,
+ detail,
+ image: proxy.image ? proxy.image : null,
+ // revalidate: 10 * 60,
+ };
+}
diff --git a/src/app/[network]/proxies/page.tsx b/src/app/[network]/proxies/page.tsx
new file mode 100644
index 0000000..e3899b4
--- /dev/null
+++ b/src/app/[network]/proxies/page.tsx
@@ -0,0 +1,28 @@
+import { Header } from "@/components/Header";
+import { Proxies } from "@/components/Proxies";
+import { formMetaTags } from "@/lib/utils";
+
+export interface VotersPageParams {
+ params: {
+ network: string;
+ };
+ searchParams: Record | null | undefined;
+}
+
+export const generateMetadata = async ({ params }: VotersPageParams) => {
+ const { network } = params;
+
+ return formMetaTags({
+ title: `${network.toUpperCase()} Positions`,
+ url: `https://heliumvote.com/${network}/voters`,
+ });
+};
+
+export default async function PositionsPage() {
+ return (
+ <>
+
+
+ >
+ );
+}
diff --git a/src/components/AssignProxyModal.tsx b/src/components/AssignProxyModal.tsx
new file mode 100644
index 0000000..80e4b89
--- /dev/null
+++ b/src/components/AssignProxyModal.tsx
@@ -0,0 +1,252 @@
+import { useMint } from "@helium/helium-react-hooks";
+import {
+ PositionWithMeta,
+ useHeliumVsrState,
+} from "@helium/voter-stake-registry-hooks";
+import { PublicKey } from "@solana/web3.js";
+import BN from "bn.js";
+import React, { useEffect, useMemo, useState } from "react";
+import { getMinDurationFmt, getTimeLeftFromNowFmt } from "@/lib/dateTools";
+import { toast } from "sonner";
+import { Dialog, DialogContent, DialogTrigger } from "./ui/dialog";
+import { Loader2 } from "lucide-react";
+import { useMetaplexMetadata } from "@/hooks/useMetaplexMetadata";
+import { humanReadable } from "@/lib/utils";
+import { Button } from "./ui/button";
+
+interface AssignProxyModalProps {
+ onSubmit: (args: {
+ positions: PositionWithMeta[];
+ recipient: PublicKey;
+ expirationTime: BN;
+ }) => Promise;
+ wallet?: PublicKey;
+}
+
+export const AssignProxyModal: React.FC<
+ React.PropsWithChildren
+> = ({ onSubmit, wallet, children }) => {
+ const [open, setOpen] = useState(false);
+
+ const { loading, positions, mint } = useHeliumVsrState();
+ const [selectedPositions, setSelectedPositions] = useState>(
+ new Set()
+ );
+ const [isSubmitting, setIsSubmitting] = useState(false);
+ const unproxiedPositions = useMemo(
+ () =>
+ positions?.filter(
+ (p) => !p.proxy || p.proxy.nextOwner.equals(PublicKey.default)
+ ) || [],
+ [positions]
+ );
+ const today = new Date();
+ const augustFirst = Date.UTC(
+ today.getMonth() > 7 ? today.getFullYear() + 1 : today.getFullYear(),
+ 7,
+ 1
+ );
+ const maxDate = Math.min(
+ augustFirst - 1000,
+ ...unproxiedPositions
+ .filter((p) => selectedPositions.has(p.pubkey.toBase58()) && p.proxy)
+ // @ts-ignore
+ .map((p) => p.proxy.expirationTime.toNumber() * 1000)
+ );
+ const maxDays = Math.floor(
+ (maxDate - today.getTime()) / (1000 * 60 * 60 * 24)
+ );
+ const [selectedDays, setSelectedDays] = useState(maxDays);
+ const [recipient, setRecipient] = useState(wallet?.toBase58() || "");
+ const expirationTime = useMemo(
+ () =>
+ selectedDays === maxDays
+ ? maxDate.valueOf() / 1000
+ : new Date().valueOf() / 1000 + selectedDays * (24 * 60 * 60),
+ [selectedDays, maxDays, maxDate]
+ );
+ useEffect(() => {
+ if (selectedDays > maxDays) {
+ setSelectedDays(maxDays);
+ }
+ }, [maxDays]);
+
+ const changeRecipient = (e: any) => {
+ setRecipient(e.target.value);
+ };
+
+ const handleOnSubmit = async () => {
+ try {
+ const positionsByKey = positions?.reduce((acc, p) => {
+ acc[p.pubkey.toString()] = p;
+ return acc;
+ }, {} as Record);
+ setIsSubmitting(true);
+
+ if (positionsByKey) {
+ await onSubmit({
+ positions: Array.from(selectedPositions).map(
+ (p) => positionsByKey[p]
+ ),
+ recipient: new PublicKey(recipient),
+ expirationTime: new BN(
+ Math.min(expirationTime, maxDate.valueOf() / 1000)
+ ),
+ });
+ setOpen(false);
+ }
+ } catch (e: any) {
+ setIsSubmitting(false);
+ toast(e.message || "Unable to assign proxy");
+ }
+ };
+ const handleOpenChange = () => {
+ setIsSubmitting(false);
+ setOpen(!open);
+ };
+
+ return (
+
+ );
+};
+
+export const PositionItem = ({
+ position,
+ isSelected,
+ onClick,
+ mint,
+}: {
+ position: PositionWithMeta;
+ isSelected: boolean;
+ mint: PublicKey;
+ onClick: () => void;
+}) => {
+ const { info: mintAcc } = useMint(mint);
+ const { symbol } = useMetaplexMetadata(mint);
+ const { lockup } = position;
+ const lockupKind = Object.keys(lockup.kind)[0] as string;
+ const isConstant = lockupKind === "constant";
+ const lockedTokens =
+ mintAcc && humanReadable(position.amountDepositedNative, mintAcc.decimals);
+ const lockupTime = isConstant
+ ? getMinDurationFmt(position.lockup.startTs, position.lockup.endTs)
+ : getTimeLeftFromNowFmt(position.lockup.endTs);
+ const lockupLabel = isConstant ? "duration" : "time left";
+ const fullLabel = `${lockedTokens} ${symbol} locked with ${lockupTime} ${lockupLabel}`;
+
+ return (
+
+ {fullLabel}
+
+ );
+};
diff --git a/src/components/Header.tsx b/src/components/Header.tsx
index 0ae01b0..1bacb75 100644
--- a/src/components/Header.tsx
+++ b/src/components/Header.tsx
@@ -1,11 +1,11 @@
import Image from "next/image";
import Link from "next/link";
-import React, { FC } from "react";
+import { FC } from "react";
import { ContentSection } from "./ContentSection";
import { CreatePositionButton } from "./CreatePositionButton";
import { NetworkTabs } from "./NetworkTabs";
+import { SubNav } from "./SubNav";
import { VeTokensCallout } from "./VeTokensCallout";
-import { ViewPositionsButton } from "./ViewPositionsButton";
import { WalletConnectButton } from "./WalletConnectButton";
export const Header: FC<{
@@ -82,7 +82,7 @@ export const Header: FC<{
) : (
-
+
)}
diff --git a/src/components/Proposal.tsx b/src/components/Proposal.tsx
index 55d47f8..97d0bf3 100644
--- a/src/components/Proposal.tsx
+++ b/src/components/Proposal.tsx
@@ -197,7 +197,13 @@ export const Proposal: FC<{
const { connected, connecting } = useWallet();
const markdownRef = useRef(null);
const [markdownExpanded, setMarkdownExpanded] = useState(false);
- const { loading: loadingGov, amountLocked, network } = useGovernance();
+ const {
+ loading: loadingGov,
+ amountLocked,
+ amountProxyLocked,
+ network,
+ } = useGovernance();
+ const totalLocked = amountLocked?.add(amountProxyLocked || new BN(0));
const pKey = useMemo(() => new PublicKey(proposalKey), [proposalKey]);
const { loading: loadingProposal, info: proposal } = useProposal(pKey);
const { loading: loadingVote, voteWeights } = useVote(pKey);
@@ -254,7 +260,7 @@ export const Proposal: FC<{
[connecting, loadingGov, loadingProposal, proposal]
);
const timeExpired = endTs && endTs.toNumber() <= Date.now().valueOf() / 1000;
- const noVotingPower = !isLoading && (!amountLocked || amountLocked.isZero());
+ const noVotingPower = !isLoading && (!totalLocked || totalLocked.isZero());
const isActive = derivedState === "active";
const isCancelled = derivedState === "cancelled";
const isFailed = derivedState === "failed";
diff --git a/src/components/Proxies.tsx b/src/components/Proxies.tsx
new file mode 100644
index 0000000..6a05c43
--- /dev/null
+++ b/src/components/Proxies.tsx
@@ -0,0 +1,128 @@
+"use client";
+
+import { useHeliumVsrState } from "@helium/voter-stake-registry-hooks";
+import InfiniteScroll from "react-infinite-scroll-component";
+import { useEffect, useState } from "react";
+import { useMint } from "@helium/helium-react-hooks";
+import BN from "bn.js";
+import Link from "next/link";
+import { useNetwork } from "@/hooks/useNetwork";
+import { ellipsisMiddle, humanReadable } from "@/lib/utils";
+import { EnhancedProxy } from "@helium/voter-stake-registry-sdk";
+import { ContentSection } from "./ContentSection";
+import { Card } from "./ui/card";
+import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar";
+import { useAsyncCallback } from "react-async-hook";
+import { usePathname } from "next/navigation";
+
+function CardDetail({ title, value }: { title: string; value: string }) {
+ return (
+
+
+ {title}
+
+ {value}
+
+ );
+}
+
+export function Proxies() {
+ const { voteService, mint } = useHeliumVsrState();
+ const { info: mintAcc } = useMint(mint);
+ const decimals = mintAcc?.decimals;
+ const [proxies, setProxies] = useState([]);
+ const [hasMore, setHasMore] = useState(false);
+ const [page, setPage] = useState(1);
+ const path = usePathname();
+
+ const { execute: fetchMoreData, loading } = useAsyncCallback(async () => {
+ if (voteService) {
+ const newProxies = await voteService.getProxies({
+ page: page,
+ limit: 100,
+ });
+ setPage((p) => p + 1);
+ if (newProxies.length == 100) {
+ setHasMore(true);
+ }
+ setProxies((prevProxies) => [...prevProxies, ...newProxies]);
+ }
+ });
+
+ useEffect(() => {
+ if (voteService) {
+ setProxies([]);
+ setPage(1);
+ fetchMoreData();
+ }
+ }, [voteService?.registrar.toBase58()]);
+
+ return (
+
+
+ Browse Proxies
+ Loading...}
+ endMessage={
+
+ No More Proxies
+
+ }
+ >
+
+ {proxies.map((proxy, index) => (
+
+
+
+ Assigned {proxy.numAssignments} positions
+
+
+
+
+
+ {proxy.name}
+
+
+
{proxy.name}
+ {ellipsisMiddle(proxy.wallet)}
+
+
+
+
+
+
+
+
+
+
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/src/components/ProxyButton.tsx b/src/components/ProxyButton.tsx
new file mode 100644
index 0000000..e602cab
--- /dev/null
+++ b/src/components/ProxyButton.tsx
@@ -0,0 +1,42 @@
+import React, { useMemo } from "react";
+import { useWallet } from "@solana/wallet-adapter-react";
+import { useHeliumVsrState } from "@helium/voter-stake-registry-hooks";
+import { PublicKey } from "@solana/web3.js";
+import { Button } from "./ui/button";
+import { Loader2 } from "lucide-react";
+
+export const ProxyButton: React.FC<{
+ className?: string;
+ onClick: () => void;
+ isLoading?: boolean;
+}> = ({ className = "", onClick, isLoading = false }) => {
+ const { connected } = useWallet();
+ const { loading, positions } = useHeliumVsrState();
+
+ const unproxiedPositions = useMemo(
+ () =>
+ positions?.filter(
+ (p) => !p.proxy || p.proxy.nextOwner.equals(PublicKey.default)
+ ),
+ [positions]
+ );
+
+ const tooltipContent = !connected
+ ? "Connect your wallet to claim"
+ : !unproxiedPositions?.length
+ ? "You don't have any positions available to proxy."
+ : "";
+
+ return (
+
+ );
+};
diff --git a/src/components/ProxyProfile.tsx b/src/components/ProxyProfile.tsx
new file mode 100644
index 0000000..2779511
--- /dev/null
+++ b/src/components/ProxyProfile.tsx
@@ -0,0 +1,151 @@
+"use client";
+
+import { ellipsisMiddle, humanReadable } from "@/lib/utils";
+import { useMint } from "@helium/helium-react-hooks";
+import {
+ useAssignProxies,
+ useHeliumVsrState,
+ useProxiedTo,
+ useUnassignProxies,
+} from "@helium/voter-stake-registry-hooks";
+import { EnhancedProxy, WithRank } from "@helium/voter-stake-registry-sdk";
+import { PublicKey } from "@solana/web3.js";
+import BN from "bn.js";
+import Image from "next/image";
+import { useMemo, useState } from "react";
+import ReactMarkdown from "react-markdown";
+import { AssignProxyModal } from "./AssignProxyModal";
+import { ProxyButton } from "./ProxyButton";
+import { RevokeProxyButton } from "./RevokeProxyButton";
+import { RevokeProxyModal } from "./RevokeProxyModal";
+import VoteHistory from "./VoteHistory";
+import { ContentSection } from "./ContentSection";
+import { Card, CardContent, CardHeader } from "./ui/card";
+import { FaArrowLeft } from "react-icons/fa6";
+import Link from "next/link";
+import { useNetwork } from "@/hooks/useNetwork";
+import { useGovernance } from "@/providers/GovernanceProvider";
+
+export function ProxyProfile({
+ proxy,
+ detail,
+ image,
+}: {
+ proxy: EnhancedProxy & WithRank;
+ detail: string;
+ image: string;
+}) {
+ const { mint } = useHeliumVsrState();
+ const { info: mintAcc } = useMint(mint);
+ const decimals = mintAcc?.decimals;
+ const [revokeProxyModalVisible, setRevokeProxyModalVisible] = useState(false);
+ const handleSetRevokeProxy = () => {
+ setRevokeProxyModalVisible(true);
+ };
+ const { assignProxies } = useAssignProxies();
+ const { unassignProxies } = useUnassignProxies();
+ const wallet = useMemo(() => new PublicKey(proxy.wallet), [proxy.wallet]);
+ const { votingPower } = useProxiedTo(wallet);
+ const { network } = useGovernance();
+
+ return (
+
+
+
+
+
+ Back to Proxies
+
+
+
+
+
+
{proxy.name}
+ {ellipsisMiddle(proxy.wallet)}
+
+
+
+
+
+ Current Rank
+
+ #{proxy.rank} of {proxy.numProxies}
+
+
+
+ Last Voted
+ {proxy.lastVotedAt?.toDateString() || "Never"}
+
+
+
+
+
+
+ {votingPower && (
+
+ My Delegations:
+ {humanReadable(votingPower, decimals)}
+
+ )}
+
+
+
+ {}}
+ isLoading={false}
+ />
+
+ {revokeProxyModalVisible && (
+ setRevokeProxyModalVisible(false)}
+ onSubmit={unassignProxies}
+ wallet={wallet}
+ />
+ )}
+
+
+ Wallet:
+ {proxy.wallet}
+
+
+ Num Delegations:
+ {proxy.numDelegations}
+
+
+ Delegated Tokens:
+
+ {proxy.delegatedVeTokens
+ ? humanReadable(new BN(proxy.delegatedVeTokens), decimals)
+ : "0"}
+
+
+
+ Percent:
+ {Number(proxy.percent).toFixed(2)}
+
+
+
+ {detail}
+
+
+
+
+
+ );
+}
diff --git a/src/components/RevokeProxyButton.tsx b/src/components/RevokeProxyButton.tsx
new file mode 100644
index 0000000..e628dd2
--- /dev/null
+++ b/src/components/RevokeProxyButton.tsx
@@ -0,0 +1,49 @@
+import React, { useMemo } from "react";
+import { useWallet } from "@solana/wallet-adapter-react";
+import { useHeliumVsrState } from "@helium/voter-stake-registry-hooks";
+import { PublicKey } from "@solana/web3.js";
+import { Button } from "./ui/button";
+import { Loader2 } from "lucide-react";
+
+export const RevokeProxyButton: React.FC<{
+ className?: string;
+ onClick: () => void;
+ isLoading?: boolean;
+ wallet?: PublicKey;
+}> = ({ wallet, className = "", onClick, isLoading = false }) => {
+ const { connected } = useWallet();
+ const { loading, positions } = useHeliumVsrState();
+
+ const proxiedPositions = useMemo(
+ () =>
+ positions?.filter(
+ (p) =>
+ p.proxy &&
+ !p.proxy.nextOwner.equals(PublicKey.default) &&
+ (!wallet || p.proxy.nextOwner.equals(wallet))
+ ),
+ [positions]
+ );
+
+ const tooltipContent = !connected
+ ? "Connect your wallet to claim"
+ : !proxiedPositions?.length
+ ? "You don't have any positions available to revoke proxy."
+ : "";
+
+ const disabled = !connected || loading || !proxiedPositions?.length;
+
+ return (
+
+ );
+};
diff --git a/src/components/RevokeProxyModal.tsx b/src/components/RevokeProxyModal.tsx
new file mode 100644
index 0000000..6871ff0
--- /dev/null
+++ b/src/components/RevokeProxyModal.tsx
@@ -0,0 +1,129 @@
+import {
+ PositionWithMeta,
+ useHeliumVsrState,
+} from "@helium/voter-stake-registry-hooks";
+import { DialogContent } from "@radix-ui/react-dialog";
+import { PublicKey } from "@solana/web3.js";
+import { Loader2 } from "lucide-react";
+import React, { useMemo, useState } from "react";
+import { toast } from "sonner";
+import { PositionItem } from "./AssignProxyModal";
+import { Button } from "./ui/button";
+import { Dialog } from "./ui/dialog";
+
+interface RevokeProxyModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+ onSubmit: (args: { positions: PositionWithMeta[] }) => Promise;
+ wallet?: PublicKey;
+}
+
+export const RevokeProxyModal: React.FC = ({
+ isOpen,
+ onClose,
+ onSubmit,
+ wallet,
+}) => {
+ const { loading, positions, mint } = useHeliumVsrState();
+ const [selectedPositions, setSelectedPositions] = useState>(
+ new Set()
+ );
+ const [isSubmitting, setIsSubmitting] = useState(false);
+ const proxiedPositions = useMemo(
+ () =>
+ positions?.filter(
+ (p) =>
+ p.proxy &&
+ !p.proxy.nextOwner.equals(PublicKey.default) &&
+ (!wallet || p.proxy.nextOwner.equals(wallet))
+ ),
+ [positions, wallet]
+ );
+
+ const handleOnSubmit = async () => {
+ try {
+ const positionsByKey = positions?.reduce((acc, p) => {
+ acc[p.pubkey.toString()] = p;
+ return acc;
+ }, {} as Record);
+ setIsSubmitting(true);
+ if (positionsByKey) {
+ await onSubmit({
+ positions: Array.from(selectedPositions).map(
+ (p) => positionsByKey[p]
+ ),
+ });
+ }
+
+ onClose();
+ } catch (e: any) {
+ setIsSubmitting(false);
+ toast(e.message || "Unable to Revoke proxy")
+ }
+ };
+
+ return (
+
+ );
+};
diff --git a/src/components/SubNav.tsx b/src/components/SubNav.tsx
new file mode 100644
index 0000000..c19dbbf
--- /dev/null
+++ b/src/components/SubNav.tsx
@@ -0,0 +1,101 @@
+"use client";
+
+import React from "react";
+import { ToggleGroup, ToggleGroupItem } from "./ui/toggle-group";
+import Link from "next/link";
+import { usePathname } from "next/navigation";
+import { FaBolt } from "react-icons/fa6";
+import { IoPerson } from "react-icons/io5";
+import { LuScrollText } from "react-icons/lu";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuTrigger,
+} from "./ui/dropdown-menu";
+import { Button } from "./ui/button";
+import { DropdownMenuLabel } from "@radix-ui/react-dropdown-menu";
+import { IconType } from "react-icons";
+
+const icons: { [key: string]: IconType } = {
+ proposals: LuScrollText,
+ proxies: IoPerson,
+ positions: FaBolt,
+};
+
+export const SubNav: React.FC = () => {
+ const path = usePathname();
+ const basePath = path.split("/").slice(0, 2).join("/");
+ const currentPath = path.split("/")[2] || "proposals";
+ const Icon = icons[currentPath];
+
+ return (
+
+
+
+
+
+
+ Proposals
+
+
+
+
+
+ Proxies
+
+
+
+
+
+ Positions
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Proposals
+
+
+
+
+
+ Proxies
+
+
+
+
+
+ Positions
+
+
+
+
+
+
+ );
+};
diff --git a/src/components/VeTokensCallout.tsx b/src/components/VeTokensCallout.tsx
index 1a7b80b..ac413d9 100644
--- a/src/components/VeTokensCallout.tsx
+++ b/src/components/VeTokensCallout.tsx
@@ -11,10 +11,10 @@ import { HNT_MINT, IOT_MINT, MOBILE_MINT, toNumber } from "@helium/spl-utils";
import {
calcPositionVotingPower,
getPositionKeys,
- getRegistrarKey,
usePositions,
useRegistrar,
} from "@helium/voter-stake-registry-hooks";
+import { getRegistrarKey } from "@helium/voter-stake-registry-sdk";
import { PublicKey } from "@solana/web3.js";
import BN from "bn.js";
import Image from "next/image";
diff --git a/src/components/ViewPositionsButton.tsx b/src/components/ViewPositionsButton.tsx
deleted file mode 100644
index a87da2c..0000000
--- a/src/components/ViewPositionsButton.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-"use client";
-
-import { FaBolt } from "react-icons/fa6";
-import Link from "next/link";
-import React, { FC } from "react";
-import { Button } from "./ui/button";
-import { usePathname } from "next/navigation";
-import classNames from "classnames";
-
-export const ViewPositionsButton: FC<{ className: string }> = ({
- className = "",
-}) => {
- const path = usePathname();
-
- return (
-
-
-
- );
-};
diff --git a/src/components/VoteBreakdown.tsx b/src/components/VoteBreakdown.tsx
index 36a3f5c..45a74c8 100644
--- a/src/components/VoteBreakdown.tsx
+++ b/src/components/VoteBreakdown.tsx
@@ -1,5 +1,5 @@
import React, { FC, useMemo, useState } from "react";
-import { humanReadable } from "@/lib/utils";
+import { ellipsisMiddle, humanReadable } from "@/lib/utils";
import { PublicKey } from "@solana/web3.js";
import {
useProposal,
@@ -24,14 +24,6 @@ import { FaChevronDown } from "react-icons/fa6";
import classNames from "classnames";
import Link from "next/link";
-const ellipsisMiddle = (wallet: string): string => {
- const length = wallet.length;
- const start = wallet.slice(0, 5);
- const end = wallet.slice(length - 5, length);
- const middle = "...";
- return start + middle + end;
-};
-
export const VoteBreakdown: FC<{
proposalKey: PublicKey;
}> = ({ proposalKey }) => {
diff --git a/src/components/VoteHistory.tsx b/src/components/VoteHistory.tsx
new file mode 100644
index 0000000..4bf20e4
--- /dev/null
+++ b/src/components/VoteHistory.tsx
@@ -0,0 +1,117 @@
+import { humanReadable } from "@/lib/utils";
+import { useMint } from "@helium/helium-react-hooks";
+import { useHeliumVsrState } from "@helium/voter-stake-registry-hooks";
+import { ProposalWithVotes } from "@helium/voter-stake-registry-sdk";
+import { PublicKey } from "@solana/web3.js";
+import BN from "bn.js";
+import { useEffect, useState } from "react";
+import InfiniteScroll from "react-infinite-scroll-component";
+
+export default function VoteHistory({ wallet }: { wallet: PublicKey }) {
+ const { voteService, mint } = useHeliumVsrState();
+ const { info: mintAcc } = useMint(mint);
+ const decimals = mintAcc?.decimals;
+ const [voteHistories, setVoteHistory] = useState([]);
+ const [hasMore, setHasMore] = useState(true);
+ const [page, setPage] = useState(1);
+
+ const fetchMoreData = async () => {
+ if (voteService) {
+ const newVoteHistory = await voteService.getVotesForWallet({
+ wallet: wallet,
+ page: page,
+ limit: 10,
+ });
+ setPage((p) => p + 1);
+ if (newVoteHistory.length < 10) {
+ setHasMore(false);
+ }
+ setVoteHistory((prevVoteHistory) => [
+ ...prevVoteHistory,
+ ...newVoteHistory,
+ ]);
+ }
+ };
+
+ useEffect(() => {
+ if (voteService) {
+ setPage(1);
+ setHasMore(true);
+ fetchMoreData();
+ }
+ }, [voteService]);
+
+ return (
+
+
Loading...}
+ endMessage={
+
+ End of vote history
+
+ }
+ >
+
+
+
+
+ Proposal
+ |
+
+ State
+ |
+
+ Weight
+ |
+
+ Choices
+ |
+
+ Percent
+ |
+
+
+
+ {voteHistories.map((voteHistory, index) => {
+ const totalWeight = voteHistory.choices.reduce((acc, c) => {
+ return acc.add(new BN(c.weight));
+ }, new BN(0));
+ console.log(totalWeight.toNumber());
+ const percent = voteHistory.votes[0]
+ ? (100 * Number(voteHistory.votes[0].weight)) /
+ totalWeight.toNumber()
+ : 0;
+
+ return (
+
+
+ {voteHistory.name}
+ |
+
+ {Object.keys(voteHistory.state)[0]}
+ |
+
+ {voteHistory.votes[0]?.weight &&
+ humanReadable(
+ new BN(voteHistory.votes[0].weight),
+ decimals
+ )}
+ |
+
+ {voteHistory.votes.map((v) => v.choiceName).join(", ")}
+ |
+
+ {percent.toFixed(2)}
+ |
+
+ );
+ })}
+
+
+
+
+ );
+}
diff --git a/src/components/ui/dropdown-menu.tsx b/src/components/ui/dropdown-menu.tsx
new file mode 100644
index 0000000..f69a0d6
--- /dev/null
+++ b/src/components/ui/dropdown-menu.tsx
@@ -0,0 +1,200 @@
+"use client"
+
+import * as React from "react"
+import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
+import { Check, ChevronRight, Circle } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+
+const DropdownMenu = DropdownMenuPrimitive.Root
+
+const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
+
+const DropdownMenuGroup = DropdownMenuPrimitive.Group
+
+const DropdownMenuPortal = DropdownMenuPrimitive.Portal
+
+const DropdownMenuSub = DropdownMenuPrimitive.Sub
+
+const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
+
+const DropdownMenuSubTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef & {
+ inset?: boolean
+ }
+>(({ className, inset, children, ...props }, ref) => (
+
+ {children}
+
+
+))
+DropdownMenuSubTrigger.displayName =
+ DropdownMenuPrimitive.SubTrigger.displayName
+
+const DropdownMenuSubContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+DropdownMenuSubContent.displayName =
+ DropdownMenuPrimitive.SubContent.displayName
+
+const DropdownMenuContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, sideOffset = 4, ...props }, ref) => (
+
+
+
+))
+DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
+
+const DropdownMenuItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef & {
+ inset?: boolean
+ }
+>(({ className, inset, ...props }, ref) => (
+
+))
+DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
+
+const DropdownMenuCheckboxItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, checked, ...props }, ref) => (
+
+
+
+
+
+
+ {children}
+
+))
+DropdownMenuCheckboxItem.displayName =
+ DropdownMenuPrimitive.CheckboxItem.displayName
+
+const DropdownMenuRadioItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+
+
+
+ {children}
+
+))
+DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
+
+const DropdownMenuLabel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef & {
+ inset?: boolean
+ }
+>(({ className, inset, ...props }, ref) => (
+
+))
+DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
+
+const DropdownMenuSeparator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
+
+const DropdownMenuShortcut = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => {
+ return (
+
+ )
+}
+DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
+
+export {
+ DropdownMenu,
+ DropdownMenuTrigger,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuCheckboxItem,
+ DropdownMenuRadioItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuShortcut,
+ DropdownMenuGroup,
+ DropdownMenuPortal,
+ DropdownMenuSub,
+ DropdownMenuSubContent,
+ DropdownMenuSubTrigger,
+ DropdownMenuRadioGroup,
+}
diff --git a/src/components/ui/toggle-group.tsx b/src/components/ui/toggle-group.tsx
new file mode 100644
index 0000000..6cadb52
--- /dev/null
+++ b/src/components/ui/toggle-group.tsx
@@ -0,0 +1,61 @@
+"use client"
+
+import * as React from "react"
+import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group"
+import { VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+import { toggleVariants } from "@/components/ui/toggle"
+
+const ToggleGroupContext = React.createContext<
+ VariantProps
+>({
+ size: "default",
+ variant: "default",
+})
+
+const ToggleGroup = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef &
+ VariantProps
+>(({ className, variant, size, children, ...props }, ref) => (
+
+
+ {children}
+
+
+))
+
+ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName
+
+const ToggleGroupItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef &
+ VariantProps
+>(({ className, children, variant, size, ...props }, ref) => {
+ const context = React.useContext(ToggleGroupContext)
+
+ return (
+
+ {children}
+
+ )
+})
+
+ToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName
+
+export { ToggleGroup, ToggleGroupItem }
diff --git a/src/components/ui/toggle.tsx b/src/components/ui/toggle.tsx
new file mode 100644
index 0000000..1adebf9
--- /dev/null
+++ b/src/components/ui/toggle.tsx
@@ -0,0 +1,45 @@
+"use client"
+
+import * as React from "react"
+import * as TogglePrimitive from "@radix-ui/react-toggle"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const toggleVariants = cva(
+ "inline-flex items-center justify-center rounded-md text-sm rounded-full py-0 px-4 font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-black/30 data-[state=on]:text-accent-foreground",
+ {
+ variants: {
+ variant: {
+ default: "bg-transparent",
+ outline:
+ "border border-input bg-transparent hover:bg-accent hover:text-accent-foreground",
+ },
+ size: {
+ default: "h-10 px-3",
+ sm: "h-9 px-2.5",
+ lg: "h-11 px-5",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ }
+)
+
+const Toggle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef &
+ VariantProps
+>(({ className, variant, size, ...props }, ref) => (
+
+))
+
+Toggle.displayName = TogglePrimitive.Root.displayName
+
+export { Toggle, toggleVariants }
diff --git a/src/hooks/useMetaplexMetadata.ts b/src/hooks/useMetaplexMetadata.ts
new file mode 100644
index 0000000..60fa1f4
--- /dev/null
+++ b/src/hooks/useMetaplexMetadata.ts
@@ -0,0 +1,131 @@
+import { TypedAccountParser } from "@helium/account-fetch-cache";
+import {
+ PROGRAM_ID as MPL_PID,
+ Metadata,
+} from "@metaplex-foundation/mpl-token-metadata";
+import { NATIVE_MINT } from "@solana/spl-token";
+import { AccountInfo, PublicKey } from "@solana/web3.js";
+import axios from "axios";
+import { useMemo } from "react";
+import { useAsync } from "react-async-hook";
+import { useAccount } from "@helium/account-fetch-cache-hooks";
+
+const cache: Record> = {};
+export function getMetadata(
+ uriIn: string | undefined
+): Promise {
+ const uri = uriIn?.replace(/\0/g, "");
+ if (uri) {
+ if (!cache[uri]) {
+ cache[uri] = axios
+ .get(uri.replace(/\0/g, ""), {
+ timeout: 3000,
+ })
+ .then((res) => res.data)
+ .catch((err: any) => {
+ console.error(`Error at uri ${uri}`, err);
+ });
+ }
+ return cache[uri];
+ }
+ return Promise.resolve(undefined);
+}
+
+export const METADATA_PARSER: TypedAccountParser = (
+ _: PublicKey,
+ account: AccountInfo
+) => {
+ return Metadata.fromAccountInfo(account)[0];
+};
+
+export function getMetadataId(mint: PublicKey): PublicKey {
+ return PublicKey.findProgramAddressSync(
+ [Buffer.from("metadata", "utf-8"), MPL_PID.toBuffer(), mint.toBuffer()],
+ MPL_PID
+ )[0];
+}
+
+type TokenInfo = {
+ name: string;
+ symbol: string;
+ logoURI: string;
+};
+
+export const tokenInfoToMetadata = (
+ tokenInfo: TokenInfo | null | undefined
+): any | undefined => {
+ if (!tokenInfo) return undefined;
+
+ return {
+ json: {
+ name: tokenInfo.name,
+ symbol: tokenInfo.symbol,
+ image: tokenInfo.logoURI,
+ },
+ symbol: tokenInfo.symbol,
+ name: tokenInfo.name,
+ };
+};
+
+const USDC = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
+export function useMetaplexMetadata(mint: PublicKey | undefined): {
+ loading: boolean;
+ metadata: Metadata | undefined;
+ json: any | undefined;
+ symbol: string | undefined;
+ name: string | undefined;
+} {
+ const metadataAddr = useMemo(() => {
+ if (mint) {
+ return getMetadataId(mint);
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [mint?.toBase58()]);
+
+ const { info: metadataAcc, loading } = useAccount(
+ metadataAddr,
+ METADATA_PARSER
+ );
+
+ const { result: json, loading: jsonLoading } = useAsync(getMetadata, [
+ metadataAcc?.data.uri.trim(),
+ ]);
+
+ if (mint?.equals(NATIVE_MINT)) {
+ return {
+ metadata: undefined,
+ loading: false,
+ json: {
+ name: "SOL",
+ symbol: "SOL",
+ image:
+ "https://github.com/solana-labs/token-list/blob/main/assets/mainnet/So11111111111111111111111111111111111111112/logo.png?raw=true",
+ },
+ symbol: "SOL",
+ name: "SOL",
+ };
+ }
+
+ if (mint?.equals(USDC)) {
+ return {
+ metadata: undefined,
+ loading: false,
+ json: {
+ name: "USDC",
+ symbol: "USDC",
+ image:
+ "https://github.com/solana-labs/token-list/blob/main/assets/mainnet/EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v/logo.png?raw=true",
+ },
+ symbol: "USDC",
+ name: "USDC",
+ };
+ }
+
+ return {
+ loading: jsonLoading || loading,
+ json,
+ metadata: metadataAcc,
+ symbol: json?.symbol || metadataAcc?.data?.symbol,
+ name: json?.name || metadataAcc?.data?.name,
+ };
+}
diff --git a/src/hooks/useNetwork.ts b/src/hooks/useNetwork.ts
new file mode 100644
index 0000000..a6470a8
--- /dev/null
+++ b/src/hooks/useNetwork.ts
@@ -0,0 +1,31 @@
+import { networksToMint } from "@/lib/constants";
+import { PublicKey } from "@solana/web3.js";
+import { usePathname } from "next/navigation";
+import { useRouter } from "next/router";
+import { useCallback } from "react";
+
+export function useNetwork() {
+ const path = usePathname();
+ const network = path.split("/")[0];
+ const router = useRouter();
+ const mint = networksToMint[network || ""];
+
+ const setMint = useCallback(
+ (mint: PublicKey) => {
+ const newNetwork = Object.entries(networksToMint).find(([_, m]) =>
+ mint.equals(m)
+ )?.[0];
+ // Construct new path
+ const newPath = router.asPath.replace(network, newNetwork || network);
+
+ router.push(newPath);
+ },
+ [network, router]
+ );
+
+ return {
+ network,
+ mint,
+ setMint,
+ };
+}
diff --git a/src/lib/constants.ts b/src/lib/constants.ts
index b1d8313..839c337 100644
--- a/src/lib/constants.ts
+++ b/src/lib/constants.ts
@@ -1,2 +1,11 @@
+import { HNT_MINT, IOT_MINT, MOBILE_MINT } from "@helium/spl-utils";
+import { PublicKey } from "@solana/web3.js";
+
// Make a smaller batch for the sake of ledger.
export const MAX_TRANSACTIONS_PER_SIGNATURE_BATCH = 5;
+
+export const networksToMint: { [key: string]: PublicKey } = {
+ hnt: HNT_MINT,
+ mobile: MOBILE_MINT,
+ iot: IOT_MINT,
+};
diff --git a/src/lib/dateTools.ts b/src/lib/dateTools.ts
new file mode 100644
index 0000000..b7dd524
--- /dev/null
+++ b/src/lib/dateTools.ts
@@ -0,0 +1,70 @@
+import { BN } from "@coral-xyz/anchor";
+
+export const DAYS_PER_YEAR = 365;
+export const SECS_PER_DAY = 86400;
+export const DAYS_PER_MONTH = DAYS_PER_YEAR / 12;
+export const HOURS_PER_DAY = 24;
+export const MINS_PER_HOUR = 60;
+
+export function getFormattedStringFromDays(
+ numberOfDays: number,
+ fullFormat = false
+) {
+ const years = Math.floor(numberOfDays / DAYS_PER_YEAR);
+ const months = Math.floor((numberOfDays % DAYS_PER_YEAR) / DAYS_PER_MONTH);
+ const days = Math.floor((numberOfDays % DAYS_PER_YEAR) % DAYS_PER_MONTH);
+ const hours = (numberOfDays - Math.floor(numberOfDays)) * HOURS_PER_DAY;
+ const hoursInt = Math.floor(hours);
+ const minutes = Math.floor((hours - hoursInt) * MINS_PER_HOUR);
+ const yearSuffix = years > 1 ? " years" : " year";
+ const monthSuffix = months > 1 ? " months" : " month";
+ const daysSuffix = days > 1 ? " days" : " day";
+ const yearsDisplay =
+ years > 0 ? years + `${fullFormat ? yearSuffix : "y"}` : null;
+ const monthsDisplay =
+ months > 0 ? months + `${fullFormat ? monthSuffix : "m"}` : null;
+ const daysDisplay =
+ days > 0 ? days + `${fullFormat ? daysSuffix : "d"}` : null;
+ const hoursDisplay = hours > 0 ? `${hoursInt}h ${minutes}min` : null;
+ const text =
+ !years && !months && days <= 1
+ ? [daysDisplay, hoursDisplay].filter(Boolean).join(" ")
+ : [yearsDisplay, monthsDisplay, daysDisplay].filter(Boolean).join(" ");
+ return text ? text : 0;
+}
+
+export const yearsToDays = (years: number) => {
+ return DAYS_PER_YEAR * years;
+};
+export const daysToYear = (days: number) => {
+ return days / DAYS_PER_YEAR;
+};
+export const yearsToSecs = (years: number) => {
+ return DAYS_PER_YEAR * years * SECS_PER_DAY;
+};
+
+export const daysToSecs = (days: number) => {
+ return days * SECS_PER_DAY;
+};
+
+export const secsToDays = (secs: number) => {
+ return secs / SECS_PER_DAY;
+};
+
+export const daysToMonths = (days: number) => {
+ return days / DAYS_PER_MONTH;
+};
+
+export const getMinDurationFmt = (startTs: BN, endTs: BN) => {
+ return getFormattedStringFromDays(getMinDurationInDays(startTs, endTs));
+};
+export const getTimeLeftFromNowFmt = (ts: BN) => {
+ const dateNowSecTimeStampBN = new BN(new Date().getTime() / 1000);
+ return getFormattedStringFromDays(
+ ts.sub(dateNowSecTimeStampBN).toNumber() / SECS_PER_DAY
+ );
+};
+
+export const getMinDurationInDays = (startTs: BN, endTs: BN) => {
+ return endTs.sub(startTs).toNumber() / SECS_PER_DAY;
+};
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
index 3b48304..d7ea43b 100644
--- a/src/lib/utils.ts
+++ b/src/lib/utils.ts
@@ -36,6 +36,14 @@ export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
+export const ellipsisMiddle = (wallet: string): string => {
+ const length = wallet.length;
+ const start = wallet.slice(0, 5);
+ const end = wallet.slice(length - 5, length);
+ const middle = "...";
+ return start + middle + end;
+};
+
export const formMetaTags = (args?: {
title?: string;
description?: string;
diff --git a/src/providers/GovernanceProvider.tsx b/src/providers/GovernanceProvider.tsx
index 983e523..f00ed0b 100644
--- a/src/providers/GovernanceProvider.tsx
+++ b/src/providers/GovernanceProvider.tsx
@@ -6,15 +6,15 @@ import { organizationKey } from "@helium/organization-sdk";
import { HNT_MINT, IOT_MINT, MOBILE_MINT } from "@helium/spl-utils";
import {
HeliumVsrStateProvider,
- getRegistrarKey,
getSubDaos,
useHeliumVsrState,
useRegistrar,
useSubDaos,
} from "@helium/voter-stake-registry-hooks";
+import { getRegistrarKey } from "@helium/voter-stake-registry-sdk";
import { PublicKey } from "@solana/web3.js";
import { useParams } from "next/navigation";
-import React, {
+import {
FC,
ReactNode,
createContext,
@@ -110,6 +110,7 @@ const GovernanceProvider: FC<{ children: ReactNode }> = ({ children }) => {
mint={mint}
wallet={anchorProvider?.wallet as Wallet}
connection={anchorProvider?.connection}
+ heliumVoteUri="http://localhost:8081"
>
{children}
diff --git a/tailwind.config.ts b/tailwind.config.ts
index a543f34..0718ff6 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -113,6 +113,7 @@ const config = {
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
+ muted: "#28303B",
},
},
borderRadius: {
diff --git a/yarn.lock b/yarn.lock
index dc36782..d734c3b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -171,7 +171,7 @@
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.1.tgz#16308cea045f0fc777b6ff20a9f25474dd8293d2"
integrity sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==
-"@helium/account-fetch-cache-hooks@^0.5.0", "@helium/account-fetch-cache-hooks@^0.7.11":
+"@helium/account-fetch-cache-hooks@^0.5.0", "@helium/account-fetch-cache-hooks@^0.7.11", "@helium/account-fetch-cache-hooks@^0.7.6":
version "0.7.11"
resolved "https://registry.yarnpkg.com/@helium/account-fetch-cache-hooks/-/account-fetch-cache-hooks-0.7.11.tgz#cc36cc403958de9b9f230bdd1adf2730b9dfd287"
integrity sha512-YoUacY5cQJStezTIw3sbmxxsuyH+P0VNtVVGnQ7jA3g+Y20BISWs/NOxf5w3cyAPscadUzTnqwGFiQ2isd9UTg==
@@ -180,7 +180,7 @@
"@solana/web3.js" "^1.78.8"
react-async-hook "^4.0.0"
-"@helium/account-fetch-cache@^0.5.0", "@helium/account-fetch-cache@^0.7.11":
+"@helium/account-fetch-cache@^0.5.0", "@helium/account-fetch-cache@^0.7.11", "@helium/account-fetch-cache@^0.7.6":
version "0.7.11"
resolved "https://registry.yarnpkg.com/@helium/account-fetch-cache/-/account-fetch-cache-0.7.11.tgz#5813509464214cbfbbe42040eb5a48a386e1eefd"
integrity sha512-5HPecniIyU/rcUc+Na7gqPRr352Dw8egrfgOzc24PdquVWvbJzrVwfcjrFL2PGEr858uTx0uIzbICrfsaMvEaQ==
@@ -196,6 +196,14 @@
js-sha256 "^0.9.0"
multiformats "^9.6.4"
+"@helium/anchor-resolvers@^0.2.17":
+ version "0.2.21"
+ resolved "https://registry.yarnpkg.com/@helium/anchor-resolvers/-/anchor-resolvers-0.2.21.tgz#8cd13bca3e7af83ffff3ab754dcf9f788d26e57e"
+ integrity sha512-rz3GJaULGmokjrq63v4sy683PMUkL31jrK5wVX01tzCYbI1woC9vISoPd3oSSuauZqQXdBhhQ49GKrpzi49JbA==
+ dependencies:
+ "@solana/spl-token" "^0.3.6"
+ "@solana/web3.js" "^1.43.4"
+
"@helium/anchor-resolvers@^0.5.0":
version "0.5.0"
resolved "https://registry.yarnpkg.com/@helium/anchor-resolvers/-/anchor-resolvers-0.5.0.tgz#499fbcd82411d07b148d7015a1e6432020a3f82d"
@@ -204,26 +212,26 @@
"@solana/spl-token" "^0.3.8"
"@solana/web3.js" "^1.78.4"
-"@helium/anchor-resolvers@^0.7.11":
- version "0.7.11"
- resolved "https://registry.yarnpkg.com/@helium/anchor-resolvers/-/anchor-resolvers-0.7.11.tgz#8e0612644129c4497dfa796c51e4db556aa43872"
- integrity sha512-npHweI0lEO00bjLmOanCZXJkfC+cPn+JPNARsF5r42eJk0QL9HLxJJBaAwihpD3Q2xpyXhk1N306MMEgXRdTOQ==
+"@helium/anchor-resolvers@^0.7.12", "@helium/anchor-resolvers@^0.7.6":
+ version "0.7.12"
+ resolved "https://registry.yarnpkg.com/@helium/anchor-resolvers/-/anchor-resolvers-0.7.12.tgz#bb6921d252c7ae4c2788569ddbf68fb57a3f2adc"
+ integrity sha512-KphCkH4IHRMxWq/SSaWRxDVV8b1w8L0tY1qFOvvH/bY+Mr1E8CiVHZCCpdi1c5k/idLDz/wHvHCUSXppFPxdDQ==
dependencies:
"@solana/spl-token" "^0.3.8"
"@solana/web3.js" "^1.78.8"
-"@helium/circuit-breaker-sdk@^0.7.11":
- version "0.7.11"
- resolved "https://registry.yarnpkg.com/@helium/circuit-breaker-sdk/-/circuit-breaker-sdk-0.7.11.tgz#032c4697bd7205423bc946371792c085cea5e31b"
- integrity sha512-t7iNkNLKBL0uPkqK+gcrAzOLnL+w1gjbpcWQ4jVfRwmzQApmhwu15WKlO1NW+av/neZeCYtvD+bOqR69ASruVA==
+"@helium/circuit-breaker-sdk@^0.7.12", "@helium/circuit-breaker-sdk@^0.7.6":
+ version "0.7.12"
+ resolved "https://registry.yarnpkg.com/@helium/circuit-breaker-sdk/-/circuit-breaker-sdk-0.7.12.tgz#400d28228f473edc80dc8b0a81ef76cf6a3f3bcd"
+ integrity sha512-4qhoYuW0Aeo9+gg1/z9Fp+IjtS4+v2Wz88imKSHYmpINJ3BNNh7q7zfuYsX/t1utQyaYxhFSejlIIP1yVdlmsw==
dependencies:
"@coral-xyz/anchor" "^0.28.0"
- "@helium/anchor-resolvers" "^0.7.11"
- "@helium/idls" "^0.7.11"
+ "@helium/anchor-resolvers" "^0.7.12"
+ "@helium/idls" "^0.7.12"
bn.js "^5.2.0"
bs58 "^4.0.1"
-"@helium/helium-react-hooks@^0.5.0", "@helium/helium-react-hooks@^0.7.11":
+"@helium/helium-react-hooks@^0.5.0", "@helium/helium-react-hooks@^0.7.11", "@helium/helium-react-hooks@^0.7.6":
version "0.7.11"
resolved "https://registry.yarnpkg.com/@helium/helium-react-hooks/-/helium-react-hooks-0.7.11.tgz#1d67bf58906f9c8d02ee9e7cb064ba6fb1668f3b"
integrity sha512-BD3cjdxn+98h+Cm1Dy5duwAk2RmmxqMkLoRQKZTiXK7X91d80bS3bHep6XbPMGlvHFokyrCcITiWp/ivHRHLLw==
@@ -237,23 +245,23 @@
pako "^2.0.3"
react-async-hook "^4.0.0"
-"@helium/helium-sub-daos-sdk@^0.7.11":
- version "0.7.11"
- resolved "https://registry.yarnpkg.com/@helium/helium-sub-daos-sdk/-/helium-sub-daos-sdk-0.7.11.tgz#eb59678610f9b5b8cdf7c4c7671ed23347acd517"
- integrity sha512-37is9bYr0W/+4rqTInkaErYKQB72Fa53csXtZCYwRsDqhYWCXT/UvTyepr+Cyg/cHc70HcL/Cmrt/UFg1brH0w==
+"@helium/helium-sub-daos-sdk@^0.7.6":
+ version "0.7.12"
+ resolved "https://registry.yarnpkg.com/@helium/helium-sub-daos-sdk/-/helium-sub-daos-sdk-0.7.12.tgz#7b38a582e8f195c82f307225e48f3f8dedc24e95"
+ integrity sha512-ziVwT9mo8OK7lWHBXmqy6M9b9UltvphNFNtysdGIWjgnZYYWrZbK6t0PKKciCBg/W9D3SHQzWiiSHfQhHCwCFA==
dependencies:
"@coral-xyz/anchor" "^0.28.0"
- "@helium/anchor-resolvers" "^0.7.11"
- "@helium/circuit-breaker-sdk" "^0.7.11"
- "@helium/treasury-management-sdk" "^0.7.11"
- "@helium/voter-stake-registry-sdk" "^0.7.11"
+ "@helium/anchor-resolvers" "^0.7.12"
+ "@helium/circuit-breaker-sdk" "^0.7.12"
+ "@helium/treasury-management-sdk" "^0.7.12"
+ "@helium/voter-stake-registry-sdk" "^0.7.12"
bn.js "^5.2.0"
bs58 "^4.0.1"
-"@helium/idls@^0.7.11":
- version "0.7.11"
- resolved "https://registry.yarnpkg.com/@helium/idls/-/idls-0.7.11.tgz#90e73f3878f1326067784ae208ee753f2fce90c6"
- integrity sha512-bZYDJpPMsDmB+Zm6ZmcHsuLkNOEw2Nf4xtQ8SWPglSoKwsXa8VB6P6cEtNavRVBfU1RBn3XQbGHuevvW5Yuq2g==
+"@helium/idls@^0.7.12", "@helium/idls@^0.7.6":
+ version "0.7.12"
+ resolved "https://registry.yarnpkg.com/@helium/idls/-/idls-0.7.12.tgz#6940c0e4750e8201994e1420b6053e3aefff75d0"
+ integrity sha512-ZcAw904YsbyUqGuejhGq7YaE+S5RZfNTSkV43T/KA/HwEtxCyWRrz8oiKEVEbYXk+XX/InQX5feW/X+3g/w+5g==
dependencies:
"@coral-xyz/anchor" "^0.28.0"
"@solana/web3.js" "^1.78.8"
@@ -274,7 +282,15 @@
"@helium/organization-sdk" "^0.0.8"
"@solana/web3.js" "^1.78.4"
-"@helium/modular-governance-idls@^0.0.8", "@helium/modular-governance-idls@^0.0.8-next":
+"@helium/modular-governance-idls@0.0.8-next.17+7ff2115":
+ version "0.0.8-next.17"
+ resolved "https://registry.yarnpkg.com/@helium/modular-governance-idls/-/modular-governance-idls-0.0.8-next.17.tgz#066c639879dec20cb71d727bec38500bc39e79da"
+ integrity sha512-lFQs2V8kPozwI+wSAHxv0l2QRdia2Dy8HJLF71rabeCHVRIBlyoYkveI9wYopEX1XxAAPtGtGvVeLR4SafLe/w==
+ dependencies:
+ "@coral-xyz/anchor" "^0.28.0"
+ "@solana/web3.js" "^1.78.4"
+
+"@helium/modular-governance-idls@^0.0.8", "@helium/modular-governance-idls@^0.0.8-next", "@helium/modular-governance-idls@^0.0.8-next.17+7ff2115":
version "0.0.8"
resolved "https://registry.yarnpkg.com/@helium/modular-governance-idls/-/modular-governance-idls-0.0.8.tgz#b913b28790ff80359c7470d9d6665468f5f1b435"
integrity sha512-vmBw9dRUsYKX5ZXiMJaCoKYv5yNBLHpXKb5LI/5cIq0KrGNC4quzMuoMzeehXGgDkIIM9VLnk8YP1Bz8CvUzqA==
@@ -282,6 +298,16 @@
"@coral-xyz/anchor" "^0.28.0"
"@solana/web3.js" "^1.78.4"
+"@helium/nft-proxy-sdk@0.0.8-next.17+7ff2115":
+ version "0.0.8-next.17"
+ resolved "https://registry.yarnpkg.com/@helium/nft-proxy-sdk/-/nft-proxy-sdk-0.0.8-next.17.tgz#50ea6f3a9f8253035527b0a4cecdc2cd78c4ce81"
+ integrity sha512-Uphw4HorYpAJmQ/Kf3ggR85/Vqe1ueZpzA8oRtMiwQpo+67pcAHplIVuwQEVkHZ5S23XwL5dNGDIZyBA9Khg0w==
+ dependencies:
+ "@coral-xyz/anchor" "^0.28.0"
+ "@helium/anchor-resolvers" "^0.2.17"
+ "@helium/modular-governance-idls" "^0.0.8-next.17+7ff2115"
+ "@solana/spl-token" "^0.3.8"
+
"@helium/organization-sdk@^0.0.8":
version "0.0.8"
resolved "https://registry.yarnpkg.com/@helium/organization-sdk/-/organization-sdk-0.0.8.tgz#4738619a929999af6529f21e80e79ab766472322"
@@ -301,15 +327,13 @@
"@helium/anchor-resolvers" "^0.5.0"
"@helium/modular-governance-idls" "^0.0.8"
-"@helium/spl-utils@^0.7.11":
- version "0.7.11"
- resolved "https://registry.yarnpkg.com/@helium/spl-utils/-/spl-utils-0.7.11.tgz#c217d6ec106ce8d2559f6b1a90cba9184223a7e6"
- integrity sha512-4JsmePhEF+3ZO1NQx4Cev+G3pXUyfeW0GUeYArjUV524zpzlc5HvpU28BJg5OD1rNaQ2ZY8Vd+upy31hofrj8g==
+"@helium/spl-utils@^0.7.6", "@helium/spl-utils@file:.yalc/@helium/spl-utils":
+ version "0.7.6"
dependencies:
"@coral-xyz/anchor" "^0.28.0"
- "@helium/account-fetch-cache" "^0.7.11"
+ "@helium/account-fetch-cache" "^0.7.6"
"@helium/address" "^4.10.2"
- "@helium/anchor-resolvers" "^0.7.11"
+ "@helium/anchor-resolvers" "^0.7.6"
"@metaplex-foundation/mpl-token-metadata" "^2.10.0"
"@solana/spl-account-compression" "^0.1.7"
"@solana/spl-token" "^0.3.8"
@@ -328,32 +352,31 @@
"@helium/anchor-resolvers" "^0.5.0"
"@helium/modular-governance-idls" "^0.0.8"
-"@helium/treasury-management-sdk@^0.7.11":
- version "0.7.11"
- resolved "https://registry.yarnpkg.com/@helium/treasury-management-sdk/-/treasury-management-sdk-0.7.11.tgz#1f2a86b6590f633a2908304bce7bdfde4a0d972b"
- integrity sha512-LvUJIPhJSEoJTNcsq8vOIYRSMVlrNte3hFPib9d5ur48PTehKHfUdNG9YAOX3UhbD8xSsIFgwe+ZgcZOFHYJbw==
+"@helium/treasury-management-sdk@^0.7.12":
+ version "0.7.12"
+ resolved "https://registry.yarnpkg.com/@helium/treasury-management-sdk/-/treasury-management-sdk-0.7.12.tgz#4d9a4f3f04e92c082eacbf38f9ff6f928863bfad"
+ integrity sha512-Dv+J/Dr6gd9K5drVlUbaBYAI0FISaxhftY55BrofUDhYhsA8drciu+Y97MXt2BGKstAWAlJCHq3HbbwQbmjsmQ==
dependencies:
"@coral-xyz/anchor" "^0.28.0"
- "@helium/anchor-resolvers" "^0.7.11"
- "@helium/circuit-breaker-sdk" "^0.7.11"
- "@helium/idls" "^0.7.11"
+ "@helium/anchor-resolvers" "^0.7.12"
+ "@helium/circuit-breaker-sdk" "^0.7.12"
+ "@helium/idls" "^0.7.12"
bn.js "^5.2.0"
bs58 "^4.0.1"
-"@helium/voter-stake-registry-hooks@^0.7.11":
- version "0.7.11"
- resolved "https://registry.yarnpkg.com/@helium/voter-stake-registry-hooks/-/voter-stake-registry-hooks-0.7.11.tgz#57c8ed321db235c756f3ccd59cc1dd259e05409a"
- integrity sha512-rsI6ATu7VMjDnaBhaD51BuEbx3ADhQjtlxJzvPdrYK41DwGDMEPbt54qDYfB6+v5EhKAmdWIryOO6bCPt6G6rg==
+"@helium/voter-stake-registry-hooks@file:.yalc/@helium/voter-stake-registry-hooks":
+ version "0.7.6"
dependencies:
"@coral-xyz/anchor" "^0.28.0"
- "@helium/account-fetch-cache" "^0.7.11"
- "@helium/account-fetch-cache-hooks" "^0.7.11"
- "@helium/circuit-breaker-sdk" "^0.7.11"
- "@helium/helium-react-hooks" "^0.7.11"
- "@helium/helium-sub-daos-sdk" "^0.7.11"
+ "@helium/account-fetch-cache" "^0.7.6"
+ "@helium/account-fetch-cache-hooks" "^0.7.6"
+ "@helium/circuit-breaker-sdk" "^0.7.6"
+ "@helium/helium-react-hooks" "^0.7.6"
+ "@helium/helium-sub-daos-sdk" "^0.7.6"
"@helium/modular-governance-hooks" "^0.0.8"
- "@helium/spl-utils" "^0.7.11"
- "@helium/voter-stake-registry-sdk" "^0.7.11"
+ "@helium/modular-governance-idls" "0.0.8-next.17+7ff2115"
+ "@helium/spl-utils" "^0.7.6"
+ "@helium/voter-stake-registry-sdk" "^0.7.6"
"@solana/wallet-adapter-base" "^0.9.22"
"@solana/wallet-adapter-react" "^0.15.32"
"@solana/web3.js" "^1.78.8"
@@ -361,14 +384,14 @@
bs58 "^4.0.1"
react-async-hook "^4.0.0"
-"@helium/voter-stake-registry-sdk@^0.7.11":
- version "0.7.11"
- resolved "https://registry.yarnpkg.com/@helium/voter-stake-registry-sdk/-/voter-stake-registry-sdk-0.7.11.tgz#70f75d7407dfac089266b902129a9f748db7fd13"
- integrity sha512-1YqH/yYibFE5dTbn8/M8eaHdiP/oYDfU6WLBJg7mmlXsxxyxRaS2t33LTdTAVv8SUwC6pBxm/0LHg4ZRXCdrDw==
+"@helium/voter-stake-registry-sdk@^0.7.12", "@helium/voter-stake-registry-sdk@^0.7.6", "@helium/voter-stake-registry-sdk@file:.yalc/@helium/voter-stake-registry-sdk":
+ version "0.7.6"
dependencies:
"@coral-xyz/anchor" "^0.28.0"
- "@helium/anchor-resolvers" "^0.7.11"
- "@helium/idls" "^0.7.11"
+ "@helium/anchor-resolvers" "^0.7.6"
+ "@helium/idls" "^0.7.6"
+ "@helium/nft-proxy-sdk" "0.0.8-next.17+7ff2115"
+ "@helium/spl-utils" "^0.7.6"
"@metaplex-foundation/mpl-token-metadata" "^2.10.0"
"@solana/spl-token" "^0.3.8"
bn.js "^5.2.0"
@@ -658,6 +681,17 @@
"@radix-ui/react-use-previous" "1.0.1"
"@radix-ui/react-use-size" "1.0.1"
+"@radix-ui/react-collection@1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.0.3.tgz#9595a66e09026187524a36c6e7e9c7d286469159"
+ integrity sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+ "@radix-ui/react-compose-refs" "1.0.1"
+ "@radix-ui/react-context" "1.0.1"
+ "@radix-ui/react-primitive" "1.0.3"
+ "@radix-ui/react-slot" "1.0.2"
+
"@radix-ui/react-compose-refs@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz#7ed868b66946aa6030e580b1ffca386dd4d21989"
@@ -693,6 +727,13 @@
aria-hidden "^1.1.1"
react-remove-scroll "2.5.5"
+"@radix-ui/react-direction@1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.0.1.tgz#9cb61bf2ccf568f3421422d182637b7f47596c9b"
+ integrity sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+
"@radix-ui/react-dismissable-layer@1.0.5":
version "1.0.5"
resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz#3f98425b82b9068dfbab5db5fff3df6ebf48b9d4"
@@ -705,6 +746,20 @@
"@radix-ui/react-use-callback-ref" "1.0.1"
"@radix-ui/react-use-escape-keydown" "1.0.3"
+"@radix-ui/react-dropdown-menu@^2.0.6":
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.0.6.tgz#cdf13c956c5e263afe4e5f3587b3071a25755b63"
+ integrity sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+ "@radix-ui/primitive" "1.0.1"
+ "@radix-ui/react-compose-refs" "1.0.1"
+ "@radix-ui/react-context" "1.0.1"
+ "@radix-ui/react-id" "1.0.1"
+ "@radix-ui/react-menu" "2.0.6"
+ "@radix-ui/react-primitive" "1.0.3"
+ "@radix-ui/react-use-controllable-state" "1.0.1"
+
"@radix-ui/react-focus-guards@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz#1ea7e32092216b946397866199d892f71f7f98ad"
@@ -746,6 +801,31 @@
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-layout-effect" "1.0.1"
+"@radix-ui/react-menu@2.0.6":
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-menu/-/react-menu-2.0.6.tgz#2c9e093c1a5d5daa87304b2a2f884e32288ae79e"
+ integrity sha512-BVkFLS+bUC8HcImkRKPSiVumA1VPOOEC5WBMiT+QAVsPzW1FJzI9KnqgGxVDPBcql5xXrHkD3JOVoXWEXD8SYA==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+ "@radix-ui/primitive" "1.0.1"
+ "@radix-ui/react-collection" "1.0.3"
+ "@radix-ui/react-compose-refs" "1.0.1"
+ "@radix-ui/react-context" "1.0.1"
+ "@radix-ui/react-direction" "1.0.1"
+ "@radix-ui/react-dismissable-layer" "1.0.5"
+ "@radix-ui/react-focus-guards" "1.0.1"
+ "@radix-ui/react-focus-scope" "1.0.4"
+ "@radix-ui/react-id" "1.0.1"
+ "@radix-ui/react-popper" "1.1.3"
+ "@radix-ui/react-portal" "1.0.4"
+ "@radix-ui/react-presence" "1.0.1"
+ "@radix-ui/react-primitive" "1.0.3"
+ "@radix-ui/react-roving-focus" "1.0.4"
+ "@radix-ui/react-slot" "1.0.2"
+ "@radix-ui/react-use-callback-ref" "1.0.1"
+ aria-hidden "^1.1.1"
+ react-remove-scroll "2.5.5"
+
"@radix-ui/react-popper@1.1.3":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.1.3.tgz#24c03f527e7ac348fabf18c89795d85d21b00b42"
@@ -797,6 +877,22 @@
"@radix-ui/react-context" "1.0.1"
"@radix-ui/react-primitive" "1.0.3"
+"@radix-ui/react-roving-focus@1.0.4":
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz#e90c4a6a5f6ac09d3b8c1f5b5e81aab2f0db1974"
+ integrity sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+ "@radix-ui/primitive" "1.0.1"
+ "@radix-ui/react-collection" "1.0.3"
+ "@radix-ui/react-compose-refs" "1.0.1"
+ "@radix-ui/react-context" "1.0.1"
+ "@radix-ui/react-direction" "1.0.1"
+ "@radix-ui/react-id" "1.0.1"
+ "@radix-ui/react-primitive" "1.0.3"
+ "@radix-ui/react-use-callback-ref" "1.0.1"
+ "@radix-ui/react-use-controllable-state" "1.0.1"
+
"@radix-ui/react-separator@^1.0.3":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@radix-ui/react-separator/-/react-separator-1.0.3.tgz#be5a931a543d5726336b112f465f58585c04c8aa"
@@ -813,6 +909,30 @@
"@babel/runtime" "^7.13.10"
"@radix-ui/react-compose-refs" "1.0.1"
+"@radix-ui/react-toggle-group@^1.0.4":
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-toggle-group/-/react-toggle-group-1.0.4.tgz#f5b5c8c477831b013bec3580c55e20a68179d6ec"
+ integrity sha512-Uaj/M/cMyiyT9Bx6fOZO0SAG4Cls0GptBWiBmBxofmDbNVnYYoyRWj/2M/6VCi/7qcXFWnHhRUfdfZFvvkuu8A==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+ "@radix-ui/primitive" "1.0.1"
+ "@radix-ui/react-context" "1.0.1"
+ "@radix-ui/react-direction" "1.0.1"
+ "@radix-ui/react-primitive" "1.0.3"
+ "@radix-ui/react-roving-focus" "1.0.4"
+ "@radix-ui/react-toggle" "1.0.3"
+ "@radix-ui/react-use-controllable-state" "1.0.1"
+
+"@radix-ui/react-toggle@1.0.3", "@radix-ui/react-toggle@^1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-toggle/-/react-toggle-1.0.3.tgz#aecb2945630d1dc5c512997556c57aba894e539e"
+ integrity sha512-Pkqg3+Bc98ftZGsl60CLANXQBBQ4W3mTFS9EJvNxKMZ7magklKV69/id1mlAlOFDDfHvlCms0fx8fA4CMKDJHg==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+ "@radix-ui/primitive" "1.0.1"
+ "@radix-ui/react-primitive" "1.0.3"
+ "@radix-ui/react-use-controllable-state" "1.0.1"
+
"@radix-ui/react-use-callback-ref@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz#f4bb1f27f2023c984e6534317ebc411fc181107a"
@@ -1138,7 +1258,7 @@
"@solana/wallet-standard-core" "^1.1.1"
"@solana/wallet-standard-wallet-adapter" "^1.1.2"
-"@solana/web3.js@^1.32.0", "@solana/web3.js@^1.56.2", "@solana/web3.js@^1.66.2", "@solana/web3.js@^1.68.0", "@solana/web3.js@^1.73.2", "@solana/web3.js@^1.78.4", "@solana/web3.js@^1.78.8", "@solana/web3.js@^1.90.0":
+"@solana/web3.js@^1.32.0", "@solana/web3.js@^1.43.4", "@solana/web3.js@^1.56.2", "@solana/web3.js@^1.66.2", "@solana/web3.js@^1.68.0", "@solana/web3.js@^1.73.2", "@solana/web3.js@^1.78.4", "@solana/web3.js@^1.78.8", "@solana/web3.js@^1.90.0":
version "1.91.0"
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.91.0.tgz#a763b0fcca0fa005adce3d02f3a4b6d1b84eccb7"
integrity sha512-iqOL9RjNra0TM9BbQWxBRUcZUiNmCJJO+vXLp0GiELUJhbNAoE/K6OV6s+gNEsC13dslvKtfA4mmzRnZNWXtIQ==
@@ -4293,6 +4413,13 @@ react-icons@^5.0.1:
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-5.0.1.tgz#1694e11bfa2a2888cab47dcc30154ce90485feee"
integrity sha512-WqLZJ4bLzlhmsvme6iFdgO8gfZP17rfjYEJ2m9RsZjZ+cc4k1hTzknEz63YS1MeT50kVzoa1Nz36f4BEx+Wigw==
+react-infinite-scroll-component@^6.1.0:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/react-infinite-scroll-component/-/react-infinite-scroll-component-6.1.0.tgz#7e511e7aa0f728ac3e51f64a38a6079ac522407f"
+ integrity sha512-SQu5nCqy8DxQWpnUVLx7V7b7LcA37aM7tvoWjTLZp1dk6EJibM5/4EJKzOnl07/BsM1Y40sKLuqjCwwH/xV0TQ==
+ dependencies:
+ throttle-debounce "^2.1.0"
+
react-is@^16.13.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
@@ -4812,6 +4939,11 @@ thenify-all@^1.0.0:
dependencies:
any-promise "^1.0.0"
+throttle-debounce@^2.1.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-2.3.0.tgz#fd31865e66502071e411817e241465b3e9c372e2"
+ integrity sha512-H7oLPV0P7+jgvrk+6mwwwBDmxTaxnu9HMXmloNLXwnNO0ZxZ31Orah2n8lU1eMPvsaowP2CX+USCgyovXfdOFQ==
+
"through@>=2.2.7 <3":
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
From dde599b8d0a3ff38421c9bf8556697776ac7c260 Mon Sep 17 00:00:00 2001
From: Noah Prince
Date: Fri, 3 May 2024 16:13:36 -0500
Subject: [PATCH 02/41] Finish refactor to proxy assignments
---
package.json | 2 +-
src/components/VoteOptions.tsx | 4 +-
src/lib/utils.ts | 4 +-
yarn.lock | 72 +++++++++++++++++-----------------
4 files changed, 41 insertions(+), 41 deletions(-)
diff --git a/package.json b/package.json
index 7aae9f6..be7e9d3 100644
--- a/package.json
+++ b/package.json
@@ -14,7 +14,7 @@
"@helium/account-fetch-cache-hooks": "^0.7.11",
"@helium/helium-react-hooks": "^0.7.11",
"@helium/modular-governance-hooks": "^0.0.8",
- "@helium/modular-governance-idls": "^0.0.8-next",
+ "@helium/modular-governance-idls": "^0.0.8-next.19+4fa4c6b",
"@helium/organization-sdk": "^0.0.8",
"@helium/spl-utils": "file:.yalc/@helium/spl-utils",
"@helium/state-controller-sdk": "^0.0.8",
diff --git a/src/components/VoteOptions.tsx b/src/components/VoteOptions.tsx
index 44b730f..edf2af7 100644
--- a/src/components/VoteOptions.tsx
+++ b/src/components/VoteOptions.tsx
@@ -1,7 +1,7 @@
"use client";
import { VoteChoiceWithMeta } from "@/lib/types";
-import { useRelinquishVote, useVote } from "@helium/voter-stake-registry-hooks";
+import { useHeliumVsrState, useRelinquishVote, useVote } from "@helium/voter-stake-registry-hooks";
import { PublicKey } from "@solana/web3.js";
import React, { FC, useState } from "react";
import { VoteOption } from "./VoteOption";
@@ -17,6 +17,8 @@ export const VoteOptions: FC<{
}> = ({ choices = [], maxChoicesPerVoter, proposalKey }) => {
const [currVote, setCurrVote] = useState(0);
const { voteWeights, canVote, vote, loading: voting } = useVote(proposalKey);
+ const { positions } = useHeliumVsrState()
+ console.log(positions)
const {
canRelinquishVote,
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
index d7ea43b..20c20fe 100644
--- a/src/lib/utils.ts
+++ b/src/lib/utils.ts
@@ -4,10 +4,9 @@ import {
HELIUM_COMMON_LUT,
HELIUM_COMMON_LUT_DEVNET,
batchInstructionsToTxsWithPriorityFee,
- batchParallelInstructionsWithPriorityFee,
bulkSendTransactions,
sendAndConfirmWithRetry,
- toVersionedTx,
+ toVersionedTx
} from "@helium/spl-utils";
import { PositionWithMeta } from "@helium/voter-stake-registry-hooks";
import { Mint } from "@solana/spl-token";
@@ -347,7 +346,6 @@ export const onInstructions =
],
}
);
- console.log("hehe")
await bulkSendTransactions(
provider,
diff --git a/yarn.lock b/yarn.lock
index d734c3b..fc8d49b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -171,7 +171,7 @@
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.1.tgz#16308cea045f0fc777b6ff20a9f25474dd8293d2"
integrity sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==
-"@helium/account-fetch-cache-hooks@^0.5.0", "@helium/account-fetch-cache-hooks@^0.7.11", "@helium/account-fetch-cache-hooks@^0.7.6":
+"@helium/account-fetch-cache-hooks@^0.5.0", "@helium/account-fetch-cache-hooks@^0.7.11", "@helium/account-fetch-cache-hooks@^0.7.12":
version "0.7.11"
resolved "https://registry.yarnpkg.com/@helium/account-fetch-cache-hooks/-/account-fetch-cache-hooks-0.7.11.tgz#cc36cc403958de9b9f230bdd1adf2730b9dfd287"
integrity sha512-YoUacY5cQJStezTIw3sbmxxsuyH+P0VNtVVGnQ7jA3g+Y20BISWs/NOxf5w3cyAPscadUzTnqwGFiQ2isd9UTg==
@@ -180,7 +180,7 @@
"@solana/web3.js" "^1.78.8"
react-async-hook "^4.0.0"
-"@helium/account-fetch-cache@^0.5.0", "@helium/account-fetch-cache@^0.7.11", "@helium/account-fetch-cache@^0.7.6":
+"@helium/account-fetch-cache@^0.5.0", "@helium/account-fetch-cache@^0.7.11", "@helium/account-fetch-cache@^0.7.12":
version "0.7.11"
resolved "https://registry.yarnpkg.com/@helium/account-fetch-cache/-/account-fetch-cache-0.7.11.tgz#5813509464214cbfbbe42040eb5a48a386e1eefd"
integrity sha512-5HPecniIyU/rcUc+Na7gqPRr352Dw8egrfgOzc24PdquVWvbJzrVwfcjrFL2PGEr858uTx0uIzbICrfsaMvEaQ==
@@ -212,7 +212,7 @@
"@solana/spl-token" "^0.3.8"
"@solana/web3.js" "^1.78.4"
-"@helium/anchor-resolvers@^0.7.12", "@helium/anchor-resolvers@^0.7.6":
+"@helium/anchor-resolvers@^0.7.12":
version "0.7.12"
resolved "https://registry.yarnpkg.com/@helium/anchor-resolvers/-/anchor-resolvers-0.7.12.tgz#bb6921d252c7ae4c2788569ddbf68fb57a3f2adc"
integrity sha512-KphCkH4IHRMxWq/SSaWRxDVV8b1w8L0tY1qFOvvH/bY+Mr1E8CiVHZCCpdi1c5k/idLDz/wHvHCUSXppFPxdDQ==
@@ -220,7 +220,7 @@
"@solana/spl-token" "^0.3.8"
"@solana/web3.js" "^1.78.8"
-"@helium/circuit-breaker-sdk@^0.7.12", "@helium/circuit-breaker-sdk@^0.7.6":
+"@helium/circuit-breaker-sdk@^0.7.12":
version "0.7.12"
resolved "https://registry.yarnpkg.com/@helium/circuit-breaker-sdk/-/circuit-breaker-sdk-0.7.12.tgz#400d28228f473edc80dc8b0a81ef76cf6a3f3bcd"
integrity sha512-4qhoYuW0Aeo9+gg1/z9Fp+IjtS4+v2Wz88imKSHYmpINJ3BNNh7q7zfuYsX/t1utQyaYxhFSejlIIP1yVdlmsw==
@@ -231,7 +231,7 @@
bn.js "^5.2.0"
bs58 "^4.0.1"
-"@helium/helium-react-hooks@^0.5.0", "@helium/helium-react-hooks@^0.7.11", "@helium/helium-react-hooks@^0.7.6":
+"@helium/helium-react-hooks@^0.5.0", "@helium/helium-react-hooks@^0.7.11", "@helium/helium-react-hooks@^0.7.12":
version "0.7.11"
resolved "https://registry.yarnpkg.com/@helium/helium-react-hooks/-/helium-react-hooks-0.7.11.tgz#1d67bf58906f9c8d02ee9e7cb064ba6fb1668f3b"
integrity sha512-BD3cjdxn+98h+Cm1Dy5duwAk2RmmxqMkLoRQKZTiXK7X91d80bS3bHep6XbPMGlvHFokyrCcITiWp/ivHRHLLw==
@@ -245,7 +245,7 @@
pako "^2.0.3"
react-async-hook "^4.0.0"
-"@helium/helium-sub-daos-sdk@^0.7.6":
+"@helium/helium-sub-daos-sdk@^0.7.12":
version "0.7.12"
resolved "https://registry.yarnpkg.com/@helium/helium-sub-daos-sdk/-/helium-sub-daos-sdk-0.7.12.tgz#7b38a582e8f195c82f307225e48f3f8dedc24e95"
integrity sha512-ziVwT9mo8OK7lWHBXmqy6M9b9UltvphNFNtysdGIWjgnZYYWrZbK6t0PKKciCBg/W9D3SHQzWiiSHfQhHCwCFA==
@@ -258,7 +258,7 @@
bn.js "^5.2.0"
bs58 "^4.0.1"
-"@helium/idls@^0.7.12", "@helium/idls@^0.7.6":
+"@helium/idls@^0.7.12":
version "0.7.12"
resolved "https://registry.yarnpkg.com/@helium/idls/-/idls-0.7.12.tgz#6940c0e4750e8201994e1420b6053e3aefff75d0"
integrity sha512-ZcAw904YsbyUqGuejhGq7YaE+S5RZfNTSkV43T/KA/HwEtxCyWRrz8oiKEVEbYXk+XX/InQX5feW/X+3g/w+5g==
@@ -282,15 +282,15 @@
"@helium/organization-sdk" "^0.0.8"
"@solana/web3.js" "^1.78.4"
-"@helium/modular-governance-idls@0.0.8-next.17+7ff2115":
- version "0.0.8-next.17"
- resolved "https://registry.yarnpkg.com/@helium/modular-governance-idls/-/modular-governance-idls-0.0.8-next.17.tgz#066c639879dec20cb71d727bec38500bc39e79da"
- integrity sha512-lFQs2V8kPozwI+wSAHxv0l2QRdia2Dy8HJLF71rabeCHVRIBlyoYkveI9wYopEX1XxAAPtGtGvVeLR4SafLe/w==
+"@helium/modular-governance-idls@0.0.8-next.19+4fa4c6b":
+ version "0.0.8-next.19"
+ resolved "https://registry.yarnpkg.com/@helium/modular-governance-idls/-/modular-governance-idls-0.0.8-next.19.tgz#517c8b99e0dcd077a6257abff9f8dadf2761fa41"
+ integrity sha512-vvrqRO58zYSRlDU0SIL6W3eUkHQFfNq4R1hTP+Q8V1Ksik+mGBIidj5jux1+Lxj/wsGKwVxsE+zQ2CYS1K13lg==
dependencies:
"@coral-xyz/anchor" "^0.28.0"
"@solana/web3.js" "^1.78.4"
-"@helium/modular-governance-idls@^0.0.8", "@helium/modular-governance-idls@^0.0.8-next", "@helium/modular-governance-idls@^0.0.8-next.17+7ff2115":
+"@helium/modular-governance-idls@^0.0.8", "@helium/modular-governance-idls@^0.0.8-next.19+4fa4c6b":
version "0.0.8"
resolved "https://registry.yarnpkg.com/@helium/modular-governance-idls/-/modular-governance-idls-0.0.8.tgz#b913b28790ff80359c7470d9d6665468f5f1b435"
integrity sha512-vmBw9dRUsYKX5ZXiMJaCoKYv5yNBLHpXKb5LI/5cIq0KrGNC4quzMuoMzeehXGgDkIIM9VLnk8YP1Bz8CvUzqA==
@@ -298,14 +298,14 @@
"@coral-xyz/anchor" "^0.28.0"
"@solana/web3.js" "^1.78.4"
-"@helium/nft-proxy-sdk@0.0.8-next.17+7ff2115":
- version "0.0.8-next.17"
- resolved "https://registry.yarnpkg.com/@helium/nft-proxy-sdk/-/nft-proxy-sdk-0.0.8-next.17.tgz#50ea6f3a9f8253035527b0a4cecdc2cd78c4ce81"
- integrity sha512-Uphw4HorYpAJmQ/Kf3ggR85/Vqe1ueZpzA8oRtMiwQpo+67pcAHplIVuwQEVkHZ5S23XwL5dNGDIZyBA9Khg0w==
+"@helium/nft-proxy-sdk@0.0.8-next.19+4fa4c6b":
+ version "0.0.8-next.19"
+ resolved "https://registry.yarnpkg.com/@helium/nft-proxy-sdk/-/nft-proxy-sdk-0.0.8-next.19.tgz#9e3d0ecc069e50ed6710fe9540e800d5ba264920"
+ integrity sha512-uwcE8GVAvScGIZtdG+M08Ck+wxYkS+KSnqun9tW2MiXS0IvbXLAiDxvYKiPswOYiaHKn+XGhEkhyyqtU1fbLDA==
dependencies:
"@coral-xyz/anchor" "^0.28.0"
"@helium/anchor-resolvers" "^0.2.17"
- "@helium/modular-governance-idls" "^0.0.8-next.17+7ff2115"
+ "@helium/modular-governance-idls" "^0.0.8-next.19+4fa4c6b"
"@solana/spl-token" "^0.3.8"
"@helium/organization-sdk@^0.0.8":
@@ -327,13 +327,13 @@
"@helium/anchor-resolvers" "^0.5.0"
"@helium/modular-governance-idls" "^0.0.8"
-"@helium/spl-utils@^0.7.6", "@helium/spl-utils@file:.yalc/@helium/spl-utils":
- version "0.7.6"
+"@helium/spl-utils@^0.7.12", "@helium/spl-utils@file:.yalc/@helium/spl-utils":
+ version "0.7.12"
dependencies:
"@coral-xyz/anchor" "^0.28.0"
- "@helium/account-fetch-cache" "^0.7.6"
+ "@helium/account-fetch-cache" "^0.7.12"
"@helium/address" "^4.10.2"
- "@helium/anchor-resolvers" "^0.7.6"
+ "@helium/anchor-resolvers" "^0.7.12"
"@metaplex-foundation/mpl-token-metadata" "^2.10.0"
"@solana/spl-account-compression" "^0.1.7"
"@solana/spl-token" "^0.3.8"
@@ -365,18 +365,18 @@
bs58 "^4.0.1"
"@helium/voter-stake-registry-hooks@file:.yalc/@helium/voter-stake-registry-hooks":
- version "0.7.6"
+ version "0.7.12"
dependencies:
"@coral-xyz/anchor" "^0.28.0"
- "@helium/account-fetch-cache" "^0.7.6"
- "@helium/account-fetch-cache-hooks" "^0.7.6"
- "@helium/circuit-breaker-sdk" "^0.7.6"
- "@helium/helium-react-hooks" "^0.7.6"
- "@helium/helium-sub-daos-sdk" "^0.7.6"
+ "@helium/account-fetch-cache" "^0.7.12"
+ "@helium/account-fetch-cache-hooks" "^0.7.12"
+ "@helium/circuit-breaker-sdk" "^0.7.12"
+ "@helium/helium-react-hooks" "^0.7.12"
+ "@helium/helium-sub-daos-sdk" "^0.7.12"
"@helium/modular-governance-hooks" "^0.0.8"
- "@helium/modular-governance-idls" "0.0.8-next.17+7ff2115"
- "@helium/spl-utils" "^0.7.6"
- "@helium/voter-stake-registry-sdk" "^0.7.6"
+ "@helium/modular-governance-idls" "0.0.8-next.19+4fa4c6b"
+ "@helium/spl-utils" "^0.7.12"
+ "@helium/voter-stake-registry-sdk" "^0.7.12"
"@solana/wallet-adapter-base" "^0.9.22"
"@solana/wallet-adapter-react" "^0.15.32"
"@solana/web3.js" "^1.78.8"
@@ -384,14 +384,14 @@
bs58 "^4.0.1"
react-async-hook "^4.0.0"
-"@helium/voter-stake-registry-sdk@^0.7.12", "@helium/voter-stake-registry-sdk@^0.7.6", "@helium/voter-stake-registry-sdk@file:.yalc/@helium/voter-stake-registry-sdk":
- version "0.7.6"
+"@helium/voter-stake-registry-sdk@^0.7.12", "@helium/voter-stake-registry-sdk@file:.yalc/@helium/voter-stake-registry-sdk":
+ version "0.7.12"
dependencies:
"@coral-xyz/anchor" "^0.28.0"
- "@helium/anchor-resolvers" "^0.7.6"
- "@helium/idls" "^0.7.6"
- "@helium/nft-proxy-sdk" "0.0.8-next.17+7ff2115"
- "@helium/spl-utils" "^0.7.6"
+ "@helium/anchor-resolvers" "^0.7.12"
+ "@helium/idls" "^0.7.12"
+ "@helium/nft-proxy-sdk" "0.0.8-next.19+4fa4c6b"
+ "@helium/spl-utils" "^0.7.12"
"@metaplex-foundation/mpl-token-metadata" "^2.10.0"
"@solana/spl-token" "^0.3.8"
bn.js "^5.2.0"
From 29da73b9dd7ae42cb6994991567c0ad0710a7f23 Mon Sep 17 00:00:00 2001
From: bry
Date: Mon, 13 May 2024 09:55:43 -0500
Subject: [PATCH 03/41] Remove instances of useHeliumVsr for useGovernance
---
bin/helium-vote.ts | 2 +-
.../proposals/realm/[proposalKey]/page.tsx | 1 -
src/app/[network]/proxies/page.tsx | 4 +-
src/components/AssignProxyModal.tsx | 8 +--
src/components/Proposal.tsx | 2 +-
src/components/Proxies.tsx | 55 +++++++++++++++----
src/components/ProxyButton.tsx | 4 +-
src/components/ProxyProfile.tsx | 14 ++---
src/components/RevokeProxyButton.tsx | 8 +--
src/components/RevokeProxyModal.tsx | 10 ++--
src/components/VoteHistory.tsx | 5 +-
src/components/VoteOptions.tsx | 4 +-
src/providers/GovernanceProvider.tsx | 8 +--
13 files changed, 70 insertions(+), 55 deletions(-)
diff --git a/bin/helium-vote.ts b/bin/helium-vote.ts
index 4638214..882aa03 100755
--- a/bin/helium-vote.ts
+++ b/bin/helium-vote.ts
@@ -1,4 +1,4 @@
-#!/usr/bin/env ts-node
+#!/usr/bin/env npx ts-node
const { hideBin } = require("yargs/helpers");
diff --git a/src/app/[network]/proposals/realm/[proposalKey]/page.tsx b/src/app/[network]/proposals/realm/[proposalKey]/page.tsx
index 21df4cd..a3cb455 100644
--- a/src/app/[network]/proposals/realm/[proposalKey]/page.tsx
+++ b/src/app/[network]/proposals/realm/[proposalKey]/page.tsx
@@ -36,7 +36,6 @@ const RealmProposalPage = async ({
const proposal: IRealmProposal = fetchRealmProposal(proposalKey);
const content = await getContent(proposal.gist);
- console.log(content);
return (
<>
diff --git a/src/app/[network]/proxies/page.tsx b/src/app/[network]/proxies/page.tsx
index e3899b4..e55ddfa 100644
--- a/src/app/[network]/proxies/page.tsx
+++ b/src/app/[network]/proxies/page.tsx
@@ -14,14 +14,14 @@ export const generateMetadata = async ({ params }: VotersPageParams) => {
return formMetaTags({
title: `${network.toUpperCase()} Positions`,
- url: `https://heliumvote.com/${network}/voters`,
+ url: `https://heliumvote.com/${network}/proxies`,
});
};
export default async function PositionsPage() {
return (
<>
-
+
>
);
diff --git a/src/components/AssignProxyModal.tsx b/src/components/AssignProxyModal.tsx
index 80e4b89..70e115d 100644
--- a/src/components/AssignProxyModal.tsx
+++ b/src/components/AssignProxyModal.tsx
@@ -1,8 +1,5 @@
import { useMint } from "@helium/helium-react-hooks";
-import {
- PositionWithMeta,
- useHeliumVsrState,
-} from "@helium/voter-stake-registry-hooks";
+import { PositionWithMeta } from "@helium/voter-stake-registry-hooks";
import { PublicKey } from "@solana/web3.js";
import BN from "bn.js";
import React, { useEffect, useMemo, useState } from "react";
@@ -13,6 +10,7 @@ import { Loader2 } from "lucide-react";
import { useMetaplexMetadata } from "@/hooks/useMetaplexMetadata";
import { humanReadable } from "@/lib/utils";
import { Button } from "./ui/button";
+import { useGovernance } from "@/providers/GovernanceProvider";
interface AssignProxyModalProps {
onSubmit: (args: {
@@ -28,7 +26,7 @@ export const AssignProxyModal: React.FC<
> = ({ onSubmit, wallet, children }) => {
const [open, setOpen] = useState(false);
- const { loading, positions, mint } = useHeliumVsrState();
+ const { loading, positions, mint } = useGovernance();
const [selectedPositions, setSelectedPositions] = useState>(
new Set()
);
diff --git a/src/components/Proposal.tsx b/src/components/Proposal.tsx
index 97d0bf3..e883135 100644
--- a/src/components/Proposal.tsx
+++ b/src/components/Proposal.tsx
@@ -323,7 +323,7 @@ export const Proposal: FC<{
: "Voting has been Cancelled"}
-
+
([]);
@@ -57,10 +61,35 @@ export function Proxies() {
}
}, [voteService?.registrar.toBase58()]);
+ const handleBrowseProxies = () => {
+ // TODO: Implement
+ };
+
return (
- Browse Proxies
+
{proxy.name}
- {ellipsisMiddle(proxy.wallet)}
+
+ {ellipsisMiddle(proxy.wallet)}
+
diff --git a/src/components/ProxyButton.tsx b/src/components/ProxyButton.tsx
index e602cab..38e71cf 100644
--- a/src/components/ProxyButton.tsx
+++ b/src/components/ProxyButton.tsx
@@ -1,9 +1,9 @@
import React, { useMemo } from "react";
import { useWallet } from "@solana/wallet-adapter-react";
-import { useHeliumVsrState } from "@helium/voter-stake-registry-hooks";
import { PublicKey } from "@solana/web3.js";
import { Button } from "./ui/button";
import { Loader2 } from "lucide-react";
+import { useGovernance } from "@/providers/GovernanceProvider";
export const ProxyButton: React.FC<{
className?: string;
@@ -11,7 +11,7 @@ export const ProxyButton: React.FC<{
isLoading?: boolean;
}> = ({ className = "", onClick, isLoading = false }) => {
const { connected } = useWallet();
- const { loading, positions } = useHeliumVsrState();
+ const { loading, positions } = useGovernance();
const unproxiedPositions = useMemo(
() =>
diff --git a/src/components/ProxyProfile.tsx b/src/components/ProxyProfile.tsx
index 2779511..35c090a 100644
--- a/src/components/ProxyProfile.tsx
+++ b/src/components/ProxyProfile.tsx
@@ -1,10 +1,10 @@
"use client";
import { ellipsisMiddle, humanReadable } from "@/lib/utils";
+import { useGovernance } from "@/providers/GovernanceProvider";
import { useMint } from "@helium/helium-react-hooks";
import {
useAssignProxies,
- useHeliumVsrState,
useProxiedTo,
useUnassignProxies,
} from "@helium/voter-stake-registry-hooks";
@@ -12,19 +12,17 @@ import { EnhancedProxy, WithRank } from "@helium/voter-stake-registry-sdk";
import { PublicKey } from "@solana/web3.js";
import BN from "bn.js";
import Image from "next/image";
+import Link from "next/link";
import { useMemo, useState } from "react";
+import { FaArrowLeft } from "react-icons/fa6";
import ReactMarkdown from "react-markdown";
import { AssignProxyModal } from "./AssignProxyModal";
+import { ContentSection } from "./ContentSection";
import { ProxyButton } from "./ProxyButton";
import { RevokeProxyButton } from "./RevokeProxyButton";
import { RevokeProxyModal } from "./RevokeProxyModal";
-import VoteHistory from "./VoteHistory";
-import { ContentSection } from "./ContentSection";
import { Card, CardContent, CardHeader } from "./ui/card";
-import { FaArrowLeft } from "react-icons/fa6";
-import Link from "next/link";
-import { useNetwork } from "@/hooks/useNetwork";
-import { useGovernance } from "@/providers/GovernanceProvider";
+import VoteHistory from "./VoteHistory";
export function ProxyProfile({
proxy,
@@ -35,7 +33,7 @@ export function ProxyProfile({
detail: string;
image: string;
}) {
- const { mint } = useHeliumVsrState();
+ const { mint } = useGovernance();
const { info: mintAcc } = useMint(mint);
const decimals = mintAcc?.decimals;
const [revokeProxyModalVisible, setRevokeProxyModalVisible] = useState(false);
diff --git a/src/components/RevokeProxyButton.tsx b/src/components/RevokeProxyButton.tsx
index e628dd2..a982207 100644
--- a/src/components/RevokeProxyButton.tsx
+++ b/src/components/RevokeProxyButton.tsx
@@ -1,9 +1,9 @@
-import React, { useMemo } from "react";
+import { useGovernance } from "@/providers/GovernanceProvider";
import { useWallet } from "@solana/wallet-adapter-react";
-import { useHeliumVsrState } from "@helium/voter-stake-registry-hooks";
import { PublicKey } from "@solana/web3.js";
-import { Button } from "./ui/button";
import { Loader2 } from "lucide-react";
+import React, { useMemo } from "react";
+import { Button } from "./ui/button";
export const RevokeProxyButton: React.FC<{
className?: string;
@@ -12,7 +12,7 @@ export const RevokeProxyButton: React.FC<{
wallet?: PublicKey;
}> = ({ wallet, className = "", onClick, isLoading = false }) => {
const { connected } = useWallet();
- const { loading, positions } = useHeliumVsrState();
+ const { loading, positions } = useGovernance();
const proxiedPositions = useMemo(
() =>
diff --git a/src/components/RevokeProxyModal.tsx b/src/components/RevokeProxyModal.tsx
index 6871ff0..b92e5fc 100644
--- a/src/components/RevokeProxyModal.tsx
+++ b/src/components/RevokeProxyModal.tsx
@@ -1,7 +1,5 @@
-import {
- PositionWithMeta,
- useHeliumVsrState,
-} from "@helium/voter-stake-registry-hooks";
+import { useGovernance } from "@/providers/GovernanceProvider";
+import { PositionWithMeta } from "@helium/voter-stake-registry-hooks";
import { DialogContent } from "@radix-ui/react-dialog";
import { PublicKey } from "@solana/web3.js";
import { Loader2 } from "lucide-react";
@@ -24,7 +22,7 @@ export const RevokeProxyModal: React.FC = ({
onSubmit,
wallet,
}) => {
- const { loading, positions, mint } = useHeliumVsrState();
+ const { loading, positions, mint } = useGovernance();
const [selectedPositions, setSelectedPositions] = useState>(
new Set()
);
@@ -58,7 +56,7 @@ export const RevokeProxyModal: React.FC = ({
onClose();
} catch (e: any) {
setIsSubmitting(false);
- toast(e.message || "Unable to Revoke proxy")
+ toast(e.message || "Unable to Revoke proxy");
}
};
diff --git a/src/components/VoteHistory.tsx b/src/components/VoteHistory.tsx
index 4bf20e4..9419809 100644
--- a/src/components/VoteHistory.tsx
+++ b/src/components/VoteHistory.tsx
@@ -1,6 +1,6 @@
import { humanReadable } from "@/lib/utils";
+import { useGovernance } from "@/providers/GovernanceProvider";
import { useMint } from "@helium/helium-react-hooks";
-import { useHeliumVsrState } from "@helium/voter-stake-registry-hooks";
import { ProposalWithVotes } from "@helium/voter-stake-registry-sdk";
import { PublicKey } from "@solana/web3.js";
import BN from "bn.js";
@@ -8,7 +8,7 @@ import { useEffect, useState } from "react";
import InfiniteScroll from "react-infinite-scroll-component";
export default function VoteHistory({ wallet }: { wallet: PublicKey }) {
- const { voteService, mint } = useHeliumVsrState();
+ const { voteService, mint } = useGovernance();
const { info: mintAcc } = useMint(mint);
const decimals = mintAcc?.decimals;
const [voteHistories, setVoteHistory] = useState([]);
@@ -79,7 +79,6 @@ export default function VoteHistory({ wallet }: { wallet: PublicKey }) {
const totalWeight = voteHistory.choices.reduce((acc, c) => {
return acc.add(new BN(c.weight));
}, new BN(0));
- console.log(totalWeight.toNumber());
const percent = voteHistory.votes[0]
? (100 * Number(voteHistory.votes[0].weight)) /
totalWeight.toNumber()
diff --git a/src/components/VoteOptions.tsx b/src/components/VoteOptions.tsx
index edf2af7..44b730f 100644
--- a/src/components/VoteOptions.tsx
+++ b/src/components/VoteOptions.tsx
@@ -1,7 +1,7 @@
"use client";
import { VoteChoiceWithMeta } from "@/lib/types";
-import { useHeliumVsrState, useRelinquishVote, useVote } from "@helium/voter-stake-registry-hooks";
+import { useRelinquishVote, useVote } from "@helium/voter-stake-registry-hooks";
import { PublicKey } from "@solana/web3.js";
import React, { FC, useState } from "react";
import { VoteOption } from "./VoteOption";
@@ -17,8 +17,6 @@ export const VoteOptions: FC<{
}> = ({ choices = [], maxChoicesPerVoter, proposalKey }) => {
const [currVote, setCurrVote] = useState(0);
const { voteWeights, canVote, vote, loading: voting } = useVote(proposalKey);
- const { positions } = useHeliumVsrState()
- console.log(positions)
const {
canRelinquishVote,
diff --git a/src/providers/GovernanceProvider.tsx b/src/providers/GovernanceProvider.tsx
index f00ed0b..9b08aad 100644
--- a/src/providers/GovernanceProvider.tsx
+++ b/src/providers/GovernanceProvider.tsx
@@ -14,13 +14,7 @@ import {
import { getRegistrarKey } from "@helium/voter-stake-registry-sdk";
import { PublicKey } from "@solana/web3.js";
import { useParams } from "next/navigation";
-import {
- FC,
- ReactNode,
- createContext,
- useContext,
- useMemo,
-} from "react";
+import { FC, ReactNode, createContext, useContext, useMemo } from "react";
import { useAsync } from "react-async-hook";
type GovNetwork = "hnt" | "mobile" | "iot";
From 02d2503137ff48a86c5b59f0e6f9dc59600690b1 Mon Sep 17 00:00:00 2001
From: bry
Date: Mon, 13 May 2024 09:59:45 -0500
Subject: [PATCH 04/41] Propposal takes 100% width on small screens now
---
src/components/Proposal.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/Proposal.tsx b/src/components/Proposal.tsx
index e883135..b80b49f 100644
--- a/src/components/Proposal.tsx
+++ b/src/components/Proposal.tsx
@@ -323,7 +323,7 @@ export const Proposal: FC<{
: "Voting has been Cancelled"}
-
+
Date: Mon, 13 May 2024 11:04:55 -0500
Subject: [PATCH 05/41] rework some styles on proxies and nuke hard coded color
value
---
src/app/globals.css | 2 +-
src/components/Header.tsx | 136 ++++++++++++++++-------------------
src/components/Positions.tsx | 33 +++++----
src/components/Proxies.tsx | 81 ++++++++++-----------
src/components/SubNav.tsx | 71 ++++++++++--------
src/components/ui/toggle.tsx | 19 ++---
tailwind.config.ts | 1 -
7 files changed, 170 insertions(+), 173 deletions(-)
diff --git a/src/app/globals.css b/src/app/globals.css
index 1370407..287f217 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -46,7 +46,7 @@
--secondary: 213 19% 32%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
- --muted-foreground: 215 20.2% 65.1%;
+ --muted-foreground: 213 19% 56%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
diff --git a/src/components/Header.tsx b/src/components/Header.tsx
index 1bacb75..b20e664 100644
--- a/src/components/Header.tsx
+++ b/src/components/Header.tsx
@@ -12,81 +12,71 @@ export const Header: FC<{
hideHero?: boolean;
hideNav?: boolean;
route?: string;
-}> = ({ hideHero = false, hideNav = false, route = undefined }) => {
- const isOnPositions = route?.includes("/positions");
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+}> = ({ hideHero = false, hideNav = false, route = undefined }) => (
+
+
- {!hideHero && (
-
-
-
-
-
- Helium
-
- Governance
-
-
- Where the community comes together to make decisions on
- the network.
-
-
+
+
+
+
+
+
+
+
+
+
+ {!hideHero && (
+
+
+
+
+
+ Helium
+
+ Governance
+
+
+ Where the community comes together to make decisions on
+ the network.
+
+
-
- {/* eslint-disable-next-line @next/next/no-img-element */}
-
-
+
+ {/* eslint-disable-next-line @next/next/no-img-element */}
+
-
- )}
- {!hideNav && (
-
-
-
- {isOnPositions ? (
-
-
-
- ) : (
-
- )}
-
- )}
-
- );
-};
+
+ )}
+ {!hideNav && (
+
+
+
+
+
+
+ )}
+
+);
diff --git a/src/components/Positions.tsx b/src/components/Positions.tsx
index d627025..1819dc6 100644
--- a/src/components/Positions.tsx
+++ b/src/components/Positions.tsx
@@ -132,21 +132,24 @@ export const Positions: FC = () => {
All Positions
- {network === "hnt" && (
-
- )}
+
+
+ {network === "hnt" && (
+
+ )}
+
{!notDecayedPositions?.length && !decayedPositions?.length && (
diff --git a/src/components/Proxies.tsx b/src/components/Proxies.tsx
index a8bc184..8743319 100644
--- a/src/components/Proxies.tsx
+++ b/src/components/Proxies.tsx
@@ -22,7 +22,7 @@ import { useGovernance } from "@/providers/GovernanceProvider";
function CardDetail({ title, value }: { title: string; value: string }) {
return (
-
+
{title}
{value}
@@ -85,7 +85,7 @@ export function Proxies() {
disabled={!proxies.length}
onClick={handleBrowseProxies}
>
-
+
Browse
@@ -104,49 +104,44 @@ export function Proxies() {
{proxies.map((proxy, index) => (
-
-
- Assigned {proxy.numAssignments} positions
-
-
-
-
-
- {proxy.name}
-
-
-
{proxy.name}
-
- {ellipsisMiddle(proxy.wallet)}
-
-
+
+
+
+
+ {proxy.name}
+
+
+
{proxy.name}
+
+ {ellipsisMiddle(proxy.wallet)}
+
+
-
-
-
-
-
+
+
+
+
diff --git a/src/components/SubNav.tsx b/src/components/SubNav.tsx
index c19dbbf..5b890ba 100644
--- a/src/components/SubNav.tsx
+++ b/src/components/SubNav.tsx
@@ -10,6 +10,8 @@ import { LuScrollText } from "react-icons/lu";
import {
DropdownMenu,
DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuItem,
DropdownMenuTrigger,
} from "./ui/dropdown-menu";
import { Button } from "./ui/button";
@@ -31,7 +33,12 @@ export const SubNav: React.FC = () => {
return (
-
+
@@ -60,39 +67,41 @@ export const SubNav: React.FC = () => {
-
-
diff --git a/src/components/ui/toggle.tsx b/src/components/ui/toggle.tsx
index 1adebf9..5895834 100644
--- a/src/components/ui/toggle.tsx
+++ b/src/components/ui/toggle.tsx
@@ -1,10 +1,10 @@
-"use client"
+"use client";
-import * as React from "react"
-import * as TogglePrimitive from "@radix-ui/react-toggle"
-import { cva, type VariantProps } from "class-variance-authority"
+import * as React from "react";
+import * as TogglePrimitive from "@radix-ui/react-toggle";
+import { cva, type VariantProps } from "class-variance-authority";
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/utils";
const toggleVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm rounded-full py-0 px-4 font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-black/30 data-[state=on]:text-accent-foreground",
@@ -12,6 +12,7 @@ const toggleVariants = cva(
variants: {
variant: {
default: "bg-transparent",
+ subNav: "bg-transparent hover:text-muted-foreground hover:bg-black/15",
outline:
"border border-input bg-transparent hover:bg-accent hover:text-accent-foreground",
},
@@ -26,7 +27,7 @@ const toggleVariants = cva(
size: "default",
},
}
-)
+);
const Toggle = React.forwardRef<
React.ElementRef
,
@@ -38,8 +39,8 @@ const Toggle = React.forwardRef<
className={cn(toggleVariants({ variant, size, className }))}
{...props}
/>
-))
+));
-Toggle.displayName = TogglePrimitive.Root.displayName
+Toggle.displayName = TogglePrimitive.Root.displayName;
-export { Toggle, toggleVariants }
+export { Toggle, toggleVariants };
diff --git a/tailwind.config.ts b/tailwind.config.ts
index 0718ff6..a543f34 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -113,7 +113,6 @@ const config = {
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
- muted: "#28303B",
},
},
borderRadius: {
From 7da543b3738fc2b5d2e41a52420db7a94e3754f2 Mon Sep 17 00:00:00 2001
From: bry
Date: Tue, 14 May 2024 12:53:18 -0500
Subject: [PATCH 06/41] change tab title
---
src/app/[network]/proxies/page.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/app/[network]/proxies/page.tsx b/src/app/[network]/proxies/page.tsx
index e55ddfa..717dcb0 100644
--- a/src/app/[network]/proxies/page.tsx
+++ b/src/app/[network]/proxies/page.tsx
@@ -13,7 +13,7 @@ export const generateMetadata = async ({ params }: VotersPageParams) => {
const { network } = params;
return formMetaTags({
- title: `${network.toUpperCase()} Positions`,
+ title: `${network.toUpperCase()} Proxies`,
url: `https://heliumvote.com/${network}/proxies`,
});
};
From da79de1b0d85bb8d595dfc27004e817de7252138 Mon Sep 17 00:00:00 2001
From: Noah Prince
Date: Fri, 24 May 2024 15:11:54 -0700
Subject: [PATCH 07/41] WIP
---
package.json | 4 +
public/images/hntWhite.svg | 5 +
public/images/iotWhite.svg | 3 +
public/images/mobileWhite.svg | 3 +
src/app/[network]/proxies/[wallet]/page.tsx | 2 +-
src/components/AssignProxyModal.tsx | 155 ++++++++++--------
src/components/CreatePositionModal.tsx | 76 +++------
src/components/Markdown.tsx | 65 ++++++++
src/components/NetworkSelect.tsx | 46 ++++++
src/components/PositionPreview.tsx | 69 ++++++++
src/components/Proposal.tsx | 64 +-------
src/components/Proxies.tsx | 123 +++++++-------
src/components/ProxyButton.tsx | 26 +--
src/components/ProxyProfile.tsx | 43 ++---
src/components/ProxySearch.tsx | 88 ++++++++++
src/components/RevokeProxyButton.tsx | 30 ++--
src/components/RevokeProxyModal.tsx | 61 +++----
src/components/SubNav.tsx | 17 +-
src/components/VoteHistory.tsx | 65 +++++++-
src/components/ui/autocomplete.tsx | 168 ++++++++++++++++++++
src/components/ui/button.tsx | 5 +-
src/components/ui/command.tsx | 155 ++++++++++++++++++
src/components/ui/dropdown-menu.tsx | 74 ++++-----
src/components/ui/popover.tsx | 31 ++++
src/components/ui/select.tsx | 160 +++++++++++++++++++
src/components/ui/slider.tsx | 28 ++++
yarn.lock | 93 ++++++++++-
27 files changed, 1287 insertions(+), 372 deletions(-)
create mode 100644 public/images/hntWhite.svg
create mode 100644 public/images/iotWhite.svg
create mode 100644 public/images/mobileWhite.svg
create mode 100644 src/components/Markdown.tsx
create mode 100644 src/components/NetworkSelect.tsx
create mode 100644 src/components/PositionPreview.tsx
create mode 100644 src/components/ProxySearch.tsx
create mode 100644 src/components/ui/autocomplete.tsx
create mode 100644 src/components/ui/command.tsx
create mode 100644 src/components/ui/popover.tsx
create mode 100644 src/components/ui/select.tsx
create mode 100644 src/components/ui/slider.tsx
diff --git a/package.json b/package.json
index be7e9d3..46ff12c 100644
--- a/package.json
+++ b/package.json
@@ -28,8 +28,11 @@
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-hover-card": "^1.0.7",
+ "@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-progress": "^1.0.3",
+ "@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-separator": "^1.0.3",
+ "@radix-ui/react-slider": "^1.1.2",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-toggle": "^1.0.3",
"@radix-ui/react-toggle-group": "^1.0.4",
@@ -45,6 +48,7 @@
"class-variance-authority": "^0.7.0",
"classnames": "^2.5.1",
"clsx": "^2.1.0",
+ "cmdk": "^1.0.0",
"date-fns": "^3.3.1",
"lucide-react": "^0.323.0",
"markdown-it": "^14.0.0",
diff --git a/public/images/hntWhite.svg b/public/images/hntWhite.svg
new file mode 100644
index 0000000..cddf61f
--- /dev/null
+++ b/public/images/hntWhite.svg
@@ -0,0 +1,5 @@
+
diff --git a/public/images/iotWhite.svg b/public/images/iotWhite.svg
new file mode 100644
index 0000000..f9cea50
--- /dev/null
+++ b/public/images/iotWhite.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/images/mobileWhite.svg b/public/images/mobileWhite.svg
new file mode 100644
index 0000000..0e24e8b
--- /dev/null
+++ b/public/images/mobileWhite.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/src/app/[network]/proxies/[wallet]/page.tsx b/src/app/[network]/proxies/[wallet]/page.tsx
index d2774a8..4fc93b5 100644
--- a/src/app/[network]/proxies/[wallet]/page.tsx
+++ b/src/app/[network]/proxies/[wallet]/page.tsx
@@ -18,7 +18,7 @@ export default async function ProxyPage({
return (
<>
-
+
>
);
diff --git a/src/components/AssignProxyModal.tsx b/src/components/AssignProxyModal.tsx
index 70e115d..0c0ce09 100644
--- a/src/components/AssignProxyModal.tsx
+++ b/src/components/AssignProxyModal.tsx
@@ -1,16 +1,16 @@
-import { useMint } from "@helium/helium-react-hooks";
+import { useGovernance } from "@/providers/GovernanceProvider";
import { PositionWithMeta } from "@helium/voter-stake-registry-hooks";
import { PublicKey } from "@solana/web3.js";
import BN from "bn.js";
+import { Loader2 } from "lucide-react";
import React, { useEffect, useMemo, useState } from "react";
-import { getMinDurationFmt, getTimeLeftFromNowFmt } from "@/lib/dateTools";
import { toast } from "sonner";
-import { Dialog, DialogContent, DialogTrigger } from "./ui/dialog";
-import { Loader2 } from "lucide-react";
-import { useMetaplexMetadata } from "@/hooks/useMetaplexMetadata";
-import { humanReadable } from "@/lib/utils";
+import { NetworkSelect } from "./NetworkSelect";
+import { PositionPreview } from "./PositionPreview";
+import { ProxySearch } from "./ProxySearch";
import { Button } from "./ui/button";
-import { useGovernance } from "@/providers/GovernanceProvider";
+import { Dialog, DialogContent, DialogTrigger } from "./ui/dialog";
+import { Slider } from "./ui/slider";
interface AssignProxyModalProps {
onSubmit: (args: {
@@ -25,8 +25,10 @@ export const AssignProxyModal: React.FC<
React.PropsWithChildren
> = ({ onSubmit, wallet, children }) => {
const [open, setOpen] = useState(false);
+ const { network: networkDefault } = useGovernance();
+ const [network, setNetwork] = useState(networkDefault);
- const { loading, positions, mint } = useGovernance();
+ const { loading, positions } = useGovernance();
const [selectedPositions, setSelectedPositions] = useState>(
new Set()
);
@@ -34,7 +36,7 @@ export const AssignProxyModal: React.FC<
const unproxiedPositions = useMemo(
() =>
positions?.filter(
- (p) => !p.proxy || p.proxy.nextOwner.equals(PublicKey.default)
+ (p) => !p.proxy || p.proxy.nextVoter.equals(PublicKey.default)
) || [],
[positions]
);
@@ -103,13 +105,31 @@ export const AssignProxyModal: React.FC<
setOpen(!open);
};
+ const selectedAll = unproxiedPositions.length === selectedPositions.size;
+
return (