@helium/blockchain-api 0.1.2 → 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 -169
  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,253 +0,0 @@
1
- import { publicProcedure } from "../../../procedures";
2
- import { CreateSplitInputSchema, CreateSplitOutputSchema } from "../schemas";
3
- import { ORPCError } from "@orpc/server";
4
- import { env } from "@/lib/env";
5
- import { createSolanaConnection } from "@/lib/solana";
6
- import { connectToDb } from "@/lib/utils/db";
7
- import { scheduleToUtcCron } from "@/lib/utils/misc";
8
- import { getAssetIdFromPubkey } from "@/lib/utils/hotspot-helpers";
9
- import {
10
- initializeCompressionRecipient,
11
- init as initLd,
12
- recipientKey,
13
- updateCompressionDestination,
14
- } from "@helium/lazy-distributor-sdk";
15
- import { init as initMiniFanout } from "@helium/mini-fanout-sdk";
16
- import {
17
- batchInstructionsToTxsWithPriorityFee,
18
- HELIUM_COMMON_LUT,
19
- HELIUM_COMMON_LUT_DEVNET,
20
- HNT_MINT,
21
- toVersionedTx,
22
- } from "@helium/spl-utils";
23
- import {
24
- init as initTuktuk,
25
- nextAvailableTaskIds,
26
- taskKey,
27
- } from "@helium/tuktuk-sdk";
28
- import {
29
- PublicKey,
30
- SystemProgram,
31
- TransactionInstruction,
32
- } from "@solana/web3.js";
33
- import BN from "bn.js";
34
- import {
35
- generateTransactionTag,
36
- TRANSACTION_TYPES,
37
- } from "@/lib/utils/transaction-tags";
38
- import {
39
- getCluster,
40
- getJitoTipTransaction,
41
- shouldUseJitoBundle,
42
- } from "@/lib/utils/transaction-submission";
43
-
44
- const TASK_QUEUE_ID = new PublicKey(
45
- "H39gEszvsi6AT4rYBiJTuZHJSF5hMHy6CKGTd7wzhsg7",
46
- );
47
- const FANOUT_FUNDING_AMOUNT = 10000000; // 0.01 SOL
48
-
49
- /**
50
- * Create a split configuration for a hotspot with reward distribution.
51
- */
52
- export const createSplit = publicProcedure
53
- .route({
54
- method: "POST",
55
- path: "/hotspots/wallet/{walletAddress}/{hotspotPubkey}/split",
56
- })
57
- .input(CreateSplitInputSchema)
58
- .output(CreateSplitOutputSchema)
59
- .errors({
60
- NOT_FOUND: { message: "Hotspot not found" },
61
- BAD_REQUEST: { message: "Invalid request" },
62
- INSUFFICIENT_FUNDS: {
63
- message: "Insufficient SOL balance to fund split setup",
64
- },
65
- })
66
- .handler(async ({ input, errors }) => {
67
- const {
68
- walletAddress,
69
- hotspotPubkey,
70
- rewardsSplit,
71
- schedule,
72
- lazyDistributor,
73
- } = input;
74
-
75
- await connectToDb();
76
-
77
- // Resolve hotspot pubkey to asset ID
78
- const assetId = await getAssetIdFromPubkey(hotspotPubkey);
79
- if (!assetId) {
80
- throw errors.NOT_FOUND({ message: "Hotspot not found" });
81
- }
82
-
83
- if (!rewardsSplit?.length) {
84
- throw errors.BAD_REQUEST({
85
- message: "At least one reward split is required",
86
- });
87
- }
88
-
89
- if (!schedule?.frequency || !schedule?.time || !schedule?.timezone) {
90
- throw errors.BAD_REQUEST({
91
- message: "Schedule frequency, time, and timezone are required",
92
- });
93
- }
94
-
95
- // Build connection and programs
96
- const { provider, wallet } = createSolanaConnection(walletAddress);
97
- const miniFanoutProgram = await initMiniFanout(provider);
98
- const tuktukProgram = await initTuktuk(provider);
99
- const ldProgram = await initLd(provider);
100
-
101
- // Convert schedule to UTC cron string
102
- const rewardsSchedule = scheduleToUtcCron(schedule);
103
-
104
- // Ensure Lazy Distributor Recipient exists for the asset
105
- const recipientK = recipientKey(
106
- new PublicKey(lazyDistributor),
107
- new PublicKey(assetId),
108
- )[0];
109
- const recipientAcc =
110
- await ldProgram.account.recipientV0.fetchNullable(recipientK);
111
- const instructions: TransactionInstruction[] = [];
112
-
113
- if (!recipientAcc) {
114
- instructions.push(
115
- await (
116
- await initializeCompressionRecipient({
117
- program: ldProgram,
118
- assetId: new PublicKey(assetId),
119
- payer: wallet.publicKey,
120
- assetEndpoint: env.SOLANA_RPC_URL,
121
- lazyDistributor: new PublicKey(lazyDistributor),
122
- })
123
- ).instruction(),
124
- );
125
- }
126
-
127
- const oracleSigner = new PublicKey(process.env.ORACLE_SIGNER!);
128
- const oracleUrl = process.env.ORACLE_URL!;
129
-
130
- const { instruction: initIx, pubkeys } = await miniFanoutProgram.methods
131
- .initializeMiniFanoutV0({
132
- seed: new PublicKey(assetId).toBuffer(),
133
- shares: rewardsSplit.map((split) => ({
134
- wallet: new PublicKey(split.address),
135
- share:
136
- split.type === "fixed"
137
- ? { fixed: { amount: new BN(split.amount) } }
138
- : { share: { amount: split.amount } },
139
- })),
140
- schedule: rewardsSchedule,
141
- preTask: {
142
- remoteV0: {
143
- url: `${oracleUrl}/v1/tuktuk/asset/${assetId}`,
144
- signer: oracleSigner,
145
- },
146
- },
147
- })
148
- .accounts({
149
- payer: wallet.publicKey,
150
- owner: wallet.publicKey,
151
- taskQueue: TASK_QUEUE_ID,
152
- rentRefund: wallet.publicKey,
153
- mint: HNT_MINT,
154
- })
155
- .prepare();
156
- instructions.push(initIx);
157
-
158
- // Ensure the user's wallet has enough SOL to fund the mini fanout rent
159
- const balance = await provider.connection.getBalance(
160
- new PublicKey(walletAddress),
161
- );
162
- if (balance < FANOUT_FUNDING_AMOUNT) {
163
- throw errors.INSUFFICIENT_FUNDS({
164
- message: "Insufficient SOL balance to fund split setup",
165
- data: {
166
- balance,
167
- required: FANOUT_FUNDING_AMOUNT,
168
- },
169
- });
170
- }
171
-
172
- instructions.push(
173
- SystemProgram.transfer({
174
- fromPubkey: wallet.publicKey,
175
- toPubkey: pubkeys.miniFanout!,
176
- lamports: FANOUT_FUNDING_AMOUNT,
177
- }),
178
- );
179
-
180
- const taskQueueAcc =
181
- await tuktukProgram.account.taskQueueV0.fetchNullable(TASK_QUEUE_ID);
182
- const [taskId, preTaskId] = nextAvailableTaskIds(
183
- taskQueueAcc!.taskBitmap,
184
- 2,
185
- );
186
-
187
- // Schedule a task for the mini fanout
188
- const scheduleIx = await (
189
- await miniFanoutProgram.methods
190
- .scheduleTaskV0({
191
- program: miniFanoutProgram,
192
- miniFanout: pubkeys.miniFanout!,
193
- taskId,
194
- preTaskId,
195
- })
196
- .accounts({
197
- taskQueue: TASK_QUEUE_ID,
198
- payer: wallet.publicKey,
199
- miniFanout: pubkeys.miniFanout!,
200
- task: taskKey(TASK_QUEUE_ID, taskId)[0],
201
- preTask: taskKey(TASK_QUEUE_ID, preTaskId)[0],
202
- nextTask: pubkeys.miniFanout!,
203
- nextPreTask: pubkeys.miniFanout!,
204
- })
205
- ).instruction();
206
- instructions.push(scheduleIx);
207
-
208
- // Point hotspot rewards destination to the mini fanout
209
- const setRecipientIx = await (
210
- await updateCompressionDestination({
211
- program: ldProgram,
212
- assetId: new PublicKey(assetId),
213
- lazyDistributor: new PublicKey(lazyDistributor),
214
- destination: pubkeys.miniFanout!,
215
- })
216
- ).instruction();
217
- instructions.push(setRecipientIx);
218
-
219
- const txs = (
220
- await batchInstructionsToTxsWithPriorityFee(provider, instructions, {
221
- addressLookupTableAddresses: [
222
- process.env.NEXT_PUBLIC_SOLANA_CLUSTER === "devnet"
223
- ? HELIUM_COMMON_LUT_DEVNET
224
- : HELIUM_COMMON_LUT,
225
- ],
226
- })
227
- ).map((tx) => toVersionedTx(tx));
228
-
229
- const tag = generateTransactionTag({
230
- type: TRANSACTION_TYPES.ADD_SPLIT,
231
- walletAddress,
232
- assetId,
233
- lazyDistributor,
234
- });
235
-
236
- if (shouldUseJitoBundle(txs.length, getCluster())) {
237
- txs.push(await getJitoTipTransaction(new PublicKey(walletAddress)));
238
- }
239
-
240
- return {
241
- transactionData: {
242
- transactions: txs.map((tx) => ({
243
- serializedTransaction: Buffer.from(tx.serialize()).toString("base64"),
244
- metadata: {
245
- type: "add_split",
246
- description: "Create split",
247
- },
248
- })),
249
- parallel: true,
250
- tag,
251
- },
252
- };
253
- });
@@ -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
- });