@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,709 +0,0 @@
1
- import { z } from "zod";
2
- import { ORPCError } from "@orpc/server";
3
- import { v4 as uuidv4 } from "uuid";
4
- import { Connection, PublicKey, TransactionInstruction } from "@solana/web3.js";
5
- import {
6
- createAssociatedTokenAccountIdempotentInstruction,
7
- createTransferCheckedInstruction,
8
- getAssociatedTokenAddressSync,
9
- } from "@solana/spl-token";
10
- import {
11
- populateMissingDraftInfo,
12
- toVersionedTx,
13
- HNT_MINT,
14
- withPriorityFees,
15
- } from "@helium/spl-utils";
16
- import { Op } from "sequelize";
17
-
18
- import { protectedProcedure, publicProcedure } from "../../procedures";
19
- import {
20
- InitKycInputSchema,
21
- CreateBankAccountInputSchema,
22
- GetBankAccountInputSchema,
23
- DeleteBankAccountInputSchema,
24
- GetSendQuoteInputSchema,
25
- SendFundsInputSchema,
26
- GetTransferInputSchema,
27
- UpdateTransferInputSchema,
28
- KycStatusOutputSchema,
29
- FeesOutputSchema,
30
- BankAccountListOutputSchema,
31
- BankAccountSchema,
32
- DeleteBankAccountOutputSchema,
33
- QuoteOutputSchema,
34
- SendFundsOutputSchema,
35
- UpdateTransferOutputSchema,
36
- } from "./schemas";
37
- import { env } from "@/lib/env";
38
- import { privy } from "@/lib/privy";
39
- import { BridgeUser } from "@/lib/models/bridge-user";
40
- import { BankAccount } from "@/lib/models/bank-account";
41
- import { BridgeTransfer } from "@/lib/models/bridge-transfer";
42
- import { TOKEN_MINTS } from "@/lib/constants/tokens";
43
- import {
44
- generateTransactionTag,
45
- TRANSACTION_TYPES,
46
- } from "@/lib/utils/transaction-tags";
47
-
48
- // Helper functions
49
- const usdToUsdc = (usdAmount: string) => {
50
- if (!usdAmount || isNaN(parseFloat(usdAmount))) return null;
51
- return (parseFloat(usdAmount) * Math.pow(10, 6)).toString();
52
- };
53
-
54
- const usdcToUsd = (usdcAmount: string) => {
55
- return parseFloat(usdcAmount) / Math.pow(10, 6);
56
- };
57
-
58
- const usdCeil = (amount: number) => {
59
- return Math.ceil(amount * Math.pow(10, 2)) / Math.pow(10, 2);
60
- };
61
-
62
- // ============================================================================
63
- // Procedures
64
- // ============================================================================
65
-
66
- const getKycStatus = protectedProcedure
67
- .route({ method: "GET", path: "/fiat/kyc" })
68
- .output(KycStatusOutputSchema)
69
- .errors({
70
- EMAIL_NOT_LINKED: { message: "Email not linked" },
71
- })
72
- .handler(async ({ context, errors }) => {
73
- const privyUser = context.session.user;
74
-
75
- if (!privyUser.email) {
76
- throw errors.EMAIL_NOT_LINKED({ message: "Email not linked" });
77
- }
78
-
79
- const bridgeUser = await BridgeUser.findOne({
80
- where: { privyUserId: privyUser.id },
81
- });
82
-
83
- if (bridgeUser) {
84
- return {
85
- kycStatus: bridgeUser.kycStatus,
86
- tosStatus: bridgeUser.tosStatus,
87
- tosLink: bridgeUser.tosLink,
88
- kycLink: bridgeUser.kycLink,
89
- kycLinkId: bridgeUser.kycLinkId,
90
- accountType: bridgeUser.accountType,
91
- };
92
- }
93
-
94
- return {
95
- kycStatus: "not_started",
96
- tosStatus: "pending",
97
- tosLink: null,
98
- kycLink: null,
99
- kycLinkId: null,
100
- };
101
- });
102
-
103
- const initKyc = protectedProcedure
104
- .route({ method: "POST", path: "/fiat/kyc" })
105
- .input(InitKycInputSchema)
106
- .output(KycStatusOutputSchema)
107
- .errors({
108
- EMAIL_NOT_LINKED: { message: "Email not linked" },
109
- BRIDGE_ERROR: { message: "Failed to create Bridge KYC link" },
110
- })
111
- .handler(async ({ input, context, errors }) => {
112
- const { type } = input;
113
- const privyUser = context.session.user;
114
-
115
- if (!privyUser.email) {
116
- throw errors.EMAIL_NOT_LINKED({ message: "Email not linked" });
117
- }
118
-
119
- let bridgeUser = await BridgeUser.findOne({
120
- where: { privyUserId: privyUser.id },
121
- });
122
-
123
- if (bridgeUser && !bridgeUser.accountType && type) {
124
- bridgeUser.accountType = type;
125
- await bridgeUser.save();
126
- }
127
-
128
- if (
129
- bridgeUser?.kycStatus === "approved" &&
130
- bridgeUser?.tosStatus === "approved"
131
- ) {
132
- return {
133
- kycStatus: bridgeUser.kycStatus,
134
- tosStatus: bridgeUser.tosStatus,
135
- tosLink: bridgeUser.tosLink,
136
- kycLink: bridgeUser.kycLink,
137
- kycLinkId: bridgeUser.kycLinkId,
138
- accountType: bridgeUser.accountType,
139
- };
140
- }
141
-
142
- let rejectionReasons: string[] = [];
143
-
144
- if (!bridgeUser) {
145
- const response = await fetch(`${env.BRIDGE_API_URL}/kyc_links`, {
146
- method: "POST",
147
- headers: {
148
- "Content-Type": "application/json",
149
- "Api-Key": env.BRIDGE_API_KEY,
150
- "Idempotency-Key": uuidv4(),
151
- },
152
- body: JSON.stringify({
153
- email: privyUser.email.address,
154
- type: type || "individual",
155
- redirect_uri: `${
156
- process.env.NEXT_PUBLIC_APP_URL
157
- }/withdraw?step=5&type=${type || "individual"}`,
158
- }),
159
- });
160
-
161
- let data = await response.json();
162
- const existsAlready = data.existing_kyc_link;
163
- if (existsAlready) {
164
- data = data.existing_kyc_link;
165
- }
166
-
167
- if (!response.ok && !existsAlready) {
168
- console.error("Failed to create Bridge KYC link", data);
169
- throw errors.BRIDGE_ERROR({
170
- message: "Failed to create Bridge KYC link",
171
- });
172
- }
173
-
174
- const [newBridgeUser] = await BridgeUser.findOrCreate({
175
- where: { privyUserId: privyUser.id },
176
- defaults: {
177
- privyUserId: privyUser.id,
178
- kycLinkId: data.id,
179
- kycStatus: data.kyc_status,
180
- tosStatus: data.tos_status,
181
- tosLink: data.tos_link,
182
- kycLink: data.kyc_link,
183
- bridgeCustomerId: data.customer_id,
184
- accountType: type || "individual",
185
- },
186
- });
187
-
188
- bridgeUser = newBridgeUser;
189
- } else {
190
- const response = await fetch(
191
- `${env.BRIDGE_API_URL}/kyc_links/${bridgeUser.kycLinkId}`,
192
- {
193
- method: "GET",
194
- headers: {
195
- "Content-Type": "application/json",
196
- "Api-Key": env.BRIDGE_API_KEY,
197
- },
198
- },
199
- );
200
-
201
- if (!response.ok) {
202
- throw errors.BRIDGE_ERROR({ message: "Failed to get Bridge KYC link" });
203
- }
204
-
205
- const data = await response.json();
206
- if (process.env.BRIDGE_API_URL?.includes("sandbox")) {
207
- bridgeUser.tosStatus = "approved";
208
- } else {
209
- bridgeUser.tosStatus = data.tos_status;
210
- }
211
- bridgeUser.kycStatus = data.kyc_status;
212
- bridgeUser.tosLink = data.tos_link;
213
- bridgeUser.kycLink = data.kyc_link;
214
- await bridgeUser.save();
215
- rejectionReasons = (data.rejection_reasons || []).map(
216
- (r: { reason: string }) => r.reason,
217
- );
218
- }
219
-
220
- return {
221
- kycStatus: bridgeUser.kycStatus,
222
- tosStatus: bridgeUser.tosStatus,
223
- tosLink: bridgeUser.tosLink,
224
- kycLink: bridgeUser.kycLink,
225
- kycLinkId: bridgeUser.kycLinkId,
226
- accountType: bridgeUser.accountType,
227
- rejectionReasons,
228
- };
229
- });
230
-
231
- const getFees = publicProcedure
232
- .route({ method: "GET", path: "/fiat/fees" })
233
- .output(FeesOutputSchema)
234
- .handler(async () => {
235
- const developerFee = process.env.BRIDGE_DEVELOPER_FEE || "0.50";
236
- const developerFeePercentage =
237
- process.env.BRIDGE_DEVELOPER_FEE_PERCENTAGE || "0.5";
238
-
239
- return {
240
- developer_fee: developerFee,
241
- developer_fee_percent: parseFloat(developerFeePercentage),
242
- };
243
- });
244
-
245
- const listBankAccounts = protectedProcedure
246
- .route({ method: "GET", path: "/fiat/bank-accounts" })
247
- .output(BankAccountListOutputSchema)
248
- .errors({
249
- NO_CUSTOMER: { message: "Bridge customer ID not found" },
250
- })
251
- .handler(async ({ context, errors }) => {
252
- const privyUser = context.session.user;
253
-
254
- const bridgeUser = await BridgeUser.findOne({
255
- where: { privyUserId: privyUser.id },
256
- });
257
-
258
- if (!bridgeUser?.bridgeCustomerId) {
259
- throw errors.NO_CUSTOMER({ message: "Bridge customer ID not found" });
260
- }
261
-
262
- const bankAccounts = await BankAccount.findAll({
263
- where: { bridgeUserId: bridgeUser.id },
264
- });
265
-
266
- return bankAccounts.map((account) => account.toJSON());
267
- });
268
-
269
- const createBankAccount = protectedProcedure
270
- .route({ method: "POST", path: "/fiat/bank-accounts" })
271
- .input(CreateBankAccountInputSchema)
272
- .output(BankAccountSchema)
273
- .errors({
274
- NO_CUSTOMER: { message: "Bridge customer ID not found" },
275
- BRIDGE_ERROR: { message: "Failed to add bank account" },
276
- })
277
- .handler(async ({ input, context, errors }) => {
278
- const privyUser = context.session.user;
279
-
280
- const bridgeUser = await BridgeUser.findOne({
281
- where: { privyUserId: privyUser.id },
282
- });
283
-
284
- if (!bridgeUser?.bridgeCustomerId || !bridgeUser.id) {
285
- throw errors.NO_CUSTOMER({ message: "Bridge customer ID not found" });
286
- }
287
-
288
- const {
289
- currency,
290
- account_type,
291
- bank_name,
292
- account_name,
293
- first_name,
294
- last_name,
295
- account_owner_name,
296
- business_name,
297
- account,
298
- address,
299
- } = input;
300
-
301
- const bankAccountResponse = await fetch(
302
- `${env.BRIDGE_API_URL}/customers/${bridgeUser.bridgeCustomerId}/external_accounts`,
303
- {
304
- method: "POST",
305
- headers: {
306
- "Content-Type": "application/json",
307
- "Api-Key": env.BRIDGE_API_KEY,
308
- "Idempotency-Key": uuidv4(),
309
- },
310
- body: JSON.stringify({
311
- currency,
312
- account_type,
313
- bank_name,
314
- account_name,
315
- first_name,
316
- last_name,
317
- account_owner_type: bridgeUser.accountType,
318
- account_owner_name,
319
- ...(business_name && { business_name }),
320
- account: {
321
- routing_number: account.routing_number,
322
- account_number: account.account_number,
323
- checking_or_savings: account.checking_or_savings,
324
- },
325
- address: {
326
- street_line_1: address.street_line_1,
327
- country: address.country,
328
- state: address.state,
329
- city: address.city,
330
- postal_code: address.postal_code,
331
- },
332
- }),
333
- },
334
- );
335
-
336
- if (!bankAccountResponse.ok) {
337
- const errorData = await bankAccountResponse.json();
338
- console.error("Failed to add bank account", errorData);
339
- throw errors.BRIDGE_ERROR({
340
- message: errorData.message || "Failed to add bank account",
341
- });
342
- }
343
-
344
- const bankAccountData = await bankAccountResponse.json();
345
-
346
- const bankAccount = await BankAccount.create({
347
- bridgeUserId: bridgeUser.id,
348
- bridgeExternalAccountId: bankAccountData.id,
349
- accountName: account_name,
350
- bankName: bank_name,
351
- lastFourDigits: account.account_number.slice(-4),
352
- routingNumber: account.routing_number,
353
- accountType: account.checking_or_savings,
354
- });
355
-
356
- return bankAccount.toJSON();
357
- });
358
-
359
- const deleteBankAccount = protectedProcedure
360
- .route({ method: "DELETE", path: "/fiat/bank-accounts/{id}" })
361
- .input(DeleteBankAccountInputSchema)
362
- .output(DeleteBankAccountOutputSchema)
363
- .errors({
364
- NO_CUSTOMER: { message: "Bridge customer ID not found" },
365
- BRIDGE_ERROR: { message: "Failed to delete bank account" },
366
- })
367
- .handler(async ({ input, context, errors }) => {
368
- const { id } = input;
369
- const privyUser = context.session.user;
370
-
371
- const bridgeUser = await BridgeUser.findOne({
372
- where: { privyUserId: privyUser.id },
373
- });
374
-
375
- if (!bridgeUser?.bridgeCustomerId) {
376
- throw errors.NO_CUSTOMER({ message: "Bridge customer ID not found" });
377
- }
378
-
379
- const bankAccount = await BankAccount.findOne({
380
- where: { bridgeExternalAccountId: id },
381
- });
382
-
383
- const response = await fetch(
384
- `${env.BRIDGE_API_URL}/customers/${bridgeUser.bridgeCustomerId}/external_accounts/${id}`,
385
- {
386
- method: "DELETE",
387
- headers: {
388
- "Content-Type": "application/json",
389
- "Api-Key": env.BRIDGE_API_KEY,
390
- },
391
- },
392
- );
393
-
394
- if (!response.ok) {
395
- const errorData = await response.json();
396
- console.error("Failed to delete bank account", errorData);
397
- throw errors.BRIDGE_ERROR({
398
- message: errorData.message || "Failed to delete bank account",
399
- });
400
- }
401
-
402
- await bankAccount?.destroy();
403
-
404
- return { success: true };
405
- });
406
-
407
- const getSendQuote = publicProcedure
408
- .route({ method: "GET", path: "/fiat/bank-accounts/{id}/send" })
409
- .input(GetSendQuoteInputSchema)
410
- .output(QuoteOutputSchema)
411
- .errors({
412
- BAD_REQUEST: { message: "Invalid request" },
413
- JUPITER_ERROR: { message: "Failed to get quote from Jupiter" },
414
- })
415
- .handler(async ({ input, errors }) => {
416
- const { usdAmount } = input;
417
-
418
- const usdcAmount = usdToUsdc(usdAmount);
419
- if (!usdcAmount) {
420
- throw errors.BAD_REQUEST({ message: "Invalid USD amount" });
421
- }
422
-
423
- const quoteResponse = await fetch(
424
- `https://lite-api.jup.ag/swap/v1/quote?inputMint=${HNT_MINT.toBase58()}&outputMint=${
425
- TOKEN_MINTS.USDC
426
- }&swapMode=ExactOut&amount=${usdcAmount}&slippageBps=50`,
427
- );
428
-
429
- if (!quoteResponse.ok) {
430
- throw errors.JUPITER_ERROR({
431
- message: "Failed to get quote from Jupiter",
432
- });
433
- }
434
-
435
- return await quoteResponse.json();
436
- });
437
-
438
- const sendFunds = publicProcedure
439
- .route({ method: "POST", path: "/fiat/bank-accounts/{id}/send" })
440
- .input(SendFundsInputSchema)
441
- .output(SendFundsOutputSchema)
442
- .errors({
443
- NOT_FOUND: { message: "Bank account not found" },
444
- BRIDGE_ERROR: { message: "Failed to create Bridge transfer" },
445
- JUPITER_ERROR: { message: "Failed to get swap instructions" },
446
- })
447
- .handler(async ({ input, errors }) => {
448
- const { id, userAddress, quoteResponse } = input;
449
-
450
- const bankAccount = await BankAccount.findByPk(id, {
451
- include: [
452
- {
453
- as: "bridgeUser",
454
- model: BridgeUser,
455
- attributes: ["bridgeCustomerId"],
456
- },
457
- ],
458
- });
459
-
460
- if (!bankAccount) {
461
- throw errors.NOT_FOUND({ message: "Bank account not found" });
462
- }
463
-
464
- // Cancel any existing transfers
465
- const existingTransfers = await BridgeTransfer.findAll({
466
- where: {
467
- bridgeUserId: bankAccount.bridgeUserId,
468
- solanaSignature: { [Op.is]: null } as unknown as string,
469
- state: { [Op.notIn]: ["cancelled", "failed"] },
470
- },
471
- });
472
-
473
- for (const transfer of existingTransfers) {
474
- try {
475
- await fetch(
476
- `https://api.bridge.xyz/v0/transfers/${transfer.bridgeTransferId}`,
477
- {
478
- method: "DELETE",
479
- headers: {
480
- "Api-Key": process.env.BRIDGE_API_KEY!,
481
- "Content-Type": "application/json",
482
- },
483
- body: JSON.stringify({}),
484
- },
485
- );
486
- await transfer.destroy();
487
- } catch (e) {
488
- console.error("Error cancelling transfer:", e);
489
- }
490
- }
491
-
492
- // Create transfer with Bridge
493
- const fee = (
494
- parseFloat(process.env.BRIDGE_DEVELOPER_FEE || "0.50") +
495
- usdCeil(
496
- (parseFloat(process.env.BRIDGE_DEVELOPER_FEE_PERCENTAGE || "0.5") /
497
- 100) *
498
- usdcToUsd(quoteResponse.outAmount),
499
- )
500
- ).toString();
501
-
502
- const bridgeTransferResponse = await fetch(
503
- "https://api.bridge.xyz/v0/transfers",
504
- {
505
- method: "POST",
506
- headers: {
507
- "Api-Key": process.env.BRIDGE_API_KEY!,
508
- "Content-Type": "application/json",
509
- "Idempotency-Key": uuidv4(),
510
- },
511
- body: JSON.stringify({
512
- on_behalf_of: bankAccount.bridgeUser!.bridgeCustomerId,
513
- amount: usdcToUsd(quoteResponse.outAmount).toString(),
514
- developer_fee: fee,
515
- source: {
516
- payment_rail: "solana",
517
- currency: "usdc",
518
- from_address: userAddress,
519
- },
520
- destination: {
521
- payment_rail: "ach",
522
- currency: "usd",
523
- external_account_id: bankAccount.bridgeExternalAccountId,
524
- },
525
- }),
526
- },
527
- );
528
-
529
- if (!bridgeTransferResponse.ok) {
530
- const error = await bridgeTransferResponse.json();
531
- throw errors.BRIDGE_ERROR({
532
- message: error.message || "Failed to create Bridge transfer",
533
- });
534
- }
535
-
536
- const bridgeTransfer = await bridgeTransferResponse.json();
537
- const destination = bridgeTransfer.source_deposit_instructions.to_address;
538
-
539
- const ata = getAssociatedTokenAddressSync(
540
- new PublicKey(TOKEN_MINTS.USDC),
541
- new PublicKey(destination),
542
- true,
543
- );
544
-
545
- await BridgeTransfer.create({
546
- bridgeTransferId: bridgeTransfer.id,
547
- bridgeUserId: bankAccount.bridgeUserId,
548
- bankAccountId: bankAccount.id!,
549
- amount: quoteResponse.outAmount,
550
- state: bridgeTransfer.state,
551
- });
552
-
553
- // Get swap instructions from Jupiter
554
- const myUsdcAta = getAssociatedTokenAddressSync(
555
- new PublicKey(TOKEN_MINTS.USDC),
556
- new PublicKey(userAddress),
557
- true,
558
- );
559
-
560
- const instructionsResponse = await fetch(
561
- "https://lite-api.jup.ag/swap/v1/swap-instructions",
562
- {
563
- method: "POST",
564
- headers: { "Content-Type": "application/json" },
565
- body: JSON.stringify({
566
- quoteResponse,
567
- userPublicKey: userAddress,
568
- destinationTokenAccount: myUsdcAta.toBase58(),
569
- dynamicComputeUnitLimit: true,
570
- prioritizationFeeLamports: {
571
- priorityLevelWithMaxLamports: {
572
- maxLamports: 1000000,
573
- priorityLevel: "medium",
574
- },
575
- },
576
- }),
577
- },
578
- );
579
-
580
- const instructions = await instructionsResponse.json();
581
- if (instructions.error) {
582
- throw errors.JUPITER_ERROR({
583
- message: "Failed to get swap instructions: " + instructions.error,
584
- });
585
- }
586
-
587
- const deserializeInstruction = (instruction: {
588
- programId: string;
589
- accounts: { pubkey: string; isSigner: boolean; isWritable: boolean }[];
590
- data: string;
591
- }) => {
592
- return new TransactionInstruction({
593
- programId: new PublicKey(instruction.programId),
594
- keys: instruction.accounts.map((key) => ({
595
- pubkey: new PublicKey(key.pubkey),
596
- isSigner: key.isSigner,
597
- isWritable: key.isWritable,
598
- })),
599
- data: Buffer.from(instruction.data, "base64"),
600
- });
601
- };
602
-
603
- const jupIxs = [
604
- createAssociatedTokenAccountIdempotentInstruction(
605
- new PublicKey(userAddress),
606
- myUsdcAta,
607
- new PublicKey(userAddress),
608
- new PublicKey(TOKEN_MINTS.USDC),
609
- ),
610
- deserializeInstruction(instructions.swapInstruction),
611
- createAssociatedTokenAccountIdempotentInstruction(
612
- new PublicKey(userAddress),
613
- ata,
614
- new PublicKey(destination),
615
- new PublicKey(TOKEN_MINTS.USDC),
616
- ),
617
- createTransferCheckedInstruction(
618
- myUsdcAta,
619
- new PublicKey(TOKEN_MINTS.USDC),
620
- ata,
621
- new PublicKey(userAddress),
622
- BigInt(quoteResponse.outAmount),
623
- 6,
624
- ),
625
- ];
626
-
627
- const draft = {
628
- instructions: jupIxs,
629
- feePayer: new PublicKey(userAddress),
630
- addressLookupTableAddresses: instructions.addressLookupTableAddresses.map(
631
- (address: string) => new PublicKey(address),
632
- ),
633
- };
634
-
635
- const connection = new Connection(process.env.SOLANA_RPC_URL!);
636
- const tx = toVersionedTx(
637
- await populateMissingDraftInfo(connection, {
638
- ...draft,
639
- instructions: await withPriorityFees({ ...draft, connection }),
640
- }),
641
- );
642
-
643
- const tag = generateTransactionTag({
644
- type: TRANSACTION_TYPES.BANK_SEND,
645
- bankAccountId: id,
646
- userAddress,
647
- amount: quoteResponse.outAmount,
648
- });
649
-
650
- return {
651
- bridgeTransfer,
652
- transactionData: {
653
- transactions: [
654
- {
655
- serializedTransaction: Buffer.from(tx.serialize()).toString(
656
- "base64",
657
- ),
658
- metadata: {
659
- type: "bank_send",
660
- description: `Withdraw $${(
661
- parseFloat(quoteResponse.outAmount) / 1e6
662
- ).toFixed(2)} worth of HNT to Bank Account`,
663
- },
664
- },
665
- ],
666
- parallel: false,
667
- tag,
668
- },
669
- };
670
- });
671
-
672
- const updateTransfer = publicProcedure
673
- .route({ method: "PATCH", path: "/fiat/transfers/{id}" })
674
- .input(UpdateTransferInputSchema)
675
- .output(UpdateTransferOutputSchema)
676
- .errors({
677
- NOT_FOUND: { message: "Transfer not found" },
678
- })
679
- .handler(async ({ input, errors }) => {
680
- const { id, solanaSignature } = input;
681
-
682
- const transfer = await BridgeTransfer.findOne({
683
- where: { bridgeTransferId: id },
684
- });
685
-
686
- if (!transfer) {
687
- throw errors.NOT_FOUND({ message: "Transfer not found" });
688
- }
689
-
690
- await transfer.update({ solanaSignature });
691
-
692
- return { success: true };
693
- });
694
-
695
- // ============================================================================
696
- // Router Export
697
- // ============================================================================
698
-
699
- export const fiatRouter = {
700
- getKycStatus,
701
- initKyc,
702
- getFees,
703
- listBankAccounts,
704
- createBankAccount,
705
- deleteBankAccount,
706
- getSendQuote,
707
- sendFunds,
708
- updateTransfer,
709
- };