amped-defi 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/README.md +757 -0
- package/dist/__mocks__/@sodax/sdk.d.ts +24 -0
- package/dist/__mocks__/@sodax/sdk.d.ts.map +1 -0
- package/dist/__mocks__/@sodax/sdk.js +24 -0
- package/dist/__mocks__/@sodax/sdk.js.map +1 -0
- package/dist/__tests__/setup.d.ts +4 -0
- package/dist/__tests__/setup.d.ts.map +1 -0
- package/dist/__tests__/setup.js +32 -0
- package/dist/__tests__/setup.js.map +1 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +281 -0
- package/dist/index.js.map +1 -0
- package/dist/policy/policyEngine.d.ts +119 -0
- package/dist/policy/policyEngine.d.ts.map +1 -0
- package/dist/policy/policyEngine.js +322 -0
- package/dist/policy/policyEngine.js.map +1 -0
- package/dist/providers/spokeProviderFactory.d.ts +38 -0
- package/dist/providers/spokeProviderFactory.d.ts.map +1 -0
- package/dist/providers/spokeProviderFactory.js +212 -0
- package/dist/providers/spokeProviderFactory.js.map +1 -0
- package/dist/sodax/client.d.ts +34 -0
- package/dist/sodax/client.d.ts.map +1 -0
- package/dist/sodax/client.js +99 -0
- package/dist/sodax/client.js.map +1 -0
- package/dist/tools/bridge.d.ts +105 -0
- package/dist/tools/bridge.d.ts.map +1 -0
- package/dist/tools/bridge.js +334 -0
- package/dist/tools/bridge.js.map +1 -0
- package/dist/tools/discovery.d.ts +141 -0
- package/dist/tools/discovery.d.ts.map +1 -0
- package/dist/tools/discovery.js +777 -0
- package/dist/tools/discovery.js.map +1 -0
- package/dist/tools/moneyMarket.d.ts +227 -0
- package/dist/tools/moneyMarket.d.ts.map +1 -0
- package/dist/tools/moneyMarket.js +867 -0
- package/dist/tools/moneyMarket.js.map +1 -0
- package/dist/tools/portfolio.d.ts +43 -0
- package/dist/tools/portfolio.d.ts.map +1 -0
- package/dist/tools/portfolio.js +538 -0
- package/dist/tools/portfolio.js.map +1 -0
- package/dist/tools/swap.d.ts +71 -0
- package/dist/tools/swap.d.ts.map +1 -0
- package/dist/tools/swap.js +762 -0
- package/dist/tools/swap.js.map +1 -0
- package/dist/tools/walletManagement.d.ts +80 -0
- package/dist/tools/walletManagement.d.ts.map +1 -0
- package/dist/tools/walletManagement.js +289 -0
- package/dist/tools/walletManagement.js.map +1 -0
- package/dist/types.d.ts +205 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/errorUtils.d.ts +2 -0
- package/dist/utils/errorUtils.d.ts.map +1 -0
- package/dist/utils/errorUtils.js +19 -0
- package/dist/utils/errorUtils.js.map +1 -0
- package/dist/utils/errors.d.ts +144 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +310 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/positionAggregator.d.ts +122 -0
- package/dist/utils/positionAggregator.d.ts.map +1 -0
- package/dist/utils/positionAggregator.js +377 -0
- package/dist/utils/positionAggregator.js.map +1 -0
- package/dist/utils/priceService.d.ts +45 -0
- package/dist/utils/priceService.d.ts.map +1 -0
- package/dist/utils/priceService.js +108 -0
- package/dist/utils/priceService.js.map +1 -0
- package/dist/utils/sodaxApi.d.ts +92 -0
- package/dist/utils/sodaxApi.d.ts.map +1 -0
- package/dist/utils/sodaxApi.js +143 -0
- package/dist/utils/sodaxApi.js.map +1 -0
- package/dist/utils/tokenResolver.d.ts +54 -0
- package/dist/utils/tokenResolver.d.ts.map +1 -0
- package/dist/utils/tokenResolver.js +252 -0
- package/dist/utils/tokenResolver.js.map +1 -0
- package/dist/wallet/backendConfig.d.ts +37 -0
- package/dist/wallet/backendConfig.d.ts.map +1 -0
- package/dist/wallet/backendConfig.js +125 -0
- package/dist/wallet/backendConfig.js.map +1 -0
- package/dist/wallet/backends/BankrBackend.d.ts +73 -0
- package/dist/wallet/backends/BankrBackend.d.ts.map +1 -0
- package/dist/wallet/backends/BankrBackend.js +315 -0
- package/dist/wallet/backends/BankrBackend.js.map +1 -0
- package/dist/wallet/backends/BankrWalletProvider.d.ts +75 -0
- package/dist/wallet/backends/BankrWalletProvider.d.ts.map +1 -0
- package/dist/wallet/backends/BankrWalletProvider.js +243 -0
- package/dist/wallet/backends/BankrWalletProvider.js.map +1 -0
- package/dist/wallet/backends/EnvBackend.d.ts +50 -0
- package/dist/wallet/backends/EnvBackend.d.ts.map +1 -0
- package/dist/wallet/backends/EnvBackend.js +114 -0
- package/dist/wallet/backends/EnvBackend.js.map +1 -0
- package/dist/wallet/backends/EvmWalletSkillBackend.d.ts +40 -0
- package/dist/wallet/backends/EvmWalletSkillBackend.d.ts.map +1 -0
- package/dist/wallet/backends/EvmWalletSkillBackend.js +81 -0
- package/dist/wallet/backends/EvmWalletSkillBackend.js.map +1 -0
- package/dist/wallet/backends/index.d.ts +10 -0
- package/dist/wallet/backends/index.d.ts.map +1 -0
- package/dist/wallet/backends/index.js +10 -0
- package/dist/wallet/backends/index.js.map +1 -0
- package/dist/wallet/index.d.ts +9 -0
- package/dist/wallet/index.d.ts.map +1 -0
- package/dist/wallet/index.js +12 -0
- package/dist/wallet/index.js.map +1 -0
- package/dist/wallet/providers/AmpedWalletProvider.d.ts +107 -0
- package/dist/wallet/providers/AmpedWalletProvider.d.ts.map +1 -0
- package/dist/wallet/providers/AmpedWalletProvider.js +208 -0
- package/dist/wallet/providers/AmpedWalletProvider.js.map +1 -0
- package/dist/wallet/providers/BankrBackend.d.ts +105 -0
- package/dist/wallet/providers/BankrBackend.d.ts.map +1 -0
- package/dist/wallet/providers/BankrBackend.js +327 -0
- package/dist/wallet/providers/BankrBackend.js.map +1 -0
- package/dist/wallet/providers/LocalKeyBackend.d.ts +62 -0
- package/dist/wallet/providers/LocalKeyBackend.d.ts.map +1 -0
- package/dist/wallet/providers/LocalKeyBackend.js +152 -0
- package/dist/wallet/providers/LocalKeyBackend.js.map +1 -0
- package/dist/wallet/providers/chainConfig.d.ts +209 -0
- package/dist/wallet/providers/chainConfig.d.ts.map +1 -0
- package/dist/wallet/providers/chainConfig.js +175 -0
- package/dist/wallet/providers/chainConfig.js.map +1 -0
- package/dist/wallet/providers/index.d.ts +30 -0
- package/dist/wallet/providers/index.d.ts.map +1 -0
- package/dist/wallet/providers/index.js +32 -0
- package/dist/wallet/providers/index.js.map +1 -0
- package/dist/wallet/providers/types.d.ts +156 -0
- package/dist/wallet/providers/types.d.ts.map +1 -0
- package/dist/wallet/providers/types.js +11 -0
- package/dist/wallet/providers/types.js.map +1 -0
- package/dist/wallet/skillWalletAdapter.d.ts +96 -0
- package/dist/wallet/skillWalletAdapter.d.ts.map +1 -0
- package/dist/wallet/skillWalletAdapter.js +280 -0
- package/dist/wallet/skillWalletAdapter.js.map +1 -0
- package/dist/wallet/types.d.ts +134 -0
- package/dist/wallet/types.d.ts.map +1 -0
- package/dist/wallet/types.js +138 -0
- package/dist/wallet/types.js.map +1 -0
- package/dist/wallet/walletManager.d.ts +111 -0
- package/dist/wallet/walletManager.d.ts.map +1 -0
- package/dist/wallet/walletManager.js +476 -0
- package/dist/wallet/walletManager.js.map +1 -0
- package/dist/wallet/walletRegistry.d.ts +95 -0
- package/dist/wallet/walletRegistry.d.ts.map +1 -0
- package/dist/wallet/walletRegistry.js +184 -0
- package/dist/wallet/walletRegistry.js.map +1 -0
- package/index.js +2 -0
- package/openclaw.plugin.json +37 -0
- package/package.json +69 -0
- package/src/__mocks__/@sodax/sdk.ts +28 -0
- package/src/__tests__/errors.test.ts +238 -0
- package/src/__tests__/policyEngine.test.ts +354 -0
- package/src/__tests__/positionAggregator.test.ts +271 -0
- package/src/__tests__/setup.ts +35 -0
- package/src/__tests__/sodaxApi.test.ts +203 -0
- package/src/__tests__/walletRegistry.test.ts +155 -0
- package/src/index.ts +376 -0
- package/src/policy/policyEngine.ts +389 -0
- package/src/providers/spokeProviderFactory.ts +283 -0
- package/src/sodax/client.ts +113 -0
- package/src/tools/bridge.ts +425 -0
- package/src/tools/discovery.ts +989 -0
- package/src/tools/moneyMarket.ts +1265 -0
- package/src/tools/portfolio.ts +697 -0
- package/src/tools/swap.ts +926 -0
- package/src/tools/walletManagement.ts +359 -0
- package/src/types.ts +228 -0
- package/src/utils/errorUtils.ts +16 -0
- package/src/utils/errors.ts +396 -0
- package/src/utils/positionAggregator.ts +559 -0
- package/src/utils/priceService.ts +153 -0
- package/src/utils/sodaxApi.ts +261 -0
- package/src/utils/tokenResolver.ts +286 -0
- package/src/wallet/backendConfig.ts +151 -0
- package/src/wallet/backends/BankrBackend.ts +399 -0
- package/src/wallet/backends/BankrWalletProvider.ts +329 -0
- package/src/wallet/backends/EnvBackend.ts +149 -0
- package/src/wallet/backends/EvmWalletSkillBackend.ts +110 -0
- package/src/wallet/backends/index.ts +10 -0
- package/src/wallet/index.ts +14 -0
- package/src/wallet/providers/AmpedWalletProvider.ts +267 -0
- package/src/wallet/providers/BankrBackend.ts +407 -0
- package/src/wallet/providers/LocalKeyBackend.ts +184 -0
- package/src/wallet/providers/chainConfig.ts +194 -0
- package/src/wallet/providers/index.ts +62 -0
- package/src/wallet/providers/types.ts +186 -0
- package/src/wallet/skillWalletAdapter.ts +335 -0
- package/src/wallet/types.ts +248 -0
- package/src/wallet/walletManager.ts +561 -0
- package/src/wallet/walletRegistry.ts +216 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SODAX API Client
|
|
3
|
+
*
|
|
4
|
+
* Provides access to SODAX backend API endpoints for querying intents,
|
|
5
|
+
* user history, and other off-chain data.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { ErrorCode, AmpedDefiError } from './errors';
|
|
9
|
+
|
|
10
|
+
const DEFAULT_BASE_URL = 'https://canary-api.sodax.com';
|
|
11
|
+
const API_VERSION = 'v1';
|
|
12
|
+
|
|
13
|
+
export interface SodaxApiConfig {
|
|
14
|
+
baseUrl?: string;
|
|
15
|
+
apiKey?: string;
|
|
16
|
+
timeoutMs?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface PaginationParams {
|
|
20
|
+
offset?: number;
|
|
21
|
+
limit?: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface PaginatedResponse<T> {
|
|
25
|
+
items: T[];
|
|
26
|
+
total: number;
|
|
27
|
+
offset: number;
|
|
28
|
+
limit: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface IntentState {
|
|
32
|
+
exists: boolean;
|
|
33
|
+
remainingInput: string;
|
|
34
|
+
receivedOutput: string;
|
|
35
|
+
pendingPayment: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface IntentEvent {
|
|
39
|
+
eventType: string;
|
|
40
|
+
txHash: string;
|
|
41
|
+
logIndex: number;
|
|
42
|
+
blockNumber: number;
|
|
43
|
+
intentState: IntentState;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface IntentDetails {
|
|
47
|
+
intentId: string;
|
|
48
|
+
creator: string;
|
|
49
|
+
inputToken: string;
|
|
50
|
+
outputToken: string;
|
|
51
|
+
inputAmount: string;
|
|
52
|
+
minOutputAmount: string;
|
|
53
|
+
deadline: string;
|
|
54
|
+
allowPartialFill: boolean;
|
|
55
|
+
srcChain: number;
|
|
56
|
+
dstChain: number;
|
|
57
|
+
srcAddress: string;
|
|
58
|
+
dstAddress: string;
|
|
59
|
+
solver: string;
|
|
60
|
+
data: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface UserIntent {
|
|
64
|
+
intentHash: string;
|
|
65
|
+
txHash: string;
|
|
66
|
+
logIndex: number;
|
|
67
|
+
chainId: number;
|
|
68
|
+
blockNumber: number;
|
|
69
|
+
open: boolean;
|
|
70
|
+
intent: IntentDetails;
|
|
71
|
+
events: IntentEvent[];
|
|
72
|
+
createdAt: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface UserIntentFilters {
|
|
76
|
+
open?: boolean;
|
|
77
|
+
srcChain?: number;
|
|
78
|
+
dstChain?: number;
|
|
79
|
+
inputToken?: string;
|
|
80
|
+
outputToken?: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export class SodaxApiClient {
|
|
84
|
+
private baseUrl: string;
|
|
85
|
+
private apiKey?: string;
|
|
86
|
+
private timeoutMs: number;
|
|
87
|
+
|
|
88
|
+
constructor(config: SodaxApiConfig = {}) {
|
|
89
|
+
this.baseUrl = config.baseUrl || process.env.SODAX_API_URL || DEFAULT_BASE_URL;
|
|
90
|
+
this.apiKey = config.apiKey || process.env.SODAX_API_KEY;
|
|
91
|
+
this.timeoutMs = config.timeoutMs || 30000;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Get intent by intentHash
|
|
96
|
+
* Most reliable lookup method - works for all intents
|
|
97
|
+
*/
|
|
98
|
+
async getIntentByHash(intentHash: string): Promise<UserIntent | null> {
|
|
99
|
+
const normalizedHash = intentHash.startsWith('0x') ? intentHash : `0x${intentHash}`;
|
|
100
|
+
const url = `${this.baseUrl}/${API_VERSION}/be/intent/${normalizedHash}`;
|
|
101
|
+
|
|
102
|
+
console.log('[sodaxApi] Fetching intent by hash:', { intentHash: normalizedHash });
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
const response = await this.fetchWithTimeout(url);
|
|
106
|
+
|
|
107
|
+
if (!response.ok) {
|
|
108
|
+
if (response.status === 404) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
const errorText = await response.text();
|
|
112
|
+
throw new AmpedDefiError(
|
|
113
|
+
ErrorCode.UNKNOWN_ERROR,
|
|
114
|
+
`SODAX API error: ${response.status} ${errorText}`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return await response.json() as UserIntent;
|
|
119
|
+
} catch (error) {
|
|
120
|
+
if (error instanceof AmpedDefiError) throw error;
|
|
121
|
+
throw new AmpedDefiError(
|
|
122
|
+
ErrorCode.UNKNOWN_ERROR,
|
|
123
|
+
`Failed to fetch intent by hash: ${error instanceof Error ? error.message : String(error)}`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get intent by transaction hash
|
|
130
|
+
* NOTE: This expects the HUB chain (Sonic) transaction hash, NOT spoke chain tx
|
|
131
|
+
*/
|
|
132
|
+
async getIntentByTxHash(txHash: string): Promise<UserIntent | null> {
|
|
133
|
+
const normalizedHash = txHash.startsWith('0x') ? txHash : `0x${txHash}`;
|
|
134
|
+
const url = `${this.baseUrl}/${API_VERSION}/be/intent/tx/${normalizedHash}`;
|
|
135
|
+
|
|
136
|
+
console.log('[sodaxApi] Fetching intent by txHash:', { txHash: normalizedHash });
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
const response = await this.fetchWithTimeout(url);
|
|
140
|
+
|
|
141
|
+
if (!response.ok) {
|
|
142
|
+
if (response.status === 404) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
const errorText = await response.text();
|
|
146
|
+
throw new AmpedDefiError(
|
|
147
|
+
ErrorCode.UNKNOWN_ERROR,
|
|
148
|
+
`SODAX API error: ${response.status} ${errorText}`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return await response.json() as UserIntent;
|
|
153
|
+
} catch (error) {
|
|
154
|
+
if (error instanceof AmpedDefiError) throw error;
|
|
155
|
+
throw new AmpedDefiError(
|
|
156
|
+
ErrorCode.UNKNOWN_ERROR,
|
|
157
|
+
`Failed to fetch intent by txHash: ${error instanceof Error ? error.message : String(error)}`
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async getUserIntents(
|
|
163
|
+
userAddress: string,
|
|
164
|
+
pagination: PaginationParams = {},
|
|
165
|
+
filters?: UserIntentFilters
|
|
166
|
+
): Promise<PaginatedResponse<UserIntent>> {
|
|
167
|
+
if (!this.isValidAddress(userAddress)) {
|
|
168
|
+
throw new AmpedDefiError(
|
|
169
|
+
ErrorCode.WALLET_INVALID_ADDRESS,
|
|
170
|
+
`Invalid user address: ${userAddress}`
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const normalizedAddress = userAddress.toLowerCase();
|
|
175
|
+
const queryParams = new URLSearchParams();
|
|
176
|
+
|
|
177
|
+
if (pagination.offset !== undefined) {
|
|
178
|
+
queryParams.set('offset', pagination.offset.toString());
|
|
179
|
+
}
|
|
180
|
+
if (pagination.limit !== undefined) {
|
|
181
|
+
queryParams.set('limit', pagination.limit.toString());
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (filters) {
|
|
185
|
+
if (filters.open !== undefined) queryParams.set('open', filters.open.toString());
|
|
186
|
+
if (filters.srcChain !== undefined) queryParams.set('srcChain', filters.srcChain.toString());
|
|
187
|
+
if (filters.dstChain !== undefined) queryParams.set('dstChain', filters.dstChain.toString());
|
|
188
|
+
if (filters.inputToken) queryParams.set('inputToken', filters.inputToken.toLowerCase());
|
|
189
|
+
if (filters.outputToken) queryParams.set('outputToken', filters.outputToken.toLowerCase());
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const queryString = queryParams.toString();
|
|
193
|
+
const url = `${this.baseUrl}/${API_VERSION}/be/intent/user/${normalizedAddress}${queryString ? `?${queryString}` : ''}`;
|
|
194
|
+
|
|
195
|
+
console.log('[sodaxApi] Fetching user intents:', { userAddress: normalizedAddress });
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
const response = await this.fetchWithTimeout(url);
|
|
199
|
+
|
|
200
|
+
if (!response.ok) {
|
|
201
|
+
const errorText = await response.text();
|
|
202
|
+
throw new AmpedDefiError(
|
|
203
|
+
ErrorCode.UNKNOWN_ERROR,
|
|
204
|
+
`SODAX API error: ${response.status} ${errorText}`
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return await response.json() as PaginatedResponse<UserIntent>;
|
|
209
|
+
} catch (error) {
|
|
210
|
+
if (error instanceof AmpedDefiError) throw error;
|
|
211
|
+
throw new AmpedDefiError(
|
|
212
|
+
ErrorCode.UNKNOWN_ERROR,
|
|
213
|
+
`Failed to fetch user intents: ${error instanceof Error ? error.message : String(error)}`
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async getOpenIntents(
|
|
219
|
+
userAddress: string,
|
|
220
|
+
pagination: PaginationParams = {}
|
|
221
|
+
): Promise<PaginatedResponse<UserIntent>> {
|
|
222
|
+
return this.getUserIntents(userAddress, pagination, { open: true });
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async getIntentHistory(
|
|
226
|
+
userAddress: string,
|
|
227
|
+
pagination: PaginationParams = {}
|
|
228
|
+
): Promise<PaginatedResponse<UserIntent>> {
|
|
229
|
+
return this.getUserIntents(userAddress, pagination, { open: false });
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
private async fetchWithTimeout(url: string): Promise<Response> {
|
|
233
|
+
const controller = new AbortController();
|
|
234
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
235
|
+
|
|
236
|
+
try {
|
|
237
|
+
const headers: Record<string, string> = { 'Accept': 'application/json' };
|
|
238
|
+
if (this.apiKey) headers['Authorization'] = `Bearer ${this.apiKey}`;
|
|
239
|
+
return await fetch(url, { signal: controller.signal, headers });
|
|
240
|
+
} finally {
|
|
241
|
+
clearTimeout(timeoutId);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
private isValidAddress(address: string): boolean {
|
|
246
|
+
return /^0x[a-fA-F0-9]{40}$/.test(address);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
let apiClient: SodaxApiClient | null = null;
|
|
251
|
+
|
|
252
|
+
export function getSodaxApiClient(config?: SodaxApiConfig): SodaxApiClient {
|
|
253
|
+
if (!apiClient) {
|
|
254
|
+
apiClient = new SodaxApiClient(config);
|
|
255
|
+
}
|
|
256
|
+
return apiClient;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export function resetSodaxApiClient(): void {
|
|
260
|
+
apiClient = null;
|
|
261
|
+
}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token Resolution Utility
|
|
3
|
+
*
|
|
4
|
+
* Resolves token symbols to addresses using the SODAX SDK config service.
|
|
5
|
+
* Supports case-insensitive symbol lookup with caching.
|
|
6
|
+
* Handles both EVM (0x) and Solana (base58) address formats.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Token } from '@sodax/types';
|
|
10
|
+
import { getSodaxClient } from '../sodax/client';
|
|
11
|
+
import { toSodaxChainId } from '../wallet/types';
|
|
12
|
+
|
|
13
|
+
// Cache tokens per chain to avoid repeated lookups
|
|
14
|
+
const tokenCache = new Map<string, Token[]>();
|
|
15
|
+
|
|
16
|
+
// Native token addresses
|
|
17
|
+
const EVM_NATIVE_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
18
|
+
const SOLANA_NATIVE_ADDRESS = '11111111111111111111111111111111';
|
|
19
|
+
|
|
20
|
+
// Native token configs per chain (18 decimals for all EVM chains, 9 for Solana)
|
|
21
|
+
const NATIVE_TOKENS: Record<string, { symbol: string; name: string; decimals: number; address: string }> = {
|
|
22
|
+
sonic: { symbol: 'S', name: 'Sonic', decimals: 18, address: EVM_NATIVE_ADDRESS },
|
|
23
|
+
ethereum: { symbol: 'ETH', name: 'Ether', decimals: 18, address: EVM_NATIVE_ADDRESS },
|
|
24
|
+
'0xa4b1.arbitrum': { symbol: 'ETH', name: 'Ether', decimals: 18, address: EVM_NATIVE_ADDRESS },
|
|
25
|
+
'0x2105.base': { symbol: 'ETH', name: 'Ether', decimals: 18, address: EVM_NATIVE_ADDRESS },
|
|
26
|
+
'0xa.optimism': { symbol: 'ETH', name: 'Ether', decimals: 18, address: EVM_NATIVE_ADDRESS },
|
|
27
|
+
'0x38.bsc': { symbol: 'BNB', name: 'BNB', decimals: 18, address: EVM_NATIVE_ADDRESS },
|
|
28
|
+
'0x89.polygon': { symbol: 'POL', name: 'POL', decimals: 18, address: EVM_NATIVE_ADDRESS },
|
|
29
|
+
'0xa86a.avax': { symbol: 'AVAX', name: 'Avalanche', decimals: 18, address: EVM_NATIVE_ADDRESS },
|
|
30
|
+
hyper: { symbol: 'HYPE', name: 'Hyperliquid', decimals: 18, address: EVM_NATIVE_ADDRESS },
|
|
31
|
+
lightlink: { symbol: 'ETH', name: 'Ether', decimals: 18, address: EVM_NATIVE_ADDRESS },
|
|
32
|
+
solana: { symbol: 'SOL', name: 'Solana', decimals: 9, address: SOLANA_NATIVE_ADDRESS },
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// Fallback token list for common chains when SDK config is unavailable
|
|
36
|
+
const FALLBACK_TOKENS: Record<string, { address: string; symbol: string; name: string; decimals: number }[]> = {
|
|
37
|
+
'0x2105.base': [
|
|
38
|
+
{ address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', symbol: 'USDC', name: 'USD Coin', decimals: 6 },
|
|
39
|
+
{ address: '0x4200000000000000000000000000000000000006', symbol: 'WETH', name: 'Wrapped Ether', decimals: 18 },
|
|
40
|
+
{ address: EVM_NATIVE_ADDRESS, symbol: 'ETH', name: 'Ether', decimals: 18 },
|
|
41
|
+
],
|
|
42
|
+
'ethereum': [
|
|
43
|
+
{ address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', symbol: 'USDC', name: 'USD Coin', decimals: 6 },
|
|
44
|
+
{ address: '0xdac17f958d2ee523a2206206994597c13d831ec7', symbol: 'USDT', name: 'Tether USD', decimals: 6 },
|
|
45
|
+
{ address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', symbol: 'DAI', name: 'Dai Stablecoin', decimals: 18 },
|
|
46
|
+
{ address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', symbol: 'WETH', name: 'Wrapped Ether', decimals: 18 },
|
|
47
|
+
{ address: EVM_NATIVE_ADDRESS, symbol: 'ETH', name: 'Ether', decimals: 18 },
|
|
48
|
+
],
|
|
49
|
+
'0xa4b1.arbitrum': [
|
|
50
|
+
{ address: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', symbol: 'USDC', name: 'USD Coin', decimals: 6 },
|
|
51
|
+
{ address: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9', symbol: 'USDT', name: 'Tether USD', decimals: 6 },
|
|
52
|
+
{ address: EVM_NATIVE_ADDRESS, symbol: 'ETH', name: 'Ether', decimals: 18 },
|
|
53
|
+
],
|
|
54
|
+
'sonic': [
|
|
55
|
+
{ address: '0x29219dd400f2Bf60E5a23d13Be72B486D4038894', symbol: 'USDC', name: 'USD Coin', decimals: 6 },
|
|
56
|
+
{ address: '0x6047828dc181963ba44974801FF68e538dA5eaF9', symbol: 'USDT', name: 'Tether USD', decimals: 6 },
|
|
57
|
+
{ address: EVM_NATIVE_ADDRESS, symbol: 'S', name: 'Sonic', decimals: 18 },
|
|
58
|
+
],
|
|
59
|
+
'solana': [
|
|
60
|
+
{ address: SOLANA_NATIVE_ADDRESS, symbol: 'SOL', name: 'Solana', decimals: 9 },
|
|
61
|
+
{ address: '3rSPCLNEF7Quw4wX8S1NyKivELoyij8eYA2gJwBgt4V5', symbol: 'bnUSD', name: 'bnUSD', decimals: 9 },
|
|
62
|
+
{ address: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', symbol: 'USDC', name: 'USD Coin', decimals: 6 },
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// Chain type detection
|
|
67
|
+
const SOLANA_CHAINS = new Set(['solana']);
|
|
68
|
+
|
|
69
|
+
function isSolanaChain(chainId: string): boolean {
|
|
70
|
+
return SOLANA_CHAINS.has(chainId.toLowerCase());
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Check if an address is a native token for the given chain
|
|
75
|
+
*/
|
|
76
|
+
function isNativeToken(address: string, chainId?: string): boolean {
|
|
77
|
+
const addrLower = address.toLowerCase();
|
|
78
|
+
if (chainId && isSolanaChain(chainId)) {
|
|
79
|
+
return addrLower === SOLANA_NATIVE_ADDRESS.toLowerCase();
|
|
80
|
+
}
|
|
81
|
+
return addrLower === EVM_NATIVE_ADDRESS;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Get native token info for a chain
|
|
86
|
+
*/
|
|
87
|
+
function getNativeTokenInfo(chainId: string): { address: string; symbol: string; name: string; decimals: number } | null {
|
|
88
|
+
const native = NATIVE_TOKENS[chainId];
|
|
89
|
+
if (!native) return null;
|
|
90
|
+
return { ...native };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Check if a string is a valid EVM address (0x format)
|
|
95
|
+
*/
|
|
96
|
+
function isEvmAddress(value: string): boolean {
|
|
97
|
+
return /^0x[a-fA-F0-9]{40}$/i.test(value);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Check if a string is a valid Solana address (base58 format)
|
|
102
|
+
* Solana addresses are 32-44 characters, base58 encoded
|
|
103
|
+
*/
|
|
104
|
+
function isSolanaAddress(value: string): boolean {
|
|
105
|
+
// Base58 charset: 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
|
|
106
|
+
// Excludes: 0, O, I, l
|
|
107
|
+
return /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(value);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Check if a string is a valid token address (EVM or Solana)
|
|
112
|
+
*/
|
|
113
|
+
function isValidTokenAddress(value: string, chainId?: string): boolean {
|
|
114
|
+
if (chainId && isSolanaChain(chainId)) {
|
|
115
|
+
return isSolanaAddress(value);
|
|
116
|
+
}
|
|
117
|
+
// For EVM chains or unknown chains, check both formats
|
|
118
|
+
return isEvmAddress(value) || isSolanaAddress(value);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Populate the token cache for a chain from SDK config service
|
|
123
|
+
* This is the canonical way to get tokens - used by both resolveToken and getTokenInfo
|
|
124
|
+
*/
|
|
125
|
+
function populateTokenCache(chainId: string): Token[] {
|
|
126
|
+
// Convert to SDK chain ID format (e.g., "base" -> "0x2105.base")
|
|
127
|
+
const sdkChainId = toSodaxChainId(chainId);
|
|
128
|
+
let tokens = tokenCache.get(chainId);
|
|
129
|
+
if (tokens) return tokens;
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
const client = getSodaxClient();
|
|
133
|
+
const configService = (client as any).config;
|
|
134
|
+
|
|
135
|
+
if (configService?.getSupportedSwapTokensByChainId) {
|
|
136
|
+
// Preferred method - returns readonly Token[]
|
|
137
|
+
tokens = [...configService.getSupportedSwapTokensByChainId(sdkChainId)] as Token[];
|
|
138
|
+
} else if (configService?.getSwapTokensByChainId) {
|
|
139
|
+
tokens = configService.getSwapTokensByChainId(sdkChainId) as Token[];
|
|
140
|
+
} else if (configService?.getSwapTokens) {
|
|
141
|
+
const allTokens = configService.getSwapTokens();
|
|
142
|
+
tokens = allTokens[sdkChainId] || [];
|
|
143
|
+
} else {
|
|
144
|
+
console.warn(`[tokenResolver] configService not available for chain ${chainId}`);
|
|
145
|
+
tokens = [];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Log what we got from SDK
|
|
149
|
+
if (tokens && tokens.length > 0) {
|
|
150
|
+
console.log(`[tokenResolver] Loaded ${tokens.length} tokens from SDK for ${chainId}`);
|
|
151
|
+
}
|
|
152
|
+
} catch (err) {
|
|
153
|
+
console.error(`[tokenResolver] Failed to fetch tokens for chain ${chainId}:`, err);
|
|
154
|
+
tokens = [];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Use fallback tokens if SDK returned empty list
|
|
158
|
+
if ((!tokens || tokens.length === 0) && FALLBACK_TOKENS[chainId] || FALLBACK_TOKENS[sdkChainId]) {
|
|
159
|
+
console.log(`[tokenResolver] Using fallback token list for ${chainId}`);
|
|
160
|
+
tokens = FALLBACK_TOKENS[chainId] || FALLBACK_TOKENS[sdkChainId] as unknown as Token[];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
tokenCache.set(chainId, tokens || []);
|
|
164
|
+
return tokens || [];
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Resolve a token symbol or address to a normalized address
|
|
169
|
+
*
|
|
170
|
+
* @param chainId - The chain ID to resolve the token on
|
|
171
|
+
* @param tokenInput - Token symbol (e.g., "USDC") or address
|
|
172
|
+
* @returns The token address (lowercase for EVM, original case for Solana)
|
|
173
|
+
* @throws Error if token symbol is not found on the chain
|
|
174
|
+
*/
|
|
175
|
+
export async function resolveToken(
|
|
176
|
+
chainId: string,
|
|
177
|
+
tokenInput: string
|
|
178
|
+
): Promise<string> {
|
|
179
|
+
// If already a valid address, normalize and return
|
|
180
|
+
if (isValidTokenAddress(tokenInput, chainId)) {
|
|
181
|
+
// EVM addresses are lowercased, Solana addresses preserve case
|
|
182
|
+
return isEvmAddress(tokenInput) ? tokenInput.toLowerCase() : tokenInput;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Get tokens from cache or SDK
|
|
186
|
+
const tokens = populateTokenCache(chainId);
|
|
187
|
+
|
|
188
|
+
// Find by symbol (case-insensitive)
|
|
189
|
+
const symbolUpper = tokenInput.toUpperCase();
|
|
190
|
+
const token = tokens.find(t => t.symbol.toUpperCase() === symbolUpper);
|
|
191
|
+
|
|
192
|
+
if (!token) {
|
|
193
|
+
// Build helpful error message with available tokens
|
|
194
|
+
const available = tokens.length > 0
|
|
195
|
+
? tokens.map(t => t.symbol).join(', ')
|
|
196
|
+
: 'No tokens loaded';
|
|
197
|
+
throw new Error(
|
|
198
|
+
`Unknown token "${tokenInput}" on chain ${chainId}. ` +
|
|
199
|
+
`Available: ${available}. ` +
|
|
200
|
+
`Alternatively, provide the token address directly.`
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return isEvmAddress(token.address) ? token.address.toLowerCase() : token.address;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Resolve multiple tokens at once (for efficiency)
|
|
209
|
+
*
|
|
210
|
+
* @param chainId - The chain ID
|
|
211
|
+
* @param tokenInputs - Array of token symbols or addresses
|
|
212
|
+
* @returns Array of resolved addresses
|
|
213
|
+
*/
|
|
214
|
+
export async function resolveTokens(
|
|
215
|
+
chainId: string,
|
|
216
|
+
tokenInputs: string[]
|
|
217
|
+
): Promise<string[]> {
|
|
218
|
+
return Promise.all(tokenInputs.map(t => resolveToken(chainId, t)));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Get token info by symbol or address
|
|
223
|
+
* Returns null if not found
|
|
224
|
+
*/
|
|
225
|
+
export async function getTokenInfo(
|
|
226
|
+
chainId: string,
|
|
227
|
+
tokenInput: string
|
|
228
|
+
): Promise<Token | null> {
|
|
229
|
+
const sdkChainId = toSodaxChainId(chainId);
|
|
230
|
+
// Handle native tokens first
|
|
231
|
+
if (isValidTokenAddress(tokenInput, chainId) && isNativeToken(tokenInput, chainId)) {
|
|
232
|
+
const nativeInfo = getNativeTokenInfo(chainId);
|
|
233
|
+
if (nativeInfo) {
|
|
234
|
+
return nativeInfo as unknown as Token;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Get tokens from cache or SDK (same path as resolveToken)
|
|
239
|
+
const tokens = populateTokenCache(chainId);
|
|
240
|
+
|
|
241
|
+
// Find by address or symbol
|
|
242
|
+
if (isValidTokenAddress(tokenInput, chainId)) {
|
|
243
|
+
// Normalize address for comparison
|
|
244
|
+
const addrNorm = isEvmAddress(tokenInput) ? tokenInput.toLowerCase() : tokenInput;
|
|
245
|
+
const found = tokens.find(t => {
|
|
246
|
+
const tokenAddr = isEvmAddress(t.address) ? t.address.toLowerCase() : t.address;
|
|
247
|
+
return tokenAddr === addrNorm;
|
|
248
|
+
});
|
|
249
|
+
if (found) {
|
|
250
|
+
return found;
|
|
251
|
+
}
|
|
252
|
+
// Check fallback tokens even if SDK tokens were loaded
|
|
253
|
+
const fallback = FALLBACK_TOKENS[chainId] || FALLBACK_TOKENS[sdkChainId];
|
|
254
|
+
if (fallback) {
|
|
255
|
+
const fallbackToken = fallback.find(t => {
|
|
256
|
+
const tokenAddr = isEvmAddress(t.address) ? t.address.toLowerCase() : t.address;
|
|
257
|
+
return tokenAddr === addrNorm;
|
|
258
|
+
});
|
|
259
|
+
if (fallbackToken) {
|
|
260
|
+
console.log(`[tokenResolver] Found ${fallbackToken.symbol} in fallback for ${chainId}`);
|
|
261
|
+
return fallbackToken as unknown as Token;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return null;
|
|
265
|
+
} else {
|
|
266
|
+
const symbolUpper = tokenInput.toUpperCase();
|
|
267
|
+
return tokens.find(t => t.symbol.toUpperCase() === symbolUpper) || null;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Clear the token cache (useful for testing or after config refresh)
|
|
273
|
+
*/
|
|
274
|
+
export function clearTokenCache(): void {
|
|
275
|
+
tokenCache.clear();
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Get all cached tokens for a chain
|
|
280
|
+
*/
|
|
281
|
+
export function getCachedTokens(chainId: string): Token[] | undefined {
|
|
282
|
+
return tokenCache.get(chainId);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Export address validation utilities for use by other modules
|
|
286
|
+
export { isEvmAddress, isSolanaAddress, isValidTokenAddress, isSolanaChain };
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wallet Backend Configuration
|
|
3
|
+
*
|
|
4
|
+
* Detects and configures the appropriate wallet backend based on
|
|
5
|
+
* environment variables or config file.
|
|
6
|
+
*
|
|
7
|
+
* Supported backends:
|
|
8
|
+
* - localKey (default): Uses evm-wallet-skill local private keys
|
|
9
|
+
* - bankr: Uses Bankr Agent API for transaction execution
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { existsSync, readFileSync } from 'fs';
|
|
13
|
+
import { join } from 'path';
|
|
14
|
+
import { homedir } from 'os';
|
|
15
|
+
import type { WalletBackendType } from './providers';
|
|
16
|
+
|
|
17
|
+
export interface BackendConfig {
|
|
18
|
+
backend: WalletBackendType;
|
|
19
|
+
bankrApiKey?: string;
|
|
20
|
+
bankrApiUrl?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Default configuration
|
|
25
|
+
*/
|
|
26
|
+
const DEFAULT_CONFIG: BackendConfig = {
|
|
27
|
+
backend: 'localKey',
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Path to plugin config file
|
|
32
|
+
*/
|
|
33
|
+
function getConfigPath(): string {
|
|
34
|
+
return join(homedir(), '.openclaw', 'extensions', 'amped-defi', 'config.json');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Load configuration from file
|
|
39
|
+
*/
|
|
40
|
+
function loadConfigFile(): Partial<BackendConfig> | null {
|
|
41
|
+
const configPath = getConfigPath();
|
|
42
|
+
|
|
43
|
+
if (!existsSync(configPath)) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
const content = readFileSync(configPath, 'utf-8');
|
|
49
|
+
const config = JSON.parse(content);
|
|
50
|
+
return {
|
|
51
|
+
backend: config.walletBackend as WalletBackendType,
|
|
52
|
+
bankrApiKey: config.bankrApiKey,
|
|
53
|
+
bankrApiUrl: config.bankrApiUrl,
|
|
54
|
+
};
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.warn('[backendConfig] Failed to load config file:', error);
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Load configuration from environment variables
|
|
63
|
+
*/
|
|
64
|
+
function loadEnvConfig(): Partial<BackendConfig> {
|
|
65
|
+
const config: Partial<BackendConfig> = {};
|
|
66
|
+
|
|
67
|
+
// Check for explicit backend selection
|
|
68
|
+
const backendEnv = process.env.AMPED_OC_WALLET_BACKEND;
|
|
69
|
+
if (backendEnv === 'bankr' || backendEnv === 'localKey') {
|
|
70
|
+
config.backend = backendEnv;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Check for Bankr API key (implies bankr backend)
|
|
74
|
+
const bankrApiKey = process.env.BANKR_API_KEY;
|
|
75
|
+
if (bankrApiKey) {
|
|
76
|
+
config.bankrApiKey = bankrApiKey;
|
|
77
|
+
// Auto-select bankr backend if API key is present
|
|
78
|
+
if (!config.backend) {
|
|
79
|
+
config.backend = 'bankr';
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Optional Bankr API URL override
|
|
84
|
+
const bankrApiUrl = process.env.BANKR_API_URL;
|
|
85
|
+
if (bankrApiUrl) {
|
|
86
|
+
config.bankrApiUrl = bankrApiUrl;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return config;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Get the resolved backend configuration
|
|
94
|
+
*
|
|
95
|
+
* Priority:
|
|
96
|
+
* 1. Environment variables
|
|
97
|
+
* 2. Config file
|
|
98
|
+
* 3. Defaults
|
|
99
|
+
*/
|
|
100
|
+
export function getBackendConfig(): BackendConfig {
|
|
101
|
+
const fileConfig = loadConfigFile() || {};
|
|
102
|
+
const envConfig = loadEnvConfig();
|
|
103
|
+
|
|
104
|
+
// Merge with priority: env > file > default
|
|
105
|
+
const config: BackendConfig = {
|
|
106
|
+
...DEFAULT_CONFIG,
|
|
107
|
+
...fileConfig,
|
|
108
|
+
...envConfig,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// Validate bankr configuration
|
|
112
|
+
if (config.backend === 'bankr' && !config.bankrApiKey) {
|
|
113
|
+
console.warn('[backendConfig] Bankr backend selected but no API key provided');
|
|
114
|
+
console.warn('[backendConfig] Set BANKR_API_KEY environment variable or add bankrApiKey to config.json');
|
|
115
|
+
console.warn('[backendConfig] Falling back to localKey backend');
|
|
116
|
+
config.backend = 'localKey';
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Set default Bankr API URL if not specified
|
|
120
|
+
if (config.backend === 'bankr' && !config.bankrApiUrl) {
|
|
121
|
+
config.bankrApiUrl = 'https://api.bankr.bot';
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
console.log(`[backendConfig] Using wallet backend: ${config.backend}`);
|
|
125
|
+
|
|
126
|
+
return config;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Check if Bankr backend is configured and available
|
|
131
|
+
*/
|
|
132
|
+
export function isBankrConfigured(): boolean {
|
|
133
|
+
const config = getBackendConfig();
|
|
134
|
+
return config.backend === 'bankr' && !!config.bankrApiKey;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Get Bankr configuration if available
|
|
139
|
+
*/
|
|
140
|
+
export function getBankrConfig(): { apiKey: string; apiUrl: string } | null {
|
|
141
|
+
const config = getBackendConfig();
|
|
142
|
+
|
|
143
|
+
if (config.backend !== 'bankr' || !config.bankrApiKey) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
apiKey: config.bankrApiKey,
|
|
149
|
+
apiUrl: config.bankrApiUrl || 'https://api.bankr.bot',
|
|
150
|
+
};
|
|
151
|
+
}
|