moneyos 0.2.0 → 0.3.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/dist/index.js CHANGED
@@ -1,75 +1,11 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/index.ts
21
- var src_exports = {};
22
- __export(src_exports, {
23
- MoneyOS: () => MoneyOS,
24
- NATIVE_TOKEN_ADDRESS: () => NATIVE_TOKEN_ADDRESS,
25
- OdosProvider: () => OdosProvider,
26
- chains: () => chains,
27
- defaultChain: () => defaultChain,
28
- getChain: () => getChain,
29
- getToken: () => getToken,
30
- getTokenAddress: () => getTokenAddress,
31
- tokens: () => tokens
32
- });
33
- module.exports = __toCommonJS(src_exports);
34
-
35
1
  // src/core/client.ts
36
- var import_viem = require("viem");
37
- var import_accounts = require("viem/accounts");
38
- var import_chains = require("viem/chains");
39
-
40
- // src/core/chains.ts
41
- var chains = {
42
- arbitrum: {
43
- id: 42161,
44
- name: "Arbitrum One",
45
- rpcUrl: "https://arb1.arbitrum.io/rpc",
46
- nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
47
- blockExplorer: "https://arbiscan.io"
48
- },
49
- ethereum: {
50
- id: 1,
51
- name: "Ethereum",
52
- rpcUrl: "https://eth.public-rpc.com",
53
- nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
54
- blockExplorer: "https://etherscan.io"
55
- },
56
- polygon: {
57
- id: 137,
58
- name: "Polygon",
59
- rpcUrl: "https://polygon-rpc.com",
60
- nativeCurrency: { name: "POL", symbol: "POL", decimals: 18 },
61
- blockExplorer: "https://polygonscan.com"
62
- }
63
- };
64
- var defaultChain = chains.arbitrum;
65
- function getChain(idOrName) {
66
- if (typeof idOrName === "number") {
67
- return Object.values(chains).find((c) => c.id === idOrName);
68
- }
69
- return chains[idOrName.toLowerCase()];
70
- }
2
+ import {
3
+ formatUnits as formatUnits2,
4
+ parseUnits as parseUnits2,
5
+ encodeFunctionData as encodeFunctionData2
6
+ } from "viem";
71
7
 
72
- // src/core/tokens.ts
8
+ // packages/core/dist/index.js
73
9
  var NATIVE_TOKEN_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
