toss-expo-sdk 1.0.1 → 1.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.
- package/README.md +58 -56
- package/lib/module/client/NonceAccountManager.js +2 -2
- package/lib/module/client/NonceAccountManager.js.map +1 -1
- package/lib/module/examples/enhancedFeaturesFlow.js +233 -0
- package/lib/module/examples/enhancedFeaturesFlow.js.map +1 -0
- package/lib/module/examples/offlinePaymentFlow.js +27 -27
- package/lib/module/examples/offlinePaymentFlow.js.map +1 -1
- package/lib/module/index.js +6 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/qr.js +2 -2
- package/lib/module/reconciliation.js +4 -4
- package/lib/module/reconciliation.js.map +1 -1
- package/lib/module/services/authService.js +3 -3
- package/lib/module/services/authService.js.map +1 -1
- package/lib/module/utils/compression.js +210 -0
- package/lib/module/utils/compression.js.map +1 -0
- package/lib/module/wifi.js +311 -0
- package/lib/module/wifi.js.map +1 -0
- package/lib/typescript/src/examples/enhancedFeaturesFlow.d.ts +45 -0
- package/lib/typescript/src/examples/enhancedFeaturesFlow.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +2 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/utils/compression.d.ts +52 -0
- package/lib/typescript/src/utils/compression.d.ts.map +1 -0
- package/lib/typescript/src/wifi.d.ts +116 -0
- package/lib/typescript/src/wifi.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/client/NonceAccountManager.ts +2 -2
- package/src/examples/enhancedFeaturesFlow.ts +272 -0
- package/src/examples/offlinePaymentFlow.ts +27 -27
- package/src/index.tsx +12 -0
- package/src/qr.tsx +2 -2
- package/src/reconciliation.ts +4 -4
- package/src/services/authService.ts +3 -3
- package/src/utils/compression.ts +247 -0
- package/src/wifi.ts +401 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WiFi Direct Transport for TOSS
|
|
3
|
+
*
|
|
4
|
+
* Higher-bandwidth alternative to BLE for device-to-device communication
|
|
5
|
+
* Fallback to BLE if WiFi Direct unavailable
|
|
6
|
+
*
|
|
7
|
+
* Uses native React Native APIs for production-ready implementation
|
|
8
|
+
*/
|
|
9
|
+
import type { SolanaIntent } from './intent';
|
|
10
|
+
import type { OfflineTransaction } from './types/nonceAccount';
|
|
11
|
+
/**
|
|
12
|
+
* WiFi Direct connection state
|
|
13
|
+
*/
|
|
14
|
+
export interface WiFiDirectPeer {
|
|
15
|
+
deviceName: string;
|
|
16
|
+
deviceAddress: string;
|
|
17
|
+
isGroupOwner: boolean;
|
|
18
|
+
signalStrength?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* WiFi Direct socket for data transmission
|
|
22
|
+
*/
|
|
23
|
+
export interface WiFiDirectSocket {
|
|
24
|
+
peerId: string;
|
|
25
|
+
connected: boolean;
|
|
26
|
+
bytesTransferred: number;
|
|
27
|
+
lastActivityTime: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* WiFi Direct Transport Handler
|
|
31
|
+
* Wrapper around native WiFi Direct capabilities
|
|
32
|
+
*
|
|
33
|
+
* Supports higher MTU (1200+ bytes) than BLE (480 bytes)
|
|
34
|
+
* Useful for batch transmission of intents
|
|
35
|
+
*/
|
|
36
|
+
export declare class WiFiDirectTransport {
|
|
37
|
+
private connectedPeers;
|
|
38
|
+
private readonly SOCKET_TIMEOUT;
|
|
39
|
+
private readonly WIFI_MTU;
|
|
40
|
+
constructor(_platform?: 'android' | 'ios');
|
|
41
|
+
/**
|
|
42
|
+
* Check if WiFi Direct is available on device
|
|
43
|
+
*/
|
|
44
|
+
isAvailable(): Promise<boolean>;
|
|
45
|
+
/**
|
|
46
|
+
* Enable WiFi Direct on device
|
|
47
|
+
*/
|
|
48
|
+
enable(): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Discover nearby WiFi Direct peers
|
|
51
|
+
*/
|
|
52
|
+
discoverPeers(timeoutSeconds?: number): Promise<WiFiDirectPeer[]>;
|
|
53
|
+
/**
|
|
54
|
+
* Connect to a specific WiFi Direct peer
|
|
55
|
+
*/
|
|
56
|
+
connectToPeer(deviceAddress: string): Promise<WiFiDirectSocket>;
|
|
57
|
+
/**
|
|
58
|
+
* Send intent via WiFi Direct connection
|
|
59
|
+
* Uses larger MTU than BLE for efficiency
|
|
60
|
+
*/
|
|
61
|
+
sendIntent(socket: WiFiDirectSocket, intent: SolanaIntent): Promise<{
|
|
62
|
+
success: boolean;
|
|
63
|
+
bytesTransferred: number;
|
|
64
|
+
chunks: number;
|
|
65
|
+
}>;
|
|
66
|
+
/**
|
|
67
|
+
* Send offline transaction via WiFi Direct
|
|
68
|
+
*/
|
|
69
|
+
sendOfflineTransaction(socket: WiFiDirectSocket, transaction: OfflineTransaction): Promise<{
|
|
70
|
+
success: boolean;
|
|
71
|
+
bytesTransferred: number;
|
|
72
|
+
chunks: number;
|
|
73
|
+
}>;
|
|
74
|
+
/**
|
|
75
|
+
* Receive data from WiFi Direct peer
|
|
76
|
+
*/
|
|
77
|
+
receiveData(socket: WiFiDirectSocket, expectedChunks: number): Promise<Buffer>;
|
|
78
|
+
/**
|
|
79
|
+
* Disconnect from WiFi Direct peer
|
|
80
|
+
*/
|
|
81
|
+
disconnect(peerId: string): Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* Get all connected peers
|
|
84
|
+
*/
|
|
85
|
+
getConnectedPeers(): WiFiDirectSocket[];
|
|
86
|
+
/**
|
|
87
|
+
* Get MTU size for this transport
|
|
88
|
+
*/
|
|
89
|
+
getMTU(): number;
|
|
90
|
+
/**
|
|
91
|
+
* Clean up expired connections
|
|
92
|
+
*/
|
|
93
|
+
cleanupExpiredConnections(): void;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Smart transport selector
|
|
97
|
+
* Automatically chooses best transport for given context
|
|
98
|
+
*/
|
|
99
|
+
export declare class SmartTransportSelector {
|
|
100
|
+
private wifiDirect;
|
|
101
|
+
constructor();
|
|
102
|
+
/**
|
|
103
|
+
* Select best available transport for intent transmission
|
|
104
|
+
*
|
|
105
|
+
* Preference order:
|
|
106
|
+
* 1. WiFi Direct (fastest, 1200 MTU)
|
|
107
|
+
* 2. BLE (fallback, 480 MTU)
|
|
108
|
+
*/
|
|
109
|
+
selectTransport(): Promise<'wifi' | 'ble'>;
|
|
110
|
+
/**
|
|
111
|
+
* Check if WiFi Direct should be used
|
|
112
|
+
* Factors: availability, proximity, battery level
|
|
113
|
+
*/
|
|
114
|
+
shouldUseWiFi(checkBattery?: boolean): Promise<boolean>;
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=wifi.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wifi.d.ts","sourceRoot":"","sources":["../../../src/wifi.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAK/D;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,cAAc,CAA4C;IAClE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAQ;gBAErB,SAAS,GAAE,SAAS,GAAG,KAAiB;IAQpD;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAiBrC;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB7B;;OAEG;IACG,aAAa,CAAC,cAAc,GAAE,MAAW,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAmB3E;;OAEG;IACG,aAAa,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA8BrE;;;OAGG;IACG,UAAU,CACd,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC;QACT,OAAO,EAAE,OAAO,CAAC;QACjB,gBAAgB,EAAE,MAAM,CAAC;QACzB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IAmDF;;OAEG;IACG,sBAAsB,CAC1B,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,kBAAkB,GAC9B,OAAO,CAAC;QACT,OAAO,EAAE,OAAO,CAAC;QACjB,gBAAgB,EAAE,MAAM,CAAC;QACzB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IAiCF;;OAEG;IACG,WAAW,CACf,MAAM,EAAE,gBAAgB,EACxB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,MAAM,CAAC;IAqClB;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB/C;;OAEG;IACH,iBAAiB,IAAI,gBAAgB,EAAE;IAMvC;;OAEG;IACH,MAAM,IAAI,MAAM;IAIhB;;OAEG;IACH,yBAAyB,IAAI,IAAI;CASlC;AAED;;;GAGG;AACH,qBAAa,sBAAsB;IACjC,OAAO,CAAC,UAAU,CAAsB;;IAMxC;;;;;;OAMG;IACG,eAAe,IAAI,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC;IAUhD;;;OAGG;IACG,aAAa,CAAC,YAAY,GAAE,OAAe,GAAG,OAAO,CAAC,OAAO,CAAC;CAgBrE"}
|
package/package.json
CHANGED
|
@@ -43,7 +43,7 @@ export class NonceAccountManager {
|
|
|
43
43
|
|
|
44
44
|
if (requireBiometric !== true) {
|
|
45
45
|
throw new Error(
|
|
46
|
-
'
|
|
46
|
+
' SECURITY ERROR: Biometric protection is mandatory for nonce accounts'
|
|
47
47
|
);
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -351,7 +351,7 @@ export class NonceAccountManager {
|
|
|
351
351
|
transaction.lastValidBlockHeight = lastValidBlockHeight;
|
|
352
352
|
|
|
353
353
|
console.log(
|
|
354
|
-
'
|
|
354
|
+
' Durable nonce account initialized: ',
|
|
355
355
|
nonceAccountKeypair.publicKey.toBase58()
|
|
356
356
|
);
|
|
357
357
|
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Complete Example: Enhanced TOSS Flow with Compression, Incentives & WiFi
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates:
|
|
5
|
+
* 1. Compression of intent metadata
|
|
6
|
+
* 2. Smart transport selection (WiFi > BLE)
|
|
7
|
+
* 3. Relay incentive tracking
|
|
8
|
+
* 4. Mesh clustering for optimal routing
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
|
|
12
|
+
import {
|
|
13
|
+
createIntent,
|
|
14
|
+
SmartTransportSelector,
|
|
15
|
+
compressIntentMetadata,
|
|
16
|
+
decompressIntentMetadata,
|
|
17
|
+
} from 'toss-expo-sdk';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Example 1: Metadata Compression Flow
|
|
21
|
+
*
|
|
22
|
+
* Compress large memo text before transmission
|
|
23
|
+
*/
|
|
24
|
+
export async function exampleCompressedIntentFlow(
|
|
25
|
+
sender: Keypair,
|
|
26
|
+
recipient: PublicKey,
|
|
27
|
+
amount: number,
|
|
28
|
+
connection: Connection
|
|
29
|
+
): Promise<void> {
|
|
30
|
+
console.log('\nExample 1: Compressed Intent Flow\n');
|
|
31
|
+
|
|
32
|
+
// Step 1: Create intent
|
|
33
|
+
const intent = await createIntent(sender, recipient, amount, connection);
|
|
34
|
+
|
|
35
|
+
// Step 2: Create metadata with long memo
|
|
36
|
+
const metadata = {
|
|
37
|
+
memo: 'Payment for services rendered during Q4 2025. This is a longer memo that benefits from compression',
|
|
38
|
+
recipientName: 'Alice Smith',
|
|
39
|
+
transactionType: 'service-payment',
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Step 3: Compress metadata
|
|
43
|
+
const { compressed, savings } = await compressIntentMetadata(metadata);
|
|
44
|
+
console.log(`Compressed metadata: ${savings}% smaller`);
|
|
45
|
+
console.log(` Original size: ${JSON.stringify(metadata).length} bytes`);
|
|
46
|
+
console.log(` Compressed size: ${JSON.stringify(compressed).length} bytes`);
|
|
47
|
+
|
|
48
|
+
// Step 4: Store/transmit compressed version
|
|
49
|
+
const transmissionPayload = {
|
|
50
|
+
intent,
|
|
51
|
+
metadata: compressed,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
console.log(`\n Transmission payload:`, {
|
|
55
|
+
intentSize: JSON.stringify(intent).length,
|
|
56
|
+
metadataSize: JSON.stringify(compressed).length,
|
|
57
|
+
total: JSON.stringify(transmissionPayload).length,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Step 5: On receive, decompress
|
|
61
|
+
const received = await decompressIntentMetadata(compressed);
|
|
62
|
+
console.log(`\nDecompressed metadata:`, received);
|
|
63
|
+
console.log(
|
|
64
|
+
' Verification: Matches original:',
|
|
65
|
+
JSON.stringify(received) === JSON.stringify(metadata)
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Example 2: Smart Transport Selection
|
|
71
|
+
*
|
|
72
|
+
* Automatically choose WiFi Direct if available, fallback to BLE
|
|
73
|
+
*/
|
|
74
|
+
export async function exampleSmartTransportFlow(): Promise<void> {
|
|
75
|
+
console.log('\n Example 2: Smart Transport Selection\n');
|
|
76
|
+
|
|
77
|
+
const selector = new SmartTransportSelector();
|
|
78
|
+
const selectedTransport = await selector.selectTransport();
|
|
79
|
+
|
|
80
|
+
console.log(`Selected transport: ${selectedTransport}`);
|
|
81
|
+
|
|
82
|
+
if (selectedTransport === 'wifi') {
|
|
83
|
+
console.log(' Benefits:');
|
|
84
|
+
console.log(' • MTU: 1200 bytes (vs BLE 480 bytes)');
|
|
85
|
+
console.log(' • Speed: ~2.5x faster');
|
|
86
|
+
console.log(' • Ideal for bulk transfers');
|
|
87
|
+
} else {
|
|
88
|
+
console.log(' Benefits:');
|
|
89
|
+
console.log(' • Widely compatible');
|
|
90
|
+
console.log(' • Lower power than WiFi');
|
|
91
|
+
console.log(' • Sufficient for most intents');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const shouldUseWiFi = await selector.shouldUseWiFi(false);
|
|
95
|
+
console.log(`\nWiFi Direct recommended: ${shouldUseWiFi}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Example 3: Relay Incentive Tracking
|
|
100
|
+
*
|
|
101
|
+
* Calculate and track rewards for devices relaying transactions
|
|
102
|
+
*/
|
|
103
|
+
export async function exampleRelayIncentiveFlow(): Promise<void> {
|
|
104
|
+
console.log('\nExample 3: Relay Incentive Tracking\n');
|
|
105
|
+
|
|
106
|
+
// Simulate relay path: Device A → Device B → Device C → Gateway
|
|
107
|
+
const relayPath = [
|
|
108
|
+
'deviceB_address_here',
|
|
109
|
+
'deviceC_address_here',
|
|
110
|
+
'gateway_address_here',
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
// Step 1: Calculate rewards for each relay (in production)
|
|
114
|
+
// const rewards = calculateRelayRewards(relayPath);
|
|
115
|
+
const rewardPerRelay = 1000; // 1000 lamports per relay per hop
|
|
116
|
+
|
|
117
|
+
console.log('Relay reward structure:');
|
|
118
|
+
for (let i = 0; i < relayPath.length; i++) {
|
|
119
|
+
console.log(
|
|
120
|
+
` ${relayPath[i]}: ${rewardPerRelay * (relayPath.length - i)} lamports`
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Step 2: Track which relays contributed
|
|
125
|
+
const totalReward = relayPath.length * rewardPerRelay;
|
|
126
|
+
console.log(`\nTotal rewards distributed: ${totalReward} lamports`);
|
|
127
|
+
|
|
128
|
+
// Step 3: In production, after successful settlement:
|
|
129
|
+
// await trackRelayContribution('intent-id', relayPath, connection, feePayer);
|
|
130
|
+
console.log('\nRelay contributions tracked for future settlement');
|
|
131
|
+
console.log(' (In production, rewards would be transferred onchain)');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Example 4: Mesh Clustering & Smart Routing
|
|
136
|
+
*
|
|
137
|
+
* Use signal strength to form clusters and find optimal paths
|
|
138
|
+
*/
|
|
139
|
+
export async function exampleMeshClusteringFlow(): Promise<void> {
|
|
140
|
+
console.log('\nExample 4: Mesh Clustering & Smart Routing\n');
|
|
141
|
+
|
|
142
|
+
// Step 1: Detect clusters (in production, would use actual device discovery)
|
|
143
|
+
console.log('Detected 3 clusters:');
|
|
144
|
+
console.log(' Cluster A: 5 devices');
|
|
145
|
+
console.log(' Cluster B: 3 devices');
|
|
146
|
+
console.log(' Cluster C: 2 devices');
|
|
147
|
+
|
|
148
|
+
// Step 2: Find optimal route to target
|
|
149
|
+
const targetDeviceId = 'target-device-123';
|
|
150
|
+
console.log(`\nOptimal route to ${targetDeviceId}:`);
|
|
151
|
+
console.log(' Multi-hop path (2 hops):');
|
|
152
|
+
console.log(' 0: relay-1');
|
|
153
|
+
console.log(' 1: target-device-123');
|
|
154
|
+
|
|
155
|
+
// Step 3: Track relay performance (in production)
|
|
156
|
+
console.log('\nRelay performance tracking:');
|
|
157
|
+
console.log(' Relay 1 score: 95/100 (fast & reliable)');
|
|
158
|
+
console.log(' Relay 2 score: 72/100 (slower)');
|
|
159
|
+
console.log(' Relay 3 score: 45/100 (failed recently)');
|
|
160
|
+
|
|
161
|
+
// Step 4: Select best relay
|
|
162
|
+
console.log('\nBest relay selected: relay-1');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Example 5: Complete Production Flow
|
|
167
|
+
*
|
|
168
|
+
* Integrates compression + WiFi transport + incentives + optimal routing
|
|
169
|
+
*/
|
|
170
|
+
export async function exampleCompleteEnhancedFlow(
|
|
171
|
+
sender: Keypair,
|
|
172
|
+
recipient: PublicKey,
|
|
173
|
+
amount: number,
|
|
174
|
+
connection: Connection,
|
|
175
|
+
_feePayer: PublicKey
|
|
176
|
+
): Promise<void> {
|
|
177
|
+
console.log('\n Example 5: Complete Enhanced TOSS Flow\n');
|
|
178
|
+
|
|
179
|
+
// Phase 1: Creation (offline)
|
|
180
|
+
console.log('Phase 1: Intent Creation (Offline)\n');
|
|
181
|
+
const intent = await createIntent(sender, recipient, amount, connection);
|
|
182
|
+
console.log(' Intent created and signed');
|
|
183
|
+
|
|
184
|
+
const metadata = {
|
|
185
|
+
memo: 'Complete flow demonstration',
|
|
186
|
+
timestamp: new Date().toISOString(),
|
|
187
|
+
};
|
|
188
|
+
const { compressed, savings } = await compressIntentMetadata(metadata);
|
|
189
|
+
console.log(` Metadata compressed (${savings}% savings)`);
|
|
190
|
+
|
|
191
|
+
// Phase 2: Transport (intelligent selection)
|
|
192
|
+
console.log('\nPhase 2: Intelligent Transport Selection\n');
|
|
193
|
+
const selector = new SmartTransportSelector();
|
|
194
|
+
const transport = await selector.selectTransport();
|
|
195
|
+
console.log(` Selected transport: ${transport.toUpperCase()}`);
|
|
196
|
+
|
|
197
|
+
// Phase 3: Routing (cluster-based optimization)
|
|
198
|
+
console.log('\nPhase 3: Optimal Path Finding\n');
|
|
199
|
+
const clusterCount = 3;
|
|
200
|
+
console.log(` Found ${clusterCount} network clusters`);
|
|
201
|
+
|
|
202
|
+
const relayPath = ['relay-1', 'relay-2'];
|
|
203
|
+
console.log(` Route: ${relayPath.length} hops to destination`);
|
|
204
|
+
|
|
205
|
+
// Phase 4: Incentive tracking
|
|
206
|
+
console.log('\nPhase 4: Relay Incentive Setup\n');
|
|
207
|
+
const rewardPerRelay = 1000;
|
|
208
|
+
const totalReward = relayPath.length * rewardPerRelay;
|
|
209
|
+
console.log(
|
|
210
|
+
` Reward pool: ${totalReward} lamports for ${relayPath.length} relays`
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
// Phase 5: Transmission
|
|
214
|
+
console.log('\nPhase 5: Intent Transmission\n');
|
|
215
|
+
const payloadSize = JSON.stringify({
|
|
216
|
+
intent,
|
|
217
|
+
metadata: compressed,
|
|
218
|
+
}).length;
|
|
219
|
+
console.log(` Payload size: ${payloadSize} bytes`);
|
|
220
|
+
console.log(` MTU: ${transport === 'wifi' ? '1200' : '480'} bytes`);
|
|
221
|
+
const fragments = Math.ceil(
|
|
222
|
+
payloadSize / (transport === 'wifi' ? 1200 : 480)
|
|
223
|
+
);
|
|
224
|
+
console.log(` Fragments: ${fragments} (optimized)`);
|
|
225
|
+
|
|
226
|
+
// Phase 6: Settlement (when online)
|
|
227
|
+
console.log('\nPhase 6: Settlement (When Online)\n');
|
|
228
|
+
console.log(' Intent ready for settlement');
|
|
229
|
+
console.log(' • Signature verified: YES');
|
|
230
|
+
console.log(' • Metadata decompressed: YES');
|
|
231
|
+
console.log(' • Relayers identified: YES');
|
|
232
|
+
console.log(` • Rewards tracked: ${totalReward} lamports`);
|
|
233
|
+
console.log('\n Complete flow ready for production deployment');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Run all examples
|
|
238
|
+
*/
|
|
239
|
+
export async function runAllExamples(connection: Connection): Promise<void> {
|
|
240
|
+
console.log('═══════════════════════════════════════════════════════════');
|
|
241
|
+
console.log(' TOSS Enhanced Features - Complete Examples');
|
|
242
|
+
console.log('═══════════════════════════════════════════════════════════');
|
|
243
|
+
|
|
244
|
+
// Create test keypairs
|
|
245
|
+
const sender = Keypair.generate();
|
|
246
|
+
const recipient = Keypair.generate().publicKey;
|
|
247
|
+
const feePayer = Keypair.generate().publicKey;
|
|
248
|
+
|
|
249
|
+
try {
|
|
250
|
+
await exampleCompressedIntentFlow(sender, recipient, 1000000, connection);
|
|
251
|
+
await exampleSmartTransportFlow();
|
|
252
|
+
await exampleRelayIncentiveFlow();
|
|
253
|
+
await exampleMeshClusteringFlow();
|
|
254
|
+
await exampleCompleteEnhancedFlow(
|
|
255
|
+
sender,
|
|
256
|
+
recipient,
|
|
257
|
+
1000000,
|
|
258
|
+
connection,
|
|
259
|
+
feePayer
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
console.log(
|
|
263
|
+
'\n═══════════════════════════════════════════════════════════'
|
|
264
|
+
);
|
|
265
|
+
console.log(' All examples completed successfully!');
|
|
266
|
+
console.log(
|
|
267
|
+
'═══════════════════════════════════════════════════════════\n'
|
|
268
|
+
);
|
|
269
|
+
} catch (error) {
|
|
270
|
+
console.error(' Example failed:', error);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
@@ -43,7 +43,7 @@ export async function exampleInitiateUserPayment(
|
|
|
43
43
|
amountLamports: number,
|
|
44
44
|
connection: Connection
|
|
45
45
|
): Promise<SolanaIntent> {
|
|
46
|
-
console.log('
|
|
46
|
+
console.log(' Creating offline payment intent between TOSS users...');
|
|
47
47
|
console.log(` From: @${senderUser.username}`);
|
|
48
48
|
console.log(` To: @${recipientUser.username}`);
|
|
49
49
|
|
|
@@ -59,13 +59,13 @@ export async function exampleInitiateUserPayment(
|
|
|
59
59
|
}
|
|
60
60
|
);
|
|
61
61
|
|
|
62
|
-
console.log(
|
|
62
|
+
console.log(` Intent created: ${intent.id}`);
|
|
63
63
|
console.log(` Amount: ${intent.amount} lamports`);
|
|
64
64
|
console.log(` Expires at: ${new Date(intent.expiry * 1000).toISOString()}`);
|
|
65
65
|
|
|
66
66
|
// Store locally
|
|
67
67
|
await secureStoreIntent(intent);
|
|
68
|
-
console.log('
|
|
68
|
+
console.log(' Intent stored securely locally\n');
|
|
69
69
|
|
|
70
70
|
return intent;
|
|
71
71
|
}
|
|
@@ -82,7 +82,7 @@ export async function exampleInitiateOfflinePayment(
|
|
|
82
82
|
amountLamports: number,
|
|
83
83
|
connection: Connection
|
|
84
84
|
): Promise<SolanaIntent> {
|
|
85
|
-
console.log('
|
|
85
|
+
console.log(' Creating offline payment intent...');
|
|
86
86
|
|
|
87
87
|
// Create the intent (this is done offline, no network needed)
|
|
88
88
|
const intent = await createIntent(
|
|
@@ -95,7 +95,7 @@ export async function exampleInitiateOfflinePayment(
|
|
|
95
95
|
}
|
|
96
96
|
);
|
|
97
97
|
|
|
98
|
-
console.log(
|
|
98
|
+
console.log(` Intent created: ${intent.id}`);
|
|
99
99
|
console.log(` From: ${intent.from}`);
|
|
100
100
|
console.log(` To: ${intent.to}`);
|
|
101
101
|
console.log(` Amount: ${intent.amount} lamports`);
|
|
@@ -103,7 +103,7 @@ export async function exampleInitiateOfflinePayment(
|
|
|
103
103
|
|
|
104
104
|
// Store locally
|
|
105
105
|
await secureStoreIntent(intent);
|
|
106
|
-
console.log('
|
|
106
|
+
console.log(' Intent stored securely locally\n');
|
|
107
107
|
|
|
108
108
|
return intent;
|
|
109
109
|
}
|
|
@@ -120,13 +120,13 @@ export async function exampleExchangeIntentWithPeer(
|
|
|
120
120
|
peerDeviceId: string,
|
|
121
121
|
peerDevice: PeerDevice
|
|
122
122
|
): Promise<void> {
|
|
123
|
-
console.log('
|
|
123
|
+
console.log(' Initiating intent exchange with peer...');
|
|
124
124
|
console.log(` Local Device: ${localDeviceId}`);
|
|
125
125
|
console.log(` Peer Device: ${peerDeviceId}`);
|
|
126
126
|
|
|
127
127
|
// Register the peer
|
|
128
128
|
deviceDiscovery.registerPeer(peerDevice);
|
|
129
|
-
console.log(
|
|
129
|
+
console.log(` Peer registered: ${peerDevice.id}`);
|
|
130
130
|
|
|
131
131
|
// Create an exchange request
|
|
132
132
|
const exchangeRequest = intentExchange.createRequest(
|
|
@@ -136,7 +136,7 @@ export async function exampleExchangeIntentWithPeer(
|
|
|
136
136
|
5 * 60 // 5 minute expiry
|
|
137
137
|
);
|
|
138
138
|
|
|
139
|
-
console.log(
|
|
139
|
+
console.log(` Exchange request created: ${exchangeRequest.requestId}`);
|
|
140
140
|
console.log(` Intent ID: ${intent.id}`);
|
|
141
141
|
console.log(` Amount: ${intent.amount} lamports`);
|
|
142
142
|
|
|
@@ -152,7 +152,7 @@ export async function exampleExchangeIntentWithPeer(
|
|
|
152
152
|
[intent.id]
|
|
153
153
|
);
|
|
154
154
|
|
|
155
|
-
console.log(`\n
|
|
155
|
+
console.log(`\n Peer accepted exchange`);
|
|
156
156
|
console.log(` Status: ${response.status}`);
|
|
157
157
|
console.log(
|
|
158
158
|
` Acknowledged intents: ${response.acknowledgedIntentIds?.join(', ')}\n`
|
|
@@ -170,7 +170,7 @@ export async function exampleExchangeIntentWithPeer(
|
|
|
170
170
|
export async function exampleMultiDeviceConflict(
|
|
171
171
|
connection: Connection
|
|
172
172
|
): Promise<void> {
|
|
173
|
-
console.log('
|
|
173
|
+
console.log(' Simulating multi-device conflict scenario...\n');
|
|
174
174
|
|
|
175
175
|
// Create keypair for "Device A"
|
|
176
176
|
const senderKeypair = Keypair.generate();
|
|
@@ -198,7 +198,7 @@ export async function exampleMultiDeviceConflict(
|
|
|
198
198
|
{ expiresIn: 3600 }
|
|
199
199
|
);
|
|
200
200
|
|
|
201
|
-
console.log('
|
|
201
|
+
console.log(' Conflict Detected!');
|
|
202
202
|
console.log(
|
|
203
203
|
` Device A created intent: ${intentA.id} at ${intentA.createdAt}`
|
|
204
204
|
);
|
|
@@ -212,7 +212,7 @@ export async function exampleMultiDeviceConflict(
|
|
|
212
212
|
const resolution =
|
|
213
213
|
MultiDeviceConflictResolver.resolveConflicts(conflictingIntents);
|
|
214
214
|
|
|
215
|
-
console.log('
|
|
215
|
+
console.log('️ Deterministic Resolution Applied:');
|
|
216
216
|
console.log(` Winner: ${resolution.winner.id}`);
|
|
217
217
|
console.log(` Winner nonce: ${resolution.winner.nonce}`);
|
|
218
218
|
console.log(
|
|
@@ -237,7 +237,7 @@ export async function exampleCompleteOfflineFlow(
|
|
|
237
237
|
connection: Connection
|
|
238
238
|
): Promise<void> {
|
|
239
239
|
console.log('='.repeat(60));
|
|
240
|
-
console.log('
|
|
240
|
+
console.log(' TOSS Complete Offline Payment Flow');
|
|
241
241
|
console.log('='.repeat(60) + '\n');
|
|
242
242
|
|
|
243
243
|
try {
|
|
@@ -272,13 +272,13 @@ export async function exampleCompleteOfflineFlow(
|
|
|
272
272
|
// Step 3: Device reconnects and initiates synchronisation
|
|
273
273
|
console.log('STEP 3: Synchronisation with Solana');
|
|
274
274
|
console.log('-'.repeat(60));
|
|
275
|
-
console.log('
|
|
276
|
-
console.log('
|
|
275
|
+
console.log(' Device reconnected to network...');
|
|
276
|
+
console.log(' Initiating sync with Solana blockchain...\n');
|
|
277
277
|
|
|
278
278
|
// Check sync status
|
|
279
279
|
const syncResult = await syncToChain(connection);
|
|
280
280
|
|
|
281
|
-
console.log('
|
|
281
|
+
console.log(' Sync Results:');
|
|
282
282
|
console.log(
|
|
283
283
|
` Successful settlements: ${syncResult.successfulSettlements.length}`
|
|
284
284
|
);
|
|
@@ -291,7 +291,7 @@ export async function exampleCompleteOfflineFlow(
|
|
|
291
291
|
console.log(` Overall complete: ${syncResult.isComplete}\n`);
|
|
292
292
|
|
|
293
293
|
if (syncResult.successfulSettlements.length > 0) {
|
|
294
|
-
console.log('
|
|
294
|
+
console.log(' Successful Settlements:');
|
|
295
295
|
for (const settlement of syncResult.successfulSettlements) {
|
|
296
296
|
console.log(` Intent ${settlement.intentId}`);
|
|
297
297
|
console.log(` Signature: ${settlement.signature}`);
|
|
@@ -303,7 +303,7 @@ export async function exampleCompleteOfflineFlow(
|
|
|
303
303
|
}
|
|
304
304
|
|
|
305
305
|
if (syncResult.failedSettlements.length > 0) {
|
|
306
|
-
console.log('
|
|
306
|
+
console.log(' Failed Settlements:');
|
|
307
307
|
for (const settlement of syncResult.failedSettlements) {
|
|
308
308
|
console.log(` Intent ${settlement.intentId}`);
|
|
309
309
|
console.log(` Reason: ${settlement.error}`);
|
|
@@ -312,7 +312,7 @@ export async function exampleCompleteOfflineFlow(
|
|
|
312
312
|
}
|
|
313
313
|
|
|
314
314
|
if (syncResult.detectedConflicts.length > 0) {
|
|
315
|
-
console.log('
|
|
315
|
+
console.log('️ Detected Conflicts:');
|
|
316
316
|
for (const conflict of syncResult.detectedConflicts) {
|
|
317
317
|
console.log(` Intent ${conflict.intentId}: ${conflict.conflict}`);
|
|
318
318
|
}
|
|
@@ -330,15 +330,15 @@ export async function exampleCompleteOfflineFlow(
|
|
|
330
330
|
(i: SolanaIntent) => i.status === 'failed'
|
|
331
331
|
);
|
|
332
332
|
|
|
333
|
-
console.log(
|
|
333
|
+
console.log(` Intent Storage:
|
|
334
334
|
Total intents: ${allIntents.length}
|
|
335
335
|
Settled: ${settledIntents.length}
|
|
336
336
|
Failed: ${failedIntents.length}\n`);
|
|
337
337
|
|
|
338
|
-
console.log('
|
|
338
|
+
console.log(' Flow complete!\n');
|
|
339
339
|
console.log('='.repeat(60));
|
|
340
340
|
} catch (error) {
|
|
341
|
-
console.error('
|
|
341
|
+
console.error(' Error during offline flow:', error);
|
|
342
342
|
if (error instanceof TossError) {
|
|
343
343
|
console.error(` Error code: ${(error as TossError).code}`);
|
|
344
344
|
}
|
|
@@ -355,23 +355,23 @@ export async function exampleVerifyIntentBeforeAcceptance(
|
|
|
355
355
|
intent: SolanaIntent,
|
|
356
356
|
connection: Connection
|
|
357
357
|
): Promise<boolean> {
|
|
358
|
-
console.log('
|
|
358
|
+
console.log(' Verifying intent signature...');
|
|
359
359
|
|
|
360
360
|
try {
|
|
361
361
|
const isValid = await verifyIntent(intent, connection);
|
|
362
362
|
|
|
363
363
|
if (isValid) {
|
|
364
|
-
console.log('
|
|
364
|
+
console.log(' Intent signature is valid');
|
|
365
365
|
console.log(` From: ${intent.from}`);
|
|
366
366
|
console.log(` To: ${intent.to}`);
|
|
367
367
|
console.log(` Amount: ${intent.amount} lamports`);
|
|
368
368
|
return true;
|
|
369
369
|
} else {
|
|
370
|
-
console.log('
|
|
370
|
+
console.log(' Intent signature is invalid');
|
|
371
371
|
return false;
|
|
372
372
|
}
|
|
373
373
|
} catch (error) {
|
|
374
|
-
console.error('
|
|
374
|
+
console.error(' Verification failed:', error);
|
|
375
375
|
return false;
|
|
376
376
|
}
|
|
377
377
|
}
|
package/src/index.tsx
CHANGED
|
@@ -101,3 +101,15 @@ export {
|
|
|
101
101
|
type IntentExchangeRequest,
|
|
102
102
|
type IntentExchangeResponse,
|
|
103
103
|
} from './discovery';
|
|
104
|
+
|
|
105
|
+
// Compression utilities
|
|
106
|
+
export {
|
|
107
|
+
compressMetadata,
|
|
108
|
+
decompressMetadata,
|
|
109
|
+
compressIntentMetadata,
|
|
110
|
+
decompressIntentMetadata,
|
|
111
|
+
estimateCompressionSavings,
|
|
112
|
+
} from './utils/compression';
|
|
113
|
+
|
|
114
|
+
// WiFi Direct transport
|
|
115
|
+
export { WiFiDirectTransport, SmartTransportSelector } from './wifi';
|
package/src/qr.tsx
CHANGED
|
@@ -16,10 +16,10 @@ export function QRScanner({ onScan }: QRScannerProps) {
|
|
|
16
16
|
const permission = useCameraPermission();
|
|
17
17
|
|
|
18
18
|
const codeScanner = useCodeScanner({
|
|
19
|
-
codeTypes: ['qr'], //
|
|
19
|
+
codeTypes: ['qr'], // correct CodeType
|
|
20
20
|
onCodeScanned: (codes: Code[]) => {
|
|
21
21
|
const code = codes[0];
|
|
22
|
-
if (!code?.value) return; //
|
|
22
|
+
if (!code?.value) return; // undefined-safe
|
|
23
23
|
|
|
24
24
|
onScan(code.value);
|
|
25
25
|
},
|