@helium/blockchain-api 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +24 -2
  2. package/package.json +31 -31
  3. package/types/README.md +24 -2
  4. package/types/generated/index.d.mts +2481 -0
  5. package/types/generated/index.d.ts +2481 -0
  6. package/types/generated/index.js +723 -0
  7. package/types/generated/index.mjs +614 -0
  8. package/types/index.ts +11 -8
  9. package/src/server/api/errors.ts +0 -152
  10. package/src/server/api/index.ts +0 -40
  11. package/src/server/api/procedures.ts +0 -144
  12. package/src/server/api/routers/fiat/router.ts +0 -709
  13. package/src/server/api/routers/fiat/schemas.ts +0 -157
  14. package/src/server/api/routers/health/router.ts +0 -41
  15. package/src/server/api/routers/hotspots/procedures/claimRewards.ts +0 -258
  16. package/src/server/api/routers/hotspots/procedures/createSplit.ts +0 -253
  17. package/src/server/api/routers/hotspots/procedures/deleteSplit.ts +0 -156
  18. package/src/server/api/routers/hotspots/procedures/getHotspots.ts +0 -31
  19. package/src/server/api/routers/hotspots/procedures/getPendingRewards.ts +0 -44
  20. package/src/server/api/routers/hotspots/procedures/getSplit.ts +0 -88
  21. package/src/server/api/routers/hotspots/procedures/transferHotspot.ts +0 -204
  22. package/src/server/api/routers/hotspots/procedures/updateRewardsDestination.ts +0 -201
  23. package/src/server/api/routers/hotspots/router.ts +0 -30
  24. package/src/server/api/routers/hotspots/schemas.ts +0 -182
  25. package/src/server/api/routers/swap/procedures/getInstructions.ts +0 -152
  26. package/src/server/api/routers/swap/procedures/getQuote.ts +0 -53
  27. package/src/server/api/routers/swap/procedures/getTokens.ts +0 -88
  28. package/src/server/api/routers/swap/router.ts +0 -15
  29. package/src/server/api/routers/swap/schemas.ts +0 -96
  30. package/src/server/api/routers/tokens/procedures/createHntAccount.ts +0 -87
  31. package/src/server/api/routers/tokens/procedures/getBalances.ts +0 -27
  32. package/src/server/api/routers/tokens/procedures/transfer.ts +0 -159
  33. package/src/server/api/routers/tokens/router.ts +0 -15
  34. package/src/server/api/routers/tokens/schemas.ts +0 -80
  35. package/src/server/api/routers/transactions/procedures/get.ts +0 -46
  36. package/src/server/api/routers/transactions/procedures/getByPayer.ts +0 -111
  37. package/src/server/api/routers/transactions/procedures/getByPayerAndTag.ts +0 -119
  38. package/src/server/api/routers/transactions/procedures/resubmit.ts +0 -68
  39. package/src/server/api/routers/transactions/procedures/submit.ts +0 -216
  40. package/src/server/api/routers/transactions/router.ts +0 -21
  41. package/src/server/api/routers/transactions/schemas.ts +0 -119
  42. package/src/server/api/routers/webhooks/router.ts +0 -75
  43. package/src/server/api/routers/welcomePacks/procedures/claim.ts +0 -157
  44. package/src/server/api/routers/welcomePacks/procedures/create.ts +0 -247
  45. package/src/server/api/routers/welcomePacks/procedures/deletePack.ts +0 -118
  46. package/src/server/api/routers/welcomePacks/procedures/get.ts +0 -36
  47. package/src/server/api/routers/welcomePacks/procedures/getByAddress.ts +0 -26
  48. package/src/server/api/routers/welcomePacks/procedures/invite.ts +0 -44
  49. package/src/server/api/routers/welcomePacks/procedures/list.ts +0 -27
  50. package/src/server/api/routers/welcomePacks/router.ts +0 -27
  51. package/src/server/api/routers/welcomePacks/schemas.ts +0 -135
  52. package/src/server/api/schemas.ts +0 -281
