Skip to content

Commit c66b3b8

Browse files
authored
Implement better delegation with automation + change (#233)
* Implement better delegation with automation + change * Bump version * Hide data split for now
1 parent 635f8e5 commit c66b3b8

14 files changed

Lines changed: 704 additions & 144 deletions

package.json

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,19 @@
1010
},
1111
"dependencies": {
1212
"@coral-xyz/anchor": "^0.31.0",
13-
"@helium/account-fetch-cache": "^0.10.11",
14-
"@helium/account-fetch-cache-hooks": "^0.10.11",
15-
"@helium/helium-react-hooks": "^0.10.11",
16-
"@helium/hpl-crons-sdk": "^0.10.11",
13+
"@helium/account-fetch-cache": "^0.10.13",
14+
"@helium/account-fetch-cache-hooks": "^0.10.13",
15+
"@helium/helium-react-hooks": "^0.10.13",
16+
"@helium/hpl-crons-sdk": "^0.10.13",
1717
"@helium/modular-governance-hooks": "^0.1.5",
1818
"@helium/modular-governance-idls": "^0.1.5",
19-
"@helium/no-emit-sdk": "^0.10.11",
19+
"@helium/no-emit-sdk": "^0.10.13",
2020
"@helium/organization-sdk": "^0.1.5",
21-
"@helium/spl-utils": "^0.10.11",
21+
"@helium/spl-utils": "^0.10.13",
2222
"@helium/state-controller-sdk": "^0.1.5",
2323
"@helium/tuktuk-sdk": "^0.0.8",
24-
"@helium/voter-stake-registry-hooks": "^0.10.11",
25-
"@helium/voter-stake-registry-sdk": "^0.10.11",
24+
"@helium/voter-stake-registry-hooks": "^0.10.13",
25+
"@helium/voter-stake-registry-sdk": "^0.10.13",
2626
"@hookform/resolvers": "^3.3.4",
2727
"@metaplex-foundation/mpl-token-metadata": "2.10.0",
2828
"@project-serum/anchor": "^0.26.0",
@@ -37,6 +37,7 @@
3737
"@radix-ui/react-separator": "^1.0.3",
3838
"@radix-ui/react-slider": "^1.1.2",
3939
"@radix-ui/react-slot": "^1.0.2",
40+
"@radix-ui/react-switch": "^1.2.4",
4041
"@radix-ui/react-toggle": "^1.0.3",
4142
"@radix-ui/react-toggle-group": "^1.0.4",
4243
"@solana/spl-token": "^0.4.0",
@@ -77,15 +78,15 @@
7778
"resolutions": {
7879
"@tanstack/react-query": "5.45.1",
7980
"@solana/web3.js": "^1.90.0",
80-
"@helium/account-fetch-cache": "^0.10.11",
81-
"@helium/account-fetch-cache-hooks": "^0.10.11",
82-
"@helium/helium-react-hooks": "^0.10.11",
83-
"@helium/voter-stake-registry-hooks": "^0.10.11",
81+
"@helium/account-fetch-cache": "^0.10.13",
82+
"@helium/account-fetch-cache-hooks": "^0.10.13",
83+
"@helium/helium-react-hooks": "^0.10.13",
84+
"@helium/voter-stake-registry-hooks": "^0.10.13",
8485
"@helium/modular-governance-idls": "^0.1.5",
85-
"@helium/spl-utils": "^0.10.11",
86+
"@helium/spl-utils": "^0.10.13",
8687
"@helium/modular-governance-hooks": "^0.1.5",
8788
"@solana/wallet-adapter-react": "^0.15.35",
88-
"@helium/voter-stake-registry-sdk": "^0.10.11"
89+
"@helium/voter-stake-registry-sdk": "^0.10.13"
8990
},
9091
"devDependencies": {
9192
"@tailwindcss/typography": "^0.5.10",
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { Switch } from "./ui/switch";
2+
3+
export interface AutomationSettingsProps {
4+
automationEnabled: boolean;
5+
setAutomationEnabled: (enabled: boolean) => void;
6+
solFees?: number;
7+
prepaidTxFees?: number;
8+
}
9+
10+
export const AutomationSettings = ({
11+
automationEnabled,
12+
setAutomationEnabled,
13+
solFees = 0,
14+
prepaidTxFees = 0,
15+
}: AutomationSettingsProps) => {
16+
return (
17+
<div className="bg-slate-850 rounded-lg p-4">
18+
<div className="flex flex-row justify-between items-center">
19+
<div className="flex flex-col">
20+
<span className="text-sm">Enable Automation</span>
21+
<span className="text-xs text-muted-foreground">
22+
Enable automatic claiming of rewards
23+
</span>
24+
</div>
25+
<Switch
26+
checked={automationEnabled}
27+
onCheckedChange={setAutomationEnabled}
28+
/>
29+
</div>
30+
<div className="flex flex-row justify-between items-center mt-4 mb-2">
31+
<span className="text-sm text-muted-foreground">Rent Fees</span>
32+
<span className="text-sm">{solFees.toFixed(6)} SOL</span>
33+
</div>
34+
<div className="flex flex-row justify-between items-center">
35+
<span className="text-sm text-muted-foreground">
36+
Prepaid Transaction Fees
37+
</span>
38+
<span className="text-sm">{prepaidTxFees.toFixed(6)} SOL</span>
39+
</div>
40+
</div>
41+
);
42+
};

src/components/CreatePositionModal.tsx

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ import { Button } from "./ui/button";
3131
import { Dialog, DialogContent, DialogTrigger } from "./ui/dialog";
3232
import { PositionPreview } from "./PositionPreview";
3333
import { MOBILE_SUB_DAO_KEY } from "@/lib/constants";
34+
import { DataSplitBars } from "./DataSplitBars";
35+
import { AutomationSettings } from "./AutomationSettings";
3436

3537
export const CreatePositionModal: FC<React.PropsWithChildren<{}>> = ({
3638
children,
@@ -41,10 +43,13 @@ export const CreatePositionModal: FC<React.PropsWithChildren<{}>> = ({
4143
const [isSubmitting, setIsSubmitting] = useState(false);
4244
const [formValues, setFormValues] = useState<LockTokensFormValues>();
4345
const [selectedSubDaoPk, setSelectedSubDaoPk] = useState<PublicKey>();
46+
const [automationEnabled, setAutomationEnabled] = useState(true);
4447
const { publicKey: wallet } = useWallet();
4548
const { mint, subDaos, registrar, refetch: refetchState } = useGovernance();
4649
const { amount: ownedAmount, decimals } = useOwnedAmount(wallet, mint);
47-
const { error: createPositionError, createPosition } = useCreatePosition();
50+
const { error: createPositionError, createPosition, rentFee, prepaidTxFees, insufficientBalance } = useCreatePosition({
51+
automationEnabled,
52+
});
4853
const steps = useMemo(() => (mint.equals(HNT_MINT) ? 3 : 2), [mint]);
4954

5055
useEffect(() => {
@@ -193,14 +198,27 @@ export const CreatePositionModal: FC<React.PropsWithChildren<{}>> = ({
193198
<div className="flex flex-col gap-4 p-4 text-sm bg-slate-600 rounded">
194199
Delegating your position to a subnetwork and voting regularly
195200
earns you HNT rewards. Select the subnetwork you believe offers
196-
the greatest potential for growth and impact. This choice does
197-
not affect the rewarded amount.
201+
the greatest potential for future revenue. This choice does not
202+
affect your HNT rewards for this delegation. Delegation
203+
percentages drive the amount of HNT emissions that go towards
204+
each subnetwork&apos;s growth.
198205
</div>
206+
207+
<DataSplitBars />
208+
199209
<SubDaoSelection
200210
hideNoneOption
201211
selectedSubDaoPk={selectedSubDaoPk}
202212
onSelect={setSelectedSubDaoPk}
203213
/>
214+
215+
<AutomationSettings
216+
automationEnabled={automationEnabled}
217+
setAutomationEnabled={setAutomationEnabled}
218+
solFees={rentFee}
219+
prepaidTxFees={prepaidTxFees}
220+
/>
221+
204222
<div className="flex flex-col gap-4 p-4 text-sm bg-slate-600 rounded">
205223
<div>
206224
<span className="font-medium">
@@ -232,8 +250,9 @@ export const CreatePositionModal: FC<React.PropsWithChildren<{}>> = ({
232250
<Button
233251
className="flex-grow text-foreground gap-2"
234252
onClick={() => setStep(step + 1)}
253+
disabled={!!insufficientBalance}
235254
>
236-
Review
255+
{insufficientBalance ? "Insufficient SOL" : "Review"}
237256
</Button>
238257
</div>
239258
</>

src/components/DataSplitBars.tsx

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { humanReadable } from "@/lib/utils"
2+
import { useDataBurnSplit, useSubDaoDelegationSplit } from "@helium/voter-stake-registry-hooks"
3+
import { useGovernance } from "@/providers/GovernanceProvider"
4+
import BN from "bn.js"
5+
import { SplitBar } from "./SplitBar"
6+
7+
export const DataSplitBars = () => {
8+
const { voteService } = useGovernance()
9+
const { data: revData, isLoading: revLoading } = useDataBurnSplit({
10+
voteService,
11+
})
12+
const { iot: iotDataUsageRev = 0, mobile: mobileDataUsageRev = 0 } =
13+
(revData || {}) as { iot: number; mobile: number }
14+
const { data: delegationData, isLoading: delegationLoading } =
15+
useSubDaoDelegationSplit({
16+
voteService,
17+
})
18+
const {
19+
iot: iotDelegation = new BN(0),
20+
mobile: mobileDelegation = new BN(0),
21+
} = (delegationData || {}) as { iot: BN; mobile: BN }
22+
23+
const totalDataUsage = Number(mobileDataUsageRev) + Number(iotDataUsageRev)
24+
const totalVetokens =
25+
mobileDelegation && iotDelegation
26+
? new BN(mobileDelegation).add(new BN(iotDelegation))
27+
: new BN(0)
28+
29+
const mobileDelegationPercentage =
30+
mobileDelegation && totalVetokens.gt(new BN(0))
31+
? new BN(mobileDelegation)
32+
.mul(new BN(10000))
33+
.div(totalVetokens)
34+
.toNumber() / 100
35+
: 0
36+
37+
const iotDelegationPercentage =
38+
iotDelegation && totalVetokens.gt(new BN(0))
39+
? new BN(iotDelegation).mul(new BN(10000)).div(totalVetokens).toNumber() /
40+
100
41+
: 0
42+
43+
const mobileDataUsagePercentage =
44+
totalDataUsage > 0
45+
? (Number(mobileDataUsageRev) / totalDataUsage) * 100
46+
: 0
47+
48+
const iotDataUsagePercentage =
49+
totalDataUsage > 0 ? (Number(iotDataUsageRev) / totalDataUsage) * 100 : 0
50+
51+
if (revLoading || delegationLoading) return null
52+
53+
return (
54+
<div className="flex flex-col gap-4 mb-4">
55+
<SplitBar
56+
title="Data Usage Revenue (30 days)"
57+
leftValue={`$${humanReadable(
58+
new BN(mobileDataUsageRev.toFixed(0)),
59+
0
60+
)?.replace(".00", "")}`}
61+
rightValue={`$${humanReadable(
62+
new BN(iotDataUsageRev.toFixed(0)),
63+
0
64+
)?.replace(".00", "")}`}
65+
leftPercentage={mobileDataUsagePercentage}
66+
rightPercentage={iotDataUsagePercentage}
67+
leftColor="bg-blue-500"
68+
rightColor="bg-green-500"
69+
leftLabel="Mobile"
70+
rightLabel="IoT"
71+
/>
72+
<SplitBar
73+
title="HNT Emissions Split from Delegations"
74+
leftValue={`${mobileDelegationPercentage.toFixed(1)}%`}
75+
rightValue={`${iotDelegationPercentage.toFixed(1)}%`}
76+
leftPercentage={mobileDelegationPercentage}
77+
rightPercentage={iotDelegationPercentage}
78+
leftColor="bg-blue-500"
79+
rightColor="bg-green-500"
80+
leftLabel="Mobile"
81+
rightLabel="IoT"
82+
/>
83+
</div>
84+
)
85+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"use client";
2+
3+
import Link from "next/link";
4+
import { DataSplitBars } from "./DataSplitBars";
5+
import { Button } from "./ui/button";
6+
import { usePathname } from "next/navigation";
7+
8+
export const DataSplitSection = () => {
9+
const path = usePathname();
10+
const basePath = path.split("/").slice(0, 2).join("/");
11+
12+
return (
13+
<div className="flex flex-col py-12">
14+
<h2 className="text-3xl md:text-4xl font-bold mb-10">
15+
Network Delegation Split
16+
</h2>
17+
<div className="flex flex-col md:flex-row gap-12 items-start">
18+
<div className="flex-1">
19+
<div className="space-y-4 max-w-lg">
20+
<p className="text-lg text-slate-400">
21+
Delegating your staked HNT to a subnetwork and voting regularly earns you HNT rewards.
22+
</p>
23+
<p className="text-lg text-slate-400">
24+
Delegation percentages drive the amount of HNT emissions that go towards each subnetwork&apos;s growth.
25+
</p>
26+
</div>
27+
</div>
28+
<div className="flex-1 w-full max-w-xl">
29+
<DataSplitBars />
30+
<div className="flex justify-end mt-6">
31+
<Link href={`${basePath}/positions`}>
32+
<Button
33+
size="lg"
34+
className="bg-[#4066FF] hover:bg-[#4066FF]/90 text-white font-medium px-8 py-6 text-lg rounded-xl transition-colors"
35+
>
36+
Delegate Now
37+
</Button>
38+
</Link>
39+
</div>
40+
</div>
41+
</div>
42+
</div>
43+
);
44+
};

src/components/Header.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { NetworkTabs } from "./NetworkTabs";
66
import { SubNav } from "./SubNav";
77
import { VeTokensCallout } from "./VeTokensCallout";
88
import { WalletConnectButton } from "./WalletConnectButton";
9+
import { DataSplitSection } from "./DataSplitSection";
910

1011
export const Header: FC<{
1112
hideHero?: boolean;
@@ -43,6 +44,7 @@ export const Header: FC<{
4344
</div>
4445
{!hideHero && (
4546
<ContentSection>
47+
{/* <DataSplitSection /> */}
4648
<div className="flex flex-row h-[342px] py-6 justify-between items-start max-md:py-12 max-md:h-auto">
4749
<div className="flex justify-center items-center">
4850
<div>

0 commit comments

Comments
 (0)