@rift-finance/wallet 0.0.2

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 (58) hide show
  1. package/Readme.md +1345 -0
  2. package/dist/base-service.d.ts +28 -0
  3. package/dist/base-service.d.ts.map +1 -0
  4. package/dist/base-service.js +49 -0
  5. package/dist/base-service.js.map +1 -0
  6. package/dist/http-client.d.ts +15 -0
  7. package/dist/http-client.d.ts.map +1 -0
  8. package/dist/http-client.js +166 -0
  9. package/dist/http-client.js.map +1 -0
  10. package/dist/index.d.ts +61 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +130 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/mpesa-http-client.d.ts +14 -0
  15. package/dist/mpesa-http-client.d.ts.map +1 -0
  16. package/dist/mpesa-http-client.js +156 -0
  17. package/dist/mpesa-http-client.js.map +1 -0
  18. package/dist/services/assets.d.ts +31 -0
  19. package/dist/services/assets.d.ts.map +1 -0
  20. package/dist/services/assets.js +75 -0
  21. package/dist/services/assets.js.map +1 -0
  22. package/dist/services/auth.d.ts +23 -0
  23. package/dist/services/auth.d.ts.map +1 -0
  24. package/dist/services/auth.js +131 -0
  25. package/dist/services/auth.js.map +1 -0
  26. package/dist/services/defi.d.ts +6 -0
  27. package/dist/services/defi.d.ts.map +1 -0
  28. package/dist/services/defi.js +15 -0
  29. package/dist/services/defi.js.map +1 -0
  30. package/dist/services/notifications.d.ts +39 -0
  31. package/dist/services/notifications.d.ts.map +1 -0
  32. package/dist/services/notifications.js +79 -0
  33. package/dist/services/notifications.js.map +1 -0
  34. package/dist/services/onramp.d.ts +43 -0
  35. package/dist/services/onramp.d.ts.map +1 -0
  36. package/dist/services/onramp.js +109 -0
  37. package/dist/services/onramp.js.map +1 -0
  38. package/dist/services/payment-links.d.ts +24 -0
  39. package/dist/services/payment-links.d.ts.map +1 -0
  40. package/dist/services/payment-links.js +121 -0
  41. package/dist/services/payment-links.js.map +1 -0
  42. package/dist/services/signer.d.ts +25 -0
  43. package/dist/services/signer.d.ts.map +1 -0
  44. package/dist/services/signer.js +52 -0
  45. package/dist/services/signer.js.map +1 -0
  46. package/dist/services/transactions.d.ts +8 -0
  47. package/dist/services/transactions.d.ts.map +1 -0
  48. package/dist/services/transactions.js +29 -0
  49. package/dist/services/transactions.js.map +1 -0
  50. package/dist/services/wallet.d.ts +7 -0
  51. package/dist/services/wallet.d.ts.map +1 -0
  52. package/dist/services/wallet.js +28 -0
  53. package/dist/services/wallet.js.map +1 -0
  54. package/dist/types.d.ts +1069 -0
  55. package/dist/types.d.ts.map +1 -0
  56. package/dist/types.js +17 -0
  57. package/dist/types.js.map +1 -0
  58. package/package.json +41 -0