@@ -1,156 +0,0 @@
1
- import { publicProcedure } from "../../../procedures";
2
- import { DeleteSplitInputSchema, DeleteSplitOutputSchema } from "../schemas";
3
- import { ORPCError } from "@orpc/server";
4
- import { connectToDb } from "@/lib/utils/db";
5
- import { AssetOwner } from "@/lib/models/hotspot";
6
- import { MiniFanout } from "@/lib/models/mini-fanout";
7
- import { Recipient } from "@/lib/models/recipient";
8
- import { createSolanaConnection } from "@/lib/solana";
9
- import { getAssetIdFromPubkey } from "@/lib/utils/hotspot-helpers";
10
- import {
11
- init as initLd,
12
- updateCompressionDestination,
13
- } from "@helium/lazy-distributor-sdk";
14
- import { init as initMiniFanout } from "@helium/mini-fanout-sdk";
15
- import {
16
- HELIUM_COMMON_LUT,
17
- HELIUM_COMMON_LUT_DEVNET,
18
- populateMissingDraftInfo,
19
- toVersionedTx,
20
- withPriorityFees,
21
- } from "@helium/spl-utils";
22
- import { init as initTuktuk } from "@helium/tuktuk-sdk";
23
- import { PublicKey } from "@solana/web3.js";
24
- import {
25
- generateTransactionTag,
26
- TRANSACTION_TYPES,
27
- } from "@/lib/utils/transaction-tags";
28
-
29
- /**
30
- * Remove the split configuration from a hotspot.
31
- */
32
- export const deleteSplit = publicProcedure
33
- .route({
34
- method: "DELETE",
35
- path: "/hotspots/wallet/{walletAddress}/{hotspotPubkey}/split",
36
- })
37
- .input(DeleteSplitInputSchema)
38
- .output(DeleteSplitOutputSchema)
39
- .errors({
40
- NOT_FOUND: { message: "Hotspot not found" },
41
- NO_SPLIT: { message: "Hotspot does not have a split" },
42
- })
43
- .handler(async ({ input, errors }) => {
44
- const { walletAddress, hotspotPubkey } = input;
45
-
46
- await connectToDb();
47
-
48
- // Resolve hotspot pubkey to asset ID
49
- const assetId = await getAssetIdFromPubkey(hotspotPubkey);
50
- if (!assetId) {
51
- throw errors.NOT_FOUND({ message: "Hotspot not found" });
52
- }
53
-
54
- // Find the hotspot
55
- const assetOwner = await AssetOwner.findOne({
56
- where: { asset: assetId },
57
- include: [
58
- {
59
- model: Recipient,
60
- as: "recipient",
61
- required: true,
62
- include: [
63
- {
64
- model: MiniFanout,
65
- as: "split",
66
- required: true,
67
- },
68
- ],
69
- },
70
- ],
71
- });
72
-
73
- if (!assetOwner) {
74
- throw errors.NOT_FOUND({ message: "Hotspot not found" });
75
- }
76
-
77
- if (!assetOwner.recipient?.split) {
78
- throw errors.NO_SPLIT({ message: "Hotspot does not have a split" });
79
- }
80
-
81
- const { provider, connection } = createSolanaConnection(walletAddress);
82
- const program = await initMiniFanout(provider);
83
- const miniFanoutK = new PublicKey(assetOwner.recipient.split.address);
84
- const miniFanout =
85
- await program.account.miniFanoutV0.fetchNullable(miniFanoutK);
86
-
87
- if (!miniFanout) {
88
- throw errors.NOT_FOUND({ message: "Fanout not found" });
89
- }
90
-
91
- const tuktukProgram = await initTuktuk(provider);
92
-
93
- // Create the transaction to remove the mini fanout
94
- const task = miniFanout.nextTask.equals(miniFanoutK)
95
- ? null
96
- : await tuktukProgram.account.taskV0.fetchNullable(miniFanout.nextTask);
97
-
98
- const closeIx = await program.methods
99
- .closeMiniFanoutV0()
100
- .accounts({
101
- miniFanout: miniFanoutK,
102
- taskRentRefund: task?.rentRefund || walletAddress,
103
- })
104
- .instruction();
105
-
106
- const ldProgram = await initLd(provider);
107
- const setRecipientIx = await (
108
- await updateCompressionDestination({
109
- program: ldProgram,
110
- assetId: new PublicKey(assetId),
111
- lazyDistributor: new PublicKey(assetOwner.recipient.lazyDistributor),
112
- destination: null,
113
- })
114
- ).instruction();
115
-
116
- const draft = {
117
- instructions: [closeIx, setRecipientIx],
118
- feePayer: new PublicKey(walletAddress),
119
- addressLookupTableAddresses: [
120
- process.env.NEXT_PUBLIC_SOLANA_CLUSTER === "devnet"
121
- ? HELIUM_COMMON_LUT_DEVNET
122
- : HELIUM_COMMON_LUT,
123
- ],
124
- };
125
-
126
- const tx = toVersionedTx(
127
- await populateMissingDraftInfo(connection, {
128
- ...draft,
129
- instructions: await withPriorityFees({ ...draft, connection }),
130
- }),
131
- );
132
-
133
- const tag = generateTransactionTag({
134
- type: TRANSACTION_TYPES.REMOVE_SPLIT,
135
- walletAddress,
136
- assetId,
137
- });
138
-
139
- return {
140
- transactionData: {
141
- transactions: [
142
- {
143
- serializedTransaction: Buffer.from(tx.serialize()).toString(
144
- "base64",
145
- ),
146
- metadata: {
147
- type: "remove_split",
148
- description: "Remove split",
149
- },
150
- },
151
- ],
152
- parallel: true,
153
- tag,
154
- },
155
- };
156
- });
@@ -1,31 +0,0 @@
1
- import { publicProcedure } from "../../../procedures";
2
- import { GetHotspotsInputSchema, HotspotsDataSchema } from "../schemas";
3
- import { getHotspotsByOwner } from "@/lib/queries/hotspots";
4
- import { ORPCError } from "@orpc/server";
5
-
6
- /**
7
- * Get hotspots by wallet address with optional filtering and pagination.
8
- */
9
- export const getHotspots = publicProcedure
10
- .route({ method: "GET", path: "/hotspots/wallet/{walletAddress}" })
11
- .input(GetHotspotsInputSchema)
12
- .output(HotspotsDataSchema)
13
- .errors({
14
- BAD_REQUEST: { message: "Invalid wallet address" },
15
- })
16
- .handler(async ({ input }) => {
17
- const { walletAddress, type, page, limit } = input;
18
-
19
- if (!walletAddress || walletAddress.length < 32) {
20
- throw new ORPCError("BAD_REQUEST", { message: "Invalid wallet address" });
21
- }
22
-
23
- const hotspots = await getHotspotsByOwner({
24
- owner: walletAddress,
25
- type,
26
- page,
27
- limit,
28
- });
29
-
30
- return hotspots;
31
- });
@@ -1,44 +0,0 @@
1
- import { publicProcedure } from "../../../procedures";
2
- import {
3
- GetPendingRewardsInputSchema,
4
- PendingRewardsOutputSchema,
5
- } from "../schemas";
6
- import { ORPCError } from "@orpc/server";
7
- import { env } from "@/lib/env";
8
-
9
- /**
10
- * Get pending rewards for all hotspots in a wallet.
11
- * Proxies to the oracle's rewards endpoint.
12
- */
13
- export const getPendingRewards = publicProcedure
14
- .route({
15
- method: "GET",
16
- path: "/hotspots/wallet/{walletAddress}/pending-rewards",
17
- })
18
- .input(GetPendingRewardsInputSchema)
19
- .output(PendingRewardsOutputSchema)
20
- .errors({
21
- BAD_REQUEST: { message: "Invalid wallet address" },
22
- ORACLE_ERROR: { message: "Oracle request failed" },
23
- })
24
- .handler(async ({ input, errors }) => {
25
- const { walletAddress } = input;
26
-
27
- if (!walletAddress || walletAddress.length < 32) {
28
- throw new ORPCError("BAD_REQUEST", { message: "Invalid wallet address" });
29
- }
30
-
31
- const oracleBase = env.ORACLE_URL;
32
- const url = new URL(`${oracleBase}/rewards`);
33
- url.searchParams.set("destination", walletAddress);
34
-
35
- const oracleRes = await fetch(url.toString());
36
- if (!oracleRes.ok) {
37
- throw errors.ORACLE_ERROR({
38
- message: `Oracle request failed (${oracleRes.status})`,
39
- });
40
- }
41
-
42
- const data = await oracleRes.json();
43
- return data;
44
- });
@@ -1,88 +0,0 @@
1
- import { publicProcedure } from "../../../procedures";
2
- import { GetSplitInputSchema, SplitResponseSchema } from "../schemas";
3
- import { connectToDb } from "@/lib/utils/db";
4
- import { AssetOwner } from "@/lib/models/hotspot";
5
- import { MiniFanout } from "@/lib/models/mini-fanout";
6
- import { Recipient } from "@/lib/models/recipient";
7
- import { getAssetIdFromPubkey } from "@/lib/utils/hotspot-helpers";
8
-
9
- interface MiniFanoutShare {
10
- wallet: string;
11
- delegate: string;
12
- share?: {
13
- fixed?: { amount?: string | number };
14
- share?: { amount?: number };
15
- };
16
- }
17
-
18
- /**
19
- * Get the split configuration for a hotspot.
20
- */
21
- export const getSplit = publicProcedure
22
- .route({
23
- method: "GET",
24
- path: "/hotspots/wallet/{walletAddress}/{hotspotPubkey}/split",
25
- })
26
- .input(GetSplitInputSchema)
27
- .output(SplitResponseSchema)
28
- .errors({
29
- NOT_FOUND: { message: "Hotspot not found or does not have a split" },
30
- })
31
- .handler(async ({ input, errors }) => {
32
- const { walletAddress, hotspotPubkey } = input;
33
-
34
- await connectToDb();
35
-
36
- // Resolve hotspot pubkey to asset ID
37
- const assetId = await getAssetIdFromPubkey(hotspotPubkey);
38
- if (!assetId) {
39
- throw errors.NOT_FOUND({ message: "Hotspot not found" });
40
- }
41
-
42
- // Look up the asset and ensure it has a mini fanout configured
43
- const assetOwner = await AssetOwner.findOne({
44
- where: { asset: assetId },
45
- include: [
46
- {
47
- model: Recipient,
48
- as: "recipient",
49
- required: true,
50
- include: [
51
- {
52
- model: MiniFanout,
53
- as: "split",
54
- required: true,
55
- },
56
- ],
57
- },
58
- ],
59
- });
60
-
61
- if (!assetOwner?.recipient?.split) {
62
- throw errors.NOT_FOUND({ message: "Hotspot does not have a split" });
63
- }
64
-
65
- const shares = assetOwner.recipient.split.shares as MiniFanoutShare[];
66
-
67
- const totalShares = shares.reduce(
68
- (acc: number, share: MiniFanoutShare) =>
69
- acc + (share.share?.share?.amount || 0),
70
- 0,
71
- );
72
-
73
- return {
74
- walletAddress,
75
- hotspotPubkey,
76
- splitAddress: assetOwner.recipient.split.address,
77
- shares: shares.map((share: MiniFanoutShare) => ({
78
- wallet: share.wallet,
79
- delegate: share.delegate,
80
- fixed: share.share?.fixed?.amount
81
- ? Number(share.share.fixed.amount)
82
- : 0,
83
- shares: share.share?.share?.amount
84
- ? share.share.share.amount / totalShares
85
- : 0,
86
- })),
87
- };
88
- });
@@ -1,204 +0,0 @@
1
- import { publicProcedure } from "../../../procedures";
2
- import {
3
- TransferHotspotInputSchema,
4
- TransferHotspotOutputSchema,
5
- } from "../schemas";
6
- import { ORPCError } from "@orpc/server";
7
- import { Connection, PublicKey } from "@solana/web3.js";
8
- import { env } from "@/lib/env";
9
- import {
10
- generateTransactionTag,
11
- TRANSACTION_TYPES,
12
- } from "@/lib/utils/transaction-tags";
13
- import {
14
- populateMissingDraftInfo,
15
- toVersionedTx,
16
- withPriorityFees,
17
- proofArgsAndAccounts,
18
- type Asset,
19
- } from "@helium/spl-utils";
20
- import {
21
- PROGRAM_ID as BUBBLEGUM_PROGRAM_ID,
22
- createTransferInstruction,
23
- } from "@metaplex-foundation/mpl-bubblegum";
24
- import {
25
- SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,
26
- SPL_NOOP_PROGRAM_ID,
27
- } from "@solana/spl-account-compression";
28
- import { entityCreatorKey } from "@helium/helium-entity-manager-sdk";
29
- import { daoKey } from "@helium/helium-sub-daos-sdk";
30
- import { HNT_MINT } from "@helium/spl-utils";
31
- import { getAssetIdFromPubkey } from "@/lib/utils/hotspot-helpers";
32
-
33
- const DAO_KEY = daoKey(HNT_MINT)[0];
34
-
35
- async function getBubblegumAuthorityPDA(
36
- merkleRollPubKey: PublicKey,
37
- ): Promise<PublicKey> {
38
- const [bubblegumAuthorityPDAKey] = await PublicKey.findProgramAddress(
39
- [merkleRollPubKey.toBuffer()],
40
- BUBBLEGUM_PROGRAM_ID,
41
- );
42
- return bubblegumAuthorityPDAKey;
43
- }
44
-
45
- function validateHeliumHotspot(asset: Asset): boolean {
46
- const heliumEntityCreator = entityCreatorKey(DAO_KEY)[0].toBase58();
47
-
48
- return (
49
- asset.creators?.some((creator) => {
50
- const address =
51
- typeof creator.address === "string"
52
- ? creator.address
53
- : creator.address.toBase58();
54
- return address === heliumEntityCreator && creator.verified;
55
- }) || false
56
- );
57
- }
58
-
59
- /**
60
- * Create a transaction to transfer a hotspot to a new owner.
61
- */
62
- export const transferHotspot = publicProcedure
63
- .route({
64
- method: "POST",
65
- path: "/hotspots/wallet/{walletAddress}/{hotspotPubkey}/transfer",
66
- })
67
- .input(TransferHotspotInputSchema)
68
- .output(TransferHotspotOutputSchema)
69
- .errors({
70
- NOT_FOUND: { message: "Hotspot not found" },
71
- BAD_REQUEST: { message: "Invalid request" },
72
- FORBIDDEN: { message: "Wallet is not the owner of this hotspot" },
73
- INVALID_HOTSPOT: { message: "Asset is not a valid Helium hotspot" },
74
- })
75
- .handler(async ({ input, errors }) => {
76
- const { walletAddress, hotspotPubkey, recipient } = input;
77
-
78
- // Resolve hotspot pubkey to asset ID
79
- const assetId = await getAssetIdFromPubkey(hotspotPubkey);
80
- if (!assetId) {
81
- throw errors.NOT_FOUND({ message: "Hotspot not found" });
82
- }
83
-
84
- // Validate public keys
85
- let payerPubkey: PublicKey;
86
- let recipientPubkey: PublicKey;
87
-
88
- try {
89
- payerPubkey = new PublicKey(walletAddress);
90
- recipientPubkey = new PublicKey(recipient);
91
- } catch {
92
- throw errors.BAD_REQUEST({ message: "Invalid public key format" });
93
- }
94
-
95
- const connection = new Connection(env.SOLANA_RPC_URL);
96
- const assetEndpoint = env.ASSET_ENDPOINT || connection.rpcEndpoint;
97
- const assetPubkey = new PublicKey(assetId);
98
-
99
- const { asset, args, accounts, remainingAccounts } =
100
- await proofArgsAndAccounts({
101
- connection,
102
- assetId: assetPubkey,
103
- assetEndpoint,
104
- });
105
-
106
- if (!asset) {
107
- throw errors.NOT_FOUND({ message: "Asset not found" });
108
- }
109
-
110
- // Validate asset is a Helium hotspot
111
- if (!validateHeliumHotspot(asset)) {
112
- throw errors.INVALID_HOTSPOT({
113
- message: "Asset is not a valid Helium hotspot",
114
- });
115
- }
116
-
117
- // Validate ownership
118
- const ownerAddress =
119
- typeof asset.ownership.owner === "string"
120
- ? asset.ownership.owner
121
- : asset.ownership.owner.toBase58();
122
-
123
- if (ownerAddress !== walletAddress) {
124
- throw errors.FORBIDDEN({
125
- message: "Wallet is not the owner of this hotspot",
126
- });
127
- }
128
-
129
- const leafOwner =
130
- typeof asset.ownership.owner === "string"
131
- ? new PublicKey(asset.ownership.owner)
132
- : asset.ownership.owner;
133
-
134
- const leafDelegate = asset.ownership.delegate
135
- ? typeof asset.ownership.delegate === "string"
136
- ? new PublicKey(asset.ownership.delegate)
137
- : asset.ownership.delegate
138
- : leafOwner;
139
-
140
- const merkleTree = accounts.merkleTree;
141
- const treeAuthority = await getBubblegumAuthorityPDA(merkleTree);
142
-
143
- const transferInstruction = createTransferInstruction(
144
- {
145
- treeAuthority,
146
- leafOwner,
147
- leafDelegate,
148
- newLeafOwner: recipientPubkey,
149
- merkleTree,
150
- logWrapper: SPL_NOOP_PROGRAM_ID,
151
- compressionProgram: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,
152
- anchorRemainingAccounts: remainingAccounts,
153
- },
154
- {
155
- ...args,
156
- nonce: args.index,
157
- },
158
- );
159
-
160
- const instructionsWithFees = await withPriorityFees({
161
- connection,
162
- instructions: [transferInstruction],
163
- feePayer: payerPubkey,
164
- });
165
-
166
- const draft = await populateMissingDraftInfo(connection, {
167
- instructions: instructionsWithFees,
168
- feePayer: payerPubkey,
169
- });
170
-
171
- const versionedTx = toVersionedTx(draft);
172
- const serializedTransaction = Buffer.from(versionedTx.serialize()).toString(
173
- "base64",
174
- );
175
-
176
- const tag = generateTransactionTag({
177
- type: TRANSACTION_TYPES.HOTSPOT_TRANSFER,
178
- walletAddress,
179
- assetId,
180
- recipient,
181
- timestamp: Date.now(),
182
- });
183
-
184
- const hotspotName = asset.content?.metadata?.name || "Hotspot";
185
-
186
- return {
187
- transactionData: {
188
- transactions: [
189
- {
190
- serializedTransaction,
191
- metadata: {
192
- type: TRANSACTION_TYPES.HOTSPOT_TRANSFER,
193
- description: `Transfer ${hotspotName} to ${recipient.slice(
194
- 0,
195
- 4,
196
- )}...${recipient.slice(-4)}`,
197
- },
198
- },
199
- ],
200
- parallel: false,
201
- tag,
202
- },
203
- };
204
- });