Skip to content

Commit 77848bf

Browse files
authored
Add delegation stats, change delegation endpoint, fix/implement automated claims
1 parent 9a0b09f commit 77848bf

34 files changed

+920
-194
lines changed

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { PublicKey } from "@solana/web3.js";
2+
3+
export const TASK_QUEUE = new PublicKey('H39gEszvsi6AT4rYBiJTuZHJSF5hMHy6CKGTd7wzhsg7')
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11

22
export { useAutomateHotspotClaims } from "./useAutomateHotspotClaims";
33
export { useCronJob } from "./useCronJob";
4-
export { useTaskQueue } from "./useTaskQueue";
4+
export { useTaskQueue } from "./useTaskQueue";
5+
export { useDelegationClaimBot } from "./useDelegationClaimBot";

packages/automation-hooks/src/hooks/useAutomateHotspotClaims.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,10 @@ import { useAsyncCallback } from 'react-async-hook'
2626
import { useCronJob } from './useCronJob'
2727
import { useTaskQueue } from './useTaskQueue'
2828
import { AnchorProvider } from '@coral-xyz/anchor'
29+
import { TASK_QUEUE } from '../constants'
2930

3031
type Schedule = 'daily' | 'weekly' | 'monthly'
3132

32-
const TASK_QUEUE = new PublicKey('H39gEszvsi6AT4rYBiJTuZHJSF5hMHy6CKGTd7wzhsg7')
33-
3433
const getScheduleCronString = (schedule: Schedule) => {
3534
// Get current time and add 1 minute
3635
const now = new Date()
@@ -192,7 +191,7 @@ export const useAutomateHotspotClaims = ({
192191
return (
193192
(MIN_RENT * LAMPORTS_PER_SOL) +
194193
// Actual claim txs
195-
duration * (minCrankReward + 5000) * (totalHotspots || 1) +
194+
duration * 20000 * (totalHotspots || 1) +
196195
// Requeue transactions (5 queues per tx)
197196
duration * minCrankReward * Math.ceil((totalHotspots || 1) / 5)
198197
)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { useAnchorAccount } from '@helium/helium-react-hooks'
2+
import { HplCrons } from '@helium/idls/lib/types/hpl_crons'
3+
import { PublicKey } from '@solana/web3.js'
4+
5+
export const useDelegationClaimBot = (delegationClaimBotKey: PublicKey | undefined) =>
6+
useAnchorAccount<HplCrons, 'delegationClaimBotV0'>(delegationClaimBotKey, 'delegationClaimBotV0')
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
export * from "./constants";
12
export * from "./hooks/index";

packages/helium-sub-daos-sdk/src/resolvers.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,9 @@ export const daoEpochInfoResolver = resolveIndividual(
7373

7474
export const subDaoEpochInfoResolver = resolveIndividual(
7575
async ({ provider, path, accounts, args }) => {
76-
if (path[path.length - 1] === "subDaoEpochInfo" && accounts.registrar) {
76+
const isOld = path[path.length - 1] === "oldSubDaoEpochInfo";
77+
const isNew = path[path.length - 1] === "subDaoEpochInfo";
78+
if ((isOld || isNew) && accounts.registrar) {
7779
const vsr = await init(provider as AnchorProvider, VSR_PROGRAM_ID);
7880
let registrar;
7981
try {
@@ -97,7 +99,7 @@ export const subDaoEpochInfoResolver = resolveIndividual(
9799
}
98100
const subDao = get(accounts, [
99101
...path.slice(0, path.length - 1),
100-
"subDao",
102+
isNew ? "subDao" : "oldSubDao",
101103
]) as PublicKey;
102104
if (subDao) {
103105
const [key] = subDaoEpochInfoKey(subDao, unixTime, PROGRAM_ID);
@@ -167,14 +169,16 @@ export const subDaoEpochInfoResolver = resolveIndividual(
167169

168170
export const closingTimeEpochInfoResolver = resolveIndividual(
169171
async ({ provider, path, accounts }) => {
170-
if (path[path.length - 1] === "closingTimeSubDaoEpochInfo") {
172+
const isNew = path[path.length - 1] === "closingTimeSubDaoEpochInfo";
173+
const isOld = path[path.length - 1] === "oldClosingTimeSubDaoEpochInfo";
174+
if (isNew || isOld) {
171175
const program = await init(provider as AnchorProvider, VSR_PROGRAM_ID);
172176
const hsdProgram = await initHsd(provider as AnchorProvider);
173177
const nftProxyProgram = await initNftProxy(provider as AnchorProvider);
174178

175179
const subDao = get(accounts, [
176180
...path.slice(0, path.length - 1),
177-
"subDao",
181+
isNew ? "subDao" : "oldSubDao",
178182
]) as PublicKey;
179183
const position = get(accounts, [
180184
...path.slice(0, path.length - 1),
@@ -203,9 +207,9 @@ export const closingTimeEpochInfoResolver = resolveIndividual(
203207
const expirationTs =
204208
!delegatedPositionAcc || delegatedPositionAcc.expirationTs.isZero()
205209
? proxyConfigAcc?.seasons
206-
?.reverse()
207-
.find((s) => new BN(now.toString()).gte(s.start))?.end ||
208-
getLockupEffectiveEndTs(positionAcc.lockup)
210+
?.reverse()
211+
.find((s) => new BN(now.toString()).gte(s.start))?.end ||
212+
getLockupEffectiveEndTs(positionAcc.lockup)
209213
: delegatedPositionAcc.expirationTs;
210214
const [key] = subDaoEpochInfoKey(
211215
subDao,
@@ -226,14 +230,16 @@ async function getSolanaUnixTimestamp(provider: Provider): Promise<bigint> {
226230

227231
export const genesisEndEpochInfoResolver = resolveIndividual(
228232
async ({ provider, path, accounts }) => {
229-
if (path[path.length - 1] === "genesisEndSubDaoEpochInfo") {
233+
const isNew = path[path.length - 1] === "genesisEndSubDaoEpochInfo";
234+
const isOld = path[path.length - 1] === "oldGenesisEndSubDaoEpochInfo";
235+
if (isNew || isOld) {
230236
const program = await init(provider as AnchorProvider, VSR_PROGRAM_ID);
231237
const hsdProgram = await initHsd(provider as AnchorProvider);
232238
const nftProxyProgram = await initNftProxy(provider as AnchorProvider);
233239

234240
const subDao = get(accounts, [
235241
...path.slice(0, path.length - 1),
236-
"subDao",
242+
isNew ? "subDao" : "oldSubDao",
237243
]) as PublicKey;
238244
const position = get(accounts, [
239245
...path.slice(0, path.length - 1),

packages/helium-vote-service/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
},
3434
"dependencies": {
3535
"@coral-xyz/anchor": "^0.31.0",
36+
"@duneanalytics/client-sdk": "^0.2.5",
3637
"@fastify/cors": "^8.1.1",
3738
"@fastify/static": "^6",
3839
"@helium/account-fetch-cache": "^0.10.11",

packages/helium-vote-service/src/index.ts

Lines changed: 74 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import cors from "@fastify/cors";
22
import fastifyStatic from "@fastify/static";
33
import { organizationKey } from "@helium/organization-sdk";
44
import { createMemoInstruction } from "@solana/spl-memo";
5+
import { DuneClient } from "@duneanalytics/client-sdk";
56
import {
67
createAtaAndTransferInstructions,
78
HNT_MINT,
@@ -52,6 +53,7 @@ import {
5253
import NodeCache from "node-cache";
5354
import BN from "bn.js";
5455
import { SystemProgram } from "@solana/web3.js";
56+
import { subDaoKey } from "@helium/helium-sub-daos-sdk";
5557

5658
const ORG_IDS = {
5759
[HNT_MINT.toBase58()]: organizationKey("Helium")[0].toBase58(),
@@ -86,6 +88,54 @@ server.get("/v1/sync", async () => {
8688
await readProxiesAndUpsert();
8789
});
8890

91+
// Save for one day
92+
const CACHE_TIME = 1000 * 60 * 60 * 24
93+
let cachedDataBurn: any | null = null
94+
let lastCacheUpdate: Date | null = null
95+
server.get("/v1/data-burn", async (request, reply) => {
96+
if (cachedDataBurn && lastCacheUpdate && lastCacheUpdate.getTime() > Date.now() - CACHE_TIME) {
97+
return cachedDataBurn
98+
}
99+
100+
const client = new DuneClient(process.env.DUNE_API_KEY || "")
101+
const result = await client.getLatestResult({ queryId: 5069123 })
102+
cachedDataBurn = result.result?.rows.reduce((acc, row: any) => {
103+
acc[row.subdao] = row.dc_burned
104+
return acc
105+
}, {} as Record<string, number>)
106+
lastCacheUpdate = new Date()
107+
return cachedDataBurn
108+
});
109+
110+
let cachedDataSubDaoDelegations: any | null = null
111+
let lastCacheUpdateSubDaoDelegations: Date | null = null
112+
server.get("/v1/subdao-delegations", async (request, reply) => {
113+
if (cachedDataSubDaoDelegations && lastCacheUpdateSubDaoDelegations && lastCacheUpdateSubDaoDelegations.getTime() > Date.now() - CACHE_TIME) {
114+
return cachedDataSubDaoDelegations
115+
}
116+
117+
const vetokens = await sequelize.query(`
118+
SELECT
119+
sum(ve_tokens) as "totalVeTokens",
120+
sub_dao as "subDao"
121+
FROM positions_with_vetokens p
122+
JOIN delegated_positions d on d.position = p.address
123+
GROUP BY sub_dao
124+
`);
125+
const result = vetokens[0];
126+
127+
const data = result.reduce((acc: Record<string, number>, row: any) => {
128+
const subDaoStr = row.subDao == subDaoKey(MOBILE_MINT)[0].toBase58() ? "mobile" : row.subDao == subDaoKey(IOT_MINT)[0].toBase58() ? "iot" : null;
129+
if (subDaoStr) {
130+
acc[subDaoStr] = row.totalVeTokens.split(".")[0];
131+
}
132+
return acc;
133+
}, {} as Record<string, number>);
134+
cachedDataSubDaoDelegations = data
135+
lastCacheUpdateSubDaoDelegations = new Date()
136+
return data
137+
})
138+
89139
server.get<{
90140
Params: { registrar: string };
91141
Querystring: {
@@ -148,25 +198,25 @@ server.get<{
148198
limit,
149199
include: position
150200
? [
151-
{
152-
model: Position,
153-
where: {
154-
address: position,
155-
},
156-
attributes: [],
157-
required: true,
201+
{
202+
model: Position,
203+
where: {
204+
address: position,
158205
},
159-
]
206+
attributes: [],
207+
required: true,
208+
},
209+
]
160210
: [
161-
{
162-
model: Position,
163-
where: {
164-
registrar: registrar,
165-
},
166-
attributes: [],
167-
required: true,
211+
{
212+
model: Position,
213+
where: {
214+
registrar: registrar,
168215
},
169-
],
216+
attributes: [],
217+
required: true,
218+
},
219+
],
170220
order: [["index", "DESC"]],
171221
});
172222
});
@@ -237,15 +287,14 @@ WITH
237287
JOIN proxy_registrars pr ON pr.wallet = proxies.wallet
238288
LEFT OUTER JOIN positions_with_proxy_assignments p ON p.voter = proxies.wallet
239289
WHERE pr.registrar = ${escapedRegistrar}
240-
${
241-
request.query.query
242-
? `AND (proxies.name ILIKE ${sequelize.escape(
243-
`%${request.query.query}%`
244-
)} OR proxies.wallet ILIKE ${sequelize.escape(
245-
`%${request.query.query}%`
246-
)})`
247-
: ""
248-
}
290+
${request.query.query
291+
? `AND (proxies.name ILIKE ${sequelize.escape(
292+
`%${request.query.query}%`
293+
)} OR proxies.wallet ILIKE ${sequelize.escape(
294+
`%${request.query.query}%`
295+
)})`
296+
: ""
297+
}
249298
GROUP BY
250299
name,
251300
image,

packages/helium-vote-service/yarn.deploy.lock

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,16 @@ __metadata:
107107
languageName: node
108108
linkType: hard
109109

110+
"@duneanalytics/client-sdk@npm:^0.2.5":
111+
version: 0.2.5
112+
resolution: "@duneanalytics/client-sdk@npm:0.2.5"
113+
dependencies:
114+
deprecated: ^0.0.2
115+
loglevel: ^1.8.0
116+
checksum: 30babbfba9638943c65461d78003aede526ed63874fe4d5989fea3844997f0f3f1089dc3ccc19f2f2bfe73e6f8a21dba6d8d4753a91f1862e0a11fa9817f1926
117+
languageName: node
118+
linkType: hard
119+
110120
"@fastify/accept-negotiator@npm:^1.0.0":
111121
version: 1.1.0
112122
resolution: "@fastify/accept-negotiator@npm:1.1.0"
@@ -285,6 +295,7 @@ __metadata:
285295
resolution: "@helium/helium-vote-service@workspace:."
286296
dependencies:
287297
"@coral-xyz/anchor": ^0.31.0
298+
"@duneanalytics/client-sdk": ^0.2.5
288299
"@fastify/cors": ^8.1.1
289300
"@fastify/static": ^6
290301
"@helium/account-fetch-cache": ^0.10.11
@@ -1636,6 +1647,13 @@ __metadata:
16361647
languageName: node
16371648
linkType: hard
16381649

1650+
"deprecated@npm:^0.0.2":
1651+
version: 0.0.2
1652+
resolution: "deprecated@npm:0.0.2"
1653+
checksum: e8e03bcb9de4d2625b5b2fbdc4cf985a53355e3366c16fe8ae4a44004733d75c916648fe32a7a61535b1d06bfe5ddd1346dd8d44903cecc5b9db587a66b839af
1654+
languageName: node
1655+
linkType: hard
1656+
16391657
"diff@npm:^4.0.1":
16401658
version: 4.0.2
16411659
resolution: "diff@npm:4.0.2"
@@ -2554,6 +2572,13 @@ __metadata:
25542572
languageName: node
25552573
linkType: hard
25562574

2575+
"loglevel@npm:^1.8.0":
2576+
version: 1.9.2
2577+
resolution: "loglevel@npm:1.9.2"
2578+
checksum: 896c67b90a507bfcfc1e9a4daa7bf789a441dd70d95cd13b998d6dd46233a3bfadfb8fadb07250432bbfb53bf61e95f2520f9b11f9d3175cc460e5c251eca0af
2579+
languageName: node
2580+
linkType: hard
2581+
25572582
"lower-case@npm:^2.0.2":
25582583
version: 2.0.2
25592584
resolution: "lower-case@npm:2.0.2"

packages/voter-stake-registry-hooks/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"@coral-xyz/anchor": "^0.31.0",
3535
"@helium/account-fetch-cache": "^0.10.11",
3636
"@helium/account-fetch-cache-hooks": "^0.10.11",
37+
"@helium/automation-hooks": "^0.10.11",
3738
"@helium/circuit-breaker-sdk": "^0.10.11",
3839
"@helium/helium-react-hooks": "^0.10.11",
3940
"@helium/helium-sub-daos-sdk": "^0.10.11",
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { useQuery } from "@tanstack/react-query";
2+
import { VoteService } from "@helium/voter-stake-registry-sdk";
3+
import { queryOptions } from "@tanstack/react-query";
4+
5+
export function dataBurnSplitQuery({
6+
voteService,
7+
}: {
8+
voteService?: VoteService;
9+
}) {
10+
return queryOptions({
11+
queryKey: [
12+
"dataBurnSplit",
13+
voteService?.config,
14+
],
15+
queryFn: () => voteService!.getDataBurnSplit(),
16+
enabled: !!voteService,
17+
});
18+
}
19+
20+
21+
export const useDataBurnSplit = ({
22+
voteService,
23+
}: {
24+
voteService?: VoteService;
25+
}) => {
26+
return useQuery(dataBurnSplitQuery({
27+
voteService: voteService,
28+
}));
29+
};

0 commit comments

Comments
 (0)