toss-expo-sdk 0.1.0
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/LICENSE +20 -0
- package/README.md +292 -0
- package/lib/module/ble.js +103 -0
- package/lib/module/ble.js.map +1 -0
- package/lib/module/client/TossClient.js +324 -0
- package/lib/module/client/TossClient.js.map +1 -0
- package/lib/module/client/index.js +4 -0
- package/lib/module/client/index.js.map +1 -0
- package/lib/module/contexts/WalletContext.js +99 -0
- package/lib/module/contexts/WalletContext.js.map +1 -0
- package/lib/module/discovery.js +434 -0
- package/lib/module/discovery.js.map +1 -0
- package/lib/module/errors.js +47 -0
- package/lib/module/errors.js.map +1 -0
- package/lib/module/examples/offlinePaymentFlow.js +234 -0
- package/lib/module/examples/offlinePaymentFlow.js.map +1 -0
- package/lib/module/index.js +32 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/intent.js +223 -0
- package/lib/module/intent.js.map +1 -0
- package/lib/module/intentManager.js +145 -0
- package/lib/module/intentManager.js.map +1 -0
- package/lib/module/internal/arciumHelper.js +50 -0
- package/lib/module/internal/arciumHelper.js.map +1 -0
- package/lib/module/nfc.js +54 -0
- package/lib/module/nfc.js.map +1 -0
- package/lib/module/noise.js +14 -0
- package/lib/module/noise.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/qr.js +57 -0
- package/lib/module/qr.js.map +1 -0
- package/lib/module/reconciliation.js +329 -0
- package/lib/module/reconciliation.js.map +1 -0
- package/lib/module/services/authService.js +205 -0
- package/lib/module/services/authService.js.map +1 -0
- package/lib/module/storage/secureStorage.js +89 -0
- package/lib/module/storage/secureStorage.js.map +1 -0
- package/lib/module/storage.js +16 -0
- package/lib/module/storage.js.map +1 -0
- package/lib/module/sync.js +64 -0
- package/lib/module/sync.js.map +1 -0
- package/lib/module/types/tossUser.js +41 -0
- package/lib/module/types/tossUser.js.map +1 -0
- package/lib/module/utils/nonceUtils.js +38 -0
- package/lib/module/utils/nonceUtils.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/__tests__/index.test.d.ts +1 -0
- package/lib/typescript/src/__tests__/index.test.d.ts.map +1 -0
- package/lib/typescript/src/__tests__/reconciliation.test.d.ts +6 -0
- package/lib/typescript/src/__tests__/reconciliation.test.d.ts.map +1 -0
- package/lib/typescript/src/ble.d.ts +10 -0
- package/lib/typescript/src/ble.d.ts.map +1 -0
- package/lib/typescript/src/client/TossClient.d.ts +110 -0
- package/lib/typescript/src/client/TossClient.d.ts.map +1 -0
- package/lib/typescript/src/client/index.d.ts +3 -0
- package/lib/typescript/src/client/index.d.ts.map +1 -0
- package/lib/typescript/src/contexts/WalletContext.d.ts +20 -0
- package/lib/typescript/src/contexts/WalletContext.d.ts.map +1 -0
- package/lib/typescript/src/discovery.d.ts +188 -0
- package/lib/typescript/src/discovery.d.ts.map +1 -0
- package/lib/typescript/src/errors.d.ts +27 -0
- package/lib/typescript/src/errors.d.ts.map +1 -0
- package/lib/typescript/src/examples/offlinePaymentFlow.d.ts +48 -0
- package/lib/typescript/src/examples/offlinePaymentFlow.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +13 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/intent.d.ts +84 -0
- package/lib/typescript/src/intent.d.ts.map +1 -0
- package/lib/typescript/src/intentManager.d.ts +46 -0
- package/lib/typescript/src/intentManager.d.ts.map +1 -0
- package/lib/typescript/src/internal/arciumHelper.d.ts +19 -0
- package/lib/typescript/src/internal/arciumHelper.d.ts.map +1 -0
- package/lib/typescript/src/nfc.d.ts +7 -0
- package/lib/typescript/src/nfc.d.ts.map +1 -0
- package/lib/typescript/src/noise.d.ts +5 -0
- package/lib/typescript/src/noise.d.ts.map +1 -0
- package/lib/typescript/src/qr.d.ts +6 -0
- package/lib/typescript/src/qr.d.ts.map +1 -0
- package/lib/typescript/src/reconciliation.d.ts +65 -0
- package/lib/typescript/src/reconciliation.d.ts.map +1 -0
- package/lib/typescript/src/services/authService.d.ts +55 -0
- package/lib/typescript/src/services/authService.d.ts.map +1 -0
- package/lib/typescript/src/storage/secureStorage.d.ts +7 -0
- package/lib/typescript/src/storage/secureStorage.d.ts.map +1 -0
- package/lib/typescript/src/storage.d.ts +4 -0
- package/lib/typescript/src/storage.d.ts.map +1 -0
- package/lib/typescript/src/sync.d.ts +40 -0
- package/lib/typescript/src/sync.d.ts.map +1 -0
- package/lib/typescript/src/types/tossUser.d.ts +39 -0
- package/lib/typescript/src/types/tossUser.d.ts.map +1 -0
- package/lib/typescript/src/utils/nonceUtils.d.ts +8 -0
- package/lib/typescript/src/utils/nonceUtils.d.ts.map +1 -0
- package/package.json +176 -0
- package/src/__tests__/index.test.tsx +1 -0
- package/src/__tests__/reconciliation.test.tsx +361 -0
- package/src/ble.ts +138 -0
- package/src/client/TossClient.ts +435 -0
- package/src/client/index.ts +2 -0
- package/src/contexts/WalletContext.tsx +127 -0
- package/src/discovery.ts +542 -0
- package/src/errors.ts +51 -0
- package/src/examples/offlinePaymentFlow.ts +331 -0
- package/src/index.tsx +61 -0
- package/src/intent.ts +328 -0
- package/src/intentManager.ts +164 -0
- package/src/internal/arciumHelper.ts +58 -0
- package/src/nfc.ts +57 -0
- package/src/noise.ts +9 -0
- package/src/qr.tsx +65 -0
- package/src/reconciliation.ts +421 -0
- package/src/services/authService.ts +238 -0
- package/src/storage/secureStorage.ts +100 -0
- package/src/storage.ts +17 -0
- package/src/sync.ts +101 -0
- package/src/types/tossUser.ts +81 -0
- package/src/utils/nonceUtils.ts +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Joshua Idele (Zypp Protocol)
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
in the Software without restriction, including without limitation the rights
|
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
furnished to do so, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
# TOSS Expo SDK
|
|
2
|
+
|
|
3
|
+
**TOSS (The Offline Solana Stack)** is a robust, production-ready SDK for building offline-capable Solana applications with secure local transport and delayed on-chain settlement.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/toss-expo-sdk)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
## 🚀 Features
|
|
9
|
+
|
|
10
|
+
### Core Capabilities
|
|
11
|
+
|
|
12
|
+
- **Offline-First Architecture**: Create and process transactions without immediate network access
|
|
13
|
+
- **Secure Wallet Integration**: Built-in wallet management with support for multiple keypairs
|
|
14
|
+
- **Durable Transactions**: Nonce-based transaction handling for reliability
|
|
15
|
+
- **TypeScript Support**: Full TypeScript definitions for better developer experience
|
|
16
|
+
|
|
17
|
+
### Security
|
|
18
|
+
|
|
19
|
+
- End-to-end encryption for all transactions
|
|
20
|
+
- Secure key management with hardware wallet support
|
|
21
|
+
- Non-custodial by design - users maintain control of their keys
|
|
22
|
+
|
|
23
|
+
### Developer Experience
|
|
24
|
+
|
|
25
|
+
- Simple, intuitive API for common operations
|
|
26
|
+
- Comprehensive error handling with detailed error codes
|
|
27
|
+
- Built-in retry mechanisms with exponential backoff
|
|
28
|
+
- React hooks for easy integration with React Native components
|
|
29
|
+
|
|
30
|
+
## 📦 Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Using npm
|
|
34
|
+
npm install toss-expo-sdk @solana/web3.js
|
|
35
|
+
|
|
36
|
+
# Using yarn
|
|
37
|
+
yarn add toss-expo-sdk @solana/web3.js
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 🏁 Quick Start
|
|
41
|
+
|
|
42
|
+
### Initialize the Client
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import { TossClient } from 'toss-expo-sdk';
|
|
46
|
+
|
|
47
|
+
const client = new TossClient({
|
|
48
|
+
projectId: 'your-project-id',
|
|
49
|
+
mode: 'devnet', // or 'testnet' | 'mainnet-beta'
|
|
50
|
+
privateTransactions: true,
|
|
51
|
+
maxRetries: 3,
|
|
52
|
+
retryDelay: 1000,
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Create and Send an Intent
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
// Create a transfer intent
|
|
60
|
+
const intent = await client.createIntent(
|
|
61
|
+
senderKeypair, // or 'current' to use connected wallet
|
|
62
|
+
recipientAddress,
|
|
63
|
+
amountInLamports,
|
|
64
|
+
{
|
|
65
|
+
memo: 'Payment for services',
|
|
66
|
+
useDurableNonce: true,
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
// Later, when online
|
|
71
|
+
const syncedIntents = await client.sync();
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Using with React Native
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { WalletProvider, useWallet } from 'toss-expo-sdk';
|
|
78
|
+
|
|
79
|
+
function App() {
|
|
80
|
+
return (
|
|
81
|
+
<WalletProvider>
|
|
82
|
+
<PaymentScreen />
|
|
83
|
+
</WalletProvider>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function PaymentScreen() {
|
|
88
|
+
const {
|
|
89
|
+
isConnected,
|
|
90
|
+
connect,
|
|
91
|
+
disconnect,
|
|
92
|
+
signMessage,
|
|
93
|
+
sendTransaction
|
|
94
|
+
} = useWallet();
|
|
95
|
+
|
|
96
|
+
// ... rest of your component
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Core Idea: Intent, Not Transaction
|
|
101
|
+
|
|
102
|
+
TOSS uses an **intent-based model**.
|
|
103
|
+
|
|
104
|
+
An **intent** is:
|
|
105
|
+
|
|
106
|
+
- a signed declaration of what a user wants to do
|
|
107
|
+
- transferable offline
|
|
108
|
+
- verifiable locally
|
|
109
|
+
- settled later on-chain
|
|
110
|
+
|
|
111
|
+
This separation allows applications to function **without connectivity** while preserving Solana’s security guarantees.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 🔒 Security
|
|
116
|
+
|
|
117
|
+
### Best Practices
|
|
118
|
+
|
|
119
|
+
1. Always verify transaction details before signing
|
|
120
|
+
2. Use the latest version of the SDK
|
|
121
|
+
3. Never expose private keys in client-side code
|
|
122
|
+
4. Implement proper error handling
|
|
123
|
+
|
|
124
|
+
### Error Handling
|
|
125
|
+
|
|
126
|
+
The SDK provides detailed error codes for handling various scenarios. All errors extend from the base `TossError` class and include a `code` property for programmatic handling.
|
|
127
|
+
|
|
128
|
+
#### Error Codes Reference
|
|
129
|
+
|
|
130
|
+
| Error Code | Description | When It Occurs |
|
|
131
|
+
| ------------------------------- | ---------------------------------- | ---------------------------------------------------------------- |
|
|
132
|
+
| `INVALID_INTENT` | The intent is malformed or invalid | When validating an intent fails due to invalid structure or data |
|
|
133
|
+
| `NETWORK_ERROR` | Network-related operation failed | When a network request fails or times out |
|
|
134
|
+
| `STORAGE_ERROR` | Local storage operation failed | When reading/writing to secure storage fails |
|
|
135
|
+
| `SIGNATURE_VERIFICATION_FAILED` | Signature verification failed | When a signature doesn't match the expected value |
|
|
136
|
+
| `INTENT_EXPIRED` | The intent has expired | When processing an intent that's past its expiration time |
|
|
137
|
+
| `INSUFFICIENT_FUNDS` | Not enough funds for the operation | When an account has insufficient balance |
|
|
138
|
+
| `TRANSACTION_FAILED` | Transaction processing failed | When a transaction is rejected by the network |
|
|
139
|
+
|
|
140
|
+
#### Error Handling Example
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
import { ERROR_CODES } from 'toss-expo-sdk';
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
await client.createIntent(...);
|
|
147
|
+
} catch (error) {
|
|
148
|
+
if (error.code === ERROR_CODES.INSUFFICIENT_FUNDS) {
|
|
149
|
+
// Handle insufficient funds
|
|
150
|
+
console.error('Insufficient funds for this transaction');
|
|
151
|
+
} else if (error.code === ERROR_CODES.INTENT_EXPIRED) {
|
|
152
|
+
// Handle expired intent
|
|
153
|
+
console.error('This transaction intent has expired');
|
|
154
|
+
} else if (error.code === ERROR_CODES.NETWORK_ERROR) {
|
|
155
|
+
// Handle network issues
|
|
156
|
+
console.error('Network error occurred. Please check your connection.');
|
|
157
|
+
} else {
|
|
158
|
+
// Handle other errors
|
|
159
|
+
console.error('An error occurred:', error.message);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
#### Error Object Structure
|
|
165
|
+
|
|
166
|
+
All errors have the following structure:
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
{
|
|
170
|
+
name: string; // Error class name (e.g., 'NetworkError')
|
|
171
|
+
message: string; // Human-readable error message
|
|
172
|
+
code: string; // Error code (from ERROR_CODES)
|
|
173
|
+
details?: any; // Additional error details (if any)
|
|
174
|
+
cause?: Error; // Original error that caused this one (if any)
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## 🔄 Synchronisation and Settlement
|
|
179
|
+
|
|
180
|
+
TOSS implements deterministic synchronisation (Section 9 of the technical paper) when devices reconnect to the network.
|
|
181
|
+
|
|
182
|
+
### Full Synchronisation Flow
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
import { syncToChain } from 'toss-expo-sdk';
|
|
186
|
+
import { Connection } from '@solana/web3.js';
|
|
187
|
+
|
|
188
|
+
const connection = new Connection('https://api.devnet.solana.com');
|
|
189
|
+
|
|
190
|
+
// When your device reconnects to internet
|
|
191
|
+
const syncResult = await syncToChain(connection);
|
|
192
|
+
|
|
193
|
+
// Handle settlements
|
|
194
|
+
console.log(`Successfully settled: ${syncResult.successfulSettlements.length}`);
|
|
195
|
+
console.log(`Failed settlements: ${syncResult.failedSettlements.length}`);
|
|
196
|
+
console.log(`Detected conflicts: ${syncResult.detectedConflicts.length}`);
|
|
197
|
+
|
|
198
|
+
// Check individual failures
|
|
199
|
+
for (const failed of syncResult.failedSettlements) {
|
|
200
|
+
console.error(`Intent ${failed.intentId} failed: ${failed.error}`);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Handle conflicts
|
|
204
|
+
for (const conflict of syncResult.detectedConflicts) {
|
|
205
|
+
console.warn(`Conflict in ${conflict.intentId}: ${conflict.conflict}`);
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Conflict Detection and Resolution
|
|
210
|
+
|
|
211
|
+
When multiple devices are offline and create conflicting intents, TOSS automatically resolves them deterministically:
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
import { MultiDeviceConflictResolver } from 'toss-expo-sdk';
|
|
215
|
+
|
|
216
|
+
// If multiple devices created the same intent
|
|
217
|
+
const conflicts = MultiDeviceConflictResolver.detectConflicts([
|
|
218
|
+
intentFromDeviceA,
|
|
219
|
+
intentFromDeviceB,
|
|
220
|
+
]);
|
|
221
|
+
|
|
222
|
+
// Deterministically resolve using:
|
|
223
|
+
// 1. Lowest nonce (replay protection)
|
|
224
|
+
// 2. Earliest timestamp (fairness)
|
|
225
|
+
// 3. Lexicographic signature (tiebreak)
|
|
226
|
+
const resolution = MultiDeviceConflictResolver.resolveConflicts(conflicts[0]);
|
|
227
|
+
|
|
228
|
+
console.log(`Winner: ${resolution.winner.id}`);
|
|
229
|
+
console.log(`Losers: ${resolution.losers.map((i) => i.id)}`);
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Device Discovery and Peer Exchange
|
|
233
|
+
|
|
234
|
+
Devices can discover and exchange intents with nearby peers:
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
import { deviceDiscovery, intentExchange } from 'toss-expo-sdk';
|
|
238
|
+
import { startTossScan } from 'toss-expo-sdk';
|
|
239
|
+
|
|
240
|
+
// Scan for nearby TOSS devices
|
|
241
|
+
startTossScan(
|
|
242
|
+
(user, device) => {
|
|
243
|
+
// Register discovered peer
|
|
244
|
+
deviceDiscovery.registerPeer({
|
|
245
|
+
id: device.id,
|
|
246
|
+
user,
|
|
247
|
+
lastSeen: Date.now(),
|
|
248
|
+
transport: 'ble',
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// Create intent exchange request
|
|
252
|
+
const request = intentExchange.createRequest(
|
|
253
|
+
myIntent,
|
|
254
|
+
'my-device-id',
|
|
255
|
+
undefined,
|
|
256
|
+
5 * 60 // 5 minute expiry
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
console.log(`Exchange request created: ${request.requestId}`);
|
|
260
|
+
},
|
|
261
|
+
(intent, device) => {
|
|
262
|
+
console.log(`Received intent from ${device.id}`);
|
|
263
|
+
}
|
|
264
|
+
);
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## 🌐 Network Support
|
|
268
|
+
|
|
269
|
+
| Network | Status | RPC Endpoint |
|
|
270
|
+
| ------- | ------- | ----------------------------------- |
|
|
271
|
+
| Devnet | ✅ Live | https://api.devnet.solana.com |
|
|
272
|
+
| Testnet | ✅ Live | https://api.testnet.solana.com |
|
|
273
|
+
| Mainnet | ✅ Live | https://api.mainnet-beta.solana.com |
|
|
274
|
+
|
|
275
|
+
## 📚 Architecture and Design
|
|
276
|
+
|
|
277
|
+
For complete implementation details including:
|
|
278
|
+
|
|
279
|
+
- Full API reference
|
|
280
|
+
- Architecture diagrams
|
|
281
|
+
- Performance considerations
|
|
282
|
+
- Future improvements
|
|
283
|
+
|
|
284
|
+
See [IMPLEMENTATION.md](./IMPLEMENTATION.md)
|
|
285
|
+
|
|
286
|
+
## 🌐 Network Support
|
|
287
|
+
|
|
288
|
+
| Network | Status | RPC Endpoint |
|
|
289
|
+
| ------- | ------- | ------------------------------ |
|
|
290
|
+
| Devnet | ✅ Live | https://api.devnet.solana.com |
|
|
291
|
+
| Testnet | ✅ Live | https://api.testnet.solana.com |
|
|
292
|
+
| Mainnet | ✅ Live | https://api.mainnet.solana.com |
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// src/ble.ts
|
|
4
|
+
import { BleManager } from 'react-native-ble-plx';
|
|
5
|
+
import { PermissionsAndroid, Platform } from 'react-native';
|
|
6
|
+
const SERVICE_UUID = '0000ff00-0000-1000-8000-00805f9b34fb';
|
|
7
|
+
const USER_CHARACTERISTIC = '0000ff01-0000-1000-8000-00805f9b34fb';
|
|
8
|
+
const INTENT_CHARACTERISTIC = '0000ff02-0000-1000-8000-00805f9b34fb';
|
|
9
|
+
const manager = new BleManager();
|
|
10
|
+
export async function requestBLEPermissions() {
|
|
11
|
+
if (Platform.OS === 'android') {
|
|
12
|
+
await PermissionsAndroid.requestMultiple([PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN, PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT, PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION]);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Connect to a BLE device
|
|
17
|
+
async function connect(device) {
|
|
18
|
+
const connectedDevice = await manager.connectToDevice(device.id);
|
|
19
|
+
await connectedDevice.discoverAllServicesAndCharacteristics();
|
|
20
|
+
return connectedDevice;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Scan for BLE devices advertising TOSS service
|
|
24
|
+
export function startTossScan(onUserFound, onIntentFound) {
|
|
25
|
+
manager.startDeviceScan([SERVICE_UUID], null, async (error, device) => {
|
|
26
|
+
if (error) {
|
|
27
|
+
console.warn('BLE scan error', error.message);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (device) {
|
|
31
|
+
try {
|
|
32
|
+
const connectedDevice = await connect(device);
|
|
33
|
+
const services = await connectedDevice.discoverAllServicesAndCharacteristics();
|
|
34
|
+
|
|
35
|
+
// Check for user data
|
|
36
|
+
const userData = await services.readCharacteristicForService(device.id, SERVICE_UUID, USER_CHARACTERISTIC);
|
|
37
|
+
if (userData?.value) {
|
|
38
|
+
const user = JSON.parse(userData.value);
|
|
39
|
+
onUserFound(user, device);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Check for intent data
|
|
43
|
+
const intentData = await services.readCharacteristicForService(device.id, SERVICE_UUID, INTENT_CHARACTERISTIC);
|
|
44
|
+
if (intentData?.value) {
|
|
45
|
+
const intent = JSON.parse(intentData.value);
|
|
46
|
+
onIntentFound(intent, device);
|
|
47
|
+
}
|
|
48
|
+
} catch (err) {
|
|
49
|
+
console.warn('Error reading device data:', err);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
import { State } from 'react-native-ble-plx';
|
|
55
|
+
|
|
56
|
+
// Advertise user data via BLE
|
|
57
|
+
export async function advertiseUser(user) {
|
|
58
|
+
try {
|
|
59
|
+
// On Android, we need to use a different approach since react-native-ble-plx
|
|
60
|
+
// doesn't support BLE advertising directly on the client side
|
|
61
|
+
if (Platform.OS === 'android') {
|
|
62
|
+
console.warn('BLE advertising is not directly supported on Android. Consider using react-native-ble-advertise for this functionality.');
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// For iOS, we can use the state change to know when advertising starts
|
|
67
|
+
const subscription = manager.onStateChange(state => {
|
|
68
|
+
if (state === State.PoweredOn) {
|
|
69
|
+
console.log(`Advertising user ${user.userId} via BLE`);
|
|
70
|
+
subscription.remove();
|
|
71
|
+
}
|
|
72
|
+
}, true);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
75
|
+
console.error('Error in BLE advertising:', errorMessage);
|
|
76
|
+
throw new Error(`Failed to advertise user: ${errorMessage}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export async function stopAdvertising() {
|
|
80
|
+
try {
|
|
81
|
+
// On iOS, we can stop scanning which will effectively stop advertising
|
|
82
|
+
manager.stopDeviceScan();
|
|
83
|
+
console.log('Stopped BLE advertising');
|
|
84
|
+
} catch (error) {
|
|
85
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
86
|
+
console.error('Error stopping BLE advertising:', errorMessage);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Send intent to a specific device
|
|
91
|
+
export async function sendIntentToDevice(deviceId, intent) {
|
|
92
|
+
const jsonIntent = JSON.stringify(intent);
|
|
93
|
+
const device = await manager.connectToDevice(deviceId);
|
|
94
|
+
await device.discoverAllServicesAndCharacteristics();
|
|
95
|
+
await device.writeCharacteristicWithResponseForService(device.id, SERVICE_UUID, INTENT_CHARACTERISTIC, Buffer.from(jsonIntent).toString('base64'));
|
|
96
|
+
await device.cancelConnection();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Stop scan
|
|
100
|
+
export function stopScan() {
|
|
101
|
+
manager.stopDeviceScan();
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=ble.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["BleManager","PermissionsAndroid","Platform","SERVICE_UUID","USER_CHARACTERISTIC","INTENT_CHARACTERISTIC","manager","requestBLEPermissions","OS","requestMultiple","PERMISSIONS","BLUETOOTH_SCAN","BLUETOOTH_CONNECT","ACCESS_FINE_LOCATION","connect","device","connectedDevice","connectToDevice","id","discoverAllServicesAndCharacteristics","startTossScan","onUserFound","onIntentFound","startDeviceScan","error","console","warn","message","services","userData","readCharacteristicForService","value","user","JSON","parse","intentData","intent","err","State","advertiseUser","subscription","onStateChange","state","PoweredOn","log","userId","remove","errorMessage","Error","String","stopAdvertising","stopDeviceScan","sendIntentToDevice","deviceId","jsonIntent","stringify","writeCharacteristicWithResponseForService","Buffer","from","toString","cancelConnection","stopScan"],"sourceRoot":"../../src","sources":["ble.ts"],"mappings":"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","ignoreList":[]}
|