@mycelium-sdk/core 1.0.0-alpha.0 → 1.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +190 -49
- package/dist/index.cjs +618 -278
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +194 -128
- package/dist/index.d.ts +194 -128
- package/dist/index.js +619 -284
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -348,9 +348,9 @@ var DefaultSmartWallet = class extends SmartWallet {
|
|
|
348
348
|
* @param amount Human-readable amount string
|
|
349
349
|
* @returns Transaction result for the deposit
|
|
350
350
|
*/
|
|
351
|
-
async earn(amount) {
|
|
351
|
+
async earn(vaultInfo, amount) {
|
|
352
352
|
this.chainManager.getSupportedChain();
|
|
353
|
-
const depositTransactionResult = this.protocolProvider.deposit(amount, this);
|
|
353
|
+
const depositTransactionResult = this.protocolProvider.deposit(vaultInfo, amount, this);
|
|
354
354
|
return depositTransactionResult;
|
|
355
355
|
}
|
|
356
356
|
/**
|
|
@@ -361,13 +361,9 @@ var DefaultSmartWallet = class extends SmartWallet {
|
|
|
361
361
|
* @category Earn
|
|
362
362
|
* @returns Vault balance or `null` if nothing deposited
|
|
363
363
|
*/
|
|
364
|
-
async
|
|
365
|
-
const depositedVault = await this.protocolProvider.fetchDepositedVaults(this);
|
|
366
|
-
if (!depositedVault) {
|
|
367
|
-
return null;
|
|
368
|
-
}
|
|
364
|
+
async getEarnBalances() {
|
|
369
365
|
const userAddress = await this.getAddress();
|
|
370
|
-
return this.protocolProvider.
|
|
366
|
+
return this.protocolProvider.getBalances(userAddress);
|
|
371
367
|
}
|
|
372
368
|
/**
|
|
373
369
|
* Withdraws from the selected protocol’s vault
|
|
@@ -378,8 +374,8 @@ var DefaultSmartWallet = class extends SmartWallet {
|
|
|
378
374
|
* @throws Error if the withdrawal fails
|
|
379
375
|
* @throws Error a user didn't deposit anything
|
|
380
376
|
*/
|
|
381
|
-
async withdraw(amount) {
|
|
382
|
-
const withdrawTransactionResult = await this.protocolProvider.withdraw(amount, this);
|
|
377
|
+
async withdraw(vaultInfo, amount) {
|
|
378
|
+
const withdrawTransactionResult = await this.protocolProvider.withdraw(vaultInfo, amount, this);
|
|
383
379
|
return withdrawTransactionResult;
|
|
384
380
|
}
|
|
385
381
|
/**
|
|
@@ -416,7 +412,7 @@ var DefaultSmartWallet = class extends SmartWallet {
|
|
|
416
412
|
return hash;
|
|
417
413
|
} catch (error) {
|
|
418
414
|
throw new Error(
|
|
419
|
-
`Failed to send transaction: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
415
|
+
`Failed to send transaction: ${error instanceof Error ? error.message.toString().slice(0, 100) : "Unknown error"}`
|
|
420
416
|
);
|
|
421
417
|
}
|
|
422
418
|
}
|
|
@@ -639,127 +635,6 @@ var chainById = Object.values(viemChains).reduce(
|
|
|
639
635
|
{}
|
|
640
636
|
);
|
|
641
637
|
|
|
642
|
-
// src/types/logger.ts
|
|
643
|
-
var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
644
|
-
LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
|
|
645
|
-
LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
|
|
646
|
-
LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
|
|
647
|
-
LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
|
|
648
|
-
return LogLevel2;
|
|
649
|
-
})(LogLevel || {});
|
|
650
|
-
|
|
651
|
-
// src/tools/Logger.ts
|
|
652
|
-
var Logger = class _Logger {
|
|
653
|
-
/**
|
|
654
|
-
* Create a new logger instance
|
|
655
|
-
* @param logLevel Initial log level, defaults to DEBUG
|
|
656
|
-
*/
|
|
657
|
-
constructor(logLevel = 0 /* DEBUG */) {
|
|
658
|
-
this.logs = [];
|
|
659
|
-
this.maxLogs = 1e3;
|
|
660
|
-
this.logLevel = logLevel;
|
|
661
|
-
}
|
|
662
|
-
/**
|
|
663
|
-
* Get singleton instance of the logger
|
|
664
|
-
* @param logLevel Optional log level to initialize if instance not yet created
|
|
665
|
-
* @returns Logger instance
|
|
666
|
-
*/
|
|
667
|
-
static getInstance(logLevel) {
|
|
668
|
-
if (!_Logger.instance) {
|
|
669
|
-
_Logger.instance = new _Logger(logLevel);
|
|
670
|
-
}
|
|
671
|
-
return _Logger.instance;
|
|
672
|
-
}
|
|
673
|
-
/** Set the log level */
|
|
674
|
-
setLogLevel(level) {
|
|
675
|
-
this.logLevel = level;
|
|
676
|
-
}
|
|
677
|
-
/** Get the current log level */
|
|
678
|
-
getLogLevel() {
|
|
679
|
-
return this.logLevel;
|
|
680
|
-
}
|
|
681
|
-
/** Internal check if a message should be logged */
|
|
682
|
-
shouldLog(level) {
|
|
683
|
-
return level >= this.logLevel;
|
|
684
|
-
}
|
|
685
|
-
/** Format log message into a readable string */
|
|
686
|
-
formatMessage(level, message, data, context) {
|
|
687
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
688
|
-
const levelName = LogLevel[level];
|
|
689
|
-
const contextStr = context ? `[${context}]` : "";
|
|
690
|
-
return `${timestamp} ${levelName}${contextStr}: ${message}`;
|
|
691
|
-
}
|
|
692
|
-
/** Add a log entry and output to console */
|
|
693
|
-
addLog(level, message, data, context) {
|
|
694
|
-
if (!this.shouldLog(level)) {
|
|
695
|
-
return;
|
|
696
|
-
}
|
|
697
|
-
const logEntry = {
|
|
698
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
699
|
-
level,
|
|
700
|
-
message,
|
|
701
|
-
data,
|
|
702
|
-
context
|
|
703
|
-
};
|
|
704
|
-
this.logs.push(logEntry);
|
|
705
|
-
if (this.logs.length > this.maxLogs) {
|
|
706
|
-
this.logs = this.logs.slice(-this.maxLogs);
|
|
707
|
-
}
|
|
708
|
-
const formattedMessage = this.formatMessage(level, message, data, context);
|
|
709
|
-
switch (level) {
|
|
710
|
-
case 0 /* DEBUG */:
|
|
711
|
-
console.debug(`\u{1F50D} ${formattedMessage}`, data || "");
|
|
712
|
-
break;
|
|
713
|
-
case 1 /* INFO */:
|
|
714
|
-
console.info(`\u2139\uFE0F ${formattedMessage}`, data || "");
|
|
715
|
-
break;
|
|
716
|
-
case 2 /* WARN */:
|
|
717
|
-
console.warn(`\u26A0\uFE0F ${formattedMessage}`, data || "");
|
|
718
|
-
break;
|
|
719
|
-
case 3 /* ERROR */:
|
|
720
|
-
console.error(`\u274C ${formattedMessage}`, data || "");
|
|
721
|
-
break;
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
/** Log a debug message */
|
|
725
|
-
debug(message, data, context) {
|
|
726
|
-
this.addLog(0 /* DEBUG */, message, data, context);
|
|
727
|
-
}
|
|
728
|
-
/** Log an info message */
|
|
729
|
-
info(message, data, context) {
|
|
730
|
-
this.addLog(1 /* INFO */, message, data, context);
|
|
731
|
-
}
|
|
732
|
-
/** Log a warning message */
|
|
733
|
-
warn(message, data, context) {
|
|
734
|
-
this.addLog(2 /* WARN */, message, data, context);
|
|
735
|
-
}
|
|
736
|
-
/** Log an error message */
|
|
737
|
-
error(message, data, context) {
|
|
738
|
-
this.addLog(3 /* ERROR */, message, data, context);
|
|
739
|
-
}
|
|
740
|
-
/** Get all logs */
|
|
741
|
-
getLogs() {
|
|
742
|
-
return [...this.logs];
|
|
743
|
-
}
|
|
744
|
-
/** Get logs by level */
|
|
745
|
-
getLogsByLevel(level) {
|
|
746
|
-
return this.logs.filter((log) => log.level === level);
|
|
747
|
-
}
|
|
748
|
-
/** Clear all logs */
|
|
749
|
-
clearLogs() {
|
|
750
|
-
this.logs = [];
|
|
751
|
-
}
|
|
752
|
-
/** Export logs as a JSON string */
|
|
753
|
-
exportLogs() {
|
|
754
|
-
return JSON.stringify(this.logs, null, 2);
|
|
755
|
-
}
|
|
756
|
-
/** Set maximum number of logs to retain in memory */
|
|
757
|
-
setMaxLogs(max) {
|
|
758
|
-
this.maxLogs = max;
|
|
759
|
-
}
|
|
760
|
-
};
|
|
761
|
-
var logger = Logger.getInstance();
|
|
762
|
-
|
|
763
638
|
// src/tools/ChainManager.ts
|
|
764
639
|
var ChainManager = class {
|
|
765
640
|
/**
|
|
@@ -815,7 +690,6 @@ var ChainManager = class {
|
|
|
815
690
|
if (!bundlerUrl) {
|
|
816
691
|
throw new Error(`No bundler URL configured for chain ID: ${chainId}`);
|
|
817
692
|
}
|
|
818
|
-
logger.info("Public client setup:", { bundlerUrl, chainId }, "ChainManager");
|
|
819
693
|
const client = createPublicClient({
|
|
820
694
|
chain: this.getChain(chainId),
|
|
821
695
|
transport: http(rpcUrl)
|
|
@@ -1048,7 +922,7 @@ var DefaultSmartWalletProvider = class extends SmartWalletProvider {
|
|
|
1048
922
|
constructor(chainManager, protocol, coinbaseCDP) {
|
|
1049
923
|
super();
|
|
1050
924
|
this.chainManager = chainManager;
|
|
1051
|
-
this.protocolProvider = protocol
|
|
925
|
+
this.protocolProvider = protocol;
|
|
1052
926
|
this.coinbaseCDP = coinbaseCDP;
|
|
1053
927
|
}
|
|
1054
928
|
/**
|
|
@@ -1311,8 +1185,7 @@ var WalletProvider = class {
|
|
|
1311
1185
|
throw new Error(
|
|
1312
1186
|
"Either walletAddress or deploymentOwners array must be provided to locate the smart wallet"
|
|
1313
1187
|
);
|
|
1314
|
-
} catch
|
|
1315
|
-
logger.error("Error getting smart wallet", error, "WalletProvider");
|
|
1188
|
+
} catch {
|
|
1316
1189
|
throw new Error(
|
|
1317
1190
|
"Either walletAddress or deploymentOwners array must be provided to locate the smart wallet"
|
|
1318
1191
|
);
|
|
@@ -1358,6 +1231,127 @@ var EmbeddedWallet = class {
|
|
|
1358
1231
|
}
|
|
1359
1232
|
};
|
|
1360
1233
|
|
|
1234
|
+
// src/types/logger.ts
|
|
1235
|
+
var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
1236
|
+
LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
|
|
1237
|
+
LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
|
|
1238
|
+
LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
|
|
1239
|
+
LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
|
|
1240
|
+
return LogLevel2;
|
|
1241
|
+
})(LogLevel || {});
|
|
1242
|
+
|
|
1243
|
+
// src/tools/Logger.ts
|
|
1244
|
+
var Logger = class _Logger {
|
|
1245
|
+
/**
|
|
1246
|
+
* Create a new logger instance
|
|
1247
|
+
* @param logLevel Initial log level, defaults to DEBUG
|
|
1248
|
+
*/
|
|
1249
|
+
constructor(logLevel = 0 /* DEBUG */) {
|
|
1250
|
+
this.logs = [];
|
|
1251
|
+
this.maxLogs = 1e3;
|
|
1252
|
+
this.logLevel = logLevel;
|
|
1253
|
+
}
|
|
1254
|
+
/**
|
|
1255
|
+
* Get singleton instance of the logger
|
|
1256
|
+
* @param logLevel Optional log level to initialize if instance not yet created
|
|
1257
|
+
* @returns Logger instance
|
|
1258
|
+
*/
|
|
1259
|
+
static getInstance(logLevel) {
|
|
1260
|
+
if (!_Logger.instance) {
|
|
1261
|
+
_Logger.instance = new _Logger(logLevel);
|
|
1262
|
+
}
|
|
1263
|
+
return _Logger.instance;
|
|
1264
|
+
}
|
|
1265
|
+
/** Set the log level */
|
|
1266
|
+
setLogLevel(level) {
|
|
1267
|
+
this.logLevel = level;
|
|
1268
|
+
}
|
|
1269
|
+
/** Get the current log level */
|
|
1270
|
+
getLogLevel() {
|
|
1271
|
+
return this.logLevel;
|
|
1272
|
+
}
|
|
1273
|
+
/** Internal check if a message should be logged */
|
|
1274
|
+
shouldLog(level) {
|
|
1275
|
+
return level >= this.logLevel;
|
|
1276
|
+
}
|
|
1277
|
+
/** Format log message into a readable string */
|
|
1278
|
+
formatMessage(level, message, data, context) {
|
|
1279
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1280
|
+
const levelName = LogLevel[level];
|
|
1281
|
+
const contextStr = context ? `[${context}]` : "";
|
|
1282
|
+
return `${timestamp} ${levelName}${contextStr}: ${message}`;
|
|
1283
|
+
}
|
|
1284
|
+
/** Add a log entry and output to console */
|
|
1285
|
+
addLog(level, message, data, context) {
|
|
1286
|
+
if (!this.shouldLog(level)) {
|
|
1287
|
+
return;
|
|
1288
|
+
}
|
|
1289
|
+
const logEntry = {
|
|
1290
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1291
|
+
level,
|
|
1292
|
+
message,
|
|
1293
|
+
data,
|
|
1294
|
+
context
|
|
1295
|
+
};
|
|
1296
|
+
this.logs.push(logEntry);
|
|
1297
|
+
if (this.logs.length > this.maxLogs) {
|
|
1298
|
+
this.logs = this.logs.slice(-this.maxLogs);
|
|
1299
|
+
}
|
|
1300
|
+
const formattedMessage = this.formatMessage(level, message, data, context);
|
|
1301
|
+
switch (level) {
|
|
1302
|
+
case 0 /* DEBUG */:
|
|
1303
|
+
console.debug(`\u{1F50D} ${formattedMessage}`, data || "");
|
|
1304
|
+
break;
|
|
1305
|
+
case 1 /* INFO */:
|
|
1306
|
+
console.info(`\u2139\uFE0F ${formattedMessage}`, data || "");
|
|
1307
|
+
break;
|
|
1308
|
+
case 2 /* WARN */:
|
|
1309
|
+
console.warn(`\u26A0\uFE0F ${formattedMessage}`, data || "");
|
|
1310
|
+
break;
|
|
1311
|
+
case 3 /* ERROR */:
|
|
1312
|
+
console.error(`\u274C ${formattedMessage}`, data || "");
|
|
1313
|
+
break;
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
/** Log a debug message */
|
|
1317
|
+
debug(message, data, context) {
|
|
1318
|
+
this.addLog(0 /* DEBUG */, message, data, context);
|
|
1319
|
+
}
|
|
1320
|
+
/** Log an info message */
|
|
1321
|
+
info(message, data, context) {
|
|
1322
|
+
this.addLog(1 /* INFO */, message, data, context);
|
|
1323
|
+
}
|
|
1324
|
+
/** Log a warning message */
|
|
1325
|
+
warn(message, data, context) {
|
|
1326
|
+
this.addLog(2 /* WARN */, message, data, context);
|
|
1327
|
+
}
|
|
1328
|
+
/** Log an error message */
|
|
1329
|
+
error(message, data, context) {
|
|
1330
|
+
this.addLog(3 /* ERROR */, message, data, context);
|
|
1331
|
+
}
|
|
1332
|
+
/** Get all logs */
|
|
1333
|
+
getLogs() {
|
|
1334
|
+
return [...this.logs];
|
|
1335
|
+
}
|
|
1336
|
+
/** Get logs by level */
|
|
1337
|
+
getLogsByLevel(level) {
|
|
1338
|
+
return this.logs.filter((log) => log.level === level);
|
|
1339
|
+
}
|
|
1340
|
+
/** Clear all logs */
|
|
1341
|
+
clearLogs() {
|
|
1342
|
+
this.logs = [];
|
|
1343
|
+
}
|
|
1344
|
+
/** Export logs as a JSON string */
|
|
1345
|
+
exportLogs() {
|
|
1346
|
+
return JSON.stringify(this.logs, null, 2);
|
|
1347
|
+
}
|
|
1348
|
+
/** Set maximum number of logs to retain in memory */
|
|
1349
|
+
setMaxLogs(max) {
|
|
1350
|
+
this.maxLogs = max;
|
|
1351
|
+
}
|
|
1352
|
+
};
|
|
1353
|
+
var logger = Logger.getInstance();
|
|
1354
|
+
|
|
1361
1355
|
// src/wallet/PrivyWallet.ts
|
|
1362
1356
|
var PrivyWallet = class extends EmbeddedWallet {
|
|
1363
1357
|
/**
|
|
@@ -1681,12 +1675,7 @@ var BaseProtocol = class {
|
|
|
1681
1675
|
};
|
|
1682
1676
|
|
|
1683
1677
|
// src/protocols/implementations/SparkProtocol.ts
|
|
1684
|
-
import {
|
|
1685
|
-
encodeFunctionData as encodeFunctionData2,
|
|
1686
|
-
erc20Abi as erc20Abi4,
|
|
1687
|
-
formatUnits as formatUnits2,
|
|
1688
|
-
parseUnits as parseUnits2
|
|
1689
|
-
} from "viem";
|
|
1678
|
+
import { encodeFunctionData as encodeFunctionData2, erc20Abi as erc20Abi4, formatUnits as formatUnits2, parseUnits as parseUnits2 } from "viem";
|
|
1690
1679
|
|
|
1691
1680
|
// src/abis/protocols/spark.ts
|
|
1692
1681
|
var SPARK_VAULT_ABI = [
|
|
@@ -1748,14 +1737,14 @@ var SPARK_SSR_ORACLE_ADDRESS = "0x65d946e533748A998B1f0E430803e39A6388f7a1";
|
|
|
1748
1737
|
var SPARK_VAULT = [
|
|
1749
1738
|
{
|
|
1750
1739
|
id: "sUSDC",
|
|
1740
|
+
protocolId: "spark",
|
|
1741
|
+
name: "sUSDC",
|
|
1742
|
+
type: "stable",
|
|
1751
1743
|
chain: "base",
|
|
1752
1744
|
vaultAddress: "0x3128a0f7f0ea68e7b7c9b00afa7e41045828e858",
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
earnTokenAddress: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
|
|
1757
|
-
earnTokenDecimals: 18,
|
|
1758
|
-
earnTokenSymbol: "sUSDC",
|
|
1745
|
+
tokenAddress: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
|
|
1746
|
+
tokenDecimals: 6,
|
|
1747
|
+
tokenSymbol: "USDC",
|
|
1759
1748
|
metadata: {}
|
|
1760
1749
|
}
|
|
1761
1750
|
];
|
|
@@ -1766,6 +1755,7 @@ var RAY = BigInt("1000000000000000000000000000");
|
|
|
1766
1755
|
var SparkProtocol = class extends BaseProtocol {
|
|
1767
1756
|
constructor() {
|
|
1768
1757
|
super(...arguments);
|
|
1758
|
+
/** All Spark vaults */
|
|
1769
1759
|
this.allVaults = [];
|
|
1770
1760
|
}
|
|
1771
1761
|
/**
|
|
@@ -1776,7 +1766,7 @@ var SparkProtocol = class extends BaseProtocol {
|
|
|
1776
1766
|
this.chainManager = chainManager;
|
|
1777
1767
|
this.selectedChainId = chainManager.getSupportedChain();
|
|
1778
1768
|
this.publicClient = chainManager.getPublicClient(this.selectedChainId);
|
|
1779
|
-
this.allVaults =
|
|
1769
|
+
this.allVaults = SPARK_VAULT;
|
|
1780
1770
|
}
|
|
1781
1771
|
/**
|
|
1782
1772
|
* Get the SSR (Sky Saving Rate) of the Spark protocol
|
|
@@ -1806,88 +1796,54 @@ var SparkProtocol = class extends BaseProtocol {
|
|
|
1806
1796
|
async getAPY() {
|
|
1807
1797
|
const ssr = await this.getSSR();
|
|
1808
1798
|
const apy = Math.exp(Math.log(ssr) * SECONDS_PER_YEAR) - 1;
|
|
1809
|
-
return
|
|
1810
|
-
}
|
|
1811
|
-
/**
|
|
1812
|
-
*
|
|
1813
|
-
* Get all vault info from a Spark protocol
|
|
1814
|
-
* @returns The list of vaults
|
|
1815
|
-
*/
|
|
1816
|
-
getVaults() {
|
|
1817
|
-
return SPARK_VAULT;
|
|
1799
|
+
return apy;
|
|
1818
1800
|
}
|
|
1819
1801
|
/**
|
|
1820
|
-
* Get the best available Spark
|
|
1821
|
-
* @
|
|
1802
|
+
* Get the best available Spark vaults
|
|
1803
|
+
* @remarks Currently, the vault is only one and relates to sUSDC. Currently return only one stable vault
|
|
1804
|
+
* @returns Best Spark vaults in 2 groups: stable and non-stable
|
|
1822
1805
|
* @throws Error if no vaults found
|
|
1823
1806
|
*/
|
|
1824
|
-
async
|
|
1807
|
+
async getBestVaults() {
|
|
1825
1808
|
if (this.allVaults.length === 0) {
|
|
1826
1809
|
throw new Error("No vaults found");
|
|
1827
1810
|
}
|
|
1828
1811
|
const selectedVault = this.allVaults[0];
|
|
1829
1812
|
selectedVault.metadata.apy = await this.getAPY();
|
|
1830
|
-
return
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
* @param smartWallet Smart wallet to inspect
|
|
1835
|
-
* @returns The vault with user deposits, or null if none found
|
|
1836
|
-
*/
|
|
1837
|
-
async fetchDepositedVaults(smartWallet) {
|
|
1838
|
-
let depositedVault = void 0;
|
|
1839
|
-
const userAddress = await smartWallet.getAddress();
|
|
1840
|
-
for (const vault of this.allVaults) {
|
|
1841
|
-
const balance = await this.getBalance(vault, userAddress);
|
|
1842
|
-
if (parseInt(balance.depositedAmount) > 0) {
|
|
1843
|
-
depositedVault = vault;
|
|
1844
|
-
}
|
|
1845
|
-
}
|
|
1846
|
-
if (depositedVault) {
|
|
1847
|
-
depositedVault.metadata.apy = await this.getAPY();
|
|
1848
|
-
}
|
|
1849
|
-
logger.info("Deposited vaults:", { depositedVault }, "SparkProtocol");
|
|
1850
|
-
return depositedVault || null;
|
|
1813
|
+
return {
|
|
1814
|
+
stable: [selectedVault],
|
|
1815
|
+
nonStable: []
|
|
1816
|
+
};
|
|
1851
1817
|
}
|
|
1852
1818
|
/**
|
|
1853
1819
|
* Deposit funds into a Spark vault
|
|
1820
|
+
* @param vaultInfo Vault information
|
|
1854
1821
|
* @param amount Amount to deposit (human-readable)
|
|
1855
1822
|
* @param smartWallet Smart wallet instance to use
|
|
1856
1823
|
* @returns Transaction result with hash
|
|
1857
1824
|
*/
|
|
1858
|
-
async deposit(amount, smartWallet) {
|
|
1859
|
-
const depositedVault = await this.fetchDepositedVaults(smartWallet);
|
|
1860
|
-
let vaultInfoToDeposit;
|
|
1861
|
-
logger.info("Previously deposited vault:", { depositedVault }, "SparkProtocol");
|
|
1862
|
-
if (depositedVault) {
|
|
1863
|
-
vaultInfoToDeposit = depositedVault;
|
|
1864
|
-
} else {
|
|
1865
|
-
vaultInfoToDeposit = await this.getBestVault();
|
|
1866
|
-
logger.info("Best vault that found:", { bestVault: vaultInfoToDeposit }, "SparkProtocol");
|
|
1867
|
-
}
|
|
1825
|
+
async deposit(vaultInfo, amount, smartWallet) {
|
|
1868
1826
|
const owner = await smartWallet.getAddress();
|
|
1869
|
-
const assets = parseUnits2(amount,
|
|
1870
|
-
logger.info("Raw deposit amount:", { amount, assets }, "SparkProtocol");
|
|
1827
|
+
const assets = parseUnits2(amount, vaultInfo.tokenDecimals);
|
|
1871
1828
|
const allowance = await this.checkAllowance(
|
|
1872
|
-
|
|
1873
|
-
|
|
1829
|
+
vaultInfo.tokenAddress,
|
|
1830
|
+
vaultInfo.vaultAddress,
|
|
1874
1831
|
owner,
|
|
1875
1832
|
this.selectedChainId
|
|
1876
1833
|
);
|
|
1877
|
-
logger.info("Current vault contract allowance:", { allowance }, "SparkProtocol");
|
|
1878
1834
|
const ops = [];
|
|
1879
1835
|
if (allowance < assets) {
|
|
1880
1836
|
ops.push({
|
|
1881
|
-
to:
|
|
1837
|
+
to: vaultInfo.tokenAddress,
|
|
1882
1838
|
data: encodeFunctionData2({
|
|
1883
1839
|
abi: erc20Abi4,
|
|
1884
1840
|
functionName: "approve",
|
|
1885
|
-
args: [
|
|
1841
|
+
args: [vaultInfo.vaultAddress, assets]
|
|
1886
1842
|
})
|
|
1887
1843
|
});
|
|
1888
1844
|
}
|
|
1889
1845
|
ops.push({
|
|
1890
|
-
to:
|
|
1846
|
+
to: vaultInfo.vaultAddress,
|
|
1891
1847
|
data: encodeFunctionData2({
|
|
1892
1848
|
abi: SPARK_VAULT_ABI,
|
|
1893
1849
|
functionName: "deposit",
|
|
@@ -1899,23 +1855,19 @@ var SparkProtocol = class extends BaseProtocol {
|
|
|
1899
1855
|
}
|
|
1900
1856
|
/**
|
|
1901
1857
|
* Withdraw funds from a Spark vault
|
|
1902
|
-
* @param
|
|
1858
|
+
* @param vaultInfo Vault information
|
|
1859
|
+
* @param amount Amount in base token units (or undefined to withdraw all)
|
|
1903
1860
|
* @param smartWallet Smart wallet instance to withdraw from
|
|
1904
1861
|
* @returns Transaction result with hash
|
|
1905
1862
|
* @throws Error if no deposited vault found
|
|
1906
1863
|
*/
|
|
1907
|
-
async withdraw(
|
|
1908
|
-
const depositedVault = await this.fetchDepositedVaults(smartWallet);
|
|
1909
|
-
if (!depositedVault) {
|
|
1910
|
-
throw new Error("No vault found to withdraw from");
|
|
1911
|
-
}
|
|
1864
|
+
async withdraw(vaultInfo, amount, smartWallet) {
|
|
1912
1865
|
const owner = await smartWallet.getAddress();
|
|
1913
1866
|
let withdrawData;
|
|
1914
|
-
if (
|
|
1915
|
-
const assets = parseUnits2(
|
|
1916
|
-
logger.info("Withdraw amount:", { amountInUnderlying, assets }, "SparkProtocol");
|
|
1867
|
+
if (amount) {
|
|
1868
|
+
const assets = parseUnits2(amount, vaultInfo.tokenDecimals);
|
|
1917
1869
|
withdrawData = {
|
|
1918
|
-
to:
|
|
1870
|
+
to: vaultInfo.vaultAddress,
|
|
1919
1871
|
data: encodeFunctionData2({
|
|
1920
1872
|
abi: SPARK_VAULT_ABI,
|
|
1921
1873
|
functionName: "withdraw",
|
|
@@ -1923,10 +1875,9 @@ var SparkProtocol = class extends BaseProtocol {
|
|
|
1923
1875
|
})
|
|
1924
1876
|
};
|
|
1925
1877
|
} else {
|
|
1926
|
-
const maxShares = await this.getMaxRedeemableShares(
|
|
1927
|
-
logger.info("Withdrawing all funds:", { maxShares }, "SparkProtocol");
|
|
1878
|
+
const maxShares = await this.getMaxRedeemableShares(vaultInfo, owner);
|
|
1928
1879
|
withdrawData = {
|
|
1929
|
-
to:
|
|
1880
|
+
to: vaultInfo.vaultAddress,
|
|
1930
1881
|
data: encodeFunctionData2({
|
|
1931
1882
|
abi: SPARK_VAULT_ABI,
|
|
1932
1883
|
functionName: "redeem",
|
|
@@ -1935,7 +1886,6 @@ var SparkProtocol = class extends BaseProtocol {
|
|
|
1935
1886
|
};
|
|
1936
1887
|
}
|
|
1937
1888
|
const hash = await smartWallet.send(withdrawData, this.selectedChainId);
|
|
1938
|
-
logger.info("Withdraw transaction sent:", { hash }, "SparkProtocol");
|
|
1939
1889
|
return { success: true, hash };
|
|
1940
1890
|
}
|
|
1941
1891
|
/**
|
|
@@ -1958,14 +1908,15 @@ var SparkProtocol = class extends BaseProtocol {
|
|
|
1958
1908
|
}
|
|
1959
1909
|
/**
|
|
1960
1910
|
* Get amount that a wallet has deposited in a vault
|
|
1961
|
-
* @param vaultInfo Vault information
|
|
1962
1911
|
* @param walletAddress Wallet address to check
|
|
1963
|
-
* @returns
|
|
1912
|
+
* @returns Array of vault balances with vaults info
|
|
1964
1913
|
*/
|
|
1965
|
-
async
|
|
1914
|
+
async getBalances(walletAddress) {
|
|
1966
1915
|
if (!this.publicClient) {
|
|
1967
1916
|
throw new Error("Public client not initialized");
|
|
1968
1917
|
}
|
|
1918
|
+
const vaultInfo = SPARK_VAULT[0];
|
|
1919
|
+
vaultInfo.metadata.apy = await this.getAPY();
|
|
1969
1920
|
const shares = await this.publicClient.readContract({
|
|
1970
1921
|
address: vaultInfo.vaultAddress,
|
|
1971
1922
|
abi: SPARK_VAULT_ABI,
|
|
@@ -1973,7 +1924,7 @@ var SparkProtocol = class extends BaseProtocol {
|
|
|
1973
1924
|
args: [walletAddress]
|
|
1974
1925
|
});
|
|
1975
1926
|
if (shares === 0n) {
|
|
1976
|
-
return {
|
|
1927
|
+
return [{ balance: null, vaultInfo }];
|
|
1977
1928
|
}
|
|
1978
1929
|
const assets = await this.publicClient.readContract({
|
|
1979
1930
|
address: vaultInfo.vaultAddress,
|
|
@@ -1981,11 +1932,12 @@ var SparkProtocol = class extends BaseProtocol {
|
|
|
1981
1932
|
functionName: "convertToAssets",
|
|
1982
1933
|
args: [shares]
|
|
1983
1934
|
});
|
|
1984
|
-
return
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1935
|
+
return [
|
|
1936
|
+
{
|
|
1937
|
+
balance: formatUnits2(assets, vaultInfo.tokenDecimals),
|
|
1938
|
+
vaultInfo
|
|
1939
|
+
}
|
|
1940
|
+
];
|
|
1989
1941
|
}
|
|
1990
1942
|
};
|
|
1991
1943
|
|
|
@@ -1997,59 +1949,233 @@ var availableProtocols = [
|
|
|
1997
1949
|
name: "Spark",
|
|
1998
1950
|
website: "https://spark.fi/",
|
|
1999
1951
|
logo: "/logos/spark.png",
|
|
2000
|
-
supportedChains: [
|
|
1952
|
+
supportedChains: [8453],
|
|
2001
1953
|
riskLevel: "low",
|
|
2002
|
-
|
|
1954
|
+
isActive: true
|
|
2003
1955
|
},
|
|
2004
1956
|
instance: new SparkProtocol()
|
|
2005
1957
|
}
|
|
2006
1958
|
];
|
|
2007
1959
|
|
|
2008
|
-
// src/
|
|
2009
|
-
|
|
2010
|
-
|
|
1960
|
+
// src/protocols/implementations/ProxyProtocol.ts
|
|
1961
|
+
import { encodeFunctionData as encodeFunctionData3, erc20Abi as erc20Abi5, parseUnits as parseUnits3 } from "viem";
|
|
1962
|
+
var ProxyProtocol = class extends BaseProtocol {
|
|
2011
1963
|
/**
|
|
2012
|
-
*
|
|
2013
|
-
*
|
|
2014
|
-
* @internal
|
|
2015
|
-
* @param apiKey API key from {@link ProtocolsRouterConfig}
|
|
2016
|
-
* @returns True if the API key is considered valid
|
|
1964
|
+
* Initialize the Spark protocol with the provided chain manager
|
|
1965
|
+
* @param chainManager Chain manager instance used for network operations
|
|
2017
1966
|
*/
|
|
2018
|
-
|
|
2019
|
-
logger.info("Validating api key...", apiKey, "ApiKeysValidator");
|
|
2020
|
-
return true;
|
|
2021
|
-
}
|
|
2022
|
-
};
|
|
2023
|
-
|
|
2024
|
-
// src/router/base/ProtocolRouterBase.ts
|
|
2025
|
-
var ProtocolRouterBase = class {
|
|
2026
|
-
/**
|
|
2027
|
-
* Initialize a base protocol router
|
|
2028
|
-
* @param riskLevel Risk level required by the integrator
|
|
2029
|
-
* @param chainManager Chain manager instance for network operations
|
|
2030
|
-
* @param minApy Optional minimum APY filter
|
|
2031
|
-
* @param apiKey Optional API key for premium protocol access
|
|
2032
|
-
*/
|
|
2033
|
-
constructor(riskLevel, chainManager, minApy, apiKey) {
|
|
2034
|
-
// TODO: Add an API key validation
|
|
2035
|
-
/** API key validator instance */
|
|
2036
|
-
this.apiKeyValidator = new ApiKeysValidator();
|
|
2037
|
-
this.riskLevel = riskLevel;
|
|
2038
|
-
this.minApy = minApy;
|
|
2039
|
-
this.apiKey = apiKey;
|
|
1967
|
+
async init(chainManager, protocolsSecurityConfig, apiClient) {
|
|
2040
1968
|
this.chainManager = chainManager;
|
|
1969
|
+
this.selectedChainId = chainManager.getSupportedChain();
|
|
1970
|
+
this.publicClient = chainManager.getPublicClient(this.selectedChainId);
|
|
1971
|
+
this.apiClient = apiClient;
|
|
1972
|
+
this.protocolsSecurityConfig = protocolsSecurityConfig;
|
|
1973
|
+
}
|
|
1974
|
+
/**
|
|
1975
|
+
* Log a vault-related operation after deposit or withdraw funds
|
|
1976
|
+
* @param userAddress Address of the user who performed the operation
|
|
1977
|
+
* @param hash Hash of the operation
|
|
1978
|
+
* @param vaultInfo Information about the vault where the operation was performed
|
|
1979
|
+
* @param chainId Chain ID where the operation was performed
|
|
1980
|
+
* @param amount Amount of the operation
|
|
1981
|
+
* @param operationType Type of the operation
|
|
1982
|
+
* @param operationStatus Status of the operation
|
|
1983
|
+
*/
|
|
1984
|
+
async logOperation(userAddress, hash, vaultInfo, chainId, amount, operationType, operationStatus) {
|
|
1985
|
+
const apiResponse = await this.apiClient.sendRequest("log", void 0, void 0, {
|
|
1986
|
+
userAddress,
|
|
1987
|
+
protocolId: vaultInfo.protocolId,
|
|
1988
|
+
vaultAddress: vaultInfo.vaultAddress,
|
|
1989
|
+
transactionHash: hash,
|
|
1990
|
+
chainId: chainId.toString(),
|
|
1991
|
+
amount,
|
|
1992
|
+
status: operationStatus,
|
|
1993
|
+
operationType
|
|
1994
|
+
});
|
|
1995
|
+
if (!apiResponse.success) {
|
|
1996
|
+
throw new Error(
|
|
1997
|
+
apiResponse.error || `Failed to log operation: ${operationType} for vault: ${vaultInfo}`
|
|
1998
|
+
);
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
/**
|
|
2002
|
+
* Get the best vaults to deposit funds
|
|
2003
|
+
* @param stableVaultsLimit Limit of stable vaults to get. Optional, default is 1
|
|
2004
|
+
* @param nonStableVaultsLimit Limit of non-stable vaults to get. Optional, default is 1
|
|
2005
|
+
* @returns Best vaults to deposit funds in 2 groups: stable and non-stable
|
|
2006
|
+
*/
|
|
2007
|
+
async getBestVaults(stableVaultsLimit = 1, nonStableVaultsLimit = 1) {
|
|
2008
|
+
const pathParams = {
|
|
2009
|
+
risk_level: this.protocolsSecurityConfig.riskLevel,
|
|
2010
|
+
chain_id: this.selectedChainId.toString(),
|
|
2011
|
+
stable_vaults_limit: stableVaultsLimit.toString(),
|
|
2012
|
+
non_stable_vaults_limit: nonStableVaultsLimit.toString()
|
|
2013
|
+
};
|
|
2014
|
+
const apiResponse = await this.apiClient.sendRequest("vaults", pathParams);
|
|
2015
|
+
if (!apiResponse.success) {
|
|
2016
|
+
throw new Error(apiResponse.error || "Failed to get best vaults");
|
|
2017
|
+
}
|
|
2018
|
+
const vaults = apiResponse.data;
|
|
2019
|
+
return {
|
|
2020
|
+
stable: vaults.stableVaults.map((vault) => {
|
|
2021
|
+
return {
|
|
2022
|
+
...vault,
|
|
2023
|
+
metadata: {
|
|
2024
|
+
apy: vault.metadata?.apy,
|
|
2025
|
+
poolTvlUsd: vault.metadata?.poolTvlUsd
|
|
2026
|
+
}
|
|
2027
|
+
};
|
|
2028
|
+
}),
|
|
2029
|
+
nonStable: vaults.nonStableVaults.map((vault) => {
|
|
2030
|
+
return {
|
|
2031
|
+
...vault,
|
|
2032
|
+
metadata: {
|
|
2033
|
+
apy: vault.metadata?.apy,
|
|
2034
|
+
poolTvlUsd: vault.metadata?.poolTvlUsd
|
|
2035
|
+
}
|
|
2036
|
+
};
|
|
2037
|
+
})
|
|
2038
|
+
};
|
|
2039
|
+
}
|
|
2040
|
+
/**
|
|
2041
|
+
* Deposit funds to a provided vault
|
|
2042
|
+
* @param vaultInfo Information about the vault to deposit funds to
|
|
2043
|
+
* @param amount Amount of funds to deposit
|
|
2044
|
+
* @param smartWallet Smart wallet to use for the deposit
|
|
2045
|
+
* @returns Result of the deposit transaction
|
|
2046
|
+
*/
|
|
2047
|
+
async deposit(vaultInfo, amount, smartWallet) {
|
|
2048
|
+
const currentAddress = await smartWallet.getAddress();
|
|
2049
|
+
const operationsCallData = [];
|
|
2050
|
+
const depositTokenDecimals = vaultInfo.tokenDecimals;
|
|
2051
|
+
const depositTokenAddress = vaultInfo.tokenAddress;
|
|
2052
|
+
const vaultAddress = vaultInfo.vaultAddress;
|
|
2053
|
+
const rawDepositAmount = parseUnits3(amount, depositTokenDecimals);
|
|
2054
|
+
const allowance = await this.checkAllowance(
|
|
2055
|
+
depositTokenAddress,
|
|
2056
|
+
vaultAddress,
|
|
2057
|
+
currentAddress,
|
|
2058
|
+
this.selectedChainId
|
|
2059
|
+
);
|
|
2060
|
+
if (allowance < rawDepositAmount) {
|
|
2061
|
+
const approveData = {
|
|
2062
|
+
to: depositTokenAddress,
|
|
2063
|
+
data: encodeFunctionData3({
|
|
2064
|
+
abi: erc20Abi5,
|
|
2065
|
+
functionName: "approve",
|
|
2066
|
+
args: [vaultAddress, rawDepositAmount]
|
|
2067
|
+
})
|
|
2068
|
+
};
|
|
2069
|
+
operationsCallData.push(approveData);
|
|
2070
|
+
}
|
|
2071
|
+
if (!vaultInfo.protocolId) {
|
|
2072
|
+
throw new Error("Vault protocol ID is required");
|
|
2073
|
+
}
|
|
2074
|
+
const apiResponse = await this.apiClient.sendRequest(
|
|
2075
|
+
"deposit",
|
|
2076
|
+
void 0,
|
|
2077
|
+
vaultInfo.protocolId,
|
|
2078
|
+
{
|
|
2079
|
+
vaultInfo,
|
|
2080
|
+
amount: amount.toString(),
|
|
2081
|
+
chainId: this.selectedChainId.toString()
|
|
2082
|
+
}
|
|
2083
|
+
);
|
|
2084
|
+
if (!apiResponse.success) {
|
|
2085
|
+
throw new Error(apiResponse.error || "Failed to receive deposit operations call data");
|
|
2086
|
+
}
|
|
2087
|
+
const receivedOperationsCallData = apiResponse.data;
|
|
2088
|
+
operationsCallData.push(receivedOperationsCallData);
|
|
2089
|
+
const hash = await smartWallet.sendBatch(operationsCallData, this.selectedChainId);
|
|
2090
|
+
const operationStatus = hash ? "completed" : "failed";
|
|
2091
|
+
this.logOperation(
|
|
2092
|
+
currentAddress,
|
|
2093
|
+
hash,
|
|
2094
|
+
vaultInfo,
|
|
2095
|
+
this.selectedChainId,
|
|
2096
|
+
amount,
|
|
2097
|
+
"deposit",
|
|
2098
|
+
operationStatus
|
|
2099
|
+
);
|
|
2100
|
+
return { hash, success: true };
|
|
2101
|
+
}
|
|
2102
|
+
/**
|
|
2103
|
+
* Withdraw funds from a provided vault
|
|
2104
|
+
* @param vaultInfo Information about the vault to withdraw funds from
|
|
2105
|
+
* @param amount Amount of funds to withdraw
|
|
2106
|
+
* @param smartWallet Smart wallet to use for the withdrawal
|
|
2107
|
+
* @returns Result of the withdrawal transaction
|
|
2108
|
+
*/
|
|
2109
|
+
async withdraw(vaultInfo, amount, smartWallet) {
|
|
2110
|
+
const currentAddress = await smartWallet.getAddress();
|
|
2111
|
+
const earningBalances = await smartWallet.getEarnBalances();
|
|
2112
|
+
if (!earningBalances) {
|
|
2113
|
+
throw new Error("No earning balances found");
|
|
2114
|
+
}
|
|
2115
|
+
const earningBalance = earningBalances.find((balance) => balance.vaultInfo.id === vaultInfo.id);
|
|
2116
|
+
if (!earningBalance) {
|
|
2117
|
+
throw new Error("No earning balance found");
|
|
2118
|
+
}
|
|
2119
|
+
const balanceInfo = earningBalance.balance;
|
|
2120
|
+
const amountToWithdraw = amount ? amount : balanceInfo.currentBalance;
|
|
2121
|
+
const apiResponse = await this.apiClient.sendRequest(
|
|
2122
|
+
"withdraw",
|
|
2123
|
+
void 0,
|
|
2124
|
+
vaultInfo.protocolId,
|
|
2125
|
+
{
|
|
2126
|
+
vaultInfo,
|
|
2127
|
+
amount: amountToWithdraw,
|
|
2128
|
+
chainId: this.selectedChainId.toString()
|
|
2129
|
+
}
|
|
2130
|
+
);
|
|
2131
|
+
if (!apiResponse.success) {
|
|
2132
|
+
throw new Error(apiResponse.error || "Failed to receive withdraw operations call data");
|
|
2133
|
+
}
|
|
2134
|
+
const withdrawOperationCallData = apiResponse.data;
|
|
2135
|
+
const hash = await smartWallet.send(withdrawOperationCallData, this.selectedChainId);
|
|
2136
|
+
const operationStatus = hash ? "completed" : "failed";
|
|
2137
|
+
this.logOperation(
|
|
2138
|
+
currentAddress,
|
|
2139
|
+
hash,
|
|
2140
|
+
vaultInfo,
|
|
2141
|
+
this.selectedChainId,
|
|
2142
|
+
amountToWithdraw,
|
|
2143
|
+
"withdrawal",
|
|
2144
|
+
operationStatus
|
|
2145
|
+
);
|
|
2146
|
+
return { hash, success: true };
|
|
2147
|
+
}
|
|
2148
|
+
/**
|
|
2149
|
+
* Get the balances of a user by a provided address
|
|
2150
|
+
* @param walletAddress Address of the user to get the balances of
|
|
2151
|
+
* @param protocolId Protocol ID to get the balances for. Optional, default is undefined
|
|
2152
|
+
* @returns Balances of the user in the protocol vaults
|
|
2153
|
+
*/
|
|
2154
|
+
async getBalances(walletAddress, protocolId) {
|
|
2155
|
+
const pathParams = {
|
|
2156
|
+
chain_id: this.selectedChainId.toString(),
|
|
2157
|
+
protocol_id: protocolId || "",
|
|
2158
|
+
userAddress: walletAddress
|
|
2159
|
+
};
|
|
2160
|
+
const apiResponse = await this.apiClient.sendRequest("balances", pathParams);
|
|
2161
|
+
if (!apiResponse.success) {
|
|
2162
|
+
throw new Error(apiResponse.error || "Failed to get balances");
|
|
2163
|
+
}
|
|
2164
|
+
const balances = apiResponse.data;
|
|
2165
|
+
return balances;
|
|
2041
2166
|
}
|
|
2042
2167
|
};
|
|
2043
2168
|
|
|
2044
2169
|
// src/router/ProtocolRouter.ts
|
|
2045
|
-
var ProtocolRouter = class
|
|
2170
|
+
var ProtocolRouter = class {
|
|
2046
2171
|
/**
|
|
2047
2172
|
* Initialize the protocol router
|
|
2048
2173
|
* @param config Router configuration including risk level, min APY, and optional API key
|
|
2049
2174
|
* @param chainManager Chain manager instance for network validation
|
|
2050
2175
|
*/
|
|
2051
|
-
constructor(
|
|
2052
|
-
|
|
2176
|
+
constructor(chainManager, isApiKeyValid) {
|
|
2177
|
+
this.chainManager = chainManager;
|
|
2178
|
+
this.isApiKeyValid = isApiKeyValid;
|
|
2053
2179
|
}
|
|
2054
2180
|
/**
|
|
2055
2181
|
* Get all protocols available for the current configuration
|
|
@@ -2057,13 +2183,9 @@ var ProtocolRouter = class extends ProtocolRouterBase {
|
|
|
2057
2183
|
* Includes all non-premium protocols and premium protocols if the API key is valid
|
|
2058
2184
|
* @returns Array of available protocol definitions
|
|
2059
2185
|
*/
|
|
2060
|
-
|
|
2061
|
-
const isKeyValid = this.apiKeyValidator.validate(this.apiKey);
|
|
2186
|
+
getActivePublicProtocols() {
|
|
2062
2187
|
const allAvailableProtocols = availableProtocols.filter((protocol) => {
|
|
2063
|
-
|
|
2064
|
-
return true;
|
|
2065
|
-
}
|
|
2066
|
-
return protocol.info.isPremium && isKeyValid;
|
|
2188
|
+
return protocol.info.isActive;
|
|
2067
2189
|
});
|
|
2068
2190
|
return allAvailableProtocols;
|
|
2069
2191
|
}
|
|
@@ -2088,18 +2210,20 @@ var ProtocolRouter = class extends ProtocolRouterBase {
|
|
|
2088
2210
|
* @throws Error if no protocols are available for the current risk level
|
|
2089
2211
|
* @returns Protocol instance considered the best match
|
|
2090
2212
|
*/
|
|
2091
|
-
|
|
2092
|
-
|
|
2213
|
+
select() {
|
|
2214
|
+
if (this.isApiKeyValid) {
|
|
2215
|
+
return new ProxyProtocol();
|
|
2216
|
+
}
|
|
2217
|
+
const protocols = this.getActivePublicProtocols();
|
|
2093
2218
|
const eligibleProtocols = protocols.filter((protocol) => {
|
|
2094
|
-
const riskMatches = protocol.info.riskLevel === this.riskLevel;
|
|
2095
2219
|
const isSupportedChain = this.isProtocolSupportedChain(protocol.info.supportedChains);
|
|
2096
|
-
return
|
|
2220
|
+
return isSupportedChain;
|
|
2097
2221
|
});
|
|
2098
2222
|
if (eligibleProtocols.length === 0) {
|
|
2099
|
-
throw new Error(`No protocols available
|
|
2223
|
+
throw new Error(`No protocols available`);
|
|
2100
2224
|
}
|
|
2101
2225
|
const bestProtocol = eligibleProtocols[0];
|
|
2102
|
-
return bestProtocol;
|
|
2226
|
+
return bestProtocol.instance;
|
|
2103
2227
|
}
|
|
2104
2228
|
};
|
|
2105
2229
|
|
|
@@ -2319,8 +2443,161 @@ var CoinbaseCDP = class {
|
|
|
2319
2443
|
}
|
|
2320
2444
|
};
|
|
2321
2445
|
|
|
2446
|
+
// src/protocols/ProtocolsNamespace.ts
|
|
2447
|
+
var ProtocolsNamespace = class {
|
|
2448
|
+
constructor(protocol) {
|
|
2449
|
+
this.protocol = protocol;
|
|
2450
|
+
}
|
|
2451
|
+
/**
|
|
2452
|
+
* Find the best vaults for protocols that were selected based on integrator's settings
|
|
2453
|
+
*
|
|
2454
|
+
* @returns Best vaults for protocols that were selected based on integrator's settings
|
|
2455
|
+
*/
|
|
2456
|
+
async getBestVaults(stableVaultsLimit = 1, nonStableVaultsLimit = 1) {
|
|
2457
|
+
return await this.protocol.getBestVaults(stableVaultsLimit, nonStableVaultsLimit);
|
|
2458
|
+
}
|
|
2459
|
+
};
|
|
2460
|
+
|
|
2461
|
+
// src/constants/general.ts
|
|
2462
|
+
var BACKEND_HOSTNAME = process.env.NODE_ENV === "dev" ? "http://localhost:3000" : "https://mycelium-cloud-production.up.railway.app";
|
|
2463
|
+
|
|
2464
|
+
// src/tools/ApiClient.ts
|
|
2465
|
+
import axios2 from "axios";
|
|
2466
|
+
var ApiClient = class {
|
|
2467
|
+
constructor(apiKey) {
|
|
2468
|
+
this.client = axios2.create({
|
|
2469
|
+
baseURL: BACKEND_HOSTNAME
|
|
2470
|
+
});
|
|
2471
|
+
/** URL settings for the endpoint: get the best vaults to deposit funds */
|
|
2472
|
+
this.bestVaultUrlSettings = {
|
|
2473
|
+
method: "GET",
|
|
2474
|
+
path: "api/v1/public/protocols/best"
|
|
2475
|
+
};
|
|
2476
|
+
/** URL settings for the endpoint: deposit funds to a provided vault */
|
|
2477
|
+
this.depositUrlSettings = {
|
|
2478
|
+
method: "POST",
|
|
2479
|
+
path: "api/v1/public/protocols/details/:protocolId/deposit"
|
|
2480
|
+
};
|
|
2481
|
+
/** URL settings for the endpoint: log a vault-related operation after deposit or withdraw funds */
|
|
2482
|
+
this.logOperationSettings = {
|
|
2483
|
+
method: "POST",
|
|
2484
|
+
path: "api/v1/public/log/operation"
|
|
2485
|
+
};
|
|
2486
|
+
/** URL settings for the endpoint: withdraw funds from a provided vault */
|
|
2487
|
+
this.withdrawUrlSettings = {
|
|
2488
|
+
method: "POST",
|
|
2489
|
+
path: "api/v1/public/protocols/details/:protocolId/withdraw"
|
|
2490
|
+
};
|
|
2491
|
+
/** URL settings for the endpoint: get the balances of a user by a provided address */
|
|
2492
|
+
this.balancesUrlSettings = {
|
|
2493
|
+
method: "GET",
|
|
2494
|
+
path: "api/v1/public/user/:userAddress/balances"
|
|
2495
|
+
};
|
|
2496
|
+
/** URL settings for the endpoint: validate the API key */
|
|
2497
|
+
this.apiKeyValidUrlSettings = {
|
|
2498
|
+
method: "GET",
|
|
2499
|
+
path: "api/v1/public/valid"
|
|
2500
|
+
};
|
|
2501
|
+
/** URL settings for the endpoint: get the config for services */
|
|
2502
|
+
this.configUrlSettings = {
|
|
2503
|
+
method: "GET",
|
|
2504
|
+
path: "api/v1/public/config"
|
|
2505
|
+
};
|
|
2506
|
+
/** URL settings mapping with operation types */
|
|
2507
|
+
this.operationTypeToUrlSettings = {
|
|
2508
|
+
deposit: this.depositUrlSettings,
|
|
2509
|
+
withdraw: this.withdrawUrlSettings,
|
|
2510
|
+
log: this.logOperationSettings,
|
|
2511
|
+
vaults: this.bestVaultUrlSettings,
|
|
2512
|
+
balances: this.balancesUrlSettings,
|
|
2513
|
+
apiKeyValidation: this.apiKeyValidUrlSettings,
|
|
2514
|
+
config: this.configUrlSettings
|
|
2515
|
+
};
|
|
2516
|
+
this.apiKey = apiKey;
|
|
2517
|
+
if (!this.validateApiKeyFormat()) {
|
|
2518
|
+
throw new Error("Invalid API key format");
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
/**
|
|
2522
|
+
* Send a request to the backend API
|
|
2523
|
+
* @param path Path of the endpoint to send the request to
|
|
2524
|
+
* @param method Method of the request
|
|
2525
|
+
* @param body Body of the request
|
|
2526
|
+
* @returns Response from the backend API
|
|
2527
|
+
*/
|
|
2528
|
+
async sendRequest(operationType, params, protocolId, body) {
|
|
2529
|
+
const { path, method } = this.operationTypeToUrlSettings[operationType];
|
|
2530
|
+
let requestPath = path;
|
|
2531
|
+
if (protocolId) {
|
|
2532
|
+
requestPath = requestPath.replace(":protocolId", protocolId);
|
|
2533
|
+
}
|
|
2534
|
+
if (params?.userAddress) {
|
|
2535
|
+
requestPath = requestPath.replace(":userAddress", params.userAddress);
|
|
2536
|
+
delete params.userAddress;
|
|
2537
|
+
}
|
|
2538
|
+
const urlParams = new URLSearchParams(params).toString();
|
|
2539
|
+
if (urlParams) {
|
|
2540
|
+
requestPath += `?${urlParams}`;
|
|
2541
|
+
}
|
|
2542
|
+
const response = await this.client.request({
|
|
2543
|
+
method,
|
|
2544
|
+
url: requestPath,
|
|
2545
|
+
data: body,
|
|
2546
|
+
headers: { Authorization: `Bearer ${this.apiKey}`, "Content-Type": "application/json" }
|
|
2547
|
+
});
|
|
2548
|
+
if (response.status !== 200) {
|
|
2549
|
+
throw new Error(`Failed to send request to ${path}`);
|
|
2550
|
+
}
|
|
2551
|
+
const apiResponse = response.data;
|
|
2552
|
+
return apiResponse;
|
|
2553
|
+
}
|
|
2554
|
+
/**
|
|
2555
|
+
* Validates whether the provided API key is valid
|
|
2556
|
+
*
|
|
2557
|
+
* @internal
|
|
2558
|
+
* @param apiKey API key from {@link ProtocolsRouterConfig}
|
|
2559
|
+
* @returns True if the API key is considered valid
|
|
2560
|
+
*/
|
|
2561
|
+
async validate() {
|
|
2562
|
+
if (!this.validateApiKeyFormat()) {
|
|
2563
|
+
throw new Error("Invalid API key format");
|
|
2564
|
+
}
|
|
2565
|
+
const apiResponse = await this.sendRequest("apiKeyValidation");
|
|
2566
|
+
if (!apiResponse.success) {
|
|
2567
|
+
throw new Error(apiResponse.error || "Failed to validate API key");
|
|
2568
|
+
}
|
|
2569
|
+
return true;
|
|
2570
|
+
}
|
|
2571
|
+
/**
|
|
2572
|
+
*
|
|
2573
|
+
* Validates the format of the API key. Must start with 'sk_' and contain only hexadecimal characters
|
|
2574
|
+
*
|
|
2575
|
+
* @internal
|
|
2576
|
+
* @param apiKey API key from {@link ProtocolsRouterConfig}
|
|
2577
|
+
* @returns
|
|
2578
|
+
*/
|
|
2579
|
+
validateApiKeyFormat() {
|
|
2580
|
+
if (!this.apiKey) {
|
|
2581
|
+
return false;
|
|
2582
|
+
}
|
|
2583
|
+
const prefix = "sk_";
|
|
2584
|
+
if (!this.apiKey.startsWith(prefix)) {
|
|
2585
|
+
return false;
|
|
2586
|
+
}
|
|
2587
|
+
const keyPart = this.apiKey.slice(prefix.length);
|
|
2588
|
+
if (keyPart.length < 32 || keyPart.length > 128) {
|
|
2589
|
+
return false;
|
|
2590
|
+
}
|
|
2591
|
+
const hexPattern = /^[0-9a-fA-F]+$/;
|
|
2592
|
+
if (!hexPattern.test(keyPart)) {
|
|
2593
|
+
return false;
|
|
2594
|
+
}
|
|
2595
|
+
return true;
|
|
2596
|
+
}
|
|
2597
|
+
};
|
|
2598
|
+
|
|
2322
2599
|
// src/index.ts
|
|
2323
|
-
var MyceliumSDK = class {
|
|
2600
|
+
var MyceliumSDK = class _MyceliumSDK {
|
|
2324
2601
|
/**
|
|
2325
2602
|
* Creates a new SDK instance
|
|
2326
2603
|
*
|
|
@@ -2328,7 +2605,7 @@ var MyceliumSDK = class {
|
|
|
2328
2605
|
* @throws Throws if an unsupported wallet provider is given
|
|
2329
2606
|
* @see MyceliumSDKConfig
|
|
2330
2607
|
*/
|
|
2331
|
-
constructor(config) {
|
|
2608
|
+
constructor(config, isPremiumAvailable, apiClient) {
|
|
2332
2609
|
/**
|
|
2333
2610
|
* Ramp namespace to manage ramp operations. Methods are available on {@link RampNamespace}
|
|
2334
2611
|
* @internal
|
|
@@ -2353,11 +2630,14 @@ var MyceliumSDK = class {
|
|
|
2353
2630
|
bundlerUrl: "https://public.pimlico.io/v2/8453/rpc"
|
|
2354
2631
|
}
|
|
2355
2632
|
);
|
|
2356
|
-
if (
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2633
|
+
if (config.protocolsSecurityConfig) {
|
|
2634
|
+
this.protocol = this.selectProtocol(
|
|
2635
|
+
config.protocolsSecurityConfig,
|
|
2636
|
+
isPremiumAvailable,
|
|
2637
|
+
apiClient
|
|
2360
2638
|
);
|
|
2639
|
+
} else {
|
|
2640
|
+
throw new Error("Protocols router config is required");
|
|
2361
2641
|
}
|
|
2362
2642
|
if (config.coinbaseCDPConfig) {
|
|
2363
2643
|
this.coinbaseCDP = new CoinbaseCDP(
|
|
@@ -2368,11 +2648,63 @@ var MyceliumSDK = class {
|
|
|
2368
2648
|
);
|
|
2369
2649
|
this.fundingNamespace = new FundingNamespace(this.coinbaseCDP);
|
|
2370
2650
|
}
|
|
2371
|
-
const protocolsRouterConfig = config.protocolsRouterConfig || {
|
|
2372
|
-
riskLevel: "low"
|
|
2373
|
-
};
|
|
2374
|
-
this.protocol = this.findProtocol(protocolsRouterConfig);
|
|
2375
2651
|
this.wallet = this.createWalletNamespace(config.walletsConfig);
|
|
2652
|
+
this.protocols = new ProtocolsNamespace(this.protocol);
|
|
2653
|
+
}
|
|
2654
|
+
/**
|
|
2655
|
+
* Initializes the SDK
|
|
2656
|
+
* @param config SDK configuration (networks, wallets, protocol router settings)
|
|
2657
|
+
* @returns SDK instance
|
|
2658
|
+
*/
|
|
2659
|
+
static async init(config) {
|
|
2660
|
+
let finalConfig;
|
|
2661
|
+
let isPremiumAvailable = false;
|
|
2662
|
+
if ("apiKey" in config && config.apiKey) {
|
|
2663
|
+
const apiClient = new ApiClient(config.apiKey);
|
|
2664
|
+
isPremiumAvailable = await apiClient.validate();
|
|
2665
|
+
if (isPremiumAvailable) {
|
|
2666
|
+
const apiResponse = await apiClient.sendRequest("config");
|
|
2667
|
+
if (!apiResponse.success) {
|
|
2668
|
+
throw new Error(apiResponse.error || "Failed to get onchain config");
|
|
2669
|
+
}
|
|
2670
|
+
const backendConfig = apiResponse.data;
|
|
2671
|
+
finalConfig = {
|
|
2672
|
+
integratorId: backendConfig.integratorId,
|
|
2673
|
+
walletsConfig: {
|
|
2674
|
+
embeddedWalletConfig: {
|
|
2675
|
+
provider: {
|
|
2676
|
+
type: "privy",
|
|
2677
|
+
providerConfig: {
|
|
2678
|
+
appId: backendConfig.privyAppId,
|
|
2679
|
+
appSecret: backendConfig.privyAppSecret
|
|
2680
|
+
}
|
|
2681
|
+
}
|
|
2682
|
+
},
|
|
2683
|
+
smartWalletConfig: {
|
|
2684
|
+
provider: {
|
|
2685
|
+
type: "default"
|
|
2686
|
+
}
|
|
2687
|
+
}
|
|
2688
|
+
},
|
|
2689
|
+
chain: {
|
|
2690
|
+
chainId: config.chainId || backendConfig.chainId,
|
|
2691
|
+
rpcUrl: backendConfig.rpcUrl,
|
|
2692
|
+
bundlerUrl: backendConfig.bundlerUrl
|
|
2693
|
+
},
|
|
2694
|
+
protocolsSecurityConfig: config.protocolsSecurityConfig,
|
|
2695
|
+
coinbaseCDPConfig: {
|
|
2696
|
+
apiKeyId: backendConfig.coinbaseCdpApiKey,
|
|
2697
|
+
apiKeySecret: backendConfig.coinbaseCdpApiKeySecret
|
|
2698
|
+
}
|
|
2699
|
+
};
|
|
2700
|
+
const sdk2 = new _MyceliumSDK(finalConfig, isPremiumAvailable, apiClient);
|
|
2701
|
+
sdk2.apiClient = apiClient;
|
|
2702
|
+
return sdk2;
|
|
2703
|
+
}
|
|
2704
|
+
}
|
|
2705
|
+
finalConfig = config;
|
|
2706
|
+
const sdk = new _MyceliumSDK(finalConfig, isPremiumAvailable);
|
|
2707
|
+
return sdk;
|
|
2376
2708
|
}
|
|
2377
2709
|
/**
|
|
2378
2710
|
* Returns the chain manager instance for multi-chain operations
|
|
@@ -2410,10 +2742,13 @@ var MyceliumSDK = class {
|
|
|
2410
2742
|
* @param config Protocol router configuration (e.g. risk level)
|
|
2411
2743
|
* @returns Selected protocol object of the type {@link Protocol}
|
|
2412
2744
|
*/
|
|
2413
|
-
|
|
2414
|
-
const protocolRouter = new ProtocolRouter(
|
|
2415
|
-
const protocol = protocolRouter.
|
|
2416
|
-
|
|
2745
|
+
selectProtocol(config, isPremiumAvailable, apiClient) {
|
|
2746
|
+
const protocolRouter = new ProtocolRouter(this.chainManager, isPremiumAvailable);
|
|
2747
|
+
const protocol = protocolRouter.select();
|
|
2748
|
+
if (!config) {
|
|
2749
|
+
throw new Error("Protocols security config is required");
|
|
2750
|
+
}
|
|
2751
|
+
protocol.init(this.chainManager, config, apiClient);
|
|
2417
2752
|
return protocol;
|
|
2418
2753
|
}
|
|
2419
2754
|
/**
|