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,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Amped Wallet Provider
|
|
3
|
+
*
|
|
4
|
+
* Custom wallet provider implementing IEvmWalletProvider from @sodax/types.
|
|
5
|
+
*
|
|
6
|
+
* This replaces wallet-sdk-core's EvmWalletProvider with a more flexible
|
|
7
|
+
* implementation that:
|
|
8
|
+
* 1. Supports all chains including LightLink and HyperEVM
|
|
9
|
+
* 2. Has pluggable backends (local keys, Bankr, etc.)
|
|
10
|
+
* 3. Provides a unified interface for the SODAX SDK
|
|
11
|
+
*
|
|
12
|
+
* Architecture:
|
|
13
|
+
* \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
|
|
14
|
+
* \u2502 AmpedWalletProvider \u2502
|
|
15
|
+
* \u2502 (implements IEvmWalletProvider) \u2502
|
|
16
|
+
* \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524
|
|
17
|
+
* \u2502 - SDK-compatible interface \u2502
|
|
18
|
+
* \u2502 - Chain resolution (all chains) \u2502
|
|
19
|
+
* \u2502 - Transaction formatting \u2502
|
|
20
|
+
* \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
|
|
21
|
+
* \u2502
|
|
22
|
+
* \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
|
|
23
|
+
* \u25bc \u25bc
|
|
24
|
+
* \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
|
|
25
|
+
* \u2502 LocalKeyBack. \u2502 \u2502 BankrBackend \u2502
|
|
26
|
+
* \u2502 (evm-wallet) \u2502 \u2502 (API calls) \u2502
|
|
27
|
+
* \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import {
|
|
31
|
+
createPublicClient,
|
|
32
|
+
http,
|
|
33
|
+
type Hash,
|
|
34
|
+
type Address,
|
|
35
|
+
} from 'viem';
|
|
36
|
+
import type {
|
|
37
|
+
IEvmWalletProvider,
|
|
38
|
+
EvmRawTransaction,
|
|
39
|
+
EvmRawTransactionReceipt
|
|
40
|
+
} from '@sodax/types';
|
|
41
|
+
import type {
|
|
42
|
+
IWalletBackend,
|
|
43
|
+
WalletBackendConfig,
|
|
44
|
+
WalletBackendType,
|
|
45
|
+
IAmpedWalletProvider,
|
|
46
|
+
LocalKeyBackendConfig,
|
|
47
|
+
BankrBackendConfig,
|
|
48
|
+
} from './types';
|
|
49
|
+
import { LocalKeyBackend, createLocalKeyBackend } from './LocalKeyBackend';
|
|
50
|
+
import { BankrBackend, createBankrBackend } from './BankrBackend';
|
|
51
|
+
import { getViemChain, getDefaultRpcUrl, resolveChainId } from './chainConfig';
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Amped Wallet Provider
|
|
55
|
+
*
|
|
56
|
+
* A drop-in replacement for wallet-sdk-core's EvmWalletProvider
|
|
57
|
+
* that supports all SODAX chains including LightLink and HyperEVM.
|
|
58
|
+
*/
|
|
59
|
+
export class AmpedWalletProvider implements IAmpedWalletProvider {
|
|
60
|
+
readonly publicClient: ReturnType<typeof createPublicClient>;
|
|
61
|
+
|
|
62
|
+
private readonly backend: IWalletBackend;
|
|
63
|
+
private readonly chainId: number;
|
|
64
|
+
|
|
65
|
+
private constructor(backend: IWalletBackend, publicClient: ReturnType<typeof createPublicClient>) {
|
|
66
|
+
this.backend = backend;
|
|
67
|
+
this.publicClient = publicClient;
|
|
68
|
+
this.chainId = backend.getChainId();
|
|
69
|
+
|
|
70
|
+
console.log(`[AmpedWalletProvider] Initialized with ${backend.type} backend`);
|
|
71
|
+
console.log(`[AmpedWalletProvider] Chain ID: ${this.chainId}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Create an AmpedWalletProvider with a local key backend
|
|
76
|
+
*
|
|
77
|
+
* @param config - Configuration matching EvmWalletProvider's PrivateKeyEvmWalletConfig
|
|
78
|
+
* @returns AmpedWalletProvider instance
|
|
79
|
+
*/
|
|
80
|
+
static async fromPrivateKey(config: {
|
|
81
|
+
privateKey: `0x${string}`;
|
|
82
|
+
chainId: string | number;
|
|
83
|
+
rpcUrl?: string;
|
|
84
|
+
}): Promise<AmpedWalletProvider> {
|
|
85
|
+
const chainId = resolveChainId(config.chainId);
|
|
86
|
+
const rpcUrl = config.rpcUrl || getDefaultRpcUrl(chainId);
|
|
87
|
+
const chain = getViemChain(chainId);
|
|
88
|
+
|
|
89
|
+
// Create backend
|
|
90
|
+
const backend = await createLocalKeyBackend({
|
|
91
|
+
type: 'localKey',
|
|
92
|
+
privateKey: config.privateKey,
|
|
93
|
+
chainId: config.chainId,
|
|
94
|
+
rpcUrl,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Use the backend's public client
|
|
98
|
+
const publicClient = backend.getPublicClient() as any;
|
|
99
|
+
|
|
100
|
+
return new AmpedWalletProvider(backend, publicClient);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Create an AmpedWalletProvider with a Bankr backend
|
|
105
|
+
*
|
|
106
|
+
* @param config - Bankr backend configuration
|
|
107
|
+
* @returns AmpedWalletProvider instance
|
|
108
|
+
*/
|
|
109
|
+
static async fromBankr(config: {
|
|
110
|
+
bankrApiUrl: string;
|
|
111
|
+
bankrApiKey: string;
|
|
112
|
+
userAddress: Address;
|
|
113
|
+
chainId: string | number;
|
|
114
|
+
rpcUrl?: string;
|
|
115
|
+
policy?: BankrBackendConfig['policy'];
|
|
116
|
+
}): Promise<AmpedWalletProvider> {
|
|
117
|
+
const chainId = resolveChainId(config.chainId);
|
|
118
|
+
const rpcUrl = config.rpcUrl || getDefaultRpcUrl(chainId);
|
|
119
|
+
const chain = getViemChain(chainId);
|
|
120
|
+
|
|
121
|
+
// Create backend
|
|
122
|
+
const backend = await createBankrBackend({
|
|
123
|
+
type: 'bankr',
|
|
124
|
+
bankrApiUrl: config.bankrApiUrl,
|
|
125
|
+
bankrApiKey: config.bankrApiKey,
|
|
126
|
+
userAddress: config.userAddress,
|
|
127
|
+
chainId: config.chainId,
|
|
128
|
+
rpcUrl,
|
|
129
|
+
policy: config.policy,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Create public client (for read operations)
|
|
133
|
+
// Bankr backend doesn't have its own public client
|
|
134
|
+
const publicClient = createPublicClient({
|
|
135
|
+
chain,
|
|
136
|
+
transport: http(rpcUrl),
|
|
137
|
+
}) as any; // Type cast needed due to viem's strict typing
|
|
138
|
+
|
|
139
|
+
return new AmpedWalletProvider(backend, publicClient);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Create from generic backend configuration
|
|
144
|
+
*/
|
|
145
|
+
static async fromConfig(config: WalletBackendConfig): Promise<AmpedWalletProvider> {
|
|
146
|
+
switch (config.type) {
|
|
147
|
+
case 'localKey':
|
|
148
|
+
return AmpedWalletProvider.fromPrivateKey({
|
|
149
|
+
privateKey: (config as LocalKeyBackendConfig).privateKey,
|
|
150
|
+
chainId: config.chainId,
|
|
151
|
+
rpcUrl: config.rpcUrl,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
case 'bankr':
|
|
155
|
+
const bankrConfig = config as BankrBackendConfig;
|
|
156
|
+
return AmpedWalletProvider.fromBankr({
|
|
157
|
+
bankrApiUrl: bankrConfig.bankrApiUrl,
|
|
158
|
+
bankrApiKey: bankrConfig.bankrApiKey,
|
|
159
|
+
userAddress: bankrConfig.userAddress,
|
|
160
|
+
chainId: config.chainId,
|
|
161
|
+
rpcUrl: config.rpcUrl,
|
|
162
|
+
policy: bankrConfig.policy,
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
default:
|
|
166
|
+
throw new Error(`Unsupported backend type: ${(config as any).type}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ===== IEvmWalletProvider Implementation =====
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Get the wallet address
|
|
174
|
+
*/
|
|
175
|
+
async getWalletAddress(): Promise<Address> {
|
|
176
|
+
return this.backend.getAddress();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Send a transaction
|
|
181
|
+
*
|
|
182
|
+
* Converts SDK's EvmRawTransaction format to internal format
|
|
183
|
+
* and delegates to the backend.
|
|
184
|
+
*/
|
|
185
|
+
async sendTransaction(evmRawTx: EvmRawTransaction): Promise<Hash> {
|
|
186
|
+
console.log(`[AmpedWalletProvider] sendTransaction`);
|
|
187
|
+
console.log(`[AmpedWalletProvider] From: ${evmRawTx.from}`);
|
|
188
|
+
console.log(`[AmpedWalletProvider] To: ${evmRawTx.to}`);
|
|
189
|
+
console.log(`[AmpedWalletProvider] Value: ${evmRawTx.value}`);
|
|
190
|
+
|
|
191
|
+
return this.backend.sendTransaction({
|
|
192
|
+
to: evmRawTx.to,
|
|
193
|
+
value: evmRawTx.value,
|
|
194
|
+
data: evmRawTx.data,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Wait for transaction receipt
|
|
200
|
+
*
|
|
201
|
+
* Converts internal receipt format to SDK's EvmRawTransactionReceipt format.
|
|
202
|
+
*/
|
|
203
|
+
async waitForTransactionReceipt(txHash: Hash): Promise<EvmRawTransactionReceipt> {
|
|
204
|
+
console.log(`[AmpedWalletProvider] waitForTransactionReceipt: ${txHash}`);
|
|
205
|
+
|
|
206
|
+
const receipt = await this.backend.waitForTransaction(txHash);
|
|
207
|
+
|
|
208
|
+
// Convert to SDK format
|
|
209
|
+
return {
|
|
210
|
+
transactionHash: receipt.transactionHash,
|
|
211
|
+
transactionIndex: '0x0', // Not tracked in our simplified receipt
|
|
212
|
+
blockHash: receipt.blockHash,
|
|
213
|
+
blockNumber: `0x${receipt.blockNumber.toString(16)}`,
|
|
214
|
+
from: receipt.from,
|
|
215
|
+
to: receipt.to,
|
|
216
|
+
cumulativeGasUsed: '0x0', // Not tracked
|
|
217
|
+
gasUsed: `0x${receipt.gasUsed.toString(16)}`,
|
|
218
|
+
contractAddress: null, // Would need to check if this was a deployment
|
|
219
|
+
logs: receipt.logs.map(log => ({
|
|
220
|
+
address: log.address,
|
|
221
|
+
topics: log.topics as [`0x${string}`, ...`0x${string}`[]] | [],
|
|
222
|
+
data: log.data,
|
|
223
|
+
blockHash: receipt.blockHash,
|
|
224
|
+
blockNumber: `0x${receipt.blockNumber.toString(16)}`,
|
|
225
|
+
logIndex: '0x0',
|
|
226
|
+
transactionHash: receipt.transactionHash,
|
|
227
|
+
transactionIndex: '0x0',
|
|
228
|
+
removed: false,
|
|
229
|
+
})),
|
|
230
|
+
logsBloom: '0x',
|
|
231
|
+
status: receipt.status === 'success' ? '0x1' : '0x0',
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// ===== IAmpedWalletProvider Extensions =====
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Get the underlying backend
|
|
239
|
+
*/
|
|
240
|
+
getBackend(): IWalletBackend {
|
|
241
|
+
return this.backend;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Get the backend type
|
|
246
|
+
*/
|
|
247
|
+
getBackendType(): WalletBackendType {
|
|
248
|
+
return this.backend.type;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Check if ready for transactions
|
|
253
|
+
*/
|
|
254
|
+
async isReady(): Promise<boolean> {
|
|
255
|
+
return this.backend.isReady();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Get chain ID
|
|
260
|
+
*/
|
|
261
|
+
getChainId(): number {
|
|
262
|
+
return this.chainId;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Re-export for convenience
|
|
267
|
+
export type { IAmpedWalletProvider };
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bankr Backend - Transaction Execution Layer
|
|
3
|
+
*
|
|
4
|
+
* CRITICAL: This backend is for EXECUTION ONLY, not routing.
|
|
5
|
+
*
|
|
6
|
+
* Architecture:
|
|
7
|
+
* SODAX SDK (routing) → BankrBackend (execution) → Blockchain
|
|
8
|
+
*
|
|
9
|
+
* What Bankr DOES:
|
|
10
|
+
* ✓ Signs the pre-computed transaction from SODAX
|
|
11
|
+
* ✓ Submits to blockchain via Bankr API
|
|
12
|
+
* ✓ Returns transaction hash
|
|
13
|
+
*
|
|
14
|
+
* What Bankr does NOT do:
|
|
15
|
+
* ✗ NO routing decisions
|
|
16
|
+
* ✗ NO DeFi protocol selection
|
|
17
|
+
* ✗ NO swap optimization
|
|
18
|
+
* ✗ NO interpretation of intent
|
|
19
|
+
*
|
|
20
|
+
* The SODAX SDK always handles routing logic. Bankr receives the exact
|
|
21
|
+
* transaction data (to, data, value, chainId) and submits it verbatim.
|
|
22
|
+
*
|
|
23
|
+
* @see SKILL.md "Transaction Execution Architecture" section
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import type { Hash, Address } from 'viem';
|
|
27
|
+
import type {
|
|
28
|
+
IWalletBackend,
|
|
29
|
+
BankrBackendConfig,
|
|
30
|
+
TransactionRequest,
|
|
31
|
+
TransactionReceipt
|
|
32
|
+
} from './types';
|
|
33
|
+
import { resolveChainId } from './chainConfig';
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Serialize error objects for readable error messages
|
|
37
|
+
*/
|
|
38
|
+
function serializeError(error: unknown): string {
|
|
39
|
+
if (error instanceof Error) return error.message;
|
|
40
|
+
if (typeof error === 'string') return error;
|
|
41
|
+
try {
|
|
42
|
+
return JSON.stringify(error, (k, v) => typeof v === 'bigint' ? v.toString() : v, 2);
|
|
43
|
+
} catch {
|
|
44
|
+
return String(error);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Bankr Agent API response types
|
|
50
|
+
*/
|
|
51
|
+
interface BankrJobSubmitResponse {
|
|
52
|
+
success: boolean;
|
|
53
|
+
jobId: string;
|
|
54
|
+
status: 'pending';
|
|
55
|
+
message: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface BankrJobStatusResponse {
|
|
59
|
+
success: boolean;
|
|
60
|
+
jobId: string;
|
|
61
|
+
status: 'pending' | 'processing' | 'completed' | 'failed' | 'cancelled';
|
|
62
|
+
prompt: string;
|
|
63
|
+
response?: string;
|
|
64
|
+
error?: string;
|
|
65
|
+
richData?: Array<{ type?: string; [key: string]: unknown }>;
|
|
66
|
+
statusUpdates?: Array<{ message: string; timestamp: string }>;
|
|
67
|
+
createdAt: string;
|
|
68
|
+
completedAt?: string;
|
|
69
|
+
processingTime?: number;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Chain ID to chain name mapping for Bankr prompts
|
|
74
|
+
*/
|
|
75
|
+
const CHAIN_NAMES: Record<number, string> = {
|
|
76
|
+
1: 'ethereum',
|
|
77
|
+
8453: 'base',
|
|
78
|
+
137: 'polygon',
|
|
79
|
+
42161: 'arbitrum',
|
|
80
|
+
10: 'optimism',
|
|
81
|
+
1890: 'lightlink',
|
|
82
|
+
146: 'sonic',
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Bankr execution backend
|
|
87
|
+
*
|
|
88
|
+
* Delegates transaction execution to Bankr's Agent API.
|
|
89
|
+
* The agent never has direct access to private keys.
|
|
90
|
+
*/
|
|
91
|
+
export class BankrBackend implements IWalletBackend {
|
|
92
|
+
readonly type = 'bankr' as const;
|
|
93
|
+
|
|
94
|
+
private readonly apiUrl: string;
|
|
95
|
+
private readonly apiKey: string;
|
|
96
|
+
private readonly userAddress: Address;
|
|
97
|
+
private readonly chainId: number;
|
|
98
|
+
private readonly policy?: BankrBackendConfig['policy'];
|
|
99
|
+
|
|
100
|
+
// Polling configuration
|
|
101
|
+
private readonly pollIntervalMs = 2000;
|
|
102
|
+
private readonly maxPollAttempts = 150; // 5 minutes max
|
|
103
|
+
|
|
104
|
+
constructor(config: BankrBackendConfig) {
|
|
105
|
+
this.apiUrl = config.bankrApiUrl || 'https://api.bankr.bot';
|
|
106
|
+
this.apiKey = config.bankrApiKey;
|
|
107
|
+
this.userAddress = config.userAddress;
|
|
108
|
+
this.chainId = resolveChainId(config.chainId);
|
|
109
|
+
this.policy = config.policy;
|
|
110
|
+
|
|
111
|
+
console.log(`[BankrBackend] Initialized for chain ${this.chainId}`);
|
|
112
|
+
console.log(`[BankrBackend] User address: ${this.userAddress}`);
|
|
113
|
+
console.log(`[BankrBackend] API URL: ${this.apiUrl}`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Get the wallet address (Bankr-provisioned)
|
|
118
|
+
*/
|
|
119
|
+
async getAddress(): Promise<Address> {
|
|
120
|
+
return this.userAddress;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Send a transaction via Bankr Agent API
|
|
125
|
+
*
|
|
126
|
+
* Formats the transaction as a natural language prompt and submits
|
|
127
|
+
* to Bankr's async job system.
|
|
128
|
+
*/
|
|
129
|
+
async sendTransaction(tx: TransactionRequest): Promise<Hash> {
|
|
130
|
+
console.log(`[BankrBackend] Sending transaction via Bankr API`);
|
|
131
|
+
console.log(`[BankrBackend] To: ${tx.to}`);
|
|
132
|
+
console.log(`[BankrBackend] Value: ${tx.value || 0n}`);
|
|
133
|
+
console.log(`[BankrBackend] Data: ${tx.data ? tx.data.slice(0, 20) + '...' : '0x'}`);
|
|
134
|
+
|
|
135
|
+
// Validate against policy
|
|
136
|
+
if (this.policy) {
|
|
137
|
+
await this.validatePolicy(tx);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Format transaction as JSON for Bankr prompt
|
|
141
|
+
const txJson = JSON.stringify({
|
|
142
|
+
to: tx.to,
|
|
143
|
+
data: tx.data || '0x',
|
|
144
|
+
value: (tx.value || 0n).toString(),
|
|
145
|
+
chainId: this.chainId,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Create natural language prompt for Bankr
|
|
149
|
+
const prompt = `Submit this transaction: ${txJson}`;
|
|
150
|
+
|
|
151
|
+
console.log(`[BankrBackend] Submitting prompt to Bankr API`);
|
|
152
|
+
|
|
153
|
+
// Submit job to Bankr
|
|
154
|
+
const jobId = await this.submitJob(prompt);
|
|
155
|
+
console.log(`[BankrBackend] Job submitted: ${jobId}`);
|
|
156
|
+
|
|
157
|
+
// Poll for completion
|
|
158
|
+
const result = await this.pollJobUntilComplete(jobId);
|
|
159
|
+
|
|
160
|
+
// Extract transaction hash from response
|
|
161
|
+
const txHash = this.extractTransactionHash(result);
|
|
162
|
+
|
|
163
|
+
if (!txHash) {
|
|
164
|
+
throw new Error(`[BankrBackend] Transaction failed: ${serializeError(result.response || result.error) || 'Unknown error'}`);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
console.log(`[BankrBackend] Transaction hash: ${txHash}`);
|
|
168
|
+
return txHash;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Wait for transaction confirmation
|
|
173
|
+
*
|
|
174
|
+
* Note: With Bankr, the transaction is already confirmed when we get
|
|
175
|
+
* the response. This method exists for interface compatibility but
|
|
176
|
+
* returns a minimal receipt.
|
|
177
|
+
*/
|
|
178
|
+
async waitForTransaction(txHash: Hash): Promise<TransactionReceipt> {
|
|
179
|
+
console.log(`[BankrBackend] waitForTransaction called for: ${txHash}`);
|
|
180
|
+
|
|
181
|
+
// Bankr transactions are confirmed when the job completes
|
|
182
|
+
// We return a minimal receipt since we don't have full details
|
|
183
|
+
return {
|
|
184
|
+
transactionHash: txHash,
|
|
185
|
+
blockNumber: 0n, // Unknown - would need to query chain
|
|
186
|
+
blockHash: '0x0000000000000000000000000000000000000000000000000000000000000000' as Hash,
|
|
187
|
+
from: this.userAddress,
|
|
188
|
+
to: null,
|
|
189
|
+
gasUsed: 0n,
|
|
190
|
+
status: 'success',
|
|
191
|
+
logs: [],
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Check if backend is ready
|
|
197
|
+
*
|
|
198
|
+
* Verifies Bankr API connectivity with a simple balance query.
|
|
199
|
+
*/
|
|
200
|
+
async isReady(): Promise<boolean> {
|
|
201
|
+
if (!this.apiUrl || !this.apiKey || !this.userAddress) {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
// Test API connectivity with a simple query
|
|
207
|
+
const response = await fetch(`${this.apiUrl}/agent/prompt`, {
|
|
208
|
+
method: 'POST',
|
|
209
|
+
headers: {
|
|
210
|
+
'X-API-Key': this.apiKey,
|
|
211
|
+
'Content-Type': 'application/json',
|
|
212
|
+
},
|
|
213
|
+
body: JSON.stringify({ prompt: 'ping' }),
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// Even a 4xx error means API is reachable
|
|
217
|
+
return response.status !== 503 && response.status !== 502;
|
|
218
|
+
} catch (error) {
|
|
219
|
+
console.error('[BankrBackend] Connectivity check failed:', error);
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Get the chain ID
|
|
226
|
+
*/
|
|
227
|
+
getChainId(): number {
|
|
228
|
+
return this.chainId;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Submit a job to Bankr Agent API
|
|
233
|
+
*/
|
|
234
|
+
private async submitJob(prompt: string): Promise<string> {
|
|
235
|
+
const response = await fetch(`${this.apiUrl}/agent/prompt`, {
|
|
236
|
+
method: 'POST',
|
|
237
|
+
headers: {
|
|
238
|
+
'X-API-Key': this.apiKey,
|
|
239
|
+
'Content-Type': 'application/json',
|
|
240
|
+
},
|
|
241
|
+
body: JSON.stringify({ prompt }),
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
if (!response.ok) {
|
|
245
|
+
const error = await response.text();
|
|
246
|
+
throw new Error(`[BankrBackend] Failed to submit job: ${response.status} ${error}`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const data = await response.json() as BankrJobSubmitResponse;
|
|
250
|
+
|
|
251
|
+
if (!data.success || !data.jobId) {
|
|
252
|
+
throw new Error(`[BankrBackend] Invalid job submission response: ${JSON.stringify(data)}`);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return data.jobId;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Poll for job completion
|
|
260
|
+
*/
|
|
261
|
+
private async pollJobUntilComplete(jobId: string): Promise<BankrJobStatusResponse> {
|
|
262
|
+
let lastStatus = '';
|
|
263
|
+
|
|
264
|
+
for (let attempt = 0; attempt < this.maxPollAttempts; attempt++) {
|
|
265
|
+
await this.sleep(this.pollIntervalMs);
|
|
266
|
+
|
|
267
|
+
const result = await this.getJobStatus(jobId);
|
|
268
|
+
|
|
269
|
+
// Log status changes
|
|
270
|
+
if (result.status !== lastStatus) {
|
|
271
|
+
console.log(`[BankrBackend] Job ${jobId} status: ${result.status}`);
|
|
272
|
+
lastStatus = result.status;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Log status updates
|
|
276
|
+
if (result.statusUpdates && result.statusUpdates.length > 0) {
|
|
277
|
+
const lastUpdate = result.statusUpdates[result.statusUpdates.length - 1];
|
|
278
|
+
console.log(`[BankrBackend] Progress: ${lastUpdate.message}`);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Check for terminal states
|
|
282
|
+
switch (result.status) {
|
|
283
|
+
case 'completed':
|
|
284
|
+
return result;
|
|
285
|
+
case 'failed':
|
|
286
|
+
throw new Error(`[BankrBackend] Job failed: ${serializeError(result.error) || 'Unknown error'}`);
|
|
287
|
+
case 'cancelled':
|
|
288
|
+
throw new Error(`[BankrBackend] Job was cancelled`);
|
|
289
|
+
case 'pending':
|
|
290
|
+
case 'processing':
|
|
291
|
+
// Continue polling
|
|
292
|
+
break;
|
|
293
|
+
default:
|
|
294
|
+
console.warn(`[BankrBackend] Unknown status: ${result.status}`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
throw new Error(`[BankrBackend] Job ${jobId} timed out after ${this.maxPollAttempts * this.pollIntervalMs / 1000} seconds`);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Get job status from Bankr API
|
|
303
|
+
*/
|
|
304
|
+
private async getJobStatus(jobId: string): Promise<BankrJobStatusResponse> {
|
|
305
|
+
const response = await fetch(`${this.apiUrl}/agent/job/${jobId}`, {
|
|
306
|
+
method: 'GET',
|
|
307
|
+
headers: {
|
|
308
|
+
'X-API-Key': this.apiKey,
|
|
309
|
+
},
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
if (!response.ok) {
|
|
313
|
+
const error = await response.text();
|
|
314
|
+
throw new Error(`[BankrBackend] Failed to get job status: ${response.status} ${error}`);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return await response.json() as BankrJobStatusResponse;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Extract transaction hash from Bankr response
|
|
322
|
+
*
|
|
323
|
+
* The response may contain the tx hash in various formats:
|
|
324
|
+
* - In richData array
|
|
325
|
+
* - In the response text (e.g., "Transaction hash: 0x...")
|
|
326
|
+
*/
|
|
327
|
+
private extractTransactionHash(result: BankrJobStatusResponse): Hash | null {
|
|
328
|
+
// Check richData for transaction info
|
|
329
|
+
if (result.richData) {
|
|
330
|
+
for (const item of result.richData) {
|
|
331
|
+
if (item.transactionHash) {
|
|
332
|
+
return item.transactionHash as Hash;
|
|
333
|
+
}
|
|
334
|
+
if (item.txHash) {
|
|
335
|
+
return item.txHash as Hash;
|
|
336
|
+
}
|
|
337
|
+
if (item.hash) {
|
|
338
|
+
return item.hash as Hash;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Try to extract from response text
|
|
344
|
+
if (result.response) {
|
|
345
|
+
// Look for hex transaction hash pattern (0x followed by 64 hex chars)
|
|
346
|
+
const hashMatch = result.response.match(/0x[a-fA-F0-9]{64}/);
|
|
347
|
+
if (hashMatch) {
|
|
348
|
+
return hashMatch[0] as Hash;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Check if response indicates failure
|
|
352
|
+
if (result.response.toLowerCase().includes('reverted') ||
|
|
353
|
+
result.response.toLowerCase().includes('failed') ||
|
|
354
|
+
result.response.toLowerCase().includes('insufficient')) {
|
|
355
|
+
console.error(`[BankrBackend] Transaction failed: ${result.response}`);
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Validate transaction against policy
|
|
365
|
+
*/
|
|
366
|
+
private async validatePolicy(tx: TransactionRequest): Promise<void> {
|
|
367
|
+
if (!this.policy) return;
|
|
368
|
+
|
|
369
|
+
// Check max value per transaction
|
|
370
|
+
if (this.policy.maxValuePerTx && tx.value && tx.value > this.policy.maxValuePerTx) {
|
|
371
|
+
throw new Error(
|
|
372
|
+
`Transaction value ${tx.value} exceeds max allowed ${this.policy.maxValuePerTx}`
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Check allowed contracts
|
|
377
|
+
if (this.policy.allowedContracts && this.policy.allowedContracts.length > 0) {
|
|
378
|
+
if (!this.policy.allowedContracts.includes(tx.to)) {
|
|
379
|
+
throw new Error(
|
|
380
|
+
`Contract ${tx.to} is not in the allowed contracts list`
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Sleep helper
|
|
388
|
+
*/
|
|
389
|
+
private sleep(ms: number): Promise<void> {
|
|
390
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Create a BankrBackend from configuration
|
|
396
|
+
*/
|
|
397
|
+
export async function createBankrBackend(config: BankrBackendConfig): Promise<BankrBackend> {
|
|
398
|
+
const backend = new BankrBackend(config);
|
|
399
|
+
|
|
400
|
+
// Verify connectivity
|
|
401
|
+
const ready = await backend.isReady();
|
|
402
|
+
if (!ready) {
|
|
403
|
+
console.warn('[BankrBackend] Backend created but connectivity check failed');
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return backend;
|
|
407
|
+
}
|