74
10
  var tokens = {
75
11
  ETH: {
@@ -78,7 +14,14 @@ var tokens = {
78
14
  decimals: 18,
79
15
  addresses: {
80
16
  42161: NATIVE_TOKEN_ADDRESS,
81
- 1: NATIVE_TOKEN_ADDRESS,
17
+ 1: NATIVE_TOKEN_ADDRESS
18
+ }
19
+ },
20
+ POL: {
21
+ symbol: "POL",
22
+ name: "POL",
23
+ decimals: 18,
24
+ addresses: {
82
25
  137: NATIVE_TOKEN_ADDRESS
83
26
  }
84
27
  },
@@ -117,9 +60,265 @@ function getToken(symbol) {
117
60
  function getTokenAddress(symbol, chainId) {
118
61
  return getToken(symbol)?.addresses[chainId];
119
62
  }
63
+ var chains = {
64
+ arbitrum: {
65
+ id: 42161,
66
+ name: "Arbitrum One",
67
+ rpcUrl: "https://arb1.arbitrum.io/rpc",
68
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
69
+ blockExplorer: "https://arbiscan.io"
70
+ },
71
+ ethereum: {
72
+ id: 1,
73
+ name: "Ethereum",
74
+ rpcUrl: "https://eth.public-rpc.com",
75
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
76
+ blockExplorer: "https://etherscan.io"
77
+ },
78
+ polygon: {
79
+ id: 137,
80
+ name: "Polygon",
81
+ rpcUrl: "https://polygon-rpc.com",
82
+ nativeCurrency: { name: "POL", symbol: "POL", decimals: 18 },
83
+ blockExplorer: "https://polygonscan.com"
84
+ }
85
+ };
86
+ var defaultChain = chains.arbitrum;
87
+ function getChain(idOrName) {
88
+ if (typeof idOrName === "number") {
89
+ return Object.values(chains).find((c) => c.id === idOrName);
90
+ }
91
+ return chains[idOrName.toLowerCase()];
92
+ }
120
93
 
121
- // src/core/client.ts
94
+ // src/core/eoa.ts
95
+ import {
96
+ createPublicClient,
97
+ createWalletClient,
98
+ http
99
+ } from "viem";
100
+ import { arbitrum, mainnet, polygon } from "viem/chains";
101
+
102
+ // src/core/signer.ts
103
+ import { nonceManager } from "viem";
104
+ import { privateKeyToAccount } from "viem/accounts";
105
+ function privateKeyToManagedAccount(privateKey) {
106
+ return privateKeyToAccount(privateKey, { nonceManager });
107
+ }
108
+
109
+ // src/core/eoa.ts
110
+ var viemChainMap = {
111
+ 42161: arbitrum,
112
+ 1: mainnet,
113
+ 137: polygon
114
+ };
115
+ function getViemChain(chainId) {
116
+ return viemChainMap[chainId];
117
+ }
118
+ var ViemReadClient = class {
119
+ clients = /* @__PURE__ */ new Map();
120
+ config;
121
+ constructor(config) {
122
+ this.config = config;
123
+ }
124
+ getClient(chainId) {
125
+ let client = this.clients.get(chainId);
126
+ if (!client) {
127
+ const chain = getViemChain(chainId);
128
+ const chainInfo = getChain(chainId);
129
+ const rpcUrl = chainId === this.config.defaultChainId ? this.config.rpcUrl : void 0;
130
+ client = createPublicClient({
131
+ chain,
132
+ transport: http(rpcUrl ?? chainInfo?.rpcUrl)
133
+ });
134
+ this.clients.set(chainId, client);
135
+ }
136
+ return client;
137
+ }
138
+ async getBalance(params) {
139
+ const client = this.getClient(params.chainId);
140
+ return client.getBalance({ address: params.address });
141
+ }
142
+ async readContract(params) {
143
+ const client = this.getClient(params.chainId);
144
+ return client.readContract({
145
+ address: params.address,
146
+ abi: params.abi,
147
+ functionName: params.functionName,
148
+ args: params.args
149
+ });
150
+ }
151
+ };
152
+ var EOAExecutor = class _EOAExecutor {
153
+ mode = "eoa";
154
+ signer;
155
+ walletClients = /* @__PURE__ */ new Map();
156
+ publicClients = /* @__PURE__ */ new Map();
157
+ config;
158
+ constructor(signer, config) {
159
+ this.signer = signer;
160
+ this.config = config;
161
+ }
162
+ /**
163
+ * Convenience factory: build an EOAExecutor from a raw private key.
164
+ * Equivalent to `new EOAExecutor(privateKeyToAccount(privateKey), config)`.
165
+ * Kept as a helper so the common "I have a hex key" path stays one line
166
+ * while the constructor itself takes a viem `Account` to accommodate
167
+ * future keystore-backed signers (hardware, KMS, MPC).
168
+ *
169
+ * Attach viem's nonce manager so back-to-back live transactions use a
170
+ * pending-aware nonce source instead of relying on RPC fill behavior.
171
+ */
172
+ static fromPrivateKey(privateKey, config) {
173
+ return new _EOAExecutor(privateKeyToManagedAccount(privateKey), config);
174
+ }
175
+ getWalletClient(chainId) {
176
+ let client = this.walletClients.get(chainId);
177
+ if (!client) {
178
+ const chain = getViemChain(chainId);
179
+ const chainInfo = getChain(chainId);
180
+ const rpcUrl = chainId === this.config.defaultChainId ? this.config.rpcUrl : void 0;
181
+ client = createWalletClient({
182
+ account: this.signer,
183
+ chain,
184
+ transport: http(rpcUrl ?? chainInfo?.rpcUrl)
185
+ });
186
+ this.walletClients.set(chainId, client);
187
+ }
188
+ return client;
189
+ }
190
+ getPublicClient(chainId) {
191
+ let client = this.publicClients.get(chainId);
192
+ if (!client) {
193
+ const chain = getViemChain(chainId);
194
+ const chainInfo = getChain(chainId);
195
+ const rpcUrl = chainId === this.config.defaultChainId ? this.config.rpcUrl : void 0;
196
+ client = createPublicClient({
197
+ chain,
198
+ transport: http(rpcUrl ?? chainInfo?.rpcUrl)
199
+ });
200
+ this.publicClients.set(chainId, client);
201
+ }
202
+ return client;
203
+ }
204
+ getAddress() {
205
+ return this.signer.address;
206
+ }
207
+ async send(call) {
208
+ const walletClient = this.getWalletClient(call.chainId);
209
+ const hash = await walletClient.sendTransaction({
210
+ account: walletClient.account,
211
+ to: call.to,
212
+ data: call.data,
213
+ value: call.value ?? 0n,
214
+ chain: walletClient.chain
215
+ });
216
+ const publicClient = this.getPublicClient(call.chainId);
217
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
218
+ if (receipt.status !== "success") {
219
+ throw new Error(
220
+ `Transaction ${hash} reverted on chain ${call.chainId} (block ${receipt.blockNumber}).`
221
+ );
222
+ }
223
+ return { hash, chainId: call.chainId };
224
+ }
225
+ capabilities() {
226
+ return {
227
+ sponsoredGas: false,
228
+ batching: false,
229
+ simulation: false
230
+ };
231
+ }
232
+ };
233
+
234
+ // src/tools/swap.ts
235
+ import {
236
+ formatUnits,
237
+ parseUnits,
238
+ encodeFunctionData
239
+ } from "viem";
122
240
  var ERC20_ABI = [
241
+ {
242
+ name: "approve",
243
+ type: "function",
244
+ stateMutability: "nonpayable",
245
+ inputs: [
246
+ { name: "spender", type: "address" },
247
+ { name: "amount", type: "uint256" }
248
+ ],
249
+ outputs: [{ name: "", type: "bool" }]
250
+ },
251
+ {
252
+ name: "allowance",
253
+ type: "function",
254
+ stateMutability: "view",
255
+ inputs: [
256
+ { name: "owner", type: "address" },
257
+ { name: "spender", type: "address" }
258
+ ],
259
+ outputs: [{ name: "", type: "uint256" }]
260
+ }
261
+ ];
262
+ async function executeSwap(input, ctx) {
263
+ const { tokenIn, tokenOut, amount, provider, chainId, slippage } = input;
264
+ const { read, execute, assets } = ctx;
265
+ const sender = execute.getAddress();
266
+ const tokenInAddress = assets.getTokenAddress(tokenIn, chainId);
267
+ const tokenOutAddress = assets.getTokenAddress(tokenOut, chainId);
268
+ if (!tokenInAddress) {
269
+ throw new Error(`Token ${tokenIn} not found on chain ${chainId}`);
270
+ }
271
+ if (!tokenOutAddress) {
272
+ throw new Error(`Token ${tokenOut} not found on chain ${chainId}`);
273
+ }
274
+ const tokenInInfo = assets.getToken(tokenIn);
275
+ const amountWei = parseUnits(amount, tokenInInfo.decimals);
276
+ const quote = await provider.getQuote({
277
+ chainId,
278
+ tokenIn: tokenInAddress,
279
+ tokenOut: tokenOutAddress,
280
+ amount: amountWei,
281
+ sender,
282
+ slippage
283
+ });
284
+ const calldata = await provider.getCalldata(quote);
285
+ const isNativeIn = tokenInAddress === assets.nativeTokenAddress;
286
+ if (!isNativeIn) {
287
+ const currentAllowance = await read.readContract({
288
+ address: tokenInAddress,
289
+ abi: ERC20_ABI,
290
+ functionName: "allowance",
291
+ args: [sender, calldata.to],
292
+ chainId
293
+ });
294
+ if (currentAllowance < amountWei) {
295
+ const approveData = encodeFunctionData({
296
+ abi: ERC20_ABI,
297
+ functionName: "approve",
298
+ args: [calldata.to, amountWei]
299
+ });
300
+ await execute.send({ to: tokenInAddress, data: approveData, chainId });
301
+ }
302
+ }
303
+ const result = await execute.send({
304
+ to: calldata.to,
305
+ data: calldata.data,
306
+ value: isNativeIn ? amountWei : calldata.value,
307
+ chainId
308
+ });
309
+ const tokenOutInfo = assets.getToken(tokenOut);
310
+ return {
311
+ hash: result.hash,
312
+ tokenIn: tokenInInfo.symbol,
313
+ tokenOut: tokenOutInfo.symbol,
314
+ amountIn: amount,
315
+ amountOut: formatUnits(BigInt(quote.expectedOut), tokenOutInfo.decimals),
316
+ chainId
317
+ };
318
+ }
319
+
320
+ // src/core/client.ts
321
+ var ERC20_ABI2 = [
123
322
  {
124
323
  name: "balanceOf",
125
324
  type: "function",
@@ -150,221 +349,158 @@ var ERC20_ABI = [
150
349
  stateMutability: "view",
151
350
  inputs: [],
152
351
  outputs: [{ name: "", type: "string" }]
153
- },
154
- {
155
- name: "approve",
156
- type: "function",
157
- stateMutability: "nonpayable",
158
- inputs: [
159
- { name: "spender", type: "address" },
160
- { name: "amount", type: "uint256" }
161
- ],
162
- outputs: [{ name: "", type: "bool" }]
163
- },
164
- {
165
- name: "allowance",
166
- type: "function",
167
- stateMutability: "view",
168
- inputs: [
169
- { name: "owner", type: "address" },
170
- { name: "spender", type: "address" }
171
- ],
172
- outputs: [{ name: "", type: "uint256" }]
173
352
  }
174
353
  ];
175
- var viemChains = {
176
- 42161: import_chains.arbitrum,
177
- 1: import_chains.mainnet,
178
- 137: import_chains.polygon
354
+ var DefaultAssetRegistry = class {
355
+ nativeTokenAddress = NATIVE_TOKEN_ADDRESS;
356
+ getToken = getToken;
357
+ getTokenAddress = getTokenAddress;
358
+ getChain = getChain;
179
359
  };
180
360
  var MoneyOS = class {
181
- config;
182
- publicClients = /* @__PURE__ */ new Map();
183
- walletClient;
361
+ read;
362
+ executor;
363
+ assets;
364
+ runtimeConfig;
184
365
  constructor(config) {
185
- this.config = {
186
- ...config,
187
- chainId: config.chainId ?? defaultChain.id
366
+ const provided = [];
367
+ if (config.execute) provided.push("execute");
368
+ if (config.privateKey) provided.push("privateKey");
369
+ if (config.signer) provided.push("signer");
370
+ if (provided.length > 1) {
371
+ throw new Error(
372
+ `MoneyOSConfig: pass at most one of \`execute\`, \`privateKey\`, or \`signer\`. Received: ${provided.join(", ")}.`
373
+ );
374
+ }
375
+ this.runtimeConfig = {
376
+ defaultChainId: config.chainId ?? defaultChain.id,
377
+ rpcUrl: config.rpcUrl
188
378
  };
189
- }
190
- getPublicClient(chainId) {
191
- const id = chainId ?? this.config.chainId;
192
- let client = this.publicClients.get(id);
193
- if (!client) {
194
- const chain = viemChains[id];
195
- const chainInfo = getChain(id);
196
- const rpcUrl = id === this.config.chainId ? this.config.rpcUrl : void 0;
197
- client = (0, import_viem.createPublicClient)({
198
- chain,
199
- transport: (0, import_viem.http)(rpcUrl ?? chainInfo?.rpcUrl)
200
- });
201
- this.publicClients.set(id, client);
379
+ this.read = config.read ?? new ViemReadClient(this.runtimeConfig);
380
+ this.assets = config.assets ?? new DefaultAssetRegistry();
381
+ if (config.execute) {
382
+ this.executor = config.execute;
383
+ } else if (config.signer) {
384
+ this.executor = new EOAExecutor(config.signer, this.runtimeConfig);
385
+ } else if (config.privateKey) {
386
+ this.executor = EOAExecutor.fromPrivateKey(
387
+ config.privateKey,
388
+ this.runtimeConfig
389
+ );
202
390
  }
203
- return client;
204
391
  }
205
- getWalletClient() {
206
- if (!this.walletClient) {
207
- if (!this.config.privateKey) {
208
- throw new Error(
209
- "No private key configured. Set privateKey in MoneyOS config."
210
- );
211
- }
212
- const account = (0, import_accounts.privateKeyToAccount)(this.config.privateKey);
213
- const chain = viemChains[this.config.chainId];
214
- const chainInfo = getChain(this.config.chainId);
215
- this.walletClient = (0, import_viem.createWalletClient)({
216
- account,
217
- chain,
218
- transport: (0, import_viem.http)(this.config.rpcUrl ?? chainInfo?.rpcUrl)
219
- });
220
- }
221
- return this.walletClient;
392
+ get runtime() {
393
+ return {
394
+ read: this.read,
395
+ execute: this.requireExecutor(),
396
+ assets: this.assets,
397
+ config: this.runtimeConfig
398
+ };
222
399
  }
223
400
  get address() {
224
- if (!this.config.privateKey) {
225
- throw new Error("No private key configured.");
401
+ return this.requireExecutor().getAddress();
402
+ }
403
+ requireExecutor() {
404
+ if (!this.executor) {
405
+ throw new Error(
406
+ "No signing account configured. Set `signer`, `privateKey`, or `execute` in MoneyOS config."
407
+ );
226
408
  }
227
- return (0, import_accounts.privateKeyToAccount)(this.config.privateKey).address;
409
+ return this.executor;
228
410
  }
229
411
  async balance(token, options) {
230
- const chainId = options?.chainId ?? this.config.chainId;
412
+ const chainId = options?.chainId ?? this.runtimeConfig.defaultChainId;
231
413
  const account = options?.address ?? this.address;
232
- const client = this.getPublicClient(chainId);
233
- if (token.toUpperCase() === "ETH") {
234
- const raw2 = await client.getBalance({ address: account });
414
+ const tokenAddress = this.assets.getTokenAddress(token, chainId);
415
+ if (!tokenAddress) {
416
+ throw new Error(`Token ${token} not found on chain ${chainId}`);
417
+ }
418
+ const tokenInfo = this.assets.getToken(token);
419
+ if (tokenAddress === NATIVE_TOKEN_ADDRESS) {
420
+ const raw2 = await this.read.getBalance({ address: account, chainId });
235
421
  return {
236
- token: "ETH",
237
- symbol: "ETH",
238
- amount: (0, import_viem.formatUnits)(raw2, 18),
422
+ token: tokenInfo.name,
423
+ symbol: tokenInfo.symbol,
424
+ amount: formatUnits2(raw2, tokenInfo.decimals),
239
425
  rawAmount: raw2,
240
- decimals: 18,
426
+ decimals: tokenInfo.decimals,
241
427
  chainId
242
428
  };
243
429
  }
244
- const tokenAddress = getTokenAddress(token, chainId);
245
- if (!tokenAddress) {
246
- throw new Error(`Token ${token} not found on chain ${chainId}`);
247
- }
248
- const tokenInfo = getToken(token);
249
- const raw = await client.readContract({
430
+ const raw = await this.read.readContract({
250
431
  address: tokenAddress,
251
- abi: ERC20_ABI,
432
+ abi: ERC20_ABI2,
252
433
  functionName: "balanceOf",
253
- args: [account]
434
+ args: [account],
435
+ chainId
254
436
  });
255
437
  return {
256
438
  token: tokenInfo.name,
257
439
  symbol: tokenInfo.symbol,
258
- amount: (0, import_viem.formatUnits)(raw, tokenInfo.decimals),
440
+ amount: formatUnits2(raw, tokenInfo.decimals),
259
441
  rawAmount: raw,
260
442
  decimals: tokenInfo.decimals,
261
443
  chainId
262
444
  };
263
445
  }
264
446
  async send(token, to, amount, options) {
265
- const chainId = options?.chainId ?? this.config.chainId;
266
- const walletClient = this.getWalletClient();
267
- const from = this.address;
268
- if (token.toUpperCase() === "ETH") {
269
- const value2 = (0, import_viem.parseUnits)(amount, 18);
270
- const account = (0, import_accounts.privateKeyToAccount)(this.config.privateKey);
271
- const hash2 = await walletClient.sendTransaction({
272
- account,
273
- to,
274
- value: value2,
275
- chain: viemChains[chainId]
276
- });
277
- return { hash: hash2, from, to, amount, token: "ETH", chainId };
278
- }
279
- const tokenAddress = getTokenAddress(token, chainId);
447
+ const execute = this.requireExecutor();
448
+ const chainId = options?.chainId ?? this.runtimeConfig.defaultChainId;
449
+ const from = execute.getAddress();
450
+ const tokenAddress = this.assets.getTokenAddress(token, chainId);
280
451
  if (!tokenAddress) {
281
452
  throw new Error(`Token ${token} not found on chain ${chainId}`);
282
453
  }
283
- const tokenInfo = getToken(token);
284
- const value = (0, import_viem.parseUnits)(amount, tokenInfo.decimals);
285
- const client = this.getPublicClient(chainId);
286
- const { request } = await client.simulateContract({
287
- address: tokenAddress,
288
- abi: ERC20_ABI,
289
- functionName: "transfer",
290
- args: [to, value],
291
- account: (0, import_accounts.privateKeyToAccount)(this.config.privateKey)
292
- });
293
- const hash = await walletClient.writeContract(request);
294
- return { hash, from, to, amount, token: tokenInfo.symbol, chainId };
295
- }
296
- async swap(tokenIn, tokenOut, amount, provider, options) {
297
- const chainId = options?.chainId ?? this.config.chainId;
298
- const walletClient = this.getWalletClient();
299
- const client = this.getPublicClient(chainId);
300
- const sender = this.address;
301
- const tokenInAddress = getTokenAddress(tokenIn, chainId);
302
- const tokenOutAddress = getTokenAddress(tokenOut, chainId);
303
- if (!tokenInAddress) {
304
- throw new Error(`Token ${tokenIn} not found on chain ${chainId}`);
305
- }
306
- if (!tokenOutAddress) {
307
- throw new Error(`Token ${tokenOut} not found on chain ${chainId}`);
308
- }
309
- const tokenInInfo = getToken(tokenIn);
310
- const amountWei = (0, import_viem.parseUnits)(amount, tokenInInfo.decimals);
311
- const quote = await provider.getQuote({
312
- chainId,
313
- tokenIn: tokenInAddress,
314
- tokenOut: tokenOutAddress,
315
- amount: amountWei,
316
- sender,
317
- slippage: options?.slippage
318
- });
319
- const calldata = await provider.getCalldata(quote);
320
- const isNativeIn = tokenInAddress === NATIVE_TOKEN_ADDRESS;
321
- if (!isNativeIn) {
322
- const currentAllowance = await client.readContract({
323
- address: tokenInAddress,
324
- abi: ERC20_ABI,
325
- functionName: "allowance",
326
- args: [sender, calldata.to]
327
- });
328
- if (currentAllowance < amountWei) {
329
- const account2 = (0, import_accounts.privateKeyToAccount)(this.config.privateKey);
330
- const { request: approveRequest } = await client.simulateContract({
331
- address: tokenInAddress,
332
- abi: ERC20_ABI,
333
- functionName: "approve",
334
- args: [calldata.to, amountWei],
335
- account: account2
336
- });
337
- await walletClient.writeContract(approveRequest);
338
- }
454
+ const tokenInfo = this.assets.getToken(token);
455
+ const value = parseUnits2(amount, tokenInfo.decimals);
456
+ if (tokenAddress === NATIVE_TOKEN_ADDRESS) {
457
+ const result2 = await execute.send({ to, value, chainId });
458
+ return {
459
+ hash: result2.hash,
460
+ from,
461
+ to,
462
+ amount,
463
+ token: tokenInfo.symbol,
464
+ chainId
465
+ };
339
466
  }
340
- const account = (0, import_accounts.privateKeyToAccount)(this.config.privateKey);
341
- const hash = await walletClient.sendTransaction({
342
- account,
343
- to: calldata.to,
344
- data: calldata.data,
345
- value: isNativeIn ? amountWei : calldata.value,
346
- chain: viemChains[chainId]
467
+ const data = encodeFunctionData2({
468
+ abi: ERC20_ABI2,
469
+ functionName: "transfer",
470
+ args: [to, value]
347
471
  });
348
- const tokenOutInfo = getToken(tokenOut);
472
+ const result = await execute.send({ to: tokenAddress, data, chainId });
349
473
  return {
350
- hash,
351
- tokenIn: tokenInInfo.symbol,
352
- tokenOut: tokenOutInfo.symbol,
353
- amountIn: amount,
354
- amountOut: (0, import_viem.formatUnits)(BigInt(quote.expectedOut), tokenOutInfo.decimals),
474
+ hash: result.hash,
475
+ from,
476
+ to,
477
+ amount,
478
+ token: tokenInfo.symbol,
355
479
  chainId
356
480
  };
357
481
  }
482
+ async swap(tokenIn, tokenOut, amount, provider, options) {
483
+ const execute = this.requireExecutor();
484
+ const chainId = options?.chainId ?? this.runtimeConfig.defaultChainId;
485
+ return executeSwap(
486
+ { tokenIn, tokenOut, amount, provider, chainId, slippage: options?.slippage },
487
+ { read: this.read, execute, assets: this.assets }
488
+ );
489
+ }
358
490
  };
359
491
 
360
492
  // src/providers/odos.ts
361
493
  var ODOS_API = "https://api.odos.xyz";
494
+ var ODOS_NATIVE_ADDRESS = "0x0000000000000000000000000000000000000000";
362
495
  var OdosProvider = class {
363
496
  name = "odos";
364
497
  apiKey;
365
498
  constructor(options) {
366
499
  this.apiKey = options?.apiKey;
367
500
  }
501
+ toOdosAddress(address) {
502
+ return address === NATIVE_TOKEN_ADDRESS ? ODOS_NATIVE_ADDRESS : address;
503
+ }
368
504
  async getQuote(params) {
369
505
  const headers = {
370
506
  "Content-Type": "application/json"
@@ -379,13 +515,13 @@ var OdosProvider = class {
379
515
  chainId: params.chainId,
380
516
  inputTokens: [
381
517
  {
382
- tokenAddress: params.tokenIn,
518
+ tokenAddress: this.toOdosAddress(params.tokenIn),
383
519
  amount: params.amount.toString()
384
520
  }
385
521
  ],
386
522
  outputTokens: [
387
523
  {
388
- tokenAddress: params.tokenOut,
524
+ tokenAddress: this.toOdosAddress(params.tokenOut),
389
525
  proportion: 1
390
526
  }
391
527
  ],
@@ -407,7 +543,8 @@ var OdosProvider = class {
407
543
  expectedOut: data.outAmounts[0],
408
544
  router: "",
409
545
  chainId: params.chainId,
410
- pathId: data.pathId
546
+ pathId: data.pathId,
547
+ sender: params.sender
411
548
  };
412
549
  }
413
550
  async getCalldata(quote) {
@@ -421,7 +558,7 @@ var OdosProvider = class {
421
558
  method: "POST",
422
559
  headers,
423
560
  body: JSON.stringify({
424
- userAddr: quote.tokenIn,
561
+ userAddr: quote.sender,
425
562
  pathId: quote.pathId,
426
563
  simulate: false
427
564
  })
@@ -438,16 +575,118 @@ var OdosProvider = class {
438
575
  };
439
576
  }
440
577
  };
441
- // Annotate the CommonJS export names for ESM import in node:
442
- 0 && (module.exports = {
578
+
579
+ // src/core/access-local.ts
580
+ import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
581
+ var LocalAccessSession = class {
582
+ kind = "local";
583
+ address;
584
+ constructor(privateKey) {
585
+ this.address = privateKeyToAccount2(privateKey).address;
586
+ }
587
+ getAddress() {
588
+ return this.address;
589
+ }
590
+ };
591
+ var LocalAccessAdapter = class {
592
+ name = "local";
593
+ privateKey;
594
+ constructor(privateKey) {
595
+ this.privateKey = privateKey;
596
+ }
597
+ async openSession(_ctx) {
598
+ return new LocalAccessSession(this.privateKey);
599
+ }
600
+ };
601
+
602
+ // src/core/keystore-file.ts
603
+ import { existsSync, readFileSync } from "fs";
604
+ import { homedir } from "os";
605
+ import { join } from "path";
606
+ import { privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
607
+ var DEFAULT_CONFIG_PATH = join(homedir(), ".moneyos", "config.json");
608
+ var FileKeyStore = class {
609
+ kind = "file";
610
+ configPath;
611
+ constructor(options = {}) {
612
+ this.configPath = options.configPath ?? DEFAULT_CONFIG_PATH;
613
+ }
614
+ async hasKey() {
615
+ const config = this.readConfig();
616
+ return Boolean(config?.privateKey);
617
+ }
618
+ async metadata() {
619
+ const config = this.readConfig();
620
+ const address = config?.privateKey ? privateKeyToAccount3(config.privateKey).address : void 0;
621
+ return {
622
+ kind: "file",
623
+ address
624
+ };
625
+ }
626
+ async loadSigner() {
627
+ const config = this.readConfig();
628
+ if (!config?.privateKey) {
629
+ throw new Error(
630
+ `FileKeyStore: no private key found at ${this.configPath}. Run \`moneyos init\` to create one.`
631
+ );
632
+ }
633
+ return privateKeyToManagedAccount(config.privateKey);
634
+ }
635
+ /**
636
+ * Read and validate the config file. Returns `undefined` if the file does
637
+ * not exist. Throws with context for any other failure mode (malformed
638
+ * JSON, wrong top-level type, wrong `privateKey` type). The hex format of
639
+ * the key itself is validated later by viem's `privateKeyToAccount`.
640
+ */
641
+ readConfig() {
642
+ if (!existsSync(this.configPath)) {
643
+ return void 0;
644
+ }
645
+ let parsed;
646
+ try {
647
+ parsed = JSON.parse(readFileSync(this.configPath, "utf-8"));
648
+ } catch (error) {
649
+ throw new Error(
650
+ `FileKeyStore: failed to read config at ${this.configPath}`,
651
+ { cause: error }
652
+ );
653
+ }
654
+ if (typeof parsed !== "object" || parsed === null) {
655
+ throw new Error(
656
+ `FileKeyStore: config at ${this.configPath} is not a JSON object`
657
+ );
658
+ }
659
+ const pk = parsed.privateKey;
660
+ if (pk === void 0 || pk === null) {
661
+ return {};
662
+ }
663
+ if (typeof pk !== "string") {
664
+ throw new Error(
665
+ `FileKeyStore: privateKey at ${this.configPath} must be a string`
666
+ );
667
+ }
668
+ return { privateKey: pk };
669
+ }
670
+ };
671
+
672
+ // src/core/factory.ts
673
+ function createMoneyOS(config) {
674
+ return new MoneyOS(config);
675
+ }
676
+ export {
677
+ EOAExecutor,
678
+ FileKeyStore,
679
+ LocalAccessAdapter,
443
680
  MoneyOS,
444
681
  NATIVE_TOKEN_ADDRESS,
445
682
  OdosProvider,
683
+ ViemReadClient,
446
684
  chains,
685
+ createMoneyOS,
447
686
  defaultChain,
448
687
  getChain,
449
688
  getToken,
450
689
  getTokenAddress,
451
690
  tokens
452
- });
691
+ };
453
692
  //# sourceMappingURL=index.js.map