@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.cjs
CHANGED
|
@@ -382,9 +382,9 @@ var DefaultSmartWallet = class extends SmartWallet {
|
|
|
382
382
|
* @param amount Human-readable amount string
|
|
383
383
|
* @returns Transaction result for the deposit
|
|
384
384
|
*/
|
|
385
|
-
async earn(amount) {
|
|
385
|
+
async earn(vaultInfo, amount) {
|
|
386
386
|
this.chainManager.getSupportedChain();
|
|
387
|
-
const depositTransactionResult = this.protocolProvider.deposit(amount, this);
|
|
387
|
+
const depositTransactionResult = this.protocolProvider.deposit(vaultInfo, amount, this);
|
|
388
388
|
return depositTransactionResult;
|
|
389
389
|
}
|
|
390
390
|
/**
|
|
@@ -395,13 +395,9 @@ var DefaultSmartWallet = class extends SmartWallet {
|
|
|
395
395
|
* @category Earn
|
|
396
396
|
* @returns Vault balance or `null` if nothing deposited
|
|
397
397
|
*/
|
|
398
|
-
async
|
|
399
|
-
const depositedVault = await this.protocolProvider.fetchDepositedVaults(this);
|
|
400
|
-
if (!depositedVault) {
|
|
401
|
-
return null;
|
|
402
|
-
}
|
|
398
|
+
async getEarnBalances() {
|
|
403
399
|
const userAddress = await this.getAddress();
|
|
404
|
-
return this.protocolProvider.
|
|
400
|
+
return this.protocolProvider.getBalances(userAddress);
|
|
405
401
|
}
|
|
406
402
|
/**
|
|
407
403
|
* Withdraws from the selected protocol’s vault
|
|
@@ -412,8 +408,8 @@ var DefaultSmartWallet = class extends SmartWallet {
|
|
|
412
408
|
* @throws Error if the withdrawal fails
|
|
413
409
|
* @throws Error a user didn't deposit anything
|
|
414
410
|
*/
|
|
415
|
-
async withdraw(amount) {
|
|
416
|
-
const withdrawTransactionResult = await this.protocolProvider.withdraw(amount, this);
|
|
411
|
+
async withdraw(vaultInfo, amount) {
|
|
412
|
+
const withdrawTransactionResult = await this.protocolProvider.withdraw(vaultInfo, amount, this);
|
|
417
413
|
return withdrawTransactionResult;
|
|
418
414
|
}
|
|
419
415
|
/**
|
|
@@ -450,7 +446,7 @@ var DefaultSmartWallet = class extends SmartWallet {
|
|
|
450
446
|
return hash;
|
|
451
447
|
} catch (error) {
|
|
452
448
|
throw new Error(
|
|
453
|
-
`Failed to send transaction: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
449
|
+
`Failed to send transaction: ${error instanceof Error ? error.message.toString().slice(0, 100) : "Unknown error"}`
|
|
454
450
|
);
|
|
455
451
|
}
|
|
456
452
|
}
|
|
@@ -671,127 +667,6 @@ var chainById = Object.values(viemChains).reduce(
|
|
|
671
667
|
{}
|
|
672
668
|
);
|
|
673
669
|
|
|
674
|
-
// src/types/logger.ts
|
|
675
|
-
var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
676
|
-
LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
|
|
677
|
-
LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
|
|
678
|
-
LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
|
|
679
|
-
LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
|
|
680
|
-
return LogLevel2;
|
|
681
|
-
})(LogLevel || {});
|
|
682
|
-
|
|
683
|
-
// src/tools/Logger.ts
|
|
684
|
-
var Logger = class _Logger {
|
|
685
|
-
/**
|
|
686
|
-
* Create a new logger instance
|
|
687
|
-
* @param logLevel Initial log level, defaults to DEBUG
|
|
688
|
-
*/
|
|
689
|
-
constructor(logLevel = 0 /* DEBUG */) {
|
|
690
|
-
this.logs = [];
|
|
691
|
-
this.maxLogs = 1e3;
|
|
692
|
-
this.logLevel = logLevel;
|
|
693
|
-
}
|
|
694
|
-
/**
|
|
695
|
-
* Get singleton instance of the logger
|
|
696
|
-
* @param logLevel Optional log level to initialize if instance not yet created
|
|
697
|
-
* @returns Logger instance
|
|
698
|
-
*/
|
|
699
|
-
static getInstance(logLevel) {
|
|
700
|
-
if (!_Logger.instance) {
|
|
701
|
-
_Logger.instance = new _Logger(logLevel);
|
|
702
|
-
}
|
|
703
|
-
return _Logger.instance;
|
|
704
|
-
}
|
|
705
|
-
/** Set the log level */
|
|
706
|
-
setLogLevel(level) {
|
|
707
|
-
this.logLevel = level;
|
|
708
|
-
}
|
|
709
|
-
/** Get the current log level */
|
|
710
|
-
getLogLevel() {
|
|
711
|
-
return this.logLevel;
|
|
712
|
-
}
|
|
713
|
-
/** Internal check if a message should be logged */
|
|
714
|
-
shouldLog(level) {
|
|
715
|
-
return level >= this.logLevel;
|
|
716
|
-
}
|
|
717
|
-
/** Format log message into a readable string */
|
|
718
|
-
formatMessage(level, message, data, context) {
|
|
719
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
720
|
-
const levelName = LogLevel[level];
|
|
721
|
-
const contextStr = context ? `[${context}]` : "";
|
|
722
|
-
return `${timestamp} ${levelName}${contextStr}: ${message}`;
|
|
723
|
-
}
|
|
724
|
-
/** Add a log entry and output to console */
|
|
725
|
-
addLog(level, message, data, context) {
|
|
726
|
-
if (!this.shouldLog(level)) {
|
|
727
|
-
return;
|
|
728
|
-
}
|
|
729
|
-
const logEntry = {
|
|
730
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
731
|
-
level,
|
|
732
|
-
message,
|
|
733
|
-
data,
|
|
734
|
-
context
|
|
735
|
-
};
|
|
736
|
-
this.logs.push(logEntry);
|
|
737
|
-
if (this.logs.length > this.maxLogs) {
|
|
738
|
-
this.logs = this.logs.slice(-this.maxLogs);
|
|
739
|
-
}
|
|
740
|
-
const formattedMessage = this.formatMessage(level, message, data, context);
|
|
741
|
-
switch (level) {
|
|
742
|
-
case 0 /* DEBUG */:
|
|
743
|
-
console.debug(`\u{1F50D} ${formattedMessage}`, data || "");
|
|
744
|
-
break;
|
|
745
|
-
case 1 /* INFO */:
|
|
746
|
-
console.info(`\u2139\uFE0F ${formattedMessage}`, data || "");
|
|
747
|
-
break;
|
|
748
|
-
case 2 /* WARN */:
|
|
749
|
-
console.warn(`\u26A0\uFE0F ${formattedMessage}`, data || "");
|
|
750
|
-
break;
|
|
751
|
-
case 3 /* ERROR */:
|
|
752
|
-
console.error(`\u274C ${formattedMessage}`, data || "");
|
|
753
|
-
break;
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
/** Log a debug message */
|
|
757
|
-
debug(message, data, context) {
|
|
758
|
-
this.addLog(0 /* DEBUG */, message, data, context);
|
|
759
|
-
}
|
|
760
|
-
/** Log an info message */
|
|
761
|
-
info(message, data, context) {
|
|
762
|
-
this.addLog(1 /* INFO */, message, data, context);
|
|
763
|
-
}
|
|
764
|
-
/** Log a warning message */
|
|
765
|
-
warn(message, data, context) {
|
|
766
|
-
this.addLog(2 /* WARN */, message, data, context);
|
|
767
|
-
}
|
|
768
|
-
/** Log an error message */
|
|
769
|
-
error(message, data, context) {
|
|
770
|
-
this.addLog(3 /* ERROR */, message, data, context);
|
|
771
|
-
}
|
|
772
|
-
/** Get all logs */
|
|
773
|
-
getLogs() {
|
|
774
|
-
return [...this.logs];
|
|
775
|
-
}
|
|
776
|
-
/** Get logs by level */
|
|
777
|
-
getLogsByLevel(level) {
|
|
778
|
-
return this.logs.filter((log) => log.level === level);
|
|
779
|
-
}
|
|
780
|
-
/** Clear all logs */
|
|
781
|
-
clearLogs() {
|
|
782
|
-
this.logs = [];
|
|
783
|
-
}
|
|
784
|
-
/** Export logs as a JSON string */
|
|
785
|
-
exportLogs() {
|
|
786
|
-
return JSON.stringify(this.logs, null, 2);
|
|
787
|
-
}
|
|
788
|
-
/** Set maximum number of logs to retain in memory */
|
|
789
|
-
setMaxLogs(max) {
|
|
790
|
-
this.maxLogs = max;
|
|
791
|
-
}
|
|
792
|
-
};
|
|
793
|
-
var logger = Logger.getInstance();
|
|
794
|
-
|
|
795
670
|
// src/tools/ChainManager.ts
|
|
796
671
|
var ChainManager = class {
|
|
797
672
|
/**
|
|
@@ -847,7 +722,6 @@ var ChainManager = class {
|
|
|
847
722
|
if (!bundlerUrl) {
|
|
848
723
|
throw new Error(`No bundler URL configured for chain ID: ${chainId}`);
|
|
849
724
|
}
|
|
850
|
-
logger.info("Public client setup:", { bundlerUrl, chainId }, "ChainManager");
|
|
851
725
|
const client = (0, import_viem5.createPublicClient)({
|
|
852
726
|
chain: this.getChain(chainId),
|
|
853
727
|
transport: (0, import_viem5.http)(rpcUrl)
|
|
@@ -1080,7 +954,7 @@ var DefaultSmartWalletProvider = class extends SmartWalletProvider {
|
|
|
1080
954
|
constructor(chainManager, protocol, coinbaseCDP) {
|
|
1081
955
|
super();
|
|
1082
956
|
this.chainManager = chainManager;
|
|
1083
|
-
this.protocolProvider = protocol
|
|
957
|
+
this.protocolProvider = protocol;
|
|
1084
958
|
this.coinbaseCDP = coinbaseCDP;
|
|
1085
959
|
}
|
|
1086
960
|
/**
|
|
@@ -1343,8 +1217,7 @@ var WalletProvider = class {
|
|
|
1343
1217
|
throw new Error(
|
|
1344
1218
|
"Either walletAddress or deploymentOwners array must be provided to locate the smart wallet"
|
|
1345
1219
|
);
|
|
1346
|
-
} catch
|
|
1347
|
-
logger.error("Error getting smart wallet", error, "WalletProvider");
|
|
1220
|
+
} catch {
|
|
1348
1221
|
throw new Error(
|
|
1349
1222
|
"Either walletAddress or deploymentOwners array must be provided to locate the smart wallet"
|
|
1350
1223
|
);
|
|
@@ -1387,6 +1260,127 @@ var EmbeddedWallet = class {
|
|
|
1387
1260
|
}
|
|
1388
1261
|
};
|
|
1389
1262
|
|
|
1263
|
+
// src/types/logger.ts
|
|
1264
|
+
var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
1265
|
+
LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
|
|
1266
|
+
LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
|
|
1267
|
+
LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
|
|
1268
|
+
LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
|
|
1269
|
+
return LogLevel2;
|
|
1270
|
+
})(LogLevel || {});
|
|
1271
|
+
|
|
1272
|
+
// src/tools/Logger.ts
|
|
1273
|
+
var Logger = class _Logger {
|
|
1274
|
+
/**
|
|
1275
|
+
* Create a new logger instance
|
|
1276
|
+
* @param logLevel Initial log level, defaults to DEBUG
|
|
1277
|
+
*/
|
|
1278
|
+
constructor(logLevel = 0 /* DEBUG */) {
|
|
1279
|
+
this.logs = [];
|
|
1280
|
+
this.maxLogs = 1e3;
|
|
1281
|
+
this.logLevel = logLevel;
|
|
1282
|
+
}
|
|
1283
|
+
/**
|
|
1284
|
+
* Get singleton instance of the logger
|
|
1285
|
+
* @param logLevel Optional log level to initialize if instance not yet created
|
|
1286
|
+
* @returns Logger instance
|
|
1287
|
+
*/
|
|
1288
|
+
static getInstance(logLevel) {
|
|
1289
|
+
if (!_Logger.instance) {
|
|
1290
|
+
_Logger.instance = new _Logger(logLevel);
|
|
1291
|
+
}
|
|
1292
|
+
return _Logger.instance;
|
|
1293
|
+
}
|
|
1294
|
+
/** Set the log level */
|
|
1295
|
+
setLogLevel(level) {
|
|
1296
|
+
this.logLevel = level;
|
|
1297
|
+
}
|
|
1298
|
+
/** Get the current log level */
|
|
1299
|
+
getLogLevel() {
|
|
1300
|
+
return this.logLevel;
|
|
1301
|
+
}
|
|
1302
|
+
/** Internal check if a message should be logged */
|
|
1303
|
+
shouldLog(level) {
|
|
1304
|
+
return level >= this.logLevel;
|
|
1305
|
+
}
|
|
1306
|
+
/** Format log message into a readable string */
|
|
1307
|
+
formatMessage(level, message, data, context) {
|
|
1308
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1309
|
+
const levelName = LogLevel[level];
|
|
1310
|
+
const contextStr = context ? `[${context}]` : "";
|
|
1311
|
+
return `${timestamp} ${levelName}${contextStr}: ${message}`;
|
|
1312
|
+
}
|
|
1313
|
+
/** Add a log entry and output to console */
|
|
1314
|
+
addLog(level, message, data, context) {
|
|
1315
|
+
if (!this.shouldLog(level)) {
|
|
1316
|
+
return;
|
|
1317
|
+
}
|
|
1318
|
+
const logEntry = {
|
|
1319
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1320
|
+
level,
|
|
1321
|
+
message,
|
|
1322
|
+
data,
|
|
1323
|
+
context
|
|
1324
|
+
};
|
|
1325
|
+
this.logs.push(logEntry);
|
|
1326
|
+
if (this.logs.length > this.maxLogs) {
|
|
1327
|
+
this.logs = this.logs.slice(-this.maxLogs);
|
|
1328
|
+
}
|
|
1329
|
+
const formattedMessage = this.formatMessage(level, message, data, context);
|
|
1330
|
+
switch (level) {
|
|
1331
|
+
case 0 /* DEBUG */:
|
|
1332
|
+
console.debug(`\u{1F50D} ${formattedMessage}`, data || "");
|
|
1333
|
+
break;
|
|
1334
|
+
case 1 /* INFO */:
|
|
1335
|
+
console.info(`\u2139\uFE0F ${formattedMessage}`, data || "");
|
|
1336
|
+
break;
|
|
1337
|
+
case 2 /* WARN */:
|
|
1338
|
+
console.warn(`\u26A0\uFE0F ${formattedMessage}`, data || "");
|
|
1339
|
+
break;
|
|
1340
|
+
case 3 /* ERROR */:
|
|
1341
|
+
console.error(`\u274C ${formattedMessage}`, data || "");
|
|
1342
|
+
break;
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
/** Log a debug message */
|
|
1346
|
+
debug(message, data, context) {
|
|
1347
|
+
this.addLog(0 /* DEBUG */, message, data, context);
|
|
1348
|
+
}
|
|
1349
|
+
/** Log an info message */
|
|
1350
|
+
info(message, data, context) {
|
|
1351
|
+
this.addLog(1 /* INFO */, message, data, context);
|
|
1352
|
+
}
|
|
1353
|
+
/** Log a warning message */
|
|
1354
|
+
warn(message, data, context) {
|
|
1355
|
+
this.addLog(2 /* WARN */, message, data, context);
|
|
1356
|
+
}
|
|
1357
|
+
/** Log an error message */
|
|
1358
|
+
error(message, data, context) {
|
|
1359
|
+
this.addLog(3 /* ERROR */, message, data, context);
|
|
1360
|
+
}
|
|
1361
|
+
/** Get all logs */
|
|
1362
|
+
getLogs() {
|
|
1363
|
+
return [...this.logs];
|
|
1364
|
+
}
|
|
1365
|
+
/** Get logs by level */
|
|
1366
|
+
getLogsByLevel(level) {
|
|
1367
|
+
return this.logs.filter((log) => log.level === level);
|
|
1368
|
+
}
|
|
1369
|
+
/** Clear all logs */
|
|
1370
|
+
clearLogs() {
|
|
1371
|
+
this.logs = [];
|
|
1372
|
+
}
|
|
1373
|
+
/** Export logs as a JSON string */
|
|
1374
|
+
exportLogs() {
|
|
1375
|
+
return JSON.stringify(this.logs, null, 2);
|
|
1376
|
+
}
|
|
1377
|
+
/** Set maximum number of logs to retain in memory */
|
|
1378
|
+
setMaxLogs(max) {
|
|
1379
|
+
this.maxLogs = max;
|
|
1380
|
+
}
|
|
1381
|
+
};
|
|
1382
|
+
var logger = Logger.getInstance();
|
|
1383
|
+
|
|
1390
1384
|
// src/wallet/PrivyWallet.ts
|
|
1391
1385
|
var PrivyWallet = class extends EmbeddedWallet {
|
|
1392
1386
|
/**
|
|
@@ -1767,14 +1761,14 @@ var SPARK_SSR_ORACLE_ADDRESS = "0x65d946e533748A998B1f0E430803e39A6388f7a1";
|
|
|
1767
1761
|
var SPARK_VAULT = [
|
|
1768
1762
|
{
|
|
1769
1763
|
id: "sUSDC",
|
|
1764
|
+
protocolId: "spark",
|
|
1765
|
+
name: "sUSDC",
|
|
1766
|
+
type: "stable",
|
|
1770
1767
|
chain: "base",
|
|
1771
1768
|
vaultAddress: "0x3128a0f7f0ea68e7b7c9b00afa7e41045828e858",
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
earnTokenAddress: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
|
|
1776
|
-
earnTokenDecimals: 18,
|
|
1777
|
-
earnTokenSymbol: "sUSDC",
|
|
1769
|
+
tokenAddress: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
|
|
1770
|
+
tokenDecimals: 6,
|
|
1771
|
+
tokenSymbol: "USDC",
|
|
1778
1772
|
metadata: {}
|
|
1779
1773
|
}
|
|
1780
1774
|
];
|
|
@@ -1785,6 +1779,7 @@ var RAY = BigInt("1000000000000000000000000000");
|
|
|
1785
1779
|
var SparkProtocol = class extends BaseProtocol {
|
|
1786
1780
|
constructor() {
|
|
1787
1781
|
super(...arguments);
|
|
1782
|
+
/** All Spark vaults */
|
|
1788
1783
|
this.allVaults = [];
|
|
1789
1784
|
}
|
|
1790
1785
|
/**
|
|
@@ -1795,7 +1790,7 @@ var SparkProtocol = class extends BaseProtocol {
|
|
|
1795
1790
|
this.chainManager = chainManager;
|
|
1796
1791
|
this.selectedChainId = chainManager.getSupportedChain();
|
|
1797
1792
|
this.publicClient = chainManager.getPublicClient(this.selectedChainId);
|
|
1798
|
-
this.allVaults =
|
|
1793
|
+
this.allVaults = SPARK_VAULT;
|
|
1799
1794
|
}
|
|
1800
1795
|
/**
|
|
1801
1796
|
* Get the SSR (Sky Saving Rate) of the Spark protocol
|
|
@@ -1825,88 +1820,54 @@ var SparkProtocol = class extends BaseProtocol {
|
|
|
1825
1820
|
async getAPY() {
|
|
1826
1821
|
const ssr = await this.getSSR();
|
|
1827
1822
|
const apy = Math.exp(Math.log(ssr) * SECONDS_PER_YEAR) - 1;
|
|
1828
|
-
return
|
|
1829
|
-
}
|
|
1830
|
-
/**
|
|
1831
|
-
*
|
|
1832
|
-
* Get all vault info from a Spark protocol
|
|
1833
|
-
* @returns The list of vaults
|
|
1834
|
-
*/
|
|
1835
|
-
getVaults() {
|
|
1836
|
-
return SPARK_VAULT;
|
|
1823
|
+
return apy;
|
|
1837
1824
|
}
|
|
1838
1825
|
/**
|
|
1839
|
-
* Get the best available Spark
|
|
1840
|
-
* @
|
|
1826
|
+
* Get the best available Spark vaults
|
|
1827
|
+
* @remarks Currently, the vault is only one and relates to sUSDC. Currently return only one stable vault
|
|
1828
|
+
* @returns Best Spark vaults in 2 groups: stable and non-stable
|
|
1841
1829
|
* @throws Error if no vaults found
|
|
1842
1830
|
*/
|
|
1843
|
-
async
|
|
1831
|
+
async getBestVaults() {
|
|
1844
1832
|
if (this.allVaults.length === 0) {
|
|
1845
1833
|
throw new Error("No vaults found");
|
|
1846
1834
|
}
|
|
1847
1835
|
const selectedVault = this.allVaults[0];
|
|
1848
1836
|
selectedVault.metadata.apy = await this.getAPY();
|
|
1849
|
-
return
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
* @param smartWallet Smart wallet to inspect
|
|
1854
|
-
* @returns The vault with user deposits, or null if none found
|
|
1855
|
-
*/
|
|
1856
|
-
async fetchDepositedVaults(smartWallet) {
|
|
1857
|
-
let depositedVault = void 0;
|
|
1858
|
-
const userAddress = await smartWallet.getAddress();
|
|
1859
|
-
for (const vault of this.allVaults) {
|
|
1860
|
-
const balance = await this.getBalance(vault, userAddress);
|
|
1861
|
-
if (parseInt(balance.depositedAmount) > 0) {
|
|
1862
|
-
depositedVault = vault;
|
|
1863
|
-
}
|
|
1864
|
-
}
|
|
1865
|
-
if (depositedVault) {
|
|
1866
|
-
depositedVault.metadata.apy = await this.getAPY();
|
|
1867
|
-
}
|
|
1868
|
-
logger.info("Deposited vaults:", { depositedVault }, "SparkProtocol");
|
|
1869
|
-
return depositedVault || null;
|
|
1837
|
+
return {
|
|
1838
|
+
stable: [selectedVault],
|
|
1839
|
+
nonStable: []
|
|
1840
|
+
};
|
|
1870
1841
|
}
|
|
1871
1842
|
/**
|
|
1872
1843
|
* Deposit funds into a Spark vault
|
|
1844
|
+
* @param vaultInfo Vault information
|
|
1873
1845
|
* @param amount Amount to deposit (human-readable)
|
|
1874
1846
|
* @param smartWallet Smart wallet instance to use
|
|
1875
1847
|
* @returns Transaction result with hash
|
|
1876
1848
|
*/
|
|
1877
|
-
async deposit(amount, smartWallet) {
|
|
1878
|
-
const depositedVault = await this.fetchDepositedVaults(smartWallet);
|
|
1879
|
-
let vaultInfoToDeposit;
|
|
1880
|
-
logger.info("Previously deposited vault:", { depositedVault }, "SparkProtocol");
|
|
1881
|
-
if (depositedVault) {
|
|
1882
|
-
vaultInfoToDeposit = depositedVault;
|
|
1883
|
-
} else {
|
|
1884
|
-
vaultInfoToDeposit = await this.getBestVault();
|
|
1885
|
-
logger.info("Best vault that found:", { bestVault: vaultInfoToDeposit }, "SparkProtocol");
|
|
1886
|
-
}
|
|
1849
|
+
async deposit(vaultInfo, amount, smartWallet) {
|
|
1887
1850
|
const owner = await smartWallet.getAddress();
|
|
1888
|
-
const assets = (0, import_viem11.parseUnits)(amount,
|
|
1889
|
-
logger.info("Raw deposit amount:", { amount, assets }, "SparkProtocol");
|
|
1851
|
+
const assets = (0, import_viem11.parseUnits)(amount, vaultInfo.tokenDecimals);
|
|
1890
1852
|
const allowance = await this.checkAllowance(
|
|
1891
|
-
|
|
1892
|
-
|
|
1853
|
+
vaultInfo.tokenAddress,
|
|
1854
|
+
vaultInfo.vaultAddress,
|
|
1893
1855
|
owner,
|
|
1894
1856
|
this.selectedChainId
|
|
1895
1857
|
);
|
|
1896
|
-
logger.info("Current vault contract allowance:", { allowance }, "SparkProtocol");
|
|
1897
1858
|
const ops = [];
|
|
1898
1859
|
if (allowance < assets) {
|
|
1899
1860
|
ops.push({
|
|
1900
|
-
to:
|
|
1861
|
+
to: vaultInfo.tokenAddress,
|
|
1901
1862
|
data: (0, import_viem11.encodeFunctionData)({
|
|
1902
1863
|
abi: import_viem11.erc20Abi,
|
|
1903
1864
|
functionName: "approve",
|
|
1904
|
-
args: [
|
|
1865
|
+
args: [vaultInfo.vaultAddress, assets]
|
|
1905
1866
|
})
|
|
1906
1867
|
});
|
|
1907
1868
|
}
|
|
1908
1869
|
ops.push({
|
|
1909
|
-
to:
|
|
1870
|
+
to: vaultInfo.vaultAddress,
|
|
1910
1871
|
data: (0, import_viem11.encodeFunctionData)({
|
|
1911
1872
|
abi: SPARK_VAULT_ABI,
|
|
1912
1873
|
functionName: "deposit",
|
|
@@ -1918,23 +1879,19 @@ var SparkProtocol = class extends BaseProtocol {
|
|
|
1918
1879
|
}
|
|
1919
1880
|
/**
|
|
1920
1881
|
* Withdraw funds from a Spark vault
|
|
1921
|
-
* @param
|
|
1882
|
+
* @param vaultInfo Vault information
|
|
1883
|
+
* @param amount Amount in base token units (or undefined to withdraw all)
|
|
1922
1884
|
* @param smartWallet Smart wallet instance to withdraw from
|
|
1923
1885
|
* @returns Transaction result with hash
|
|
1924
1886
|
* @throws Error if no deposited vault found
|
|
1925
1887
|
*/
|
|
1926
|
-
async withdraw(
|
|
1927
|
-
const depositedVault = await this.fetchDepositedVaults(smartWallet);
|
|
1928
|
-
if (!depositedVault) {
|
|
1929
|
-
throw new Error("No vault found to withdraw from");
|
|
1930
|
-
}
|
|
1888
|
+
async withdraw(vaultInfo, amount, smartWallet) {
|
|
1931
1889
|
const owner = await smartWallet.getAddress();
|
|
1932
1890
|
let withdrawData;
|
|
1933
|
-
if (
|
|
1934
|
-
const assets = (0, import_viem11.parseUnits)(
|
|
1935
|
-
logger.info("Withdraw amount:", { amountInUnderlying, assets }, "SparkProtocol");
|
|
1891
|
+
if (amount) {
|
|
1892
|
+
const assets = (0, import_viem11.parseUnits)(amount, vaultInfo.tokenDecimals);
|
|
1936
1893
|
withdrawData = {
|
|
1937
|
-
to:
|
|
1894
|
+
to: vaultInfo.vaultAddress,
|
|
1938
1895
|
data: (0, import_viem11.encodeFunctionData)({
|
|
1939
1896
|
abi: SPARK_VAULT_ABI,
|
|
1940
1897
|
functionName: "withdraw",
|
|
@@ -1942,10 +1899,9 @@ var SparkProtocol = class extends BaseProtocol {
|
|
|
1942
1899
|
})
|
|
1943
1900
|
};
|
|
1944
1901
|
} else {
|
|
1945
|
-
const maxShares = await this.getMaxRedeemableShares(
|
|
1946
|
-
logger.info("Withdrawing all funds:", { maxShares }, "SparkProtocol");
|
|
1902
|
+
const maxShares = await this.getMaxRedeemableShares(vaultInfo, owner);
|
|
1947
1903
|
withdrawData = {
|
|
1948
|
-
to:
|
|
1904
|
+
to: vaultInfo.vaultAddress,
|
|
1949
1905
|
data: (0, import_viem11.encodeFunctionData)({
|
|
1950
1906
|
abi: SPARK_VAULT_ABI,
|
|
1951
1907
|
functionName: "redeem",
|
|
@@ -1954,7 +1910,6 @@ var SparkProtocol = class extends BaseProtocol {
|
|
|
1954
1910
|
};
|
|
1955
1911
|
}
|
|
1956
1912
|
const hash = await smartWallet.send(withdrawData, this.selectedChainId);
|
|
1957
|
-
logger.info("Withdraw transaction sent:", { hash }, "SparkProtocol");
|
|
1958
1913
|
return { success: true, hash };
|
|
1959
1914
|
}
|
|
1960
1915
|
/**
|
|
@@ -1977,14 +1932,15 @@ var SparkProtocol = class extends BaseProtocol {
|
|
|
1977
1932
|
}
|
|
1978
1933
|
/**
|
|
1979
1934
|
* Get amount that a wallet has deposited in a vault
|
|
1980
|
-
* @param vaultInfo Vault information
|
|
1981
1935
|
* @param walletAddress Wallet address to check
|
|
1982
|
-
* @returns
|
|
1936
|
+
* @returns Array of vault balances with vaults info
|
|
1983
1937
|
*/
|
|
1984
|
-
async
|
|
1938
|
+
async getBalances(walletAddress) {
|
|
1985
1939
|
if (!this.publicClient) {
|
|
1986
1940
|
throw new Error("Public client not initialized");
|
|
1987
1941
|
}
|
|
1942
|
+
const vaultInfo = SPARK_VAULT[0];
|
|
1943
|
+
vaultInfo.metadata.apy = await this.getAPY();
|
|
1988
1944
|
const shares = await this.publicClient.readContract({
|
|
1989
1945
|
address: vaultInfo.vaultAddress,
|
|
1990
1946
|
abi: SPARK_VAULT_ABI,
|
|
@@ -1992,7 +1948,7 @@ var SparkProtocol = class extends BaseProtocol {
|
|
|
1992
1948
|
args: [walletAddress]
|
|
1993
1949
|
});
|
|
1994
1950
|
if (shares === 0n) {
|
|
1995
|
-
return {
|
|
1951
|
+
return [{ balance: null, vaultInfo }];
|
|
1996
1952
|
}
|
|
1997
1953
|
const assets = await this.publicClient.readContract({
|
|
1998
1954
|
address: vaultInfo.vaultAddress,
|
|
@@ -2000,11 +1956,12 @@ var SparkProtocol = class extends BaseProtocol {
|
|
|
2000
1956
|
functionName: "convertToAssets",
|
|
2001
1957
|
args: [shares]
|
|
2002
1958
|
});
|
|
2003
|
-
return
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
1959
|
+
return [
|
|
1960
|
+
{
|
|
1961
|
+
balance: (0, import_viem11.formatUnits)(assets, vaultInfo.tokenDecimals),
|
|
1962
|
+
vaultInfo
|
|
1963
|
+
}
|
|
1964
|
+
];
|
|
2008
1965
|
}
|
|
2009
1966
|
};
|
|
2010
1967
|
|
|
@@ -2016,59 +1973,233 @@ var availableProtocols = [
|
|
|
2016
1973
|
name: "Spark",
|
|
2017
1974
|
website: "https://spark.fi/",
|
|
2018
1975
|
logo: "/logos/spark.png",
|
|
2019
|
-
supportedChains: [
|
|
1976
|
+
supportedChains: [8453],
|
|
2020
1977
|
riskLevel: "low",
|
|
2021
|
-
|
|
1978
|
+
isActive: true
|
|
2022
1979
|
},
|
|
2023
1980
|
instance: new SparkProtocol()
|
|
2024
1981
|
}
|
|
2025
1982
|
];
|
|
2026
1983
|
|
|
2027
|
-
// src/
|
|
2028
|
-
var
|
|
2029
|
-
|
|
1984
|
+
// src/protocols/implementations/ProxyProtocol.ts
|
|
1985
|
+
var import_viem12 = require("viem");
|
|
1986
|
+
var ProxyProtocol = class extends BaseProtocol {
|
|
2030
1987
|
/**
|
|
2031
|
-
*
|
|
2032
|
-
*
|
|
2033
|
-
* @internal
|
|
2034
|
-
* @param apiKey API key from {@link ProtocolsRouterConfig}
|
|
2035
|
-
* @returns True if the API key is considered valid
|
|
1988
|
+
* Initialize the Spark protocol with the provided chain manager
|
|
1989
|
+
* @param chainManager Chain manager instance used for network operations
|
|
2036
1990
|
*/
|
|
2037
|
-
|
|
2038
|
-
logger.info("Validating api key...", apiKey, "ApiKeysValidator");
|
|
2039
|
-
return true;
|
|
2040
|
-
}
|
|
2041
|
-
};
|
|
2042
|
-
|
|
2043
|
-
// src/router/base/ProtocolRouterBase.ts
|
|
2044
|
-
var ProtocolRouterBase = class {
|
|
2045
|
-
/**
|
|
2046
|
-
* Initialize a base protocol router
|
|
2047
|
-
* @param riskLevel Risk level required by the integrator
|
|
2048
|
-
* @param chainManager Chain manager instance for network operations
|
|
2049
|
-
* @param minApy Optional minimum APY filter
|
|
2050
|
-
* @param apiKey Optional API key for premium protocol access
|
|
2051
|
-
*/
|
|
2052
|
-
constructor(riskLevel, chainManager, minApy, apiKey) {
|
|
2053
|
-
// TODO: Add an API key validation
|
|
2054
|
-
/** API key validator instance */
|
|
2055
|
-
this.apiKeyValidator = new ApiKeysValidator();
|
|
2056
|
-
this.riskLevel = riskLevel;
|
|
2057
|
-
this.minApy = minApy;
|
|
2058
|
-
this.apiKey = apiKey;
|
|
1991
|
+
async init(chainManager, protocolsSecurityConfig, apiClient) {
|
|
2059
1992
|
this.chainManager = chainManager;
|
|
1993
|
+
this.selectedChainId = chainManager.getSupportedChain();
|
|
1994
|
+
this.publicClient = chainManager.getPublicClient(this.selectedChainId);
|
|
1995
|
+
this.apiClient = apiClient;
|
|
1996
|
+
this.protocolsSecurityConfig = protocolsSecurityConfig;
|
|
1997
|
+
}
|
|
1998
|
+
/**
|
|
1999
|
+
* Log a vault-related operation after deposit or withdraw funds
|
|
2000
|
+
* @param userAddress Address of the user who performed the operation
|
|
2001
|
+
* @param hash Hash of the operation
|
|
2002
|
+
* @param vaultInfo Information about the vault where the operation was performed
|
|
2003
|
+
* @param chainId Chain ID where the operation was performed
|
|
2004
|
+
* @param amount Amount of the operation
|
|
2005
|
+
* @param operationType Type of the operation
|
|
2006
|
+
* @param operationStatus Status of the operation
|
|
2007
|
+
*/
|
|
2008
|
+
async logOperation(userAddress, hash, vaultInfo, chainId, amount, operationType, operationStatus) {
|
|
2009
|
+
const apiResponse = await this.apiClient.sendRequest("log", void 0, void 0, {
|
|
2010
|
+
userAddress,
|
|
2011
|
+
protocolId: vaultInfo.protocolId,
|
|
2012
|
+
vaultAddress: vaultInfo.vaultAddress,
|
|
2013
|
+
transactionHash: hash,
|
|
2014
|
+
chainId: chainId.toString(),
|
|
2015
|
+
amount,
|
|
2016
|
+
status: operationStatus,
|
|
2017
|
+
operationType
|
|
2018
|
+
});
|
|
2019
|
+
if (!apiResponse.success) {
|
|
2020
|
+
throw new Error(
|
|
2021
|
+
apiResponse.error || `Failed to log operation: ${operationType} for vault: ${vaultInfo}`
|
|
2022
|
+
);
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
/**
|
|
2026
|
+
* Get the best vaults to deposit funds
|
|
2027
|
+
* @param stableVaultsLimit Limit of stable vaults to get. Optional, default is 1
|
|
2028
|
+
* @param nonStableVaultsLimit Limit of non-stable vaults to get. Optional, default is 1
|
|
2029
|
+
* @returns Best vaults to deposit funds in 2 groups: stable and non-stable
|
|
2030
|
+
*/
|
|
2031
|
+
async getBestVaults(stableVaultsLimit = 1, nonStableVaultsLimit = 1) {
|
|
2032
|
+
const pathParams = {
|
|
2033
|
+
risk_level: this.protocolsSecurityConfig.riskLevel,
|
|
2034
|
+
chain_id: this.selectedChainId.toString(),
|
|
2035
|
+
stable_vaults_limit: stableVaultsLimit.toString(),
|
|
2036
|
+
non_stable_vaults_limit: nonStableVaultsLimit.toString()
|
|
2037
|
+
};
|
|
2038
|
+
const apiResponse = await this.apiClient.sendRequest("vaults", pathParams);
|
|
2039
|
+
if (!apiResponse.success) {
|
|
2040
|
+
throw new Error(apiResponse.error || "Failed to get best vaults");
|
|
2041
|
+
}
|
|
2042
|
+
const vaults = apiResponse.data;
|
|
2043
|
+
return {
|
|
2044
|
+
stable: vaults.stableVaults.map((vault) => {
|
|
2045
|
+
return {
|
|
2046
|
+
...vault,
|
|
2047
|
+
metadata: {
|
|
2048
|
+
apy: vault.metadata?.apy,
|
|
2049
|
+
poolTvlUsd: vault.metadata?.poolTvlUsd
|
|
2050
|
+
}
|
|
2051
|
+
};
|
|
2052
|
+
}),
|
|
2053
|
+
nonStable: vaults.nonStableVaults.map((vault) => {
|
|
2054
|
+
return {
|
|
2055
|
+
...vault,
|
|
2056
|
+
metadata: {
|
|
2057
|
+
apy: vault.metadata?.apy,
|
|
2058
|
+
poolTvlUsd: vault.metadata?.poolTvlUsd
|
|
2059
|
+
}
|
|
2060
|
+
};
|
|
2061
|
+
})
|
|
2062
|
+
};
|
|
2063
|
+
}
|
|
2064
|
+
/**
|
|
2065
|
+
* Deposit funds to a provided vault
|
|
2066
|
+
* @param vaultInfo Information about the vault to deposit funds to
|
|
2067
|
+
* @param amount Amount of funds to deposit
|
|
2068
|
+
* @param smartWallet Smart wallet to use for the deposit
|
|
2069
|
+
* @returns Result of the deposit transaction
|
|
2070
|
+
*/
|
|
2071
|
+
async deposit(vaultInfo, amount, smartWallet) {
|
|
2072
|
+
const currentAddress = await smartWallet.getAddress();
|
|
2073
|
+
const operationsCallData = [];
|
|
2074
|
+
const depositTokenDecimals = vaultInfo.tokenDecimals;
|
|
2075
|
+
const depositTokenAddress = vaultInfo.tokenAddress;
|
|
2076
|
+
const vaultAddress = vaultInfo.vaultAddress;
|
|
2077
|
+
const rawDepositAmount = (0, import_viem12.parseUnits)(amount, depositTokenDecimals);
|
|
2078
|
+
const allowance = await this.checkAllowance(
|
|
2079
|
+
depositTokenAddress,
|
|
2080
|
+
vaultAddress,
|
|
2081
|
+
currentAddress,
|
|
2082
|
+
this.selectedChainId
|
|
2083
|
+
);
|
|
2084
|
+
if (allowance < rawDepositAmount) {
|
|
2085
|
+
const approveData = {
|
|
2086
|
+
to: depositTokenAddress,
|
|
2087
|
+
data: (0, import_viem12.encodeFunctionData)({
|
|
2088
|
+
abi: import_viem12.erc20Abi,
|
|
2089
|
+
functionName: "approve",
|
|
2090
|
+
args: [vaultAddress, rawDepositAmount]
|
|
2091
|
+
})
|
|
2092
|
+
};
|
|
2093
|
+
operationsCallData.push(approveData);
|
|
2094
|
+
}
|
|
2095
|
+
if (!vaultInfo.protocolId) {
|
|
2096
|
+
throw new Error("Vault protocol ID is required");
|
|
2097
|
+
}
|
|
2098
|
+
const apiResponse = await this.apiClient.sendRequest(
|
|
2099
|
+
"deposit",
|
|
2100
|
+
void 0,
|
|
2101
|
+
vaultInfo.protocolId,
|
|
2102
|
+
{
|
|
2103
|
+
vaultInfo,
|
|
2104
|
+
amount: amount.toString(),
|
|
2105
|
+
chainId: this.selectedChainId.toString()
|
|
2106
|
+
}
|
|
2107
|
+
);
|
|
2108
|
+
if (!apiResponse.success) {
|
|
2109
|
+
throw new Error(apiResponse.error || "Failed to receive deposit operations call data");
|
|
2110
|
+
}
|
|
2111
|
+
const receivedOperationsCallData = apiResponse.data;
|
|
2112
|
+
operationsCallData.push(receivedOperationsCallData);
|
|
2113
|
+
const hash = await smartWallet.sendBatch(operationsCallData, this.selectedChainId);
|
|
2114
|
+
const operationStatus = hash ? "completed" : "failed";
|
|
2115
|
+
this.logOperation(
|
|
2116
|
+
currentAddress,
|
|
2117
|
+
hash,
|
|
2118
|
+
vaultInfo,
|
|
2119
|
+
this.selectedChainId,
|
|
2120
|
+
amount,
|
|
2121
|
+
"deposit",
|
|
2122
|
+
operationStatus
|
|
2123
|
+
);
|
|
2124
|
+
return { hash, success: true };
|
|
2125
|
+
}
|
|
2126
|
+
/**
|
|
2127
|
+
* Withdraw funds from a provided vault
|
|
2128
|
+
* @param vaultInfo Information about the vault to withdraw funds from
|
|
2129
|
+
* @param amount Amount of funds to withdraw
|
|
2130
|
+
* @param smartWallet Smart wallet to use for the withdrawal
|
|
2131
|
+
* @returns Result of the withdrawal transaction
|
|
2132
|
+
*/
|
|
2133
|
+
async withdraw(vaultInfo, amount, smartWallet) {
|
|
2134
|
+
const currentAddress = await smartWallet.getAddress();
|
|
2135
|
+
const earningBalances = await smartWallet.getEarnBalances();
|
|
2136
|
+
if (!earningBalances) {
|
|
2137
|
+
throw new Error("No earning balances found");
|
|
2138
|
+
}
|
|
2139
|
+
const earningBalance = earningBalances.find((balance) => balance.vaultInfo.id === vaultInfo.id);
|
|
2140
|
+
if (!earningBalance) {
|
|
2141
|
+
throw new Error("No earning balance found");
|
|
2142
|
+
}
|
|
2143
|
+
const balanceInfo = earningBalance.balance;
|
|
2144
|
+
const amountToWithdraw = amount ? amount : balanceInfo.currentBalance;
|
|
2145
|
+
const apiResponse = await this.apiClient.sendRequest(
|
|
2146
|
+
"withdraw",
|
|
2147
|
+
void 0,
|
|
2148
|
+
vaultInfo.protocolId,
|
|
2149
|
+
{
|
|
2150
|
+
vaultInfo,
|
|
2151
|
+
amount: amountToWithdraw,
|
|
2152
|
+
chainId: this.selectedChainId.toString()
|
|
2153
|
+
}
|
|
2154
|
+
);
|
|
2155
|
+
if (!apiResponse.success) {
|
|
2156
|
+
throw new Error(apiResponse.error || "Failed to receive withdraw operations call data");
|
|
2157
|
+
}
|
|
2158
|
+
const withdrawOperationCallData = apiResponse.data;
|
|
2159
|
+
const hash = await smartWallet.send(withdrawOperationCallData, this.selectedChainId);
|
|
2160
|
+
const operationStatus = hash ? "completed" : "failed";
|
|
2161
|
+
this.logOperation(
|
|
2162
|
+
currentAddress,
|
|
2163
|
+
hash,
|
|
2164
|
+
vaultInfo,
|
|
2165
|
+
this.selectedChainId,
|
|
2166
|
+
amountToWithdraw,
|
|
2167
|
+
"withdrawal",
|
|
2168
|
+
operationStatus
|
|
2169
|
+
);
|
|
2170
|
+
return { hash, success: true };
|
|
2171
|
+
}
|
|
2172
|
+
/**
|
|
2173
|
+
* Get the balances of a user by a provided address
|
|
2174
|
+
* @param walletAddress Address of the user to get the balances of
|
|
2175
|
+
* @param protocolId Protocol ID to get the balances for. Optional, default is undefined
|
|
2176
|
+
* @returns Balances of the user in the protocol vaults
|
|
2177
|
+
*/
|
|
2178
|
+
async getBalances(walletAddress, protocolId) {
|
|
2179
|
+
const pathParams = {
|
|
2180
|
+
chain_id: this.selectedChainId.toString(),
|
|
2181
|
+
protocol_id: protocolId || "",
|
|
2182
|
+
userAddress: walletAddress
|
|
2183
|
+
};
|
|
2184
|
+
const apiResponse = await this.apiClient.sendRequest("balances", pathParams);
|
|
2185
|
+
if (!apiResponse.success) {
|
|
2186
|
+
throw new Error(apiResponse.error || "Failed to get balances");
|
|
2187
|
+
}
|
|
2188
|
+
const balances = apiResponse.data;
|
|
2189
|
+
return balances;
|
|
2060
2190
|
}
|
|
2061
2191
|
};
|
|
2062
2192
|
|
|
2063
2193
|
// src/router/ProtocolRouter.ts
|
|
2064
|
-
var ProtocolRouter = class
|
|
2194
|
+
var ProtocolRouter = class {
|
|
2065
2195
|
/**
|
|
2066
2196
|
* Initialize the protocol router
|
|
2067
2197
|
* @param config Router configuration including risk level, min APY, and optional API key
|
|
2068
2198
|
* @param chainManager Chain manager instance for network validation
|
|
2069
2199
|
*/
|
|
2070
|
-
constructor(
|
|
2071
|
-
|
|
2200
|
+
constructor(chainManager, isApiKeyValid) {
|
|
2201
|
+
this.chainManager = chainManager;
|
|
2202
|
+
this.isApiKeyValid = isApiKeyValid;
|
|
2072
2203
|
}
|
|
2073
2204
|
/**
|
|
2074
2205
|
* Get all protocols available for the current configuration
|
|
@@ -2076,13 +2207,9 @@ var ProtocolRouter = class extends ProtocolRouterBase {
|
|
|
2076
2207
|
* Includes all non-premium protocols and premium protocols if the API key is valid
|
|
2077
2208
|
* @returns Array of available protocol definitions
|
|
2078
2209
|
*/
|
|
2079
|
-
|
|
2080
|
-
const isKeyValid = this.apiKeyValidator.validate(this.apiKey);
|
|
2210
|
+
getActivePublicProtocols() {
|
|
2081
2211
|
const allAvailableProtocols = availableProtocols.filter((protocol) => {
|
|
2082
|
-
|
|
2083
|
-
return true;
|
|
2084
|
-
}
|
|
2085
|
-
return protocol.info.isPremium && isKeyValid;
|
|
2212
|
+
return protocol.info.isActive;
|
|
2086
2213
|
});
|
|
2087
2214
|
return allAvailableProtocols;
|
|
2088
2215
|
}
|
|
@@ -2107,18 +2234,20 @@ var ProtocolRouter = class extends ProtocolRouterBase {
|
|
|
2107
2234
|
* @throws Error if no protocols are available for the current risk level
|
|
2108
2235
|
* @returns Protocol instance considered the best match
|
|
2109
2236
|
*/
|
|
2110
|
-
|
|
2111
|
-
|
|
2237
|
+
select() {
|
|
2238
|
+
if (this.isApiKeyValid) {
|
|
2239
|
+
return new ProxyProtocol();
|
|
2240
|
+
}
|
|
2241
|
+
const protocols = this.getActivePublicProtocols();
|
|
2112
2242
|
const eligibleProtocols = protocols.filter((protocol) => {
|
|
2113
|
-
const riskMatches = protocol.info.riskLevel === this.riskLevel;
|
|
2114
2243
|
const isSupportedChain = this.isProtocolSupportedChain(protocol.info.supportedChains);
|
|
2115
|
-
return
|
|
2244
|
+
return isSupportedChain;
|
|
2116
2245
|
});
|
|
2117
2246
|
if (eligibleProtocols.length === 0) {
|
|
2118
|
-
throw new Error(`No protocols available
|
|
2247
|
+
throw new Error(`No protocols available`);
|
|
2119
2248
|
}
|
|
2120
2249
|
const bestProtocol = eligibleProtocols[0];
|
|
2121
|
-
return bestProtocol;
|
|
2250
|
+
return bestProtocol.instance;
|
|
2122
2251
|
}
|
|
2123
2252
|
};
|
|
2124
2253
|
|
|
@@ -2338,8 +2467,161 @@ var CoinbaseCDP = class {
|
|
|
2338
2467
|
}
|
|
2339
2468
|
};
|
|
2340
2469
|
|
|
2470
|
+
// src/protocols/ProtocolsNamespace.ts
|
|
2471
|
+
var ProtocolsNamespace = class {
|
|
2472
|
+
constructor(protocol) {
|
|
2473
|
+
this.protocol = protocol;
|
|
2474
|
+
}
|
|
2475
|
+
/**
|
|
2476
|
+
* Find the best vaults for protocols that were selected based on integrator's settings
|
|
2477
|
+
*
|
|
2478
|
+
* @returns Best vaults for protocols that were selected based on integrator's settings
|
|
2479
|
+
*/
|
|
2480
|
+
async getBestVaults(stableVaultsLimit = 1, nonStableVaultsLimit = 1) {
|
|
2481
|
+
return await this.protocol.getBestVaults(stableVaultsLimit, nonStableVaultsLimit);
|
|
2482
|
+
}
|
|
2483
|
+
};
|
|
2484
|
+
|
|
2485
|
+
// src/constants/general.ts
|
|
2486
|
+
var BACKEND_HOSTNAME = process.env.NODE_ENV === "dev" ? "http://localhost:3000" : "https://mycelium-cloud-production.up.railway.app";
|
|
2487
|
+
|
|
2488
|
+
// src/tools/ApiClient.ts
|
|
2489
|
+
var import_axios2 = __toESM(require("axios"), 1);
|
|
2490
|
+
var ApiClient = class {
|
|
2491
|
+
constructor(apiKey) {
|
|
2492
|
+
this.client = import_axios2.default.create({
|
|
2493
|
+
baseURL: BACKEND_HOSTNAME
|
|
2494
|
+
});
|
|
2495
|
+
/** URL settings for the endpoint: get the best vaults to deposit funds */
|
|
2496
|
+
this.bestVaultUrlSettings = {
|
|
2497
|
+
method: "GET",
|
|
2498
|
+
path: "api/v1/public/protocols/best"
|
|
2499
|
+
};
|
|
2500
|
+
/** URL settings for the endpoint: deposit funds to a provided vault */
|
|
2501
|
+
this.depositUrlSettings = {
|
|
2502
|
+
method: "POST",
|
|
2503
|
+
path: "api/v1/public/protocols/details/:protocolId/deposit"
|
|
2504
|
+
};
|
|
2505
|
+
/** URL settings for the endpoint: log a vault-related operation after deposit or withdraw funds */
|
|
2506
|
+
this.logOperationSettings = {
|
|
2507
|
+
method: "POST",
|
|
2508
|
+
path: "api/v1/public/log/operation"
|
|
2509
|
+
};
|
|
2510
|
+
/** URL settings for the endpoint: withdraw funds from a provided vault */
|
|
2511
|
+
this.withdrawUrlSettings = {
|
|
2512
|
+
method: "POST",
|
|
2513
|
+
path: "api/v1/public/protocols/details/:protocolId/withdraw"
|
|
2514
|
+
};
|
|
2515
|
+
/** URL settings for the endpoint: get the balances of a user by a provided address */
|
|
2516
|
+
this.balancesUrlSettings = {
|
|
2517
|
+
method: "GET",
|
|
2518
|
+
path: "api/v1/public/user/:userAddress/balances"
|
|
2519
|
+
};
|
|
2520
|
+
/** URL settings for the endpoint: validate the API key */
|
|
2521
|
+
this.apiKeyValidUrlSettings = {
|
|
2522
|
+
method: "GET",
|
|
2523
|
+
path: "api/v1/public/valid"
|
|
2524
|
+
};
|
|
2525
|
+
/** URL settings for the endpoint: get the config for services */
|
|
2526
|
+
this.configUrlSettings = {
|
|
2527
|
+
method: "GET",
|
|
2528
|
+
path: "api/v1/public/config"
|
|
2529
|
+
};
|
|
2530
|
+
/** URL settings mapping with operation types */
|
|
2531
|
+
this.operationTypeToUrlSettings = {
|
|
2532
|
+
deposit: this.depositUrlSettings,
|
|
2533
|
+
withdraw: this.withdrawUrlSettings,
|
|
2534
|
+
log: this.logOperationSettings,
|
|
2535
|
+
vaults: this.bestVaultUrlSettings,
|
|
2536
|
+
balances: this.balancesUrlSettings,
|
|
2537
|
+
apiKeyValidation: this.apiKeyValidUrlSettings,
|
|
2538
|
+
config: this.configUrlSettings
|
|
2539
|
+
};
|
|
2540
|
+
this.apiKey = apiKey;
|
|
2541
|
+
if (!this.validateApiKeyFormat()) {
|
|
2542
|
+
throw new Error("Invalid API key format");
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
/**
|
|
2546
|
+
* Send a request to the backend API
|
|
2547
|
+
* @param path Path of the endpoint to send the request to
|
|
2548
|
+
* @param method Method of the request
|
|
2549
|
+
* @param body Body of the request
|
|
2550
|
+
* @returns Response from the backend API
|
|
2551
|
+
*/
|
|
2552
|
+
async sendRequest(operationType, params, protocolId, body) {
|
|
2553
|
+
const { path, method } = this.operationTypeToUrlSettings[operationType];
|
|
2554
|
+
let requestPath = path;
|
|
2555
|
+
if (protocolId) {
|
|
2556
|
+
requestPath = requestPath.replace(":protocolId", protocolId);
|
|
2557
|
+
}
|
|
2558
|
+
if (params?.userAddress) {
|
|
2559
|
+
requestPath = requestPath.replace(":userAddress", params.userAddress);
|
|
2560
|
+
delete params.userAddress;
|
|
2561
|
+
}
|
|
2562
|
+
const urlParams = new URLSearchParams(params).toString();
|
|
2563
|
+
if (urlParams) {
|
|
2564
|
+
requestPath += `?${urlParams}`;
|
|
2565
|
+
}
|
|
2566
|
+
const response = await this.client.request({
|
|
2567
|
+
method,
|
|
2568
|
+
url: requestPath,
|
|
2569
|
+
data: body,
|
|
2570
|
+
headers: { Authorization: `Bearer ${this.apiKey}`, "Content-Type": "application/json" }
|
|
2571
|
+
});
|
|
2572
|
+
if (response.status !== 200) {
|
|
2573
|
+
throw new Error(`Failed to send request to ${path}`);
|
|
2574
|
+
}
|
|
2575
|
+
const apiResponse = response.data;
|
|
2576
|
+
return apiResponse;
|
|
2577
|
+
}
|
|
2578
|
+
/**
|
|
2579
|
+
* Validates whether the provided API key is valid
|
|
2580
|
+
*
|
|
2581
|
+
* @internal
|
|
2582
|
+
* @param apiKey API key from {@link ProtocolsRouterConfig}
|
|
2583
|
+
* @returns True if the API key is considered valid
|
|
2584
|
+
*/
|
|
2585
|
+
async validate() {
|
|
2586
|
+
if (!this.validateApiKeyFormat()) {
|
|
2587
|
+
throw new Error("Invalid API key format");
|
|
2588
|
+
}
|
|
2589
|
+
const apiResponse = await this.sendRequest("apiKeyValidation");
|
|
2590
|
+
if (!apiResponse.success) {
|
|
2591
|
+
throw new Error(apiResponse.error || "Failed to validate API key");
|
|
2592
|
+
}
|
|
2593
|
+
return true;
|
|
2594
|
+
}
|
|
2595
|
+
/**
|
|
2596
|
+
*
|
|
2597
|
+
* Validates the format of the API key. Must start with 'sk_' and contain only hexadecimal characters
|
|
2598
|
+
*
|
|
2599
|
+
* @internal
|
|
2600
|
+
* @param apiKey API key from {@link ProtocolsRouterConfig}
|
|
2601
|
+
* @returns
|
|
2602
|
+
*/
|
|
2603
|
+
validateApiKeyFormat() {
|
|
2604
|
+
if (!this.apiKey) {
|
|
2605
|
+
return false;
|
|
2606
|
+
}
|
|
2607
|
+
const prefix = "sk_";
|
|
2608
|
+
if (!this.apiKey.startsWith(prefix)) {
|
|
2609
|
+
return false;
|
|
2610
|
+
}
|
|
2611
|
+
const keyPart = this.apiKey.slice(prefix.length);
|
|
2612
|
+
if (keyPart.length < 32 || keyPart.length > 128) {
|
|
2613
|
+
return false;
|
|
2614
|
+
}
|
|
2615
|
+
const hexPattern = /^[0-9a-fA-F]+$/;
|
|
2616
|
+
if (!hexPattern.test(keyPart)) {
|
|
2617
|
+
return false;
|
|
2618
|
+
}
|
|
2619
|
+
return true;
|
|
2620
|
+
}
|
|
2621
|
+
};
|
|
2622
|
+
|
|
2341
2623
|
// src/index.ts
|
|
2342
|
-
var MyceliumSDK = class {
|
|
2624
|
+
var MyceliumSDK = class _MyceliumSDK {
|
|
2343
2625
|
/**
|
|
2344
2626
|
* Creates a new SDK instance
|
|
2345
2627
|
*
|
|
@@ -2347,7 +2629,7 @@ var MyceliumSDK = class {
|
|
|
2347
2629
|
* @throws Throws if an unsupported wallet provider is given
|
|
2348
2630
|
* @see MyceliumSDKConfig
|
|
2349
2631
|
*/
|
|
2350
|
-
constructor(config) {
|
|
2632
|
+
constructor(config, isPremiumAvailable, apiClient) {
|
|
2351
2633
|
/**
|
|
2352
2634
|
* Ramp namespace to manage ramp operations. Methods are available on {@link RampNamespace}
|
|
2353
2635
|
* @internal
|
|
@@ -2372,11 +2654,14 @@ var MyceliumSDK = class {
|
|
|
2372
2654
|
bundlerUrl: "https://public.pimlico.io/v2/8453/rpc"
|
|
2373
2655
|
}
|
|
2374
2656
|
);
|
|
2375
|
-
if (
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2657
|
+
if (config.protocolsSecurityConfig) {
|
|
2658
|
+
this.protocol = this.selectProtocol(
|
|
2659
|
+
config.protocolsSecurityConfig,
|
|
2660
|
+
isPremiumAvailable,
|
|
2661
|
+
apiClient
|
|
2379
2662
|
);
|
|
2663
|
+
} else {
|
|
2664
|
+
throw new Error("Protocols router config is required");
|
|
2380
2665
|
}
|
|
2381
2666
|
if (config.coinbaseCDPConfig) {
|
|
2382
2667
|
this.coinbaseCDP = new CoinbaseCDP(
|
|
@@ -2387,11 +2672,63 @@ var MyceliumSDK = class {
|
|
|
2387
2672
|
);
|
|
2388
2673
|
this.fundingNamespace = new FundingNamespace(this.coinbaseCDP);
|
|
2389
2674
|
}
|
|
2390
|
-
const protocolsRouterConfig = config.protocolsRouterConfig || {
|
|
2391
|
-
riskLevel: "low"
|
|
2392
|
-
};
|
|
2393
|
-
this.protocol = this.findProtocol(protocolsRouterConfig);
|
|
2394
2675
|
this.wallet = this.createWalletNamespace(config.walletsConfig);
|
|
2676
|
+
this.protocols = new ProtocolsNamespace(this.protocol);
|
|
2677
|
+
}
|
|
2678
|
+
/**
|
|
2679
|
+
* Initializes the SDK
|
|
2680
|
+
* @param config SDK configuration (networks, wallets, protocol router settings)
|
|
2681
|
+
* @returns SDK instance
|
|
2682
|
+
*/
|
|
2683
|
+
static async init(config) {
|
|
2684
|
+
let finalConfig;
|
|
2685
|
+
let isPremiumAvailable = false;
|
|
2686
|
+
if ("apiKey" in config && config.apiKey) {
|
|
2687
|
+
const apiClient = new ApiClient(config.apiKey);
|
|
2688
|
+
isPremiumAvailable = await apiClient.validate();
|
|
2689
|
+
if (isPremiumAvailable) {
|
|
2690
|
+
const apiResponse = await apiClient.sendRequest("config");
|
|
2691
|
+
if (!apiResponse.success) {
|
|
2692
|
+
throw new Error(apiResponse.error || "Failed to get onchain config");
|
|
2693
|
+
}
|
|
2694
|
+
const backendConfig = apiResponse.data;
|
|
2695
|
+
finalConfig = {
|
|
2696
|
+
integratorId: backendConfig.integratorId,
|
|
2697
|
+
walletsConfig: {
|
|
2698
|
+
embeddedWalletConfig: {
|
|
2699
|
+
provider: {
|
|
2700
|
+
type: "privy",
|
|
2701
|
+
providerConfig: {
|
|
2702
|
+
appId: backendConfig.privyAppId,
|
|
2703
|
+
appSecret: backendConfig.privyAppSecret
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
},
|
|
2707
|
+
smartWalletConfig: {
|
|
2708
|
+
provider: {
|
|
2709
|
+
type: "default"
|
|
2710
|
+
}
|
|
2711
|
+
}
|
|
2712
|
+
},
|
|
2713
|
+
chain: {
|
|
2714
|
+
chainId: config.chainId || backendConfig.chainId,
|
|
2715
|
+
rpcUrl: backendConfig.rpcUrl,
|
|
2716
|
+
bundlerUrl: backendConfig.bundlerUrl
|
|
2717
|
+
},
|
|
2718
|
+
protocolsSecurityConfig: config.protocolsSecurityConfig,
|
|
2719
|
+
coinbaseCDPConfig: {
|
|
2720
|
+
apiKeyId: backendConfig.coinbaseCdpApiKey,
|
|
2721
|
+
apiKeySecret: backendConfig.coinbaseCdpApiKeySecret
|
|
2722
|
+
}
|
|
2723
|
+
};
|
|
2724
|
+
const sdk2 = new _MyceliumSDK(finalConfig, isPremiumAvailable, apiClient);
|
|
2725
|
+
sdk2.apiClient = apiClient;
|
|
2726
|
+
return sdk2;
|
|
2727
|
+
}
|
|
2728
|
+
}
|
|
2729
|
+
finalConfig = config;
|
|
2730
|
+
const sdk = new _MyceliumSDK(finalConfig, isPremiumAvailable);
|
|
2731
|
+
return sdk;
|
|
2395
2732
|
}
|
|
2396
2733
|
/**
|
|
2397
2734
|
* Returns the chain manager instance for multi-chain operations
|
|
@@ -2429,10 +2766,13 @@ var MyceliumSDK = class {
|
|
|
2429
2766
|
* @param config Protocol router configuration (e.g. risk level)
|
|
2430
2767
|
* @returns Selected protocol object of the type {@link Protocol}
|
|
2431
2768
|
*/
|
|
2432
|
-
|
|
2433
|
-
const protocolRouter = new ProtocolRouter(
|
|
2434
|
-
const protocol = protocolRouter.
|
|
2435
|
-
|
|
2769
|
+
selectProtocol(config, isPremiumAvailable, apiClient) {
|
|
2770
|
+
const protocolRouter = new ProtocolRouter(this.chainManager, isPremiumAvailable);
|
|
2771
|
+
const protocol = protocolRouter.select();
|
|
2772
|
+
if (!config) {
|
|
2773
|
+
throw new Error("Protocols security config is required");
|
|
2774
|
+
}
|
|
2775
|
+
protocol.init(this.chainManager, config, apiClient);
|
|
2436
2776
|
return protocol;
|
|
2437
2777
|
}
|
|
2438
2778
|
/**
|