@rift-finance/wallet 1.4.28 → 1.4.30
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.
- package/Readme.md +154 -1234
- package/package.json +1 -1
package/Readme.md
CHANGED
|
@@ -1,1345 +1,265 @@
|
|
|
1
1
|
# @rift-finance/wallet
|
|
2
2
|
|
|
3
|
-
Official TypeScript SDK for
|
|
3
|
+
Official TypeScript SDK for Rift Finance — multi-chain wallets, gasless transactions, swaps, bridges, on/off-ramps, and payment links. Designed for server-side use (Node) and trusted runtimes (mobile, workers).
|
|
4
4
|
|
|
5
|
+
> Building a **browser** integration? Use [`@rift-finance/react`](https://www.npmjs.com/package/@rift-finance/react) or the [hosted widget](https://widget.riftfi.xyz) instead — they're purpose-built for untrusted environments.
|
|
5
6
|
|
|
6
|
-
|
|
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
|
|
7
|
+
## Install
|
|
36
8
|
|
|
37
9
|
```bash
|
|
38
10
|
npm install @rift-finance/wallet
|
|
39
11
|
```
|
|
40
12
|
|
|
41
|
-
|
|
13
|
+
## Quick start
|
|
42
14
|
|
|
43
|
-
```
|
|
15
|
+
```ts
|
|
44
16
|
import Rift, { Environment } from "@rift-finance/wallet";
|
|
45
17
|
|
|
46
18
|
const rift = new Rift({
|
|
47
|
-
apiKey:
|
|
19
|
+
apiKey: process.env.RIFT_API_KEY!, // sk_… from the dashboard
|
|
48
20
|
environment: Environment.PRODUCTION,
|
|
49
21
|
});
|
|
50
22
|
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
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",
|
|
23
|
+
// 1) Sign the user in — choose one auth method.
|
|
24
|
+
const session = await rift.auth.login({
|
|
25
|
+
email: "alice@example.com",
|
|
62
26
|
otpCode: "123456",
|
|
63
27
|
});
|
|
28
|
+
// → { user, address, btcAddress, accessToken }
|
|
29
|
+
// The bearer token is now stored on the rift instance.
|
|
64
30
|
|
|
65
|
-
//
|
|
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
|
|
31
|
+
// 2) Use the API.
|
|
74
32
|
const balances = await rift.wallet.getChainBalance();
|
|
75
|
-
console.log("💰 Wallet balances:", balances);
|
|
76
33
|
|
|
77
|
-
|
|
78
|
-
|
|
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",
|
|
34
|
+
const tx = await rift.transactions.send({
|
|
35
|
+
to: "0xRecipient...",
|
|
103
36
|
value: "10",
|
|
104
37
|
token: "USDC",
|
|
105
38
|
chain: "ARBITRUM",
|
|
106
39
|
type: "gasless",
|
|
107
|
-
|
|
108
|
-
|
|
40
|
+
email: "alice@example.com",
|
|
41
|
+
otpCode: "123456", // step-up OTP for the tx itself
|
|
109
42
|
});
|
|
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
43
|
```
|
|
153
44
|
|
|
154
|
-
##
|
|
155
|
-
|
|
156
|
-
Rift SDK supports multiple authentication methods to accommodate different use cases.
|
|
157
|
-
|
|
158
|
-
### ⚠️ CRITICAL: Authentication Parameter Rules
|
|
45
|
+
## Authentication
|
|
159
46
|
|
|
160
|
-
**
|
|
47
|
+
Five ways to sign a user in. **Pick one** — passing combined credentials (e.g. both `email` and `externalId`) is rejected.
|
|
161
48
|
|
|
162
|
-
|
|
163
|
-
- ❌ **NEVER** provide `phoneNumber` + `externalId` together
|
|
164
|
-
- ❌ **NEVER** provide `email` + `externalId` together
|
|
165
|
-
- ❌ **NEVER** provide all three together
|
|
49
|
+
### Email / phone OTP
|
|
166
50
|
|
|
167
|
-
|
|
51
|
+
```ts
|
|
52
|
+
await rift.auth.sendOtp({ email: "alice@example.com" });
|
|
168
53
|
|
|
169
|
-
|
|
170
|
-
|
|
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",
|
|
54
|
+
const session = await rift.auth.login({
|
|
55
|
+
email: "alice@example.com",
|
|
212
56
|
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
57
|
});
|
|
283
58
|
```
|
|
284
59
|
|
|
285
|
-
|
|
60
|
+
Phone works the same way with `phone` / `phoneNumber`.
|
|
286
61
|
|
|
287
|
-
|
|
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
|
-
});
|
|
62
|
+
### Google sign-in
|
|
296
63
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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
|
|
64
|
+
```ts
|
|
65
|
+
const session = await rift.auth.loginWithGoogle({
|
|
66
|
+
idToken: googleIdToken,
|
|
315
67
|
});
|
|
316
68
|
```
|
|
317
69
|
|
|
318
|
-
|
|
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
|
|
70
|
+
Pass the `id_token` your client got from Google Identity Services. The backend verifies the signature with Google, then either creates a wallet for the email or signs the existing user in.
|
|
587
71
|
|
|
588
|
-
|
|
72
|
+
> Once a user is bound to Google, OTP/password login is rejected for them — they keep using Google. Configure allowed Google Client IDs in your project's **Auth** tab in the dashboard.
|
|
589
73
|
|
|
590
|
-
|
|
591
|
-
// Get current redirect URLs for your project
|
|
592
|
-
const redirectLinks = await rift.paymentLinks.getRedirectLinks({
|
|
593
|
-
project_api_key: "your-project-api-key",
|
|
594
|
-
});
|
|
74
|
+
### Apple sign-in
|
|
595
75
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
76
|
+
```ts
|
|
77
|
+
const session = await rift.auth.loginWithApple({
|
|
78
|
+
idToken: appleIdentityToken,
|
|
79
|
+
displayName: "Alice", // only used on the first sign-in
|
|
599
80
|
});
|
|
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
81
|
```
|
|
678
82
|
|
|
679
|
-
|
|
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
|
|
83
|
+
Apple only delivers email + name on the **first** sign-in. The durable identity is the stable Apple `sub` claim — pass it whenever you have it.
|
|
684
84
|
|
|
685
|
-
|
|
85
|
+
### External ID + password
|
|
686
86
|
|
|
687
|
-
```
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
87
|
+
```ts
|
|
88
|
+
await rift.auth.signup({
|
|
89
|
+
externalId: "your-internal-uid",
|
|
90
|
+
password: "...",
|
|
691
91
|
});
|
|
692
92
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
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
|
-
},
|
|
93
|
+
const session = await rift.auth.login({
|
|
94
|
+
externalId: "your-internal-uid",
|
|
95
|
+
password: "...",
|
|
712
96
|
});
|
|
713
|
-
|
|
714
|
-
// Supported chains: ETHEREUM, ARBITRUM, BASE, OPTIMISM, BNB, POLYGON, LISK, BERACHAIN
|
|
715
97
|
```
|
|
716
98
|
|
|
717
|
-
###
|
|
718
|
-
|
|
719
|
-
Sign transactions without broadcasting them to the network. Perfect for offline signing or when you need to review transactions before sending:
|
|
99
|
+
### Logout & state
|
|
720
100
|
|
|
721
|
-
```
|
|
722
|
-
//
|
|
723
|
-
const
|
|
724
|
-
|
|
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
|
-
});
|
|
101
|
+
```ts
|
|
102
|
+
rift.auth.isAuthenticated(); // boolean
|
|
103
|
+
const me = await rift.auth.getUser();
|
|
104
|
+
rift.auth.logout(); // clears the bearer locally
|
|
764
105
|
```
|
|
765
106
|
|
|
766
|
-
|
|
107
|
+
## Persisting sessions
|
|
767
108
|
|
|
768
|
-
|
|
109
|
+
The SDK keeps the bearer in memory. Where you persist it across restarts depends on your runtime:
|
|
769
110
|
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
to: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4Db45",
|
|
776
|
-
value: "500000000000000000", // 0.5 ETH
|
|
777
|
-
gasLimit: "21000", // Optional - will auto-estimate if not provided
|
|
778
|
-
},
|
|
779
|
-
});
|
|
111
|
+
| Runtime | Where to store | Restore on next boot |
|
|
112
|
+
|---|---|---|
|
|
113
|
+
| Node server | Redis / DB row keyed by your user id | `rift.setBearerToken(token)` |
|
|
114
|
+
| React Native | `expo-secure-store` / Keychain | `rift.setBearerToken(token)` |
|
|
115
|
+
| CLI / scripts | OS keychain (e.g. `keytar`) | `rift.setBearerToken(token)` |
|
|
780
116
|
|
|
781
|
-
|
|
782
|
-
const
|
|
783
|
-
|
|
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
|
-
});
|
|
117
|
+
```ts
|
|
118
|
+
const stored = await redis.get(`rift:${userId}:token`);
|
|
119
|
+
if (stored) rift.setBearerToken(stored);
|
|
816
120
|
```
|
|
817
121
|
|
|
818
|
-
|
|
122
|
+
## Core APIs
|
|
819
123
|
|
|
820
|
-
|
|
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
|
-
});
|
|
124
|
+
### Wallet
|
|
828
125
|
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
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
|
-
}
|
|
126
|
+
```ts
|
|
127
|
+
await rift.wallet.getChainBalance({ chain: "ARBITRUM" });
|
|
128
|
+
await rift.wallet.getTokenBalance({ chain: "ARBITRUM", token: "USDC" });
|
|
868
129
|
```
|
|
869
130
|
|
|
870
|
-
###
|
|
131
|
+
### Transactions
|
|
871
132
|
|
|
872
|
-
```
|
|
873
|
-
//
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
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({
|
|
133
|
+
```ts
|
|
134
|
+
// Send tokens. `type: "gasless"` uses sponsored gas.
|
|
135
|
+
await rift.transactions.send({
|
|
136
|
+
to: "0x...",
|
|
137
|
+
value: "10",
|
|
138
|
+
token: "USDC",
|
|
891
139
|
chain: "ARBITRUM",
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
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,
|
|
140
|
+
type: "gasless",
|
|
141
|
+
email: "alice@example.com",
|
|
142
|
+
otpCode: "123456", // re-auth for the transfer
|
|
909
143
|
});
|
|
910
|
-
```
|
|
911
|
-
|
|
912
|
-
### Error Handling
|
|
913
144
|
|
|
914
|
-
|
|
915
|
-
|
|
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
|
-
}
|
|
145
|
+
await rift.transactions.getHistory({ limit: 10, page: 1 });
|
|
146
|
+
await rift.transactions.getFee({ to: "0x...", value: "10", token: "USDC", chain: "ARBITRUM" });
|
|
935
147
|
```
|
|
936
148
|
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
Swap tokens across chains with best rates:
|
|
149
|
+
### DeFi swaps
|
|
940
150
|
|
|
941
|
-
```
|
|
942
|
-
|
|
943
|
-
const swapResult = await rift.defi.swap({
|
|
151
|
+
```ts
|
|
152
|
+
await rift.defi.swap({
|
|
944
153
|
chain: "ARBITRUM",
|
|
945
154
|
flow: "gasless",
|
|
946
155
|
token_to_sell: "USDC",
|
|
947
156
|
token_to_buy: "ETH",
|
|
948
157
|
value: "100",
|
|
949
158
|
});
|
|
950
|
-
|
|
951
|
-
console.log("🔄 Swap completed:", swapResult);
|
|
952
159
|
```
|
|
953
160
|
|
|
954
|
-
|
|
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.
|
|
161
|
+
### Off-ramps & on-ramps
|
|
957
162
|
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
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.)
|
|
163
|
+
```ts
|
|
164
|
+
// Cash out USDC → KES via M-Pesa, NGN bank, etc.
|
|
165
|
+
const quote = await rift.offramp.preview({
|
|
166
|
+
amount: "100",
|
|
167
|
+
token: "USDC",
|
|
168
|
+
currency: "KES",
|
|
978
169
|
});
|
|
979
170
|
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
"
|
|
985
|
-
|
|
986
|
-
"
|
|
987
|
-
|
|
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
|
-
}
|
|
171
|
+
// On-ramp via M-Pesa STK push
|
|
172
|
+
await rift.onramp.initiateSafaricomSTK({
|
|
173
|
+
amount: 100, // KES
|
|
174
|
+
phone: "0713322025",
|
|
175
|
+
cryptoAsset: "POL-USDC",
|
|
176
|
+
cryptoWalletAddress: "0x...",
|
|
177
|
+
externalReference: "user123",
|
|
178
|
+
});
|
|
998
179
|
```
|
|
999
180
|
|
|
1000
|
-
###
|
|
1001
|
-
|
|
1002
|
-
Monitor the status of M-Pesa transactions and crypto transfers:
|
|
181
|
+
### Payment links
|
|
1003
182
|
|
|
1004
|
-
```
|
|
1005
|
-
//
|
|
1006
|
-
const
|
|
1007
|
-
|
|
183
|
+
```ts
|
|
184
|
+
// Request money
|
|
185
|
+
const req = await rift.paymentLinks.requestPayment({
|
|
186
|
+
amount: 100, chain: "BASE", token: "USDC",
|
|
1008
187
|
});
|
|
1009
188
|
|
|
1010
|
-
//
|
|
1011
|
-
|
|
1012
|
-
|
|
189
|
+
// Send money to a specific recipient
|
|
190
|
+
await rift.paymentLinks.createSpecificSendLink({
|
|
191
|
+
time: "1h",
|
|
192
|
+
recipientEmail: "bob@example.com",
|
|
193
|
+
value: "50",
|
|
194
|
+
token: "USDC",
|
|
195
|
+
chain: "ARBITRUM",
|
|
196
|
+
email: "alice@example.com",
|
|
197
|
+
otpCode: "123456",
|
|
1013
198
|
});
|
|
1014
199
|
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
"
|
|
1020
|
-
|
|
1021
|
-
"
|
|
1022
|
-
|
|
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,
|
|
200
|
+
// Open link anyone can claim
|
|
201
|
+
await rift.paymentLinks.createOpenSendLink({
|
|
202
|
+
time: "24h",
|
|
203
|
+
value: "25",
|
|
204
|
+
token: "USDC",
|
|
205
|
+
chain: "BASE",
|
|
206
|
+
email: "alice@example.com",
|
|
207
|
+
otpCode: "123456",
|
|
1197
208
|
});
|
|
1198
209
|
```
|
|
1199
210
|
|
|
1200
|
-
|
|
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
|
|
211
|
+
### Signer (advanced)
|
|
1259
212
|
|
|
1260
|
-
|
|
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
|
-
```
|
|
213
|
+
For direct chain interaction (raw tx signing, contract calls, off-chain message signing):
|
|
1266
214
|
|
|
1267
|
-
|
|
215
|
+
```ts
|
|
216
|
+
await rift.signer.signMessage({ chain: "ETHEREUM", message: "Hello" });
|
|
1268
217
|
|
|
1269
|
-
|
|
1270
|
-
|
|
218
|
+
await rift.signer.sendTransaction({
|
|
219
|
+
chain: "POLYGON",
|
|
220
|
+
transactionData: { to: "0x...", value: "0", data: "0x...", gasLimit: "65000" },
|
|
221
|
+
});
|
|
1271
222
|
```
|
|
1272
223
|
|
|
1273
|
-
|
|
224
|
+
For most use cases, prefer `rift.transactions.send()` — it's higher-level and gasless.
|
|
1274
225
|
|
|
1275
|
-
|
|
1276
|
-
// STK Push Initiation
|
|
1277
|
-
rift.onramp.initiateSafaricomSTK(request); // Initiate M-Pesa STK push
|
|
226
|
+
## Supported networks
|
|
1278
227
|
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
228
|
+
| Network | Chain ID | Native | Common tokens |
|
|
229
|
+
|---|---|---|---|
|
|
230
|
+
| Arbitrum | 42161 | ETH | USDC, USDT |
|
|
231
|
+
| Base | 8453 | ETH | USDC |
|
|
232
|
+
| Optimism | 10 | ETH | USDC, USDT |
|
|
233
|
+
| Ethereum | 1 | ETH | USDC, USDT |
|
|
234
|
+
| Polygon | 137 | MATIC | USDC, USDT |
|
|
235
|
+
| BNB Chain | 56 | BNB | USDT, USDC |
|
|
236
|
+
| Lisk | 1135 | LSK | USDC |
|
|
237
|
+
| Berachain | 80085 | BERA | WBERA, USDC |
|
|
238
|
+
| Celo | 42220 | CELO | cUSD |
|
|
1282
239
|
|
|
1283
|
-
|
|
1284
|
-
rift.onramp.getUserTransactionHistory(filters?); // Get user's onramp history
|
|
1285
|
-
```
|
|
240
|
+
## Error handling
|
|
1286
241
|
|
|
1287
|
-
|
|
242
|
+
The SDK throws structured errors with `status`, `message`, and (often) an `error` machine-code:
|
|
1288
243
|
|
|
1289
|
-
```
|
|
244
|
+
```ts
|
|
1290
245
|
try {
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
if (
|
|
1295
|
-
|
|
1296
|
-
|
|
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
|
-
}
|
|
246
|
+
await rift.transactions.send({ /* ... */ });
|
|
247
|
+
} catch (e: any) {
|
|
248
|
+
if (e.status === 401) /* re-auth */;
|
|
249
|
+
else if (e.status === 429) /* back off + retry */;
|
|
250
|
+
else if (e.message?.includes("insufficient")) /* show balance to user */;
|
|
251
|
+
else throw e;
|
|
1307
252
|
}
|
|
1308
253
|
```
|
|
1309
254
|
|
|
1310
|
-
##
|
|
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">
|
|
255
|
+
## Full docs
|
|
1340
256
|
|
|
1341
|
-
**
|
|
257
|
+
- **Integration walkthrough**: https://service.riftfi.xyz/docs
|
|
258
|
+
- **HTTP API explorer**: https://developers.riftfi.xyz
|
|
259
|
+
- **OpenAPI spec**: https://developers.riftfi.xyz/docs.json
|
|
260
|
+
- **React bindings**: https://www.npmjs.com/package/@rift-finance/react
|
|
261
|
+
- **MCP server for AI assistants**: https://www.npmjs.com/package/@rift-finance/mcp-server
|
|
1342
262
|
|
|
1343
|
-
|
|
263
|
+
## License
|
|
1344
264
|
|
|
1345
|
-
|
|
265
|
+
MIT
|