@one_deploy/sdk 1.0.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/.turbo/turbo-build.log +0 -0
- package/.turbo/turbo-type-check.log +0 -0
- package/dist/config/index.d.mts +74 -0
- package/dist/config/index.d.ts +74 -0
- package/dist/config/index.js +242 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/index.mjs +224 -0
- package/dist/config/index.mjs.map +1 -0
- package/dist/engine-5ndtBaCr.d.ts +1039 -0
- package/dist/engine-CrlhH0nw.d.mts +1039 -0
- package/dist/hooks/index.d.mts +56 -0
- package/dist/hooks/index.d.ts +56 -0
- package/dist/hooks/index.js +1360 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/index.mjs +1356 -0
- package/dist/hooks/index.mjs.map +1 -0
- package/dist/index.d.mts +356 -0
- package/dist/index.d.ts +356 -0
- package/dist/index.js +5068 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +4949 -0
- package/dist/index.mjs.map +1 -0
- package/dist/price-CgqXPnT3.d.ts +13 -0
- package/dist/price-ClbLHHjv.d.mts +13 -0
- package/dist/providers/index.d.mts +121 -0
- package/dist/providers/index.d.ts +121 -0
- package/dist/providers/index.js +1642 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/index.mjs +1600 -0
- package/dist/providers/index.mjs.map +1 -0
- package/dist/react-native.d.mts +120 -0
- package/dist/react-native.d.ts +120 -0
- package/dist/react-native.js +1792 -0
- package/dist/react-native.js.map +1 -0
- package/dist/react-native.mjs +1755 -0
- package/dist/react-native.mjs.map +1 -0
- package/dist/services/index.d.mts +85 -0
- package/dist/services/index.d.ts +85 -0
- package/dist/services/index.js +1466 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/index.mjs +1458 -0
- package/dist/services/index.mjs.map +1 -0
- package/dist/types/index.d.mts +759 -0
- package/dist/types/index.d.ts +759 -0
- package/dist/types/index.js +4 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/index.mjs +3 -0
- package/dist/types/index.mjs.map +1 -0
- package/dist/utils/index.d.mts +36 -0
- package/dist/utils/index.d.ts +36 -0
- package/dist/utils/index.js +164 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/index.mjs +142 -0
- package/dist/utils/index.mjs.map +1 -0
- package/package.json +101 -0
- package/src/components/OneConnectButton.tsx +143 -0
- package/src/components/OneNFTGallery.tsx +324 -0
- package/src/components/OneOfframpWidget.tsx +660 -0
- package/src/components/OneOnrampWidget.tsx +596 -0
- package/src/components/OnePayWidget.tsx +160 -0
- package/src/components/OneReceiveWidget.tsx +272 -0
- package/src/components/OneSendWidget.tsx +248 -0
- package/src/components/OneSwapWidget.tsx +715 -0
- package/src/components/OneTransactionButton.tsx +150 -0
- package/src/components/OneWalletBalance.tsx +354 -0
- package/src/components/index.ts +24 -0
- package/src/config/index.ts +299 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useTokenPrice.ts +162 -0
- package/src/hooks/useWalletBalance.ts +98 -0
- package/src/index.ts +193 -0
- package/src/providers/OneProvider.tsx +452 -0
- package/src/providers/ThirdwebProvider.tsx +203 -0
- package/src/providers/index.ts +26 -0
- package/src/react-native.ts +378 -0
- package/src/services/engine.ts +1854 -0
- package/src/services/index.ts +30 -0
- package/src/services/price.ts +164 -0
- package/src/services/supabase.ts +180 -0
- package/src/types/index.ts +887 -0
- package/src/utils/index.ts +200 -0
- package/tsconfig.json +22 -0
- package/tsup.config.ts +25 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { type ReactNode, useMemo, useEffect, useState } from 'react';
|
|
4
|
+
import { createThirdwebClient, type ThirdwebClient } from 'thirdweb';
|
|
5
|
+
import { ThirdwebProvider as BaseThirdwebProvider } from 'thirdweb/react';
|
|
6
|
+
import { inAppWallet, smartWallet } from 'thirdweb/wallets';
|
|
7
|
+
import type { Chain } from 'thirdweb/chains';
|
|
8
|
+
import { base, ethereum, polygon, arbitrum, optimism } from 'thirdweb/chains';
|
|
9
|
+
|
|
10
|
+
// ===== Types =====
|
|
11
|
+
|
|
12
|
+
export interface OneThirdwebConfig {
|
|
13
|
+
// Engine URL to fetch thirdweb config (clientId managed by Engine)
|
|
14
|
+
engineUrl?: string;
|
|
15
|
+
// Or use pre-configured ONE ecosystem clientId (fetched from Engine)
|
|
16
|
+
// Users do NOT need to provide their own clientId
|
|
17
|
+
appName?: string;
|
|
18
|
+
appIcon?: string;
|
|
19
|
+
defaultChain?: Chain;
|
|
20
|
+
supportedChains?: Chain[];
|
|
21
|
+
sponsorGas?: boolean;
|
|
22
|
+
authOptions?: {
|
|
23
|
+
email?: boolean;
|
|
24
|
+
phone?: boolean;
|
|
25
|
+
google?: boolean;
|
|
26
|
+
apple?: boolean;
|
|
27
|
+
facebook?: boolean;
|
|
28
|
+
discord?: boolean;
|
|
29
|
+
passkey?: boolean;
|
|
30
|
+
guest?: boolean;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface OneThirdwebProviderProps {
|
|
35
|
+
children: ReactNode;
|
|
36
|
+
config?: OneThirdwebConfig;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ===== Default Configuration =====
|
|
40
|
+
|
|
41
|
+
const DEFAULT_CHAINS: Chain[] = [base, ethereum, polygon, arbitrum, optimism];
|
|
42
|
+
|
|
43
|
+
const DEFAULT_AUTH_OPTIONS = {
|
|
44
|
+
email: true,
|
|
45
|
+
phone: false,
|
|
46
|
+
google: true,
|
|
47
|
+
apple: true,
|
|
48
|
+
facebook: false,
|
|
49
|
+
discord: false,
|
|
50
|
+
passkey: true,
|
|
51
|
+
guest: false,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// ===== Context for Client Access =====
|
|
55
|
+
|
|
56
|
+
const ThirdwebClientContext = React.createContext<ThirdwebClient | null>(null);
|
|
57
|
+
|
|
58
|
+
export function useThirdwebClient(): ThirdwebClient {
|
|
59
|
+
const client = React.useContext(ThirdwebClientContext);
|
|
60
|
+
if (!client) {
|
|
61
|
+
throw new Error('useThirdwebClient must be used within OneThirdwebProvider');
|
|
62
|
+
}
|
|
63
|
+
return client;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ===== Create Wallets Configuration =====
|
|
67
|
+
|
|
68
|
+
function createWalletConfig(config: OneThirdwebConfig) {
|
|
69
|
+
const authOptions = { ...DEFAULT_AUTH_OPTIONS, ...config.authOptions };
|
|
70
|
+
|
|
71
|
+
// Build auth options array
|
|
72
|
+
const authMethods: string[] = [];
|
|
73
|
+
if (authOptions.google) authMethods.push('google');
|
|
74
|
+
if (authOptions.apple) authMethods.push('apple');
|
|
75
|
+
if (authOptions.facebook) authMethods.push('facebook');
|
|
76
|
+
if (authOptions.discord) authMethods.push('discord');
|
|
77
|
+
if (authOptions.passkey) authMethods.push('passkey');
|
|
78
|
+
|
|
79
|
+
// Create in-app wallet with email and social logins
|
|
80
|
+
const inApp = inAppWallet({
|
|
81
|
+
auth: {
|
|
82
|
+
options: authMethods as any[],
|
|
83
|
+
},
|
|
84
|
+
metadata: config.appName ? {
|
|
85
|
+
name: config.appName,
|
|
86
|
+
image: config.appIcon ? { src: config.appIcon, width: 100, height: 100 } : undefined,
|
|
87
|
+
} : undefined,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// If gas sponsorship is enabled, wrap in smart wallet
|
|
91
|
+
if (config.sponsorGas) {
|
|
92
|
+
const chain = config.defaultChain || base;
|
|
93
|
+
return [
|
|
94
|
+
smartWallet({
|
|
95
|
+
chain,
|
|
96
|
+
sponsorGas: true,
|
|
97
|
+
}),
|
|
98
|
+
];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return [inApp];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ===== Default Engine URL =====
|
|
105
|
+
const DEFAULT_ENGINE_URL = process.env.NEXT_PUBLIC_ONE_ENGINE_URL || '/api';
|
|
106
|
+
|
|
107
|
+
// ===== Provider Component =====
|
|
108
|
+
|
|
109
|
+
export function OneThirdwebProvider({
|
|
110
|
+
children,
|
|
111
|
+
config = {},
|
|
112
|
+
}: OneThirdwebProviderProps) {
|
|
113
|
+
const [clientId, setClientId] = useState<string | null>(null);
|
|
114
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
115
|
+
const [error, setError] = useState<string | null>(null);
|
|
116
|
+
|
|
117
|
+
// Fetch clientId from Engine on mount
|
|
118
|
+
// Engine manages the Thirdweb clientId centrally
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
const fetchClientConfig = async () => {
|
|
121
|
+
try {
|
|
122
|
+
const engineUrl = config.engineUrl || DEFAULT_ENGINE_URL;
|
|
123
|
+
const response = await fetch(`${engineUrl}/v1/config/thirdweb`);
|
|
124
|
+
|
|
125
|
+
if (response.ok) {
|
|
126
|
+
const data = await response.json();
|
|
127
|
+
if (data.success && data.data?.clientId) {
|
|
128
|
+
setClientId(data.data.clientId);
|
|
129
|
+
} else {
|
|
130
|
+
// Fallback to environment variable if API fails
|
|
131
|
+
const envClientId = process.env.NEXT_PUBLIC_THIRDWEB_CLIENT_ID;
|
|
132
|
+
if (envClientId) {
|
|
133
|
+
setClientId(envClientId);
|
|
134
|
+
} else {
|
|
135
|
+
setError('Failed to load wallet configuration');
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
// Fallback to environment variable
|
|
140
|
+
const envClientId = process.env.NEXT_PUBLIC_THIRDWEB_CLIENT_ID;
|
|
141
|
+
if (envClientId) {
|
|
142
|
+
setClientId(envClientId);
|
|
143
|
+
} else {
|
|
144
|
+
setError('Wallet service unavailable');
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
} catch (err) {
|
|
148
|
+
// Fallback to environment variable on network error
|
|
149
|
+
const envClientId = process.env.NEXT_PUBLIC_THIRDWEB_CLIENT_ID;
|
|
150
|
+
if (envClientId) {
|
|
151
|
+
setClientId(envClientId);
|
|
152
|
+
} else {
|
|
153
|
+
console.error('Failed to fetch thirdweb config:', err);
|
|
154
|
+
setError('Failed to initialize wallet');
|
|
155
|
+
}
|
|
156
|
+
} finally {
|
|
157
|
+
setIsLoading(false);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
fetchClientConfig();
|
|
162
|
+
}, [config.engineUrl]);
|
|
163
|
+
|
|
164
|
+
// Create thirdweb client once we have clientId
|
|
165
|
+
const client = useMemo(() => {
|
|
166
|
+
if (!clientId) return null;
|
|
167
|
+
return createThirdwebClient({ clientId });
|
|
168
|
+
}, [clientId]);
|
|
169
|
+
|
|
170
|
+
// Create wallet configuration
|
|
171
|
+
const wallets = useMemo(() => createWalletConfig(config), [config]);
|
|
172
|
+
|
|
173
|
+
// Loading state
|
|
174
|
+
if (isLoading) {
|
|
175
|
+
return (
|
|
176
|
+
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: '100px' }}>
|
|
177
|
+
<span style={{ color: '#9ca3af' }}>Initializing wallet...</span>
|
|
178
|
+
</div>
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Error state
|
|
183
|
+
if (error || !client) {
|
|
184
|
+
return (
|
|
185
|
+
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: '100px' }}>
|
|
186
|
+
<span style={{ color: '#ef4444' }}>{error || 'Wallet initialization failed'}</span>
|
|
187
|
+
</div>
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return (
|
|
192
|
+
<ThirdwebClientContext.Provider value={client}>
|
|
193
|
+
<BaseThirdwebProvider>
|
|
194
|
+
{children}
|
|
195
|
+
</BaseThirdwebProvider>
|
|
196
|
+
</ThirdwebClientContext.Provider>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ===== Export wallets for external use =====
|
|
201
|
+
|
|
202
|
+
export { inAppWallet, smartWallet } from 'thirdweb/wallets';
|
|
203
|
+
export { base, ethereum, polygon, arbitrum, optimism } from 'thirdweb/chains';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export {
|
|
2
|
+
OneProvider,
|
|
3
|
+
useOne,
|
|
4
|
+
useOneAuth,
|
|
5
|
+
useOneWallet,
|
|
6
|
+
useOneOnramp,
|
|
7
|
+
useOneSwap,
|
|
8
|
+
useOneTrading,
|
|
9
|
+
useOneEngine,
|
|
10
|
+
OneContext,
|
|
11
|
+
} from './OneProvider';
|
|
12
|
+
|
|
13
|
+
// Thirdweb Integration
|
|
14
|
+
export {
|
|
15
|
+
OneThirdwebProvider,
|
|
16
|
+
useThirdwebClient,
|
|
17
|
+
inAppWallet,
|
|
18
|
+
smartWallet,
|
|
19
|
+
base,
|
|
20
|
+
ethereum,
|
|
21
|
+
polygon,
|
|
22
|
+
arbitrum,
|
|
23
|
+
optimism,
|
|
24
|
+
type OneThirdwebConfig,
|
|
25
|
+
type OneThirdwebProviderProps,
|
|
26
|
+
} from './ThirdwebProvider';
|
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ONE SDK - React Native Entry Point
|
|
3
|
+
*
|
|
4
|
+
* This file exports SDK features optimized for React Native usage.
|
|
5
|
+
* It excludes web-specific features and includes mobile-specific utilities.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// ===== Configuration =====
|
|
9
|
+
export { initOneSDK, getConfig, CHAIN_CONFIGS, TOKEN_NAMES, COINGECKO_IDS } from './config';
|
|
10
|
+
export type { OneConfig } from './config';
|
|
11
|
+
|
|
12
|
+
// ===== Types =====
|
|
13
|
+
export * from './types';
|
|
14
|
+
|
|
15
|
+
// ===== ONE Engine Client =====
|
|
16
|
+
import { OneEngineClient as _OneEngineClient, createOneEngineClient } from './services/engine';
|
|
17
|
+
export { _OneEngineClient as OneEngineClient, createOneEngineClient };
|
|
18
|
+
|
|
19
|
+
export type {
|
|
20
|
+
EngineAuthResponse,
|
|
21
|
+
EngineWalletBalance,
|
|
22
|
+
EngineTransactionRequest,
|
|
23
|
+
EngineTransactionResponse,
|
|
24
|
+
OnrampSessionRequest,
|
|
25
|
+
OnrampSession,
|
|
26
|
+
OnrampQuote,
|
|
27
|
+
OnrampTransaction,
|
|
28
|
+
SwapQuoteRequest,
|
|
29
|
+
SwapQuote,
|
|
30
|
+
SwapExecuteRequest,
|
|
31
|
+
SwapResult,
|
|
32
|
+
} from './services/engine';
|
|
33
|
+
|
|
34
|
+
// ===== Price Service =====
|
|
35
|
+
export { PriceService, priceService } from './services/price';
|
|
36
|
+
|
|
37
|
+
// ===== Utilities =====
|
|
38
|
+
export {
|
|
39
|
+
// Address
|
|
40
|
+
shortenAddress,
|
|
41
|
+
isValidAddress,
|
|
42
|
+
checksumAddress,
|
|
43
|
+
// Numbers
|
|
44
|
+
formatNumber,
|
|
45
|
+
formatUSD,
|
|
46
|
+
formatPercent,
|
|
47
|
+
formatTokenAmount,
|
|
48
|
+
// Date/Time
|
|
49
|
+
formatDate,
|
|
50
|
+
formatDateTime,
|
|
51
|
+
formatRelativeTime,
|
|
52
|
+
// Validation
|
|
53
|
+
isValidEmail,
|
|
54
|
+
isValidPhone,
|
|
55
|
+
// Strings
|
|
56
|
+
capitalize,
|
|
57
|
+
truncate,
|
|
58
|
+
slugify,
|
|
59
|
+
// Async
|
|
60
|
+
sleep,
|
|
61
|
+
retry,
|
|
62
|
+
// Objects
|
|
63
|
+
omit,
|
|
64
|
+
pick,
|
|
65
|
+
// Errors
|
|
66
|
+
OneSDKError,
|
|
67
|
+
isOneSDKError,
|
|
68
|
+
} from './utils';
|
|
69
|
+
|
|
70
|
+
// ===== React Native Specific Utilities =====
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Storage adapter interface for React Native
|
|
74
|
+
* Implement this with AsyncStorage or other storage solutions
|
|
75
|
+
*/
|
|
76
|
+
export interface StorageAdapter {
|
|
77
|
+
getItem(key: string): Promise<string | null>;
|
|
78
|
+
setItem(key: string, value: string): Promise<void>;
|
|
79
|
+
removeItem(key: string): Promise<void>;
|
|
80
|
+
clear(): Promise<void>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Create a cached engine client with persistent storage
|
|
85
|
+
*/
|
|
86
|
+
export function createCachedEngineClient(
|
|
87
|
+
storage: StorageAdapter,
|
|
88
|
+
options?: {
|
|
89
|
+
baseUrl?: string;
|
|
90
|
+
clientId?: string;
|
|
91
|
+
secretKey?: string;
|
|
92
|
+
}
|
|
93
|
+
) {
|
|
94
|
+
const client = createOneEngineClient(options);
|
|
95
|
+
|
|
96
|
+
// Wrapper with token persistence
|
|
97
|
+
return {
|
|
98
|
+
...client,
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Initialize with stored token
|
|
102
|
+
*/
|
|
103
|
+
async initialize(): Promise<boolean> {
|
|
104
|
+
const token = await storage.getItem('one_access_token');
|
|
105
|
+
if (token) {
|
|
106
|
+
client.setAccessToken(token);
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
return false;
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Login and persist token
|
|
114
|
+
*/
|
|
115
|
+
async login(email: string, otp: string) {
|
|
116
|
+
const result = await client.verifyEmailOtp(email, otp);
|
|
117
|
+
if (result.success && result.data?.accessToken) {
|
|
118
|
+
await storage.setItem('one_access_token', result.data.accessToken);
|
|
119
|
+
if (result.data.refreshToken) {
|
|
120
|
+
await storage.setItem('one_refresh_token', result.data.refreshToken);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return result;
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Logout and clear stored tokens
|
|
128
|
+
*/
|
|
129
|
+
async logout() {
|
|
130
|
+
await client.signOut();
|
|
131
|
+
await storage.removeItem('one_access_token');
|
|
132
|
+
await storage.removeItem('one_refresh_token');
|
|
133
|
+
client.clearAccessToken();
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Refresh token from storage
|
|
138
|
+
*/
|
|
139
|
+
async refreshFromStorage() {
|
|
140
|
+
const refreshToken = await storage.getItem('one_refresh_token');
|
|
141
|
+
if (refreshToken) {
|
|
142
|
+
const result = await client.refreshToken(refreshToken);
|
|
143
|
+
if (result.success && result.data?.accessToken) {
|
|
144
|
+
await storage.setItem('one_access_token', result.data.accessToken);
|
|
145
|
+
if (result.data.refreshToken) {
|
|
146
|
+
await storage.setItem('one_refresh_token', result.data.refreshToken);
|
|
147
|
+
}
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return false;
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
// Expose underlying client
|
|
155
|
+
getClient: () => client,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Deep linking handler for ONE SDK
|
|
161
|
+
*/
|
|
162
|
+
export interface DeepLinkHandler {
|
|
163
|
+
/**
|
|
164
|
+
* Parse deep link URL
|
|
165
|
+
*/
|
|
166
|
+
parse(url: string): {
|
|
167
|
+
type: 'onramp_callback' | 'wallet_connect' | 'payment' | 'unknown';
|
|
168
|
+
params: Record<string, string>;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Generate deep link URL
|
|
173
|
+
*/
|
|
174
|
+
generate(type: string, params: Record<string, string>): string;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function createDeepLinkHandler(scheme: string = 'onewallet'): DeepLinkHandler {
|
|
178
|
+
return {
|
|
179
|
+
parse(url: string) {
|
|
180
|
+
try {
|
|
181
|
+
const urlObj = new URL(url);
|
|
182
|
+
const path = urlObj.pathname.replace(/^\//, '');
|
|
183
|
+
const params: Record<string, string> = {};
|
|
184
|
+
|
|
185
|
+
urlObj.searchParams.forEach((value, key) => {
|
|
186
|
+
params[key] = value;
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
let type: 'onramp_callback' | 'wallet_connect' | 'payment' | 'unknown' = 'unknown';
|
|
190
|
+
|
|
191
|
+
if (path.includes('onramp') || path.includes('callback')) {
|
|
192
|
+
type = 'onramp_callback';
|
|
193
|
+
} else if (path.includes('wc') || path.includes('walletconnect')) {
|
|
194
|
+
type = 'wallet_connect';
|
|
195
|
+
} else if (path.includes('pay') || path.includes('payment')) {
|
|
196
|
+
type = 'payment';
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return { type, params };
|
|
200
|
+
} catch {
|
|
201
|
+
return { type: 'unknown', params: {} };
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
generate(type: string, params: Record<string, string>): string {
|
|
206
|
+
const searchParams = new URLSearchParams(params);
|
|
207
|
+
return `${scheme}://${type}?${searchParams.toString()}`;
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Biometric authentication helper types
|
|
214
|
+
*/
|
|
215
|
+
export interface BiometricConfig {
|
|
216
|
+
promptMessage: string;
|
|
217
|
+
cancelButtonText?: string;
|
|
218
|
+
fallbackLabel?: string;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export interface BiometricResult {
|
|
222
|
+
success: boolean;
|
|
223
|
+
error?: string;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* QR Code scanning result types
|
|
228
|
+
*/
|
|
229
|
+
export interface QRScanResult {
|
|
230
|
+
type: 'address' | 'wallet_connect' | 'payment_request' | 'unknown';
|
|
231
|
+
data: string;
|
|
232
|
+
parsed?: {
|
|
233
|
+
address?: string;
|
|
234
|
+
chainId?: number;
|
|
235
|
+
amount?: string;
|
|
236
|
+
token?: string;
|
|
237
|
+
message?: string;
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Parse QR code data for wallet operations
|
|
243
|
+
*/
|
|
244
|
+
export function parseQRCode(data: string): QRScanResult {
|
|
245
|
+
// Ethereum address
|
|
246
|
+
if (/^0x[a-fA-F0-9]{40}$/.test(data)) {
|
|
247
|
+
return {
|
|
248
|
+
type: 'address',
|
|
249
|
+
data,
|
|
250
|
+
parsed: { address: data },
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// EIP-681 payment request (ethereum:0x...?value=...&token=...)
|
|
255
|
+
if (data.startsWith('ethereum:')) {
|
|
256
|
+
const match = data.match(/^ethereum:(0x[a-fA-F0-9]{40})(?:@(\d+))?(?:\?(.*))?$/);
|
|
257
|
+
if (match) {
|
|
258
|
+
const [, address, chainId, queryString] = match;
|
|
259
|
+
const params = new URLSearchParams(queryString || '');
|
|
260
|
+
return {
|
|
261
|
+
type: 'payment_request',
|
|
262
|
+
data,
|
|
263
|
+
parsed: {
|
|
264
|
+
address,
|
|
265
|
+
chainId: chainId ? parseInt(chainId) : undefined,
|
|
266
|
+
amount: params.get('value') || undefined,
|
|
267
|
+
token: params.get('token') || undefined,
|
|
268
|
+
message: params.get('message') || undefined,
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// WalletConnect URI
|
|
275
|
+
if (data.startsWith('wc:')) {
|
|
276
|
+
return {
|
|
277
|
+
type: 'wallet_connect',
|
|
278
|
+
data,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return { type: 'unknown', data };
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Format crypto amount for display on mobile
|
|
287
|
+
*/
|
|
288
|
+
export function formatCryptoAmount(
|
|
289
|
+
amount: number | string,
|
|
290
|
+
symbol: string,
|
|
291
|
+
options: {
|
|
292
|
+
maxDecimals?: number;
|
|
293
|
+
showSymbol?: boolean;
|
|
294
|
+
compact?: boolean;
|
|
295
|
+
} = {}
|
|
296
|
+
): string {
|
|
297
|
+
const { maxDecimals = 6, showSymbol = true, compact = false } = options;
|
|
298
|
+
const num = typeof amount === 'string' ? parseFloat(amount) : amount;
|
|
299
|
+
|
|
300
|
+
if (isNaN(num)) return showSymbol ? `0 ${symbol}` : '0';
|
|
301
|
+
|
|
302
|
+
let formatted: string;
|
|
303
|
+
|
|
304
|
+
if (compact && num >= 1000000) {
|
|
305
|
+
formatted = (num / 1000000).toFixed(2) + 'M';
|
|
306
|
+
} else if (compact && num >= 1000) {
|
|
307
|
+
formatted = (num / 1000).toFixed(2) + 'K';
|
|
308
|
+
} else if (num < 0.000001 && num > 0) {
|
|
309
|
+
formatted = '<0.000001';
|
|
310
|
+
} else {
|
|
311
|
+
const decimals = num < 1 ? maxDecimals : Math.min(maxDecimals, 4);
|
|
312
|
+
formatted = num.toFixed(decimals).replace(/\.?0+$/, '');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return showSymbol ? `${formatted} ${symbol}` : formatted;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Generate share content for transactions
|
|
320
|
+
*/
|
|
321
|
+
export function generateShareContent(params: {
|
|
322
|
+
type: 'receive' | 'payment_request' | 'transaction';
|
|
323
|
+
address?: string;
|
|
324
|
+
amount?: string;
|
|
325
|
+
token?: string;
|
|
326
|
+
txHash?: string;
|
|
327
|
+
chainId?: number;
|
|
328
|
+
}): { title: string; message: string; url?: string } {
|
|
329
|
+
const { type, address, amount, token, txHash, chainId } = params;
|
|
330
|
+
|
|
331
|
+
switch (type) {
|
|
332
|
+
case 'receive':
|
|
333
|
+
return {
|
|
334
|
+
title: 'My Wallet Address',
|
|
335
|
+
message: `Send ${token || 'crypto'} to my wallet:\n${address}`,
|
|
336
|
+
url: address ? `ethereum:${address}${chainId ? `@${chainId}` : ''}` : undefined,
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
case 'payment_request':
|
|
340
|
+
return {
|
|
341
|
+
title: 'Payment Request',
|
|
342
|
+
message: `Please send ${amount} ${token} to:\n${address}`,
|
|
343
|
+
url: `ethereum:${address}${chainId ? `@${chainId}` : ''}?value=${amount}${token ? `&token=${token}` : ''}`,
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
case 'transaction':
|
|
347
|
+
return {
|
|
348
|
+
title: 'Transaction Sent',
|
|
349
|
+
message: `Transaction confirmed!\nHash: ${txHash}`,
|
|
350
|
+
url: txHash ? getExplorerUrl(chainId || 1, txHash, 'tx') : undefined,
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
default:
|
|
354
|
+
return { title: '', message: '' };
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Get block explorer URL
|
|
360
|
+
*/
|
|
361
|
+
export function getExplorerUrl(
|
|
362
|
+
chainId: number,
|
|
363
|
+
hash: string,
|
|
364
|
+
type: 'tx' | 'address' | 'token' = 'tx'
|
|
365
|
+
): string {
|
|
366
|
+
const explorers: Record<number, string> = {
|
|
367
|
+
1: 'https://etherscan.io',
|
|
368
|
+
137: 'https://polygonscan.com',
|
|
369
|
+
42161: 'https://arbiscan.io',
|
|
370
|
+
10: 'https://optimistic.etherscan.io',
|
|
371
|
+
8453: 'https://basescan.org',
|
|
372
|
+
56: 'https://bscscan.com',
|
|
373
|
+
43114: 'https://snowtrace.io',
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
const baseUrl = explorers[chainId] || explorers[1];
|
|
377
|
+
return `${baseUrl}/${type}/${hash}`;
|
|
378
|
+
}
|