package/Readme.md ADDED
@@ -0,0 +1,1345 @@
1
+ # @rift-finance/wallet
2
+
3
+ Official TypeScript SDK for **Rift Finance wallet module** - A comprehensive multi-chain wallet and DeFi platform that enables seamless crypto transactions, AI-powered features, and cross-chain operations.
4
+
5
+
6
+ **Authentication is now required for transactions and payment links:**
7
+
8
+ - `transactions.send()` - Must include authentication (phone+OTP, email+OTP, or externalId+password)
9
+ - `paymentLinks.createSpecificSendLink()` - Must include authentication + recipient identification (recipientUsername/recipientPhoneNumber/recipientEmail)
10
+ - `paymentLinks.createOpenSendLink()` - Must include authentication
11
+
12
+ See examples below for the new authentication patterns.
13
+
14
+ ## 🌟 Features
15
+
16
+ - 🌐 **Multi-chain Support**: Arbitrum, Base, Optimism, Ethereum, BNB, Polygon, Lisk, Berachain
17
+ - 💸 **Gasless Transactions**: Execute transactions without paying gas fees
18
+ - 🔄 **DeFi Swaps**: Built-in token swapping across chains with best rates
19
+ - 💳 **Payment Links**: Create and manage crypto payment requests with custom redirect URLs
20
+ - 📱 **M-Pesa Onramp**: Convert Kenyan Shillings to crypto via M-Pesa STK Push
21
+ - 🛠️ **Signer**: Direct blockchain interaction with signing, transaction sending, and wallet management
22
+ - 🔐 **Secret Sharing**: Secure API key sharing marketplace
23
+ - 📊 **Portfolio Tracking**: Multi-chain balance and transaction history
24
+ - 🔒 **Enterprise Ready**: Production-grade security and reliability
25
+
26
+ ## 📚 Documentation
27
+
28
+ - 📖 **[Complete Documentation](https://docs.riftfi.xyz)**
29
+ - 🚀 **[Getting Started Guide](https://docs.riftfi.xyz/getting-started)**
30
+ - 📋 **[API Reference](https://docs.riftfi.xyz/api-reference)**
31
+ - 💡 **[Examples & Tutorials](https://docs.riftfi.xyz/examples)**
32
+
33
+ ## 🚀 Quick Start
34
+
35
+ ### Installation
36
+
37
+ ```bash
38
+ npm install @rift-finance/wallet
39
+ ```
40
+
41
+ ### Basic Usage
42
+
43
+ ```typescript
44
+ import Rift, { Environment } from "@rift-finance/wallet";
45
+
46
+ const rift = new Rift({
47
+ apiKey: "your-api-key",
48
+ environment: Environment.PRODUCTION,
49
+ });
50
+
51
+ // Authenticate user - Multiple authentication methods available (MUTUALLY EXCLUSIVE):
52
+
53
+ // Method 1: Phone + OTP ONLY (do NOT provide email or externalId)
54
+ const phoneAuth = await rift.auth.login({
55
+ phoneNumber: "+1234567890",
56
+ otpCode: "123456",
57
+ });
58
+
59
+ // Method 2: Email + OTP ONLY (do NOT provide phone or externalId)
60
+ const emailAuth = await rift.auth.login({
61
+ email: "user@example.com",
62
+ otpCode: "123456",
63
+ });
64
+
65
+ // Method 3: External ID + Password ONLY (do NOT provide phone or email)
66
+ const passwordAuth = await rift.auth.login({
67
+ externalId: "user@example.com",
68
+ password: "your-secure-password",
69
+ });
70
+
71
+ //internally, the rift instance is gonna be authenticated
72
+
73
+ // Get wallet balance across all chains
74
+ const balances = await rift.wallet.getChainBalance();
75
+ console.log("💰 Wallet balances:", balances);
76
+
77
+ // Send a transaction (requires authentication)
78
+ // Method 1: Using phone + OTP
79
+ const transaction = await rift.transactions.send({
80
+ to: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4Db45",
81
+ value: "10",
82
+ token: "USDC",
83
+ chain: "ARBITRUM",
84
+ type: "gasless",
85
+ phoneNumber: "+1234567890",
86
+ otpCode: "123456",
87
+ });
88
+
89
+ // Method 2: Using email + OTP
90
+ const transactionEmail = await rift.transactions.send({
91
+ to: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4Db45",
92
+ value: "10",
93
+ token: "USDC",
94
+ chain: "ARBITRUM",
95
+ type: "gasless",
96
+ email: "user@example.com",
97
+ otpCode: "123456",
98
+ });
99
+
100
+ // Method 3: Using external ID + password
101
+ const transactionExternal = await rift.transactions.send({
102
+ to: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4Db45",
103
+ value: "10",
104
+ token: "USDC",
105
+ chain: "ARBITRUM",
106
+ type: "gasless",
107
+ externalId: "user123",
108
+ password: "securePassword",
109
+ });
110
+
111
+ console.log("✅ Transaction sent:", transaction);
112
+
113
+ // Get transaction history with pagination
114
+ const history = await rift.transactions.getHistory({
115
+ limit: 10,
116
+ page: 1,
117
+ token: "USDC",
118
+ chain: "ARBITRUM",
119
+ });
120
+
121
+ console.log("📈 Transaction history:", {
122
+ transactions: history.transactions, // Array of TransactionHistory objects
123
+ pagination: {
124
+ total: history.pagination.total,
125
+ pages: history.pagination.pages,
126
+ currentPage: history.pagination.currentPage,
127
+ perPage: history.pagination.perPage,
128
+ },
129
+ });
130
+
131
+ // Each transaction in history.transactions contains:
132
+ // {
133
+ // id: string,
134
+ // userId: string,
135
+ // transactionHash: string,
136
+ // chain: string,
137
+ // token: string,
138
+ // currency?: string,
139
+ // amount: number,
140
+ // recipientAddress: string,
141
+ // createdAt: string
142
+ // }
143
+
144
+ // 📱 M-Pesa Onramp: Convert KES to crypto (Kenya users)
145
+ // const mpesaOnramp = await rift.onramp.initiateSafaricomSTK({
146
+ // amount: 100, // 100 KES
147
+ // phone: "0713322025",
148
+ // cryptoAsset: "POL-USDC",
149
+ // cryptoWalletAddress: "0x31DEBea3ba4101bb582dc31fDB3068bE686791b0",
150
+ // externalReference: "user123",
151
+ // });
152
+ ```
153
+
154
+ ## 🔐 Authentication
155
+
156
+ Rift SDK supports multiple authentication methods to accommodate different use cases.
157
+
158
+ ### ⚠️ CRITICAL: Authentication Parameter Rules
159
+
160
+ **Authentication parameters are MUTUALLY EXCLUSIVE**:
161
+
162
+ - ❌ **NEVER** provide `phoneNumber` + `email` together
163
+ - ❌ **NEVER** provide `phoneNumber` + `externalId` together
164
+ - ❌ **NEVER** provide `email` + `externalId` together
165
+ - ❌ **NEVER** provide all three together
166
+
167
+ **Choose ONE authentication method per request:**
168
+
169
+ - 📱 Phone-based: Use `phoneNumber` + `otpCode` ONLY
170
+ - 📧 Email-based: Use `email` + `otpCode` ONLY
171
+ - 🔑 External ID: Use `externalId` + `password` ONLY
172
+
173
+ ### User Registration
174
+
175
+ ```typescript
176
+ // Method 1: Register with External ID + Password ONLY
177
+ // ⚠️ Do NOT provide phoneNumber or email when using externalId
178
+ const signupWithPassword = await rift.auth.signup({
179
+ externalId: "user@example.com", // Can be email, username, or any unique ID
180
+ password: "secure-password",
181
+ // phoneNumber: "+1234567890", // ❌ NEVER provide with externalId
182
+ // email: "user@example.com", // ❌ NEVER provide with externalId
183
+ });
184
+
185
+ // Method 2: Register with Phone ONLY (OTP-based)
186
+ // ⚠️ Do NOT provide email or externalId when using phoneNumber
187
+ const signupWithPhone = await rift.auth.signup({
188
+ phoneNumber: "+1234567890",
189
+ // email: "user@example.com", // ❌ NEVER provide with phoneNumber
190
+ // externalId: "user123", // ❌ NEVER provide with phoneNumber
191
+ });
192
+
193
+ // Method 3: Register with Email ONLY (OTP-based)
194
+ // ⚠️ Do NOT provide phoneNumber or externalId when using email
195
+ const signupWithEmail = await rift.auth.signup({
196
+ email: "user@example.com",
197
+ // phoneNumber: "+1234567890", // ❌ NEVER provide with email
198
+ // externalId: "user123", // ❌ NEVER provide with email
199
+ });
200
+ ```
201
+
202
+ ### User Authentication
203
+
204
+ ```typescript
205
+ // Method 1: Phone + OTP Login ONLY
206
+ // ⚠️ Do NOT provide email or externalId when using phoneNumber
207
+ // First, send OTP
208
+ await rift.auth.sendOtp({ phone: "+1234567890" });
209
+ // Then login with OTP
210
+ const phoneLogin = await rift.auth.login({
211
+ phoneNumber: "+1234567890",
212
+ otpCode: "123456",
213
+ // externalId: "user@example.com", // ❌ NEVER provide with phoneNumber
214
+ // email: "user@example.com", // ❌ NEVER provide with phoneNumber
215
+ });
216
+
217
+ // Method 2: Email + OTP Login ONLY
218
+ // ⚠️ Do NOT provide phoneNumber or externalId when using email
219
+ // Send OTP to email
220
+ await rift.auth.sendOtp({ email: "user@example.com" });
221
+ // Login with email OTP
222
+ const emailLogin = await rift.auth.login({
223
+ email: "user@example.com",
224
+ otpCode: "123456",
225
+ // phoneNumber: "+1234567890", // ❌ NEVER provide with email
226
+ // externalId: "user123", // ❌ NEVER provide with email
227
+ });
228
+
229
+ // Method 3: External ID + Password Login ONLY
230
+ // ⚠️ Do NOT provide phoneNumber or email when using externalId
231
+ const passwordLogin = await rift.auth.login({
232
+ externalId: "user@example.com",
233
+ password: "secure-password",
234
+ // phoneNumber: "+1234567890", // ❌ NEVER provide with externalId
235
+ // email: "user@example.com", // ❌ NEVER provide with externalId
236
+ });
237
+
238
+ // Check authentication status
239
+ if (rift.auth.isAuthenticated()) {
240
+ console.log("✅ User is authenticated");
241
+
242
+ // Get user details
243
+ const user = await rift.auth.getUser();
244
+ console.log("👤 User info:", user);
245
+ }
246
+
247
+ // Logout
248
+ rift.auth.logout();
249
+ ```
250
+
251
+ ### OTP Management
252
+
253
+ ```typescript
254
+ // Send OTP to phone ONLY
255
+ // ⚠️ Do NOT provide email when using phone
256
+ await rift.auth.sendOtp({
257
+ phone: "+1234567890",
258
+ // email: "user@example.com", // ❌ NEVER provide with phone
259
+ });
260
+
261
+ // Send OTP to email ONLY
262
+ // ⚠️ Do NOT provide phone when using email
263
+ await rift.auth.sendOtp({
264
+ email: "user@example.com",
265
+ // phone: "+1234567890", // ❌ NEVER provide with email
266
+ });
267
+
268
+ // Verify OTP for phone ONLY
269
+ // ⚠️ Do NOT provide email when using phone
270
+ const verifyPhone = await rift.auth.verifyOtp({
271
+ phone: "+1234567890",
272
+ code: "123456",
273
+ // email: "user@example.com", // ❌ NEVER provide with phone
274
+ });
275
+
276
+ // Verify OTP for email ONLY
277
+ // ⚠️ Do NOT provide phone when using email
278
+ const verifyEmail = await rift.auth.verifyOtp({
279
+ email: "user@example.com",
280
+ code: "123456",
281
+ // phone: "+1234567890", // ❌ NEVER provide with email
282
+ });
283
+ ```
284
+
285
+ ### Account Deletion
286
+
287
+ ```typescript
288
+ // Delete with External ID + Password ONLY
289
+ // ⚠️ Do NOT provide phoneNumber or email when using externalId
290
+ await rift.auth.deleteUser({
291
+ externalId: "user@example.com",
292
+ password: "secure-password",
293
+ // phoneNumber: "+1234567890", // ❌ NEVER provide with externalId
294
+ // email: "user@example.com", // ❌ NEVER provide with externalId
295
+ });
296
+
297
+ // Delete with Phone + OTP ONLY
298
+ // ⚠️ Do NOT provide email or externalId when using phoneNumber
299
+ await rift.auth.sendOtp({ phone: "+1234567890" });
300
+ await rift.auth.deleteUser({
301
+ phoneNumber: "+1234567890",
302
+ otpCode: "123456",
303
+ // email: "user@example.com", // ❌ NEVER provide with phoneNumber
304
+ // externalId: "user123", // ❌ NEVER provide with phoneNumber
305
+ });
306
+
307
+ // Delete with Email + OTP ONLY
308
+ // ⚠️ Do NOT provide phoneNumber or externalId when using email
309
+ await rift.auth.sendOtp({ email: "user@example.com" });
310
+ await rift.auth.deleteUser({
311
+ email: "user@example.com",
312
+ otpCode: "123456",
313
+ // phoneNumber: "+1234567890", // ❌ NEVER provide with email
314
+ // externalId: "user123", // ❌ NEVER provide with email
315
+ });
316
+ ```
317
+
318
+ ## 💳 Payment Links
319
+
320
+ > **Important**: All payment link operations now require authentication. You must provide one of the following authentication methods:
321
+ >
322
+ > - Phone Number + OTP Code
323
+ > - Email + OTP Code
324
+ > - External ID + Password
325
+
326
+ ### Fetch Users for Payment Links
327
+
328
+ Before creating payment links or send links, you can fetch all users from your project to help with recipient selection. This is especially useful for building user-friendly interfaces where users can select recipients by their email, phone number, or external ID:
329
+
330
+ ```typescript
331
+ // Fetch all users grouped by identifier type
332
+ const users = await rift.paymentLinks.getAllUsers();
333
+
334
+ console.log("👥 Available users:", {
335
+ byExternalId: users.externalId, // Array of external IDs
336
+ byPhoneNumber: users.phoneNumber, // Array of phone numbers
337
+ byEmail: users.email, // Array of email addresses
338
+ });
339
+
340
+ // Use for recipient selection in your UI
341
+ // - Show dropdown/search of available emails for send links
342
+ // - Auto-complete phone numbers when creating specific send links
343
+ - Validate external IDs before creating payment requests
344
+ // - Build contact lists for easy recipient selection
345
+
346
+ // Example: Create send link to a user from the fetched list
347
+ if (users.email.length > 0) {
348
+ const recipientEmail = users.email[0]; // First available email
349
+ const sendLink = await rift.paymentLinks.createSpecificSendLink({
350
+ time: "1h",
351
+ recipientEmail: recipientEmail, // Use fetched email as recipient
352
+ value: "50",
353
+ token: "USDC",
354
+ chain: "ARBITRUM",
355
+ // Sender authentication required (example with phone + OTP)
356
+ phoneNumber: "+1234567890",
357
+ otpCode: "123456",
358
+ });
359
+ console.log("💸 Send link created for:", recipientEmail);
360
+ }
361
+ ```
362
+
363
+ ### Payment Requests (Request money from others)
364
+
365
+ Create payment requests that anyone can pay:
366
+
367
+ ```typescript
368
+ // Create a payment request
369
+ const paymentRequest = await rift.paymentLinks.requestPayment({
370
+ amount: 100,
371
+ chain: "BASE",
372
+ token: "USDC",
373
+ });
374
+
375
+ console.log("💳 Payment request:", paymentRequest.data);
376
+
377
+ // Someone pays your payment request
378
+ await rift.paymentLinks.payPaymentRequest(paymentRequest.data);
379
+
380
+ // Get all your payment requests
381
+ const paymentRequests = await rift.paymentLinks.listPaymentRequests({
382
+ expired: "false",
383
+ limit: "10",
384
+ page: "1",
385
+ });
386
+
387
+ console.log("📋 Your payment requests:", paymentRequests);
388
+
389
+ // Cancel a payment request if needed
390
+ const cancelResult = await rift.paymentLinks.cancelPaymentRequest(
391
+ paymentRequest.data
392
+ );
393
+ console.log("❌ Payment request cancelled:", cancelResult);
394
+ ```
395
+
396
+ ### Send Links (Send money to others)
397
+
398
+ Create links to send money to specific or open recipients:
399
+
400
+ **Time Format**: Use number followed by time unit:
401
+
402
+ - `s` = seconds (e.g., `30s`)
403
+ - `m` = minutes (e.g., `5m`)
404
+ - `h` = hours (e.g., `2h`)
405
+ - `d` = days (e.g., `7d`)
406
+
407
+ ```typescript
408
+ // Create a specific send link (for a particular recipient)
409
+ // Requires: 1) Recipient identification AND 2) Sender authentication
410
+
411
+ // Option 1: Send to username with phone authentication
412
+ const specificSendLinkUsername =
413
+ await rift.paymentLinks.createSpecificSendLink({
414
+ time: "1h", // Expiration time: 1s, 1m, 1h, 1d
415
+ recipientUsername: "john_doe", // Recipient's username
416
+ value: "50",
417
+ token: "USDC",
418
+ chain: "ARBITRUM",
419
+ // Sender authentication (phone + OTP)
420
+ phoneNumber: "+1234567890",
421
+ otpCode: "123456",
422
+ });
423
+
424
+ // Option 2: Send to phone number with email authentication
425
+ const specificSendLinkPhone = await rift.paymentLinks.createSpecificSendLink({
426
+ time: "2h", // Expiration time: 1s, 1m, 1h, 1d
427
+ recipientPhoneNumber: "+9876543210", // Recipient's phone number
428
+ value: "75",
429
+ token: "USDC",
430
+ chain: "BASE",
431
+ // Sender authentication (email + OTP)
432
+ email: "sender@example.com",
433
+ otpCode: "654321",
434
+ });
435
+
436
+ // Option 3: Send to email with external ID authentication
437
+ const specificSendLinkEmail = await rift.paymentLinks.createSpecificSendLink({
438
+ time: "24h", // Expiration time: 1s, 1m, 1h, 1d
439
+ recipientEmail: "recipient@example.com", // Recipient's email
440
+ value: "100",
441
+ token: "USDC",
442
+ chain: "POLYGON",
443
+ // Sender authentication (external ID + password)
444
+ externalId: "sender123",
445
+ password: "securePassword",
446
+ });
447
+
448
+ // Create an open send link (anyone can claim, requires sender authentication)
449
+ // Method 1: Using phone + OTP
450
+ const openSendLink = await rift.paymentLinks.createOpenSendLink({
451
+ time: "1h", // Expiration time: 1s, 1m, 1h, 1d
452
+ value: "25",
453
+ token: "USDC",
454
+ chain: "BASE",
455
+ phoneNumber: "+1234567890",
456
+ otpCode: "123456",
457
+ });
458
+
459
+ // Method 2: Using email + OTP
460
+ const openSendLinkEmail = await rift.paymentLinks.createOpenSendLink({
461
+ time: "1h",
462
+ value: "25",
463
+ token: "USDC",
464
+ chain: "BASE",
465
+ email: "sender@example.com",
466
+ otpCode: "654321",
467
+ });
468
+
469
+ // Method 3: Using external ID + password
470
+ const openSendLinkExternal = await rift.paymentLinks.createOpenSendLink({
471
+ time: "1h",
472
+ value: "25",
473
+ token: "USDC",
474
+ chain: "BASE",
475
+ externalId: "sender123",
476
+ password: "securePassword",
477
+ });
478
+
479
+ // Claim a specific send link (no recipient address needed)
480
+ const claimSpecific = await rift.paymentLinks.claimSpecificSendLink({
481
+ id: "send-link-id",
482
+ });
483
+
484
+ // Claim an open send link (no recipient address needed)
485
+ const claimOpen = await rift.paymentLinks.claimOpenSendLink({
486
+ id: "send-link-id",
487
+ });
488
+
489
+ // Get all your send links
490
+ const sendLinks = await rift.paymentLinks.listSendLinks({
491
+ fulfilled: "false", // "true" for claimed links, "false" for unclaimed
492
+ limit: "10",
493
+ page: "1",
494
+ });
495
+
496
+ console.log("💸 Your send links:", sendLinks);
497
+
498
+ // Cancel a send link
499
+ await rift.paymentLinks.cancelSendLink("send-link-url-id");
500
+ console.log("🔒 Send link cancelled");
501
+ ```
502
+
503
+ ### Register Custom Redirect URLs
504
+
505
+ Configure custom redirect URLs for your payment links to provide seamless user experience. **Requires OTP verification for security.**
506
+
507
+ ```typescript
508
+ // Step 1: Send OTP for verification (choose email OR phone)
509
+ // Option A: Send OTP to email
510
+ await rift.auth.sendOtp({ email: "owner@yourapp.com" });
511
+
512
+ // Option B: Send OTP to phone
513
+ await rift.auth.sendOtp({ phone: "+1234567890" });
514
+
515
+ // Step 2: Register redirect URL for payment requests with OTP
516
+ // Users will be redirected to your app when they visit payment links
517
+ // You can provide any combination of url, mobile_url, and telegram_url
518
+
519
+ // Option A: Register with email + OTP - Web URL only
520
+ const requestRedirectEmail =
521
+ await rift.paymentLinks.registerRequestLinkRedirectUrl({
522
+ url: "https://yourapp.com/pay", // Your web payment handling URL
523
+ email: "owner@yourapp.com", // Project owner's email
524
+ otpCode: "123456", // OTP received via email
525
+ project_api_key: "your-project-api-key", // Your project's API key
526
+ // phoneNumber: "+1234567890", // ❌ NEVER provide with email
527
+ });
528
+
529
+ // Option B: Register with phone + OTP - Multiple URLs for different platforms
530
+ const requestRedirectPhone =
531
+ await rift.paymentLinks.registerRequestLinkRedirectUrl({
532
+ url: "https://yourapp.com/pay", // Web URL
533
+ mobile_url: "yourapp://pay", // Mobile deep link
534
+ telegram_url: "https://t.me/yourbot?pay=", // Telegram bot URL
535
+ phoneNumber: "+1234567890", // Project owner's phone
536
+ otpCode: "123456", // OTP received via SMS
537
+ project_api_key: "your-project-api-key", // Your project's API key
538
+ // email: "owner@yourapp.com", // ❌ NEVER provide with phoneNumber
539
+ });
540
+
541
+ console.log("🔗 Request redirect registered:", requestRedirectEmail);
542
+ // Response includes: { message, data: { url, telegram_url?, mobile_url?, project_api_key, createdAt, updatedAt } }
543
+
544
+ // Step 3: Register redirect URL for send links with OTP
545
+ // Users will be redirected to your app when they visit send links
546
+
547
+ // Option A: Register with email + OTP - Telegram URL only
548
+ const sendRedirectEmail = await rift.paymentLinks.registerSendLinkRedirectUrl(
549
+ {
550
+ telegram_url: "https://t.me/yourbot?claim=", // Your Telegram bot claim URL
551
+ email: "owner@yourapp.com", // Project owner's email
552
+ otpCode: "123456", // OTP received via email
553
+ project_api_key: "your-project-api-key", // Your project's API key
554
+ // phoneNumber: "+1234567890", // ❌ NEVER provide with email
555
+ }
556
+ );
557
+
558
+ // Option B: Register with phone + OTP - All URL types
559
+ const sendRedirectPhone = await rift.paymentLinks.registerSendLinkRedirectUrl(
560
+ {
561
+ url: "https://yourapp.com/claim", // Web claim URL
562
+ mobile_url: "yourapp://claim", // Mobile deep link
563
+ telegram_url: "https://t.me/yourbot?claim=", // Telegram bot URL
564
+ phoneNumber: "+1234567890", // Project owner's phone
565
+ otpCode: "123456", // OTP received via SMS
566
+ project_api_key: "your-project-api-key", // Your project's API key
567
+ // email: "owner@yourapp.com", // ❌ NEVER provide with phoneNumber
568
+ }
569
+ );
570
+
571
+ console.log("🔗 Send redirect registered:", sendRedirectPhone);
572
+ // Response includes: { message, data: { url, telegram_url?, mobile_url?, project_api_key, createdAt, updatedAt } }
573
+
574
+ // URL Examples by Platform:
575
+ // WEB: https://yourapp.com/pay?nonce=payment_nonce
576
+ // MOBILE: yourapp://pay?nonce=payment_nonce (deep link)
577
+ // TELEGRAM: https://t.me/yourbot?claim=send_link_id
578
+
579
+ // Flexible URL Configuration:
580
+ // - Provide url for web applications and websites
581
+ // - Provide mobile_url for mobile app deep links
582
+ // - Provide telegram_url for Telegram bots and mini apps
583
+ // - You can provide any combination of these URLs
584
+ ```
585
+
586
+ ### Get Current Redirect URLs
587
+
588
+ Retrieve the currently registered redirect URLs for your project:
589
+
590
+ ```typescript
591
+ // Get current redirect URLs for your project
592
+ const redirectLinks = await rift.paymentLinks.getRedirectLinks({
593
+ project_api_key: "your-project-api-key",
594
+ });
595
+
596
+ console.log("🔗 Current redirect URLs:", {
597
+ requestRedirectLink: redirectLinks.requestRedirectLink, // For payment requests
598
+ sendLinkRedirect: redirectLinks.sendLinkRedirect, // For send links
599
+ });
600
+
601
+ // Each redirect link contains:
602
+ // {
603
+ // url: string,
604
+ // telegram_url?: string,
605
+ // mobile_url?: string,
606
+ // project_api_key: string,
607
+ // createdAt: string,
608
+ // updatedAt: string
609
+ // }
610
+
611
+ // Use this to check if redirect URLs are configured
612
+ if (redirectLinks.requestRedirectLink) {
613
+ console.log("✅ Payment request redirect configured:", {
614
+ webUrl: redirectLinks.requestRedirectLink.url,
615
+ telegramUrl: redirectLinks.requestRedirectLink.telegram_url,
616
+ mobileUrl: redirectLinks.requestRedirectLink.mobile_url,
617
+ });
618
+ } else {
619
+ console.log("⚠️ No payment request redirect URL configured");
620
+ }
621
+
622
+ if (redirectLinks.sendLinkRedirect) {
623
+ console.log("✅ Send link redirect configured:", {
624
+ webUrl: redirectLinks.sendLinkRedirect.url,
625
+ telegramUrl: redirectLinks.sendLinkRedirect.telegram_url,
626
+ mobileUrl: redirectLinks.sendLinkRedirect.mobile_url,
627
+ });
628
+ } else {
629
+ console.log("⚠️ No send link redirect URL configured");
630
+ }
631
+ ```
632
+
633
+ ### Payment Links Error Handling
634
+
635
+ ```typescript
636
+ try {
637
+ // Creating payment requests or send links
638
+ const paymentRequest = await rift.paymentLinks.requestPayment({
639
+ amount: 100,
640
+ chain: "BASE",
641
+ token: "USDC",
642
+ });
643
+
644
+ console.log("✅ Payment request created:", paymentRequest.data);
645
+ } catch (error) {
646
+ if (error.error?.includes("Token not found")) {
647
+ console.log("❌ Invalid token/chain combination");
648
+ // Show supported combinations to user
649
+ } else if (error.message?.includes("Invalid time format")) {
650
+ console.log("⏰ Invalid time format for send link");
651
+ console.log("💡 Use format: number + unit (1s, 5m, 2h, 30d)");
652
+ } else if (error.status === 401) {
653
+ console.log("🔐 Authentication required");
654
+ // Redirect to login
655
+ } else {
656
+ console.log("💥 Payment link error:", error.message);
657
+ }
658
+ }
659
+
660
+ try {
661
+ // Claiming or paying payment links
662
+ await rift.paymentLinks.payPaymentRequest("payment-nonce");
663
+ console.log("✅ Payment successful");
664
+ } catch (error) {
665
+ if (error.message?.includes("Payment link not found")) {
666
+ console.log("❌ Payment link not found or expired");
667
+ } else if (error.message?.includes("already paid")) {
668
+ console.log("⚠️ Payment link already paid");
669
+ } else if (error.message?.includes("Unauthorized")) {
670
+ console.log("🚫 Not authorized to pay this link");
671
+ } else if (error.message?.includes("insufficient")) {
672
+ console.log("💰 Insufficient balance for payment");
673
+ } else {
674
+ console.log("💥 Payment failed:", error.message);
675
+ }
676
+ }
677
+ ```
678
+
679
+ ## 🛠️ Signer
680
+
681
+ The Signer service provides direct blockchain interaction capabilities for advanced users and integrations. It allows you to manage wallet instances, sign transactions, send transactions, and sign messages across multiple chains.
682
+
683
+ ### Get Wallet Instance Information
684
+
685
+ Get comprehensive wallet details for any supported chain:
686
+
687
+ ```typescript
688
+ // Get wallet instance details for a specific chain
689
+ const walletInfo = await rift.signer.getWalletInstance({
690
+ chain: "ETHEREUM",
691
+ });
692
+
693
+ console.log("🔐 Wallet Instance Info:", {
694
+ address: walletInfo.address,
695
+ publicKey: walletInfo.publicKey,
696
+ chainInfo: {
697
+ id: walletInfo.chain.id,
698
+ name: walletInfo.chain.name,
699
+ nativeToken: walletInfo.chain.nativeToken,
700
+ supportedTokens: walletInfo.chain.tokens,
701
+ },
702
+ provider: {
703
+ url: walletInfo.provider.url,
704
+ chainId: walletInfo.provider.chainId,
705
+ name: walletInfo.provider.name,
706
+ },
707
+ capabilities: {
708
+ isWallet: walletInfo._isWallet,
709
+ isSigner: walletInfo._isSigner,
710
+ availableMethods: walletInfo.availableMethods,
711
+ },
712
+ });
713
+
714
+ // Supported chains: ETHEREUM, ARBITRUM, BASE, OPTIMISM, BNB, POLYGON, LISK, BERACHAIN
715
+ ```
716
+
717
+ ### Sign Transactions
718
+
719
+ Sign transactions without broadcasting them to the network. Perfect for offline signing or when you need to review transactions before sending:
720
+
721
+ ```typescript
722
+ // Basic ETH transfer signature
723
+ const signedEthTx = await rift.signer.signTransaction({
724
+ chain: "ETHEREUM",
725
+ transactionData: {
726
+ to: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4Db45",
727
+ value: "1000000000000000000", // 1 ETH in wei
728
+ gasLimit: "21000",
729
+ gasPrice: "20000000000", // 20 gwei
730
+ nonce: 42,
731
+ },
732
+ });
733
+
734
+ // EIP-1559 transaction with priority fees
735
+ const signedEIP1559Tx = await rift.signer.signTransaction({
736
+ chain: "ARBITRUM",
737
+ transactionData: {
738
+ to: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4Db45",
739
+ value: "500000000000000000", // 0.5 ETH
740
+ gasLimit: "21000",
741
+ maxFeePerGas: "30000000000", // 30 gwei
742
+ maxPriorityFeePerGas: "2000000000", // 2 gwei
743
+ type: 2, // EIP-1559 transaction
744
+ },
745
+ });
746
+
747
+ // Contract interaction signature
748
+ const signedContractTx = await rift.signer.signTransaction({
749
+ chain: "BASE",
750
+ transactionData: {
751
+ to: "0xa0b86a33e6411c8f62a587c5c51e3f58a4d9b8d4", // Contract address
752
+ value: "0",
753
+ data: "0xa9059cbb000000000000000000000000742d35cc6634c0532925a3b8d4c9db96c4b4db45000000000000000000000000000000000000000000000000000000000000007b", // ERC-20 transfer
754
+ gasLimit: "60000",
755
+ },
756
+ });
757
+
758
+ console.log("✍️ Signed Transaction:", {
759
+ signedTransaction: signedEthTx.signedTransaction,
760
+ txHash: signedEthTx.txHash,
761
+ from: signedEthTx.from,
762
+ originalTx: signedEthTx.originalTx,
763
+ });
764
+ ```
765
+
766
+ ### Send Transactions
767
+
768
+ Sign and broadcast transactions directly to the blockchain with automatic gas estimation:
769
+
770
+ ```typescript
771
+ // Simple ETH transfer
772
+ const ethTransfer = await rift.signer.sendTransaction({
773
+ chain: "ARBITRUM",
774
+ transactionData: {
775
+ to: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4Db45",
776
+ value: "500000000000000000", // 0.5 ETH
777
+ gasLimit: "21000", // Optional - will auto-estimate if not provided
778
+ },
779
+ });
780
+
781
+ // Token transfer (ERC-20)
782
+ const tokenTransfer = await rift.signer.sendTransaction({
783
+ chain: "POLYGON",
784
+ transactionData: {
785
+ to: "0x2791bca1f2de4661ed88a30c99a7a9449aa84174", // USDC contract
786
+ value: "0",
787
+ data: "0xa9059cbb000000000000000000000000742d35cc6634c0532925a3b8d4c9db96c4b4db45000000000000000000000000000000000000000000000000000000000000007b",
788
+ gasLimit: "65000",
789
+ },
790
+ });
791
+
792
+ // Contract interaction with automatic nonce
793
+ const contractCall = await rift.signer.sendTransaction({
794
+ chain: "BNB",
795
+ transactionData: {
796
+ to: "0xe9e7cea3dedca5984780bafc599bd69add087d56", // BUSD contract
797
+ value: "0",
798
+ data: "0x70a08231000000000000000000000000742d35cc6634c0532925a3b8d4c9db96c4b4db45", // balanceOf
799
+ gasLimit: "50000",
800
+ // nonce will be automatically determined
801
+ },
802
+ });
803
+
804
+ console.log("🚀 Transaction Result:", {
805
+ hash: ethTransfer.hash,
806
+ from: ethTransfer.from,
807
+ to: ethTransfer.to,
808
+ value: ethTransfer.value,
809
+ gasUsed: ethTransfer.gasLimit,
810
+ confirmations: ethTransfer.confirmations,
811
+ blockNumber: ethTransfer.blockNumber,
812
+ blockHash: ethTransfer.blockHash,
813
+ timestamp: ethTransfer.timestamp,
814
+ rawTransaction: ethTransfer.raw,
815
+ });
816
+ ```
817
+
818
+ ### Sign Messages
819
+
820
+ Sign arbitrary messages for authentication, verification, or off-chain interactions:
821
+
822
+ ```typescript
823
+ // Simple message signing
824
+ const messageSignature = await rift.signer.signMessage({
825
+ chain: "ETHEREUM",
826
+ message: "Hello, Rift Finance! Timestamp: " + Date.now(),
827
+ });
828
+
829
+ // Login message signing
830
+ const loginSignature = await rift.signer.signMessage({
831
+ chain: "ARBITRUM",
832
+ message: `Welcome to DApp!\n\nNonce: ${Date.now()}\nWallet: ${
833
+ walletInfo.address
834
+ }`,
835
+ });
836
+
837
+ // Structured data signing (personal_sign format)
838
+ const structuredSignature = await rift.signer.signMessage({
839
+ chain: "BASE",
840
+ message: JSON.stringify({
841
+ action: "transfer",
842
+ amount: "100",
843
+ token: "USDC",
844
+ timestamp: Date.now(),
845
+ }),
846
+ });
847
+
848
+ console.log("📝 Message Signature:", {
849
+ originalMessage: messageSignature.message,
850
+ signature: messageSignature.signature,
851
+ signer: messageSignature.signer,
852
+ messageHash: messageSignature.messageHash,
853
+ recoveredAddress: messageSignature.recoveredAddress,
854
+ });
855
+
856
+ // Verify the signature
857
+ const isValidSignature =
858
+ messageSignature.signer === messageSignature.recoveredAddress;
859
+ console.log("✅ Signature valid:", isValidSignature);
860
+
861
+ // Use signature for authentication
862
+ if (isValidSignature) {
863
+ console.log(
864
+ "🔐 Message successfully verified for address:",
865
+ messageSignature.signer
866
+ );
867
+ }
868
+ ```
869
+
870
+ ### Multi-Chain Usage Examples
871
+
872
+ ```typescript
873
+ // Working with multiple chains in sequence
874
+ const chains = ["ETHEREUM", "ARBITRUM", "BASE", "POLYGON"];
875
+
876
+ for (const chain of chains) {
877
+ // Get wallet info for each chain
878
+ const walletInfo = await rift.signer.getWalletInstance({ chain });
879
+ console.log(`💳 ${chain} wallet:`, walletInfo.address);
880
+
881
+ // Sign a message on each chain
882
+ const signature = await rift.signer.signMessage({
883
+ chain,
884
+ message: `Hello from ${chain} at ${Date.now()}`,
885
+ });
886
+ console.log(`✍️ ${chain} signature:`, signature.signature);
887
+ }
888
+
889
+ // Cross-chain transaction coordination
890
+ const arbitrumTx = await rift.signer.sendTransaction({
891
+ chain: "ARBITRUM",
892
+ transactionData: {
893
+ to: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4Db45",
894
+ value: "100000000000000000", // 0.1 ETH
895
+ },
896
+ });
897
+
898
+ const baseTx = await rift.signer.sendTransaction({
899
+ chain: "BASE",
900
+ transactionData: {
901
+ to: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4Db45",
902
+ value: "100000000000000000", // 0.1 ETH
903
+ },
904
+ });
905
+
906
+ console.log("🌉 Cross-chain transactions:", {
907
+ arbitrum: arbitrumTx.hash,
908
+ base: baseTx.hash,
909
+ });
910
+ ```
911
+
912
+ ### Error Handling
913
+
914
+ ```typescript
915
+ try {
916
+ const result = await rift.signer.sendTransaction({
917
+ chain: "ETHEREUM",
918
+ transactionData: {
919
+ to: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4Db45",
920
+ value: "1000000000000000000",
921
+ },
922
+ });
923
+ console.log("✅ Transaction successful:", result.hash);
924
+ } catch (error) {
925
+ if (error.message?.includes("insufficient funds")) {
926
+ console.log("💰 Insufficient balance for transaction");
927
+ } else if (error.message?.includes("gas")) {
928
+ console.log("⛽ Gas estimation failed - check gas limits");
929
+ } else if (error.error?.includes("unsupported chain")) {
930
+ console.log("🌐 Chain not supported:", error.supportedChains);
931
+ } else {
932
+ console.log("💥 Transaction failed:", error);
933
+ }
934
+ }
935
+ ```
936
+
937
+ ## 🔄 DeFi Swaps
938
+
939
+ Swap tokens across chains with best rates:
940
+
941
+ ```typescript
942
+ // Swap USDC to ETH on Arbitrum (gasless!)
943
+ const swapResult = await rift.defi.swap({
944
+ chain: "ARBITRUM",
945
+ flow: "gasless",
946
+ token_to_sell: "USDC",
947
+ token_to_buy: "ETH",
948
+ value: "100",
949
+ });
950
+
951
+ console.log("🔄 Swap completed:", swapResult);
952
+ ```
953
+
954
+ ## 📱 M-Pesa Onramp
955
+
956
+ Convert Kenyan Shillings (KES) to crypto using M-Pesa mobile money. Rift provides seamless integration with Safaricom's M-Pesa STK Push for instant crypto onramping.
957
+
958
+ ### Supported Crypto Assets
959
+
960
+ - **POL-USDC** - USDC on Polygon
961
+ - **BERA-USDC** - USDC on Berachain
962
+ - **ETH** - Ethereum
963
+ - **WBERA** - Wrapped BERA on Berachain
964
+
965
+ ### Initiate M-Pesa STK Push
966
+
967
+ Start a crypto purchase by triggering an STK push to the user's phone:
968
+
969
+ ```typescript
970
+ // Initiate M-Pesa STK push for crypto onramping
971
+ const stkResponse = await rift.onramp.initiateSafaricomSTK({
972
+ email: "user@example.com", // Optional
973
+ amount: 100, // Amount in KES
974
+ phone: "0713322025", // User's M-Pesa phone number
975
+ cryptoAsset: "POL-USDC", // Crypto to receive
976
+ cryptoWalletAddress: "0x31DEBea3ba4101bb582dc31fDB3068bE686791b0",
977
+ externalReference: "user123", // Unique identifier (Telegram ID, user ID, etc.)
978
+ });
979
+
980
+ console.log("📱 STK Push initiated:", stkResponse);
981
+
982
+ Response structure:
983
+ {
984
+ "success": true,
985
+ "message": "STK Push initiated successfully. Check your phone.",
986
+ "data": {
987
+ "message": "STK Push initiated successfully.",
988
+ "merchantRequestID": "ed4e-4482-896f-139740cf342c4176666",
989
+ "checkoutRequestID": "ws_CO_05062025134410304713322025",
990
+ "safaricomResponse": { ... },
991
+ "cryptoIntent": {
992
+ "asset": "POL-USDC",
993
+ "walletAddress": "0x31DEBea3ba4101bb582dc31fDB3068bE686791b0"
994
+ },
995
+ "note": "Upon successful payment, POL-USDC will be sent to wallet"
996
+ }
997
+ }
998
+ ```
999
+
1000
+ ### Check Transaction Status
1001
+
1002
+ Monitor the status of M-Pesa transactions and crypto transfers:
1003
+
1004
+ ```typescript
1005
+ // Check transaction status by checkout request ID
1006
+ const status = await rift.onramp.getSafaricomTransactionStatus({
1007
+ checkoutRequestId: "ws_CO_05062025134410304713322025",
1008
+ });
1009
+
1010
+ // Or check by merchant request ID
1011
+ const statusByMerchant = await rift.onramp.getSafaricomTransactionStatus({
1012
+ merchantRequestId: "ed4e-4482-896f-139740cf342c4176666",
1013
+ });
1014
+
1015
+ console.log("📊 Transaction status:", status);
1016
+
1017
+ Response for successful transaction:
1018
+ {
1019
+ "success": true,
1020
+ "status": "success",
1021
+ "data": {
1022
+ "id": "f11306df-473e-4487-9e73-6c821e475558",
1023
+ "status": "success",
1024
+ "amount": 100,
1025
+ "currency": "KES",
1026
+ "phoneNumber": "254713322025",
1027
+ "mpesaReceiptNumber": "TF53L3KG7L",
1028
+ "cryptoStatus": "success",
1029
+ "cryptoTxHash": "0xf9c9805a6f8fb783d928fa0a686ad8a3a6b191804a9d8a1865bd34f136af0b66",
1030
+ "cryptoAmount": 0.735294,
1031
+ "amountInUSD": 0.735294,
1032
+ "transactionDate": "20250605102014",
1033
+ "createdAt": "2025-06-05T07:19:59.534Z"
1034
+ }
1035
+ }
1036
+ ```
1037
+
1038
+ ### Auto-Poll Transaction Status
1039
+
1040
+ Automatically check transaction status until completion:
1041
+
1042
+ ```typescript
1043
+ // Auto-poll using checkout request ID (recommended)
1044
+ const finalStatus = await rift.onramp.pollSafaricomTransactionStatus(
1045
+ "ws_CO_05062025134410304713322025", // checkoutRequestId
1046
+ undefined, // merchantId (not needed)
1047
+ 10, // maxAttempts (default: 10)
1048
+ 10000 // intervalMs - poll every 10 seconds (default: 10000)
1049
+ );
1050
+
1051
+ // Auto-poll using merchant request ID
1052
+ const finalStatusByMerchant =
1053
+ await rift.onramp.pollSafaricomTransactionStatus(
1054
+ undefined, // checkoutRequestId (not needed)
1055
+ "ed4e-4482-896f-139740cf342c4176666", // merchantId
1056
+ 15, // Try up to 15 times
1057
+ 5000 // Poll every 5 seconds
1058
+ );
1059
+
1060
+ // Handle the result
1061
+ if (finalStatus.status === "success") {
1062
+ console.log("🎉 Payment successful!");
1063
+ console.log("💰 Crypto amount:", finalStatus.data.cryptoAmount);
1064
+ console.log("🔗 Crypto TX hash:", finalStatus.data.cryptoTxHash);
1065
+ console.log("📱 M-Pesa receipt:", finalStatus.data.mpesaReceiptNumber);
1066
+ } else if (finalStatus.status === "failed") {
1067
+ console.log("❌ Payment failed:", finalStatus.data.failureReason);
1068
+ } else {
1069
+ console.log("⏳ Payment still pending");
1070
+ }
1071
+ ```
1072
+
1073
+ ### Complete M-Pesa Flow Example
1074
+
1075
+ Here's a complete example showing the entire onramp process:
1076
+
1077
+ ```typescript
1078
+ async function completeMpesaOnramp() {
1079
+ try {
1080
+ // Step 1: Initiate STK push
1081
+ console.log("📱 Initiating M-Pesa STK push...");
1082
+ const stkResponse = await rift.onramp.initiateSafaricomSTK({
1083
+ amount: 500, // 500 KES
1084
+ phone: "0713322025",
1085
+ cryptoAsset: "POL-USDC",
1086
+ cryptoWalletAddress: "0x31DEBea3ba4101bb582dc31fDB3068bE686791b0",
1087
+ externalReference: "telegram_user_12345",
1088
+ });
1089
+
1090
+ if (!stkResponse.success) {
1091
+ throw new Error("Failed to initiate STK push");
1092
+ }
1093
+
1094
+ console.log("✅ STK push sent to phone");
1095
+ const checkoutRequestId = stkResponse.data.checkoutRequestID;
1096
+
1097
+ // Step 2: Poll for completion
1098
+ console.log("⏳ Waiting for M-Pesa payment...");
1099
+ const result = await rift.onramp.pollSafaricomTransactionStatus(
1100
+ checkoutRequestId,
1101
+ undefined,
1102
+ 20, // Wait up to 20 attempts
1103
+ 15000 // Check every 15 seconds
1104
+ );
1105
+
1106
+ // Step 3: Handle result
1107
+ switch (result.status) {
1108
+ case "success":
1109
+ console.log("🎉 Onramp successful!");
1110
+ console.log(
1111
+ `💰 Received: ${result.data.cryptoAmount} ${result.data.cryptoIntent.asset}`
1112
+ );
1113
+ console.log(`🔗 TX Hash: ${result.data.cryptoTxHash}`);
1114
+ console.log(`📱 M-Pesa Receipt: ${result.data.mpesaReceiptNumber}`);
1115
+ break;
1116
+
1117
+ case "failed":
1118
+ console.log("❌ Onramp failed");
1119
+ console.log(`💔 Reason: ${result.data.failureReason}`);
1120
+ if (result.data.cryptoFailureReason) {
1121
+ console.log(`🔗 Crypto error: ${result.data.cryptoFailureReason}`);
1122
+ }
1123
+ break;
1124
+
1125
+ case "pending":
1126
+ console.log("⏳ Transaction still pending after maximum wait time");
1127
+ console.log("💡 You can continue checking status manually");
1128
+ break;
1129
+ }
1130
+
1131
+ return result;
1132
+ } catch (error) {
1133
+ console.error("💥 M-Pesa onramp error:", error);
1134
+ throw error;
1135
+ }
1136
+ }
1137
+
1138
+ // Use the complete flow
1139
+ completeMpesaOnramp()
1140
+ .then((result) => console.log("🏁 Onramp completed:", result.status))
1141
+ .catch((error) => console.error("🚨 Onramp failed:", error.message));
1142
+ ```
1143
+
1144
+ ### Error Handling for M-Pesa
1145
+
1146
+ ```typescript
1147
+ try {
1148
+ const stkResponse = await rift.onramp.initiateSafaricomSTK({
1149
+ amount: 100,
1150
+ phone: "0713322025",
1151
+ cryptoAsset: "POL-USDC",
1152
+ cryptoWalletAddress: "0x31DEBea3ba4101bb582dc31fDB3068bE686791b0",
1153
+ externalReference: "user123",
1154
+ });
1155
+ } catch (error) {
1156
+ if (error.status === 400) {
1157
+ console.log("❌ Invalid request parameters:", error.message);
1158
+ // Check phone number format, amount limits, etc.
1159
+ } else if (error.status === 429) {
1160
+ console.log("⏰ Rate limited - too many requests");
1161
+ // Implement retry with backoff
1162
+ } else if (error.status === 500) {
1163
+ console.log("🔧 M-Pesa service temporarily unavailable");
1164
+ // Show maintenance message
1165
+ } else {
1166
+ console.log("💥 Unexpected error:", error);
1167
+ }
1168
+ }
1169
+ ```
1170
+
1171
+ ## 🌐 Supported Networks
1172
+
1173
+ | Network | Chain ID | Native Token | Status |
1174
+ | --------- | -------- | ------------ | ------- |
1175
+ | Arbitrum | 42161 | ETH | ✅ Live |
1176
+ | Base | 8453 | ETH | ✅ Live |
1177
+ | Optimism | 10 | ETH | ✅ Live |
1178
+ | Ethereum | 1 | ETH | ✅ Live |
1179
+ | BNB Chain | 56 | BNB | ✅ Live |
1180
+ | Polygon | 137 | MATIC | ✅ Live |
1181
+ | Lisk | 1135 | LSK | ✅ Live |
1182
+ | Berachain | 80085 | BERA | ✅ Live |
1183
+
1184
+ ## 💰 Supported Tokens
1185
+
1186
+ - **Stablecoins**: USDC, USDT, USDC.e
1187
+ - **Major Cryptos**: ETH, BTC, WBERA
1188
+ - **Native Tokens**: LSK, BNB, MATIC
1189
+ - **And many more...**
1190
+
1191
+ ## 🔧 Advanced Configuration
1192
+
1193
+ ```typescript
1194
+ const rift = new Rift({
1195
+ apiKey: "your-api-key",
1196
+ environment: Environment.PRODUCTION,
1197
+ });
1198
+ ```
1199
+
1200
+ ## 📋 Complete API Reference
1201
+
1202
+ ### 🔐 Authentication
1203
+
1204
+ ```typescript
1205
+ rift.auth.signup(request); // Create new account
1206
+ rift.auth.login(request); // Login with phone/OTP
1207
+ rift.auth.sendOtp(request); // Send OTP code
1208
+ rift.auth.verifyOtp(request); // Verify OTP
1209
+ rift.auth.getUser(); // Get current user info
1210
+ rift.auth.deleteUser(request); // Delete user account
1211
+ ```
1212
+
1213
+ ### 💼 Wallet Management
1214
+
1215
+ ```typescript
1216
+ rift.wallet.getTokenBalance(request); // Get specific token balance
1217
+ rift.wallet.getChainBalance(request); // Get all balances on chain
1218
+ ```
1219
+
1220
+ ### 💸 Transactions
1221
+
1222
+ > **Note**: `send()` requires authentication with one of: phoneNumber + otpCode, email + otpCode, or externalId + password
1223
+
1224
+ ```typescript
1225
+ rift.transactions.send(request); // Send crypto transaction (requires auth)
1226
+ rift.transactions.getHistory(request?); // Get paginated transaction history with TransactionHistory objects
1227
+ rift.transactions.getFee(request); // Get transaction fee estimate
1228
+ ```
1229
+
1230
+ ### 💳 Payment Links
1231
+
1232
+ > **Note**: `createSpecificSendLink()` and `createOpenSendLink()` require authentication
1233
+
1234
+ ```typescript
1235
+ // User Management
1236
+ rift.paymentLinks.getAllUsers(); // Fetch all users grouped by identifier type (externalId, phoneNumber, email)
1237
+
1238
+ // Payment Requests (requesting money from others)
1239
+ rift.paymentLinks.requestPayment(request); // Create payment request
1240
+ rift.paymentLinks.payPaymentRequest(nonce); // Pay a payment request
1241
+ rift.paymentLinks.listPaymentRequests(request?); // Get list of payment requests
1242
+ rift.paymentLinks.cancelPaymentRequest(nonce); // Cancel a payment request
1243
+
1244
+ // Send Links (sending money to others)
1245
+ rift.paymentLinks.createSpecificSendLink(request); // Create specific send link (requires auth + recipientUsername/recipientPhoneNumber/recipientEmail)
1246
+ rift.paymentLinks.createOpenSendLink(request); // Create open send link (requires auth)
1247
+ rift.paymentLinks.claimSpecificSendLink(request); // Claim specific send link (id only)
1248
+ rift.paymentLinks.claimOpenSendLink(request); // Claim open send link (id only)
1249
+ rift.paymentLinks.listSendLinks(request?); // Get list of send links
1250
+ rift.paymentLinks.cancelSendLink(urlId); // Cancel a send link
1251
+
1252
+ // Redirect URL Registration (requires OTP verification + project API key)
1253
+ rift.paymentLinks.registerRequestLinkRedirectUrl(request); // Register payment request redirect URL (email/phoneNumber + OTP + url/mobile_url/telegram_url + project_api_key)
1254
+ rift.paymentLinks.registerSendLinkRedirectUrl(request); // Register send link redirect URL (email/phoneNumber + OTP + url/mobile_url/telegram_url + project_api_key)
1255
+ rift.paymentLinks.getRedirectLinks(request); // Get current redirect URLs for project (requires project_api_key)
1256
+ ```
1257
+
1258
+ ### 🛠️ Signer
1259
+
1260
+ ```typescript
1261
+ rift.signer.getWalletInstance(request); // Get wallet instance info for chain
1262
+ rift.signer.signTransaction(request); // Sign transaction without broadcasting
1263
+ rift.signer.sendTransaction(request); // Sign and broadcast transaction
1264
+ rift.signer.signMessage(request); // Sign arbitrary message for verification
1265
+ ```
1266
+
1267
+ ### 🔄 DeFi Operations
1268
+
1269
+ ```typescript
1270
+ rift.defi.swap(request); // Swap tokens across chains
1271
+ ```
1272
+
1273
+ ### 📱 M-Pesa Onramp
1274
+
1275
+ ```typescript
1276
+ // STK Push Initiation
1277
+ rift.onramp.initiateSafaricomSTK(request); // Initiate M-Pesa STK push
1278
+
1279
+ // Transaction Status
1280
+ rift.onramp.getSafaricomTransactionStatus(request); // Get transaction status
1281
+ rift.onramp.pollSafaricomTransactionStatus(checkoutId?, merchantId?, maxAttempts?, intervalMs?); // Auto-poll status
1282
+
1283
+ // Transaction History
1284
+ rift.onramp.getUserTransactionHistory(filters?); // Get user's onramp history
1285
+ ```
1286
+
1287
+ ## 🚨 Error Handling
1288
+
1289
+ ```typescript
1290
+ try {
1291
+ const result = await rift.transactions.send(request);
1292
+ console.log("✅ Success:", result);
1293
+ } catch (error) {
1294
+ if (error.status === 401) {
1295
+ console.log("🔐 Authentication required");
1296
+ // Redirect to login
1297
+ } else if (error.status === 400) {
1298
+ console.log("❌ Bad request:", error.message);
1299
+ // Show user-friendly error
1300
+ } else if (error.status === 429) {
1301
+ console.log("⏰ Rate limited, please try again later");
1302
+ // Implement retry logic
1303
+ } else {
1304
+ console.log("💥 Unexpected error:", error);
1305
+ // Log for debugging
1306
+ }
1307
+ }
1308
+ ```
1309
+
1310
+ ## 🔗 Useful Links
1311
+
1312
+ - 🌐 **[Rift Finance Website](https://riftfi.xyz)**
1313
+ - 📖 **[Complete Documentation](https://docs.riftfi.xyz)**
1314
+ - 🎮 **[Interactive Playground](https://docs.riftfi.xyz/playground)**
1315
+ - 💬 **[Discord Community](https://discord.gg/rift-finance)**
1316
+ - 🐛 **[GitHub Issues](https://github.com/rift-finance/wallet-sdk/issues)**
1317
+ - 📧 **[Support Email](mailto:support@riftfi.xyz)**
1318
+ - 🐦 **[Twitter Updates](https://twitter.com/rift_finance)**
1319
+
1320
+ ## 🤝 Contributing
1321
+
1322
+ We welcome contributions! Please see our [Contributing Guide](https://github.com/rift-finance/wallet-sdk/blob/main/CONTRIBUTING.md) for details.
1323
+
1324
+ ## 📄 License
1325
+
1326
+ MIT License - see [LICENSE](https://github.com/rift-finance/wallet-sdk/blob/main/LICENSE) file for details.
1327
+
1328
+ ## 🆘 Support
1329
+
1330
+ Need help? We're here for you:
1331
+
1332
+ - 📖 Check our [Documentation](https://docs.riftfi.xyz)
1333
+ - 💬 Join our [Discord](https://discord.gg/rift-finance)
1334
+ - 📧 Email us at [support@riftfi.xyz](mailto:support@riftfi.xyz)
1335
+ - 🐛 Report bugs on [GitHub](https://github.com/rift-finance/wallet-sdk/issues)
1336
+
1337
+ ---
1338
+
1339
+ <div align="center">
1340
+
1341
+ **Built with ❤️ by the Rift Finance Team**
1342
+
1343
+ [Website](https://riftfi.xyz) • [Docs](https://docs.riftfi.xyz) • [Discord](https://discord.gg/rift-finance) • [Twitter](https://twitter.com/rift_finance)
1344
+
1345
+ </div>