@axonfi/sdk 0.5.1 → 0.5.3
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 +15 -7
- package/dist/index.cjs +409 -386
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +410 -387
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { keccak256, stringToBytes, createPublicClient, http, createWalletClient, erc20Abi
|
|
1
|
+
import { keccak256, stringToBytes, parseUnits, createPublicClient, http, createWalletClient, erc20Abi } from 'viem';
|
|
2
2
|
import { privateKeyToAccount } from 'viem/accounts';
|
|
3
3
|
import { arbitrumSepolia, arbitrum, baseSepolia, base } from 'viem/chains';
|
|
4
4
|
import { scryptAsync } from '@noble/hashes/scrypt';
|
|
@@ -2544,314 +2544,6 @@ var AxonVaultFactoryAbi = [
|
|
|
2544
2544
|
"inputs": []
|
|
2545
2545
|
}
|
|
2546
2546
|
];
|
|
2547
|
-
function getChain(chainId) {
|
|
2548
|
-
switch (chainId) {
|
|
2549
|
-
case 8453:
|
|
2550
|
-
return base;
|
|
2551
|
-
case 84532:
|
|
2552
|
-
return baseSepolia;
|
|
2553
|
-
case 42161:
|
|
2554
|
-
return arbitrum;
|
|
2555
|
-
case 421614:
|
|
2556
|
-
return arbitrumSepolia;
|
|
2557
|
-
default:
|
|
2558
|
-
throw new Error(
|
|
2559
|
-
`Unsupported chainId: ${chainId}. Supported: 8453 (Base), 84532 (Base Sepolia), 42161 (Arbitrum), 421614 (Arbitrum Sepolia)`
|
|
2560
|
-
);
|
|
2561
|
-
}
|
|
2562
|
-
}
|
|
2563
|
-
function createAxonPublicClient(chainId, rpcUrl) {
|
|
2564
|
-
return createPublicClient({
|
|
2565
|
-
chain: getChain(chainId),
|
|
2566
|
-
transport: http(rpcUrl)
|
|
2567
|
-
});
|
|
2568
|
-
}
|
|
2569
|
-
function createAxonWalletClient(privateKey, chainId) {
|
|
2570
|
-
const account = privateKeyToAccount(privateKey);
|
|
2571
|
-
return createWalletClient({
|
|
2572
|
-
account,
|
|
2573
|
-
chain: getChain(chainId),
|
|
2574
|
-
transport: http()
|
|
2575
|
-
// signing is local — transport is unused but required by viem
|
|
2576
|
-
});
|
|
2577
|
-
}
|
|
2578
|
-
var USDC_DECIMALS = 6n;
|
|
2579
|
-
var USDC_UNIT = 10n ** USDC_DECIMALS;
|
|
2580
|
-
function toBotConfigParams(input) {
|
|
2581
|
-
return {
|
|
2582
|
-
maxPerTxAmount: BigInt(Math.round(input.maxPerTxAmount * Number(USDC_UNIT))),
|
|
2583
|
-
maxRebalanceAmount: BigInt(Math.round(input.maxRebalanceAmount * Number(USDC_UNIT))),
|
|
2584
|
-
spendingLimits: input.spendingLimits.map((sl) => ({
|
|
2585
|
-
amount: BigInt(Math.round(sl.amount * Number(USDC_UNIT))),
|
|
2586
|
-
maxCount: BigInt(sl.maxCount),
|
|
2587
|
-
windowSeconds: BigInt(sl.windowSeconds)
|
|
2588
|
-
})),
|
|
2589
|
-
aiTriggerThreshold: BigInt(Math.round(input.aiTriggerThreshold * Number(USDC_UNIT))),
|
|
2590
|
-
requireAiVerification: input.requireAiVerification
|
|
2591
|
-
};
|
|
2592
|
-
}
|
|
2593
|
-
var DEFAULT_RELAYER_URL = "https://relay.axonfi.xyz";
|
|
2594
|
-
async function getFactoryAddress(chainId, relayerUrl) {
|
|
2595
|
-
const base2 = relayerUrl ?? DEFAULT_RELAYER_URL;
|
|
2596
|
-
const resp = await fetch(`${base2}/v1/chains`);
|
|
2597
|
-
if (!resp.ok) throw new Error(`Failed to fetch chain config from relayer [${resp.status}]`);
|
|
2598
|
-
const data = await resp.json();
|
|
2599
|
-
const chain = data.chains?.find((c) => c.chainId === chainId);
|
|
2600
|
-
if (!chain?.factoryAddress) {
|
|
2601
|
-
throw new Error(`No factory address available for chainId ${chainId}. Check the relayer's chain configuration.`);
|
|
2602
|
-
}
|
|
2603
|
-
return chain.factoryAddress;
|
|
2604
|
-
}
|
|
2605
|
-
async function getBotConfig(publicClient, vaultAddress, botAddress) {
|
|
2606
|
-
const result = await publicClient.readContract({
|
|
2607
|
-
address: vaultAddress,
|
|
2608
|
-
abi: AxonVaultAbi,
|
|
2609
|
-
functionName: "getBotConfig",
|
|
2610
|
-
args: [botAddress]
|
|
2611
|
-
});
|
|
2612
|
-
return {
|
|
2613
|
-
isActive: result.isActive,
|
|
2614
|
-
registeredAt: result.registeredAt,
|
|
2615
|
-
maxPerTxAmount: result.maxPerTxAmount,
|
|
2616
|
-
maxRebalanceAmount: result.maxRebalanceAmount,
|
|
2617
|
-
spendingLimits: result.spendingLimits.map((sl) => ({
|
|
2618
|
-
amount: sl.amount,
|
|
2619
|
-
maxCount: sl.maxCount,
|
|
2620
|
-
windowSeconds: sl.windowSeconds
|
|
2621
|
-
})),
|
|
2622
|
-
aiTriggerThreshold: result.aiTriggerThreshold,
|
|
2623
|
-
requireAiVerification: result.requireAiVerification
|
|
2624
|
-
};
|
|
2625
|
-
}
|
|
2626
|
-
async function isBotActive(publicClient, vaultAddress, botAddress) {
|
|
2627
|
-
return publicClient.readContract({
|
|
2628
|
-
address: vaultAddress,
|
|
2629
|
-
abi: AxonVaultAbi,
|
|
2630
|
-
functionName: "isBotActive",
|
|
2631
|
-
args: [botAddress]
|
|
2632
|
-
});
|
|
2633
|
-
}
|
|
2634
|
-
async function getOperatorCeilings(publicClient, vaultAddress) {
|
|
2635
|
-
const result = await publicClient.readContract({
|
|
2636
|
-
address: vaultAddress,
|
|
2637
|
-
abi: AxonVaultAbi,
|
|
2638
|
-
functionName: "operatorCeilings"
|
|
2639
|
-
});
|
|
2640
|
-
const [maxPerTxAmount, maxBotDailyLimit, maxOperatorBots, vaultDailyAggregate, minAiTriggerFloor] = result;
|
|
2641
|
-
return {
|
|
2642
|
-
maxPerTxAmount,
|
|
2643
|
-
maxBotDailyLimit,
|
|
2644
|
-
maxOperatorBots,
|
|
2645
|
-
vaultDailyAggregate,
|
|
2646
|
-
minAiTriggerFloor
|
|
2647
|
-
};
|
|
2648
|
-
}
|
|
2649
|
-
async function operatorMaxDrainPerDay(publicClient, vaultAddress) {
|
|
2650
|
-
return publicClient.readContract({
|
|
2651
|
-
address: vaultAddress,
|
|
2652
|
-
abi: AxonVaultAbi,
|
|
2653
|
-
functionName: "operatorMaxDrainPerDay"
|
|
2654
|
-
});
|
|
2655
|
-
}
|
|
2656
|
-
async function isVaultPaused(publicClient, vaultAddress) {
|
|
2657
|
-
return publicClient.readContract({
|
|
2658
|
-
address: vaultAddress,
|
|
2659
|
-
abi: AxonVaultAbi,
|
|
2660
|
-
functionName: "paused"
|
|
2661
|
-
});
|
|
2662
|
-
}
|
|
2663
|
-
async function getDomainSeparator(publicClient, vaultAddress) {
|
|
2664
|
-
return publicClient.readContract({
|
|
2665
|
-
address: vaultAddress,
|
|
2666
|
-
abi: AxonVaultAbi,
|
|
2667
|
-
functionName: "DOMAIN_SEPARATOR"
|
|
2668
|
-
});
|
|
2669
|
-
}
|
|
2670
|
-
async function getVaultVersion(publicClient, vaultAddress) {
|
|
2671
|
-
const version = await publicClient.readContract({
|
|
2672
|
-
address: vaultAddress,
|
|
2673
|
-
abi: AxonVaultAbi,
|
|
2674
|
-
functionName: "VERSION"
|
|
2675
|
-
});
|
|
2676
|
-
return Number(version);
|
|
2677
|
-
}
|
|
2678
|
-
async function getVaultOwner(publicClient, vaultAddress) {
|
|
2679
|
-
return publicClient.readContract({
|
|
2680
|
-
address: vaultAddress,
|
|
2681
|
-
abi: AxonVaultAbi,
|
|
2682
|
-
functionName: "owner"
|
|
2683
|
-
});
|
|
2684
|
-
}
|
|
2685
|
-
async function getVaultOperator(publicClient, vaultAddress) {
|
|
2686
|
-
return publicClient.readContract({
|
|
2687
|
-
address: vaultAddress,
|
|
2688
|
-
abi: AxonVaultAbi,
|
|
2689
|
-
functionName: "operator"
|
|
2690
|
-
});
|
|
2691
|
-
}
|
|
2692
|
-
async function isDestinationAllowed(publicClient, vaultAddress, botAddress, destination) {
|
|
2693
|
-
const isBlacklisted = await publicClient.readContract({
|
|
2694
|
-
address: vaultAddress,
|
|
2695
|
-
abi: AxonVaultAbi,
|
|
2696
|
-
functionName: "globalDestinationBlacklist",
|
|
2697
|
-
args: [destination]
|
|
2698
|
-
});
|
|
2699
|
-
if (isBlacklisted) {
|
|
2700
|
-
return { allowed: false, reason: "Destination is on the global blacklist" };
|
|
2701
|
-
}
|
|
2702
|
-
const globalCount = await publicClient.readContract({
|
|
2703
|
-
address: vaultAddress,
|
|
2704
|
-
abi: AxonVaultAbi,
|
|
2705
|
-
functionName: "globalDestinationCount"
|
|
2706
|
-
});
|
|
2707
|
-
if (globalCount > 0n) {
|
|
2708
|
-
const isGlobalWhitelisted = await publicClient.readContract({
|
|
2709
|
-
address: vaultAddress,
|
|
2710
|
-
abi: AxonVaultAbi,
|
|
2711
|
-
functionName: "globalDestinationWhitelist",
|
|
2712
|
-
args: [destination]
|
|
2713
|
-
});
|
|
2714
|
-
if (!isGlobalWhitelisted) {
|
|
2715
|
-
return { allowed: false, reason: "Destination is not on the global whitelist" };
|
|
2716
|
-
}
|
|
2717
|
-
}
|
|
2718
|
-
const botCount = await publicClient.readContract({
|
|
2719
|
-
address: vaultAddress,
|
|
2720
|
-
abi: AxonVaultAbi,
|
|
2721
|
-
functionName: "botDestinationCount",
|
|
2722
|
-
args: [botAddress]
|
|
2723
|
-
});
|
|
2724
|
-
if (botCount > 0n) {
|
|
2725
|
-
const isBotWhitelisted = await publicClient.readContract({
|
|
2726
|
-
address: vaultAddress,
|
|
2727
|
-
abi: AxonVaultAbi,
|
|
2728
|
-
functionName: "botDestinationWhitelist",
|
|
2729
|
-
args: [botAddress, destination]
|
|
2730
|
-
});
|
|
2731
|
-
if (!isBotWhitelisted) {
|
|
2732
|
-
return { allowed: false, reason: "Destination is not on the bot whitelist" };
|
|
2733
|
-
}
|
|
2734
|
-
}
|
|
2735
|
-
return { allowed: true };
|
|
2736
|
-
}
|
|
2737
|
-
async function getRebalanceTokenCount(publicClient, vaultAddress) {
|
|
2738
|
-
const count = await publicClient.readContract({
|
|
2739
|
-
address: vaultAddress,
|
|
2740
|
-
abi: AxonVaultAbi,
|
|
2741
|
-
functionName: "rebalanceTokenCount"
|
|
2742
|
-
});
|
|
2743
|
-
return Number(count);
|
|
2744
|
-
}
|
|
2745
|
-
async function isRebalanceTokenWhitelisted(publicClient, vaultAddress, token) {
|
|
2746
|
-
return publicClient.readContract({
|
|
2747
|
-
address: vaultAddress,
|
|
2748
|
-
abi: AxonVaultAbi,
|
|
2749
|
-
functionName: "rebalanceTokenWhitelist",
|
|
2750
|
-
args: [token]
|
|
2751
|
-
});
|
|
2752
|
-
}
|
|
2753
|
-
async function deployVault(walletClient, publicClient, relayerUrl) {
|
|
2754
|
-
if (!walletClient.account) {
|
|
2755
|
-
throw new Error("walletClient has no account attached");
|
|
2756
|
-
}
|
|
2757
|
-
const chainId = walletClient.chain?.id;
|
|
2758
|
-
if (!chainId) throw new Error("walletClient has no chain configured");
|
|
2759
|
-
const factoryAddress = await getFactoryAddress(chainId, relayerUrl);
|
|
2760
|
-
const hash = await walletClient.writeContract({
|
|
2761
|
-
address: factoryAddress,
|
|
2762
|
-
abi: AxonVaultFactoryAbi,
|
|
2763
|
-
functionName: "deployVault",
|
|
2764
|
-
args: [],
|
|
2765
|
-
account: walletClient.account,
|
|
2766
|
-
chain: walletClient.chain ?? null
|
|
2767
|
-
});
|
|
2768
|
-
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
2769
|
-
for (const log of receipt.logs) {
|
|
2770
|
-
try {
|
|
2771
|
-
if (log.topics.length >= 3 && log.topics[2]) {
|
|
2772
|
-
const vaultAddress = `0x${log.topics[2].slice(26)}`;
|
|
2773
|
-
return vaultAddress;
|
|
2774
|
-
}
|
|
2775
|
-
} catch {
|
|
2776
|
-
}
|
|
2777
|
-
}
|
|
2778
|
-
throw new Error("VaultDeployed event not found in transaction receipt");
|
|
2779
|
-
}
|
|
2780
|
-
async function addBot(walletClient, publicClient, vaultAddress, botAddress, config) {
|
|
2781
|
-
if (!walletClient.account) {
|
|
2782
|
-
throw new Error("walletClient has no account attached");
|
|
2783
|
-
}
|
|
2784
|
-
const params = toBotConfigParams(config);
|
|
2785
|
-
const hash = await walletClient.writeContract({
|
|
2786
|
-
address: vaultAddress,
|
|
2787
|
-
abi: AxonVaultAbi,
|
|
2788
|
-
functionName: "addBot",
|
|
2789
|
-
args: [botAddress, params],
|
|
2790
|
-
account: walletClient.account,
|
|
2791
|
-
chain: walletClient.chain ?? null
|
|
2792
|
-
});
|
|
2793
|
-
await publicClient.waitForTransactionReceipt({ hash });
|
|
2794
|
-
return hash;
|
|
2795
|
-
}
|
|
2796
|
-
async function updateBotConfig(walletClient, publicClient, vaultAddress, botAddress, config) {
|
|
2797
|
-
if (!walletClient.account) {
|
|
2798
|
-
throw new Error("walletClient has no account attached");
|
|
2799
|
-
}
|
|
2800
|
-
const params = toBotConfigParams(config);
|
|
2801
|
-
const hash = await walletClient.writeContract({
|
|
2802
|
-
address: vaultAddress,
|
|
2803
|
-
abi: AxonVaultAbi,
|
|
2804
|
-
functionName: "updateBotConfig",
|
|
2805
|
-
args: [botAddress, params],
|
|
2806
|
-
account: walletClient.account,
|
|
2807
|
-
chain: walletClient.chain ?? null
|
|
2808
|
-
});
|
|
2809
|
-
await publicClient.waitForTransactionReceipt({ hash });
|
|
2810
|
-
return hash;
|
|
2811
|
-
}
|
|
2812
|
-
async function removeBot(walletClient, publicClient, vaultAddress, botAddress) {
|
|
2813
|
-
if (!walletClient.account) {
|
|
2814
|
-
throw new Error("walletClient has no account attached");
|
|
2815
|
-
}
|
|
2816
|
-
const hash = await walletClient.writeContract({
|
|
2817
|
-
address: vaultAddress,
|
|
2818
|
-
abi: AxonVaultAbi,
|
|
2819
|
-
functionName: "removeBot",
|
|
2820
|
-
args: [botAddress],
|
|
2821
|
-
account: walletClient.account,
|
|
2822
|
-
chain: walletClient.chain ?? null
|
|
2823
|
-
});
|
|
2824
|
-
await publicClient.waitForTransactionReceipt({ hash });
|
|
2825
|
-
return hash;
|
|
2826
|
-
}
|
|
2827
|
-
async function deposit(walletClient, publicClient, vaultAddress, token, amount, ref = "0x0000000000000000000000000000000000000000000000000000000000000000") {
|
|
2828
|
-
if (!walletClient.account) {
|
|
2829
|
-
throw new Error("walletClient has no account attached");
|
|
2830
|
-
}
|
|
2831
|
-
const isEth = token.toLowerCase() === NATIVE_ETH.toLowerCase();
|
|
2832
|
-
if (!isEth) {
|
|
2833
|
-
const approveTx = await walletClient.writeContract({
|
|
2834
|
-
address: token,
|
|
2835
|
-
abi: erc20Abi,
|
|
2836
|
-
functionName: "approve",
|
|
2837
|
-
args: [vaultAddress, amount],
|
|
2838
|
-
account: walletClient.account,
|
|
2839
|
-
chain: walletClient.chain ?? null
|
|
2840
|
-
});
|
|
2841
|
-
await publicClient.waitForTransactionReceipt({ hash: approveTx });
|
|
2842
|
-
}
|
|
2843
|
-
const hash = await walletClient.writeContract({
|
|
2844
|
-
address: vaultAddress,
|
|
2845
|
-
abi: AxonVaultAbi,
|
|
2846
|
-
functionName: "deposit",
|
|
2847
|
-
args: [token, amount, ref],
|
|
2848
|
-
account: walletClient.account,
|
|
2849
|
-
chain: walletClient.chain ?? null,
|
|
2850
|
-
...isEth ? { value: amount } : {}
|
|
2851
|
-
});
|
|
2852
|
-
await publicClient.waitForTransactionReceipt({ hash });
|
|
2853
|
-
return hash;
|
|
2854
|
-
}
|
|
2855
2547
|
|
|
2856
2548
|
// src/tokens.ts
|
|
2857
2549
|
var Token = /* @__PURE__ */ ((Token2) => {
|
|
@@ -3055,99 +2747,430 @@ var KNOWN_TOKENS = {
|
|
|
3055
2747
|
42161: "0xfc5A1A6EB076a2C7aD06eD22C90d7E710E35ad0a"
|
|
3056
2748
|
}
|
|
3057
2749
|
}
|
|
3058
|
-
};
|
|
3059
|
-
var DEFAULT_APPROVED_TOKENS = [
|
|
3060
|
-
"USDC",
|
|
3061
|
-
"USDT",
|
|
3062
|
-
"DAI",
|
|
3063
|
-
"WETH",
|
|
3064
|
-
"WBTC",
|
|
3065
|
-
"cbBTC",
|
|
3066
|
-
"wstETH",
|
|
3067
|
-
"weETH",
|
|
3068
|
-
"cbETH",
|
|
3069
|
-
"rETH"
|
|
3070
|
-
];
|
|
3071
|
-
function getDefaultApprovedTokens(chainId) {
|
|
3072
|
-
const addresses = [];
|
|
3073
|
-
for (const symbol of DEFAULT_APPROVED_TOKENS) {
|
|
3074
|
-
const entry = KNOWN_TOKENS[symbol];
|
|
3075
|
-
const addr = entry.addresses[chainId];
|
|
3076
|
-
if (addr) addresses.push(addr);
|
|
2750
|
+
};
|
|
2751
|
+
var DEFAULT_APPROVED_TOKENS = [
|
|
2752
|
+
"USDC",
|
|
2753
|
+
"USDT",
|
|
2754
|
+
"DAI",
|
|
2755
|
+
"WETH",
|
|
2756
|
+
"WBTC",
|
|
2757
|
+
"cbBTC",
|
|
2758
|
+
"wstETH",
|
|
2759
|
+
"weETH",
|
|
2760
|
+
"cbETH",
|
|
2761
|
+
"rETH"
|
|
2762
|
+
];
|
|
2763
|
+
function getDefaultApprovedTokens(chainId) {
|
|
2764
|
+
const addresses = [];
|
|
2765
|
+
for (const symbol of DEFAULT_APPROVED_TOKENS) {
|
|
2766
|
+
const entry = KNOWN_TOKENS[symbol];
|
|
2767
|
+
const addr = entry.addresses[chainId];
|
|
2768
|
+
if (addr) addresses.push(addr);
|
|
2769
|
+
}
|
|
2770
|
+
return addresses;
|
|
2771
|
+
}
|
|
2772
|
+
var addressToSymbol = /* @__PURE__ */ new Map();
|
|
2773
|
+
for (const token of Object.values(KNOWN_TOKENS)) {
|
|
2774
|
+
for (const addr of Object.values(token.addresses)) {
|
|
2775
|
+
addressToSymbol.set(addr.toLowerCase(), token.symbol);
|
|
2776
|
+
}
|
|
2777
|
+
}
|
|
2778
|
+
function getKnownTokensForChain(chainId) {
|
|
2779
|
+
const result = [];
|
|
2780
|
+
for (const token of Object.values(KNOWN_TOKENS)) {
|
|
2781
|
+
const addr = token.addresses[chainId];
|
|
2782
|
+
if (addr) {
|
|
2783
|
+
result.push({ ...token, address: addr });
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
return result;
|
|
2787
|
+
}
|
|
2788
|
+
function getTokenSymbolByAddress(address) {
|
|
2789
|
+
return addressToSymbol.get(address.toLowerCase()) ?? null;
|
|
2790
|
+
}
|
|
2791
|
+
function resolveToken(token, chainId) {
|
|
2792
|
+
if (typeof token === "string" && token.startsWith("0x")) {
|
|
2793
|
+
if (token === "0x0000000000000000000000000000000000000000") {
|
|
2794
|
+
throw new Error("Token address cannot be the zero address");
|
|
2795
|
+
}
|
|
2796
|
+
return token;
|
|
2797
|
+
}
|
|
2798
|
+
const entry = KNOWN_TOKENS[token];
|
|
2799
|
+
if (!entry) {
|
|
2800
|
+
throw new Error(`Unknown token symbol: ${token}`);
|
|
2801
|
+
}
|
|
2802
|
+
const addr = entry.addresses[chainId];
|
|
2803
|
+
if (!addr) {
|
|
2804
|
+
throw new Error(`Token ${token} is not available on chain ${chainId}`);
|
|
2805
|
+
}
|
|
2806
|
+
return addr;
|
|
2807
|
+
}
|
|
2808
|
+
|
|
2809
|
+
// src/amounts.ts
|
|
2810
|
+
function resolveTokenDecimals(token, chainId) {
|
|
2811
|
+
if (typeof token === "string" && token.startsWith("0x")) {
|
|
2812
|
+
const symbol = getTokenSymbolByAddress(token);
|
|
2813
|
+
if (!symbol) {
|
|
2814
|
+
throw new Error(
|
|
2815
|
+
`Unknown token address ${token} \u2014 cannot determine decimals. Use a bigint amount instead, or pass a known token symbol.`
|
|
2816
|
+
);
|
|
2817
|
+
}
|
|
2818
|
+
const entry2 = KNOWN_TOKENS[symbol];
|
|
2819
|
+
return entry2.decimals;
|
|
2820
|
+
}
|
|
2821
|
+
const entry = KNOWN_TOKENS[token];
|
|
2822
|
+
if (!entry) {
|
|
2823
|
+
throw new Error(
|
|
2824
|
+
`Unknown token symbol "${token}" \u2014 cannot determine decimals. Use a bigint amount instead, or use a known symbol (${Object.keys(KNOWN_TOKENS).join(", ")}).`
|
|
2825
|
+
);
|
|
2826
|
+
}
|
|
2827
|
+
return entry.decimals;
|
|
2828
|
+
}
|
|
2829
|
+
function parseAmount(amount, token, chainId) {
|
|
2830
|
+
if (typeof amount === "bigint") {
|
|
2831
|
+
return amount;
|
|
2832
|
+
}
|
|
2833
|
+
const decimals = resolveTokenDecimals(token);
|
|
2834
|
+
const str = typeof amount === "number" ? amount.toString() : amount;
|
|
2835
|
+
const dotIndex = str.indexOf(".");
|
|
2836
|
+
if (dotIndex !== -1) {
|
|
2837
|
+
const decimalPlaces = str.length - dotIndex - 1;
|
|
2838
|
+
if (decimalPlaces > decimals) {
|
|
2839
|
+
throw new Error(
|
|
2840
|
+
`Amount "${str}" has ${decimalPlaces} decimal places, but ${typeof token === "string" && token.startsWith("0x") ? "this token" : token} only supports ${decimals}. Truncate or round your amount.`
|
|
2841
|
+
);
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2844
|
+
return parseUnits(str, decimals);
|
|
2845
|
+
}
|
|
2846
|
+
|
|
2847
|
+
// src/vault.ts
|
|
2848
|
+
function getChain(chainId) {
|
|
2849
|
+
switch (chainId) {
|
|
2850
|
+
case 8453:
|
|
2851
|
+
return base;
|
|
2852
|
+
case 84532:
|
|
2853
|
+
return baseSepolia;
|
|
2854
|
+
case 42161:
|
|
2855
|
+
return arbitrum;
|
|
2856
|
+
case 421614:
|
|
2857
|
+
return arbitrumSepolia;
|
|
2858
|
+
default:
|
|
2859
|
+
throw new Error(
|
|
2860
|
+
`Unsupported chainId: ${chainId}. Supported: 8453 (Base), 84532 (Base Sepolia), 42161 (Arbitrum), 421614 (Arbitrum Sepolia)`
|
|
2861
|
+
);
|
|
2862
|
+
}
|
|
2863
|
+
}
|
|
2864
|
+
function createAxonPublicClient(chainId, rpcUrl) {
|
|
2865
|
+
return createPublicClient({
|
|
2866
|
+
chain: getChain(chainId),
|
|
2867
|
+
transport: http(rpcUrl)
|
|
2868
|
+
});
|
|
2869
|
+
}
|
|
2870
|
+
function createAxonWalletClient(privateKey, chainId) {
|
|
2871
|
+
const account = privateKeyToAccount(privateKey);
|
|
2872
|
+
return createWalletClient({
|
|
2873
|
+
account,
|
|
2874
|
+
chain: getChain(chainId),
|
|
2875
|
+
transport: http()
|
|
2876
|
+
// signing is local — transport is unused but required by viem
|
|
2877
|
+
});
|
|
2878
|
+
}
|
|
2879
|
+
var USDC_DECIMALS = 6n;
|
|
2880
|
+
var USDC_UNIT = 10n ** USDC_DECIMALS;
|
|
2881
|
+
function toBotConfigParams(input) {
|
|
2882
|
+
return {
|
|
2883
|
+
maxPerTxAmount: BigInt(Math.round(input.maxPerTxAmount * Number(USDC_UNIT))),
|
|
2884
|
+
maxRebalanceAmount: BigInt(Math.round(input.maxRebalanceAmount * Number(USDC_UNIT))),
|
|
2885
|
+
spendingLimits: input.spendingLimits.map((sl) => ({
|
|
2886
|
+
amount: BigInt(Math.round(sl.amount * Number(USDC_UNIT))),
|
|
2887
|
+
maxCount: BigInt(sl.maxCount),
|
|
2888
|
+
windowSeconds: BigInt(sl.windowSeconds)
|
|
2889
|
+
})),
|
|
2890
|
+
aiTriggerThreshold: BigInt(Math.round(input.aiTriggerThreshold * Number(USDC_UNIT))),
|
|
2891
|
+
requireAiVerification: input.requireAiVerification
|
|
2892
|
+
};
|
|
2893
|
+
}
|
|
2894
|
+
var DEFAULT_RELAYER_URL = "https://relay.axonfi.xyz";
|
|
2895
|
+
async function getFactoryAddress(chainId, relayerUrl) {
|
|
2896
|
+
const base2 = relayerUrl ?? DEFAULT_RELAYER_URL;
|
|
2897
|
+
const resp = await fetch(`${base2}/v1/chains`);
|
|
2898
|
+
if (!resp.ok) throw new Error(`Failed to fetch chain config from relayer [${resp.status}]`);
|
|
2899
|
+
const data = await resp.json();
|
|
2900
|
+
const chain = data.chains?.find((c) => c.chainId === chainId);
|
|
2901
|
+
if (!chain?.factoryAddress) {
|
|
2902
|
+
throw new Error(`No factory address available for chainId ${chainId}. Check the relayer's chain configuration.`);
|
|
2903
|
+
}
|
|
2904
|
+
return chain.factoryAddress;
|
|
2905
|
+
}
|
|
2906
|
+
async function getBotConfig(publicClient, vaultAddress, botAddress) {
|
|
2907
|
+
const result = await publicClient.readContract({
|
|
2908
|
+
address: vaultAddress,
|
|
2909
|
+
abi: AxonVaultAbi,
|
|
2910
|
+
functionName: "getBotConfig",
|
|
2911
|
+
args: [botAddress]
|
|
2912
|
+
});
|
|
2913
|
+
return {
|
|
2914
|
+
isActive: result.isActive,
|
|
2915
|
+
registeredAt: result.registeredAt,
|
|
2916
|
+
maxPerTxAmount: result.maxPerTxAmount,
|
|
2917
|
+
maxRebalanceAmount: result.maxRebalanceAmount,
|
|
2918
|
+
spendingLimits: result.spendingLimits.map((sl) => ({
|
|
2919
|
+
amount: sl.amount,
|
|
2920
|
+
maxCount: sl.maxCount,
|
|
2921
|
+
windowSeconds: sl.windowSeconds
|
|
2922
|
+
})),
|
|
2923
|
+
aiTriggerThreshold: result.aiTriggerThreshold,
|
|
2924
|
+
requireAiVerification: result.requireAiVerification
|
|
2925
|
+
};
|
|
2926
|
+
}
|
|
2927
|
+
async function isBotActive(publicClient, vaultAddress, botAddress) {
|
|
2928
|
+
return publicClient.readContract({
|
|
2929
|
+
address: vaultAddress,
|
|
2930
|
+
abi: AxonVaultAbi,
|
|
2931
|
+
functionName: "isBotActive",
|
|
2932
|
+
args: [botAddress]
|
|
2933
|
+
});
|
|
2934
|
+
}
|
|
2935
|
+
async function getOperatorCeilings(publicClient, vaultAddress) {
|
|
2936
|
+
const result = await publicClient.readContract({
|
|
2937
|
+
address: vaultAddress,
|
|
2938
|
+
abi: AxonVaultAbi,
|
|
2939
|
+
functionName: "operatorCeilings"
|
|
2940
|
+
});
|
|
2941
|
+
const [maxPerTxAmount, maxBotDailyLimit, maxOperatorBots, vaultDailyAggregate, minAiTriggerFloor] = result;
|
|
2942
|
+
return {
|
|
2943
|
+
maxPerTxAmount,
|
|
2944
|
+
maxBotDailyLimit,
|
|
2945
|
+
maxOperatorBots,
|
|
2946
|
+
vaultDailyAggregate,
|
|
2947
|
+
minAiTriggerFloor
|
|
2948
|
+
};
|
|
2949
|
+
}
|
|
2950
|
+
async function operatorMaxDrainPerDay(publicClient, vaultAddress) {
|
|
2951
|
+
return publicClient.readContract({
|
|
2952
|
+
address: vaultAddress,
|
|
2953
|
+
abi: AxonVaultAbi,
|
|
2954
|
+
functionName: "operatorMaxDrainPerDay"
|
|
2955
|
+
});
|
|
2956
|
+
}
|
|
2957
|
+
async function isVaultPaused(publicClient, vaultAddress) {
|
|
2958
|
+
return publicClient.readContract({
|
|
2959
|
+
address: vaultAddress,
|
|
2960
|
+
abi: AxonVaultAbi,
|
|
2961
|
+
functionName: "paused"
|
|
2962
|
+
});
|
|
2963
|
+
}
|
|
2964
|
+
async function getDomainSeparator(publicClient, vaultAddress) {
|
|
2965
|
+
return publicClient.readContract({
|
|
2966
|
+
address: vaultAddress,
|
|
2967
|
+
abi: AxonVaultAbi,
|
|
2968
|
+
functionName: "DOMAIN_SEPARATOR"
|
|
2969
|
+
});
|
|
2970
|
+
}
|
|
2971
|
+
async function getVaultVersion(publicClient, vaultAddress) {
|
|
2972
|
+
const version = await publicClient.readContract({
|
|
2973
|
+
address: vaultAddress,
|
|
2974
|
+
abi: AxonVaultAbi,
|
|
2975
|
+
functionName: "VERSION"
|
|
2976
|
+
});
|
|
2977
|
+
return Number(version);
|
|
2978
|
+
}
|
|
2979
|
+
async function getVaultOwner(publicClient, vaultAddress) {
|
|
2980
|
+
return publicClient.readContract({
|
|
2981
|
+
address: vaultAddress,
|
|
2982
|
+
abi: AxonVaultAbi,
|
|
2983
|
+
functionName: "owner"
|
|
2984
|
+
});
|
|
2985
|
+
}
|
|
2986
|
+
async function getVaultOperator(publicClient, vaultAddress) {
|
|
2987
|
+
return publicClient.readContract({
|
|
2988
|
+
address: vaultAddress,
|
|
2989
|
+
abi: AxonVaultAbi,
|
|
2990
|
+
functionName: "operator"
|
|
2991
|
+
});
|
|
2992
|
+
}
|
|
2993
|
+
async function isDestinationAllowed(publicClient, vaultAddress, botAddress, destination) {
|
|
2994
|
+
const isBlacklisted = await publicClient.readContract({
|
|
2995
|
+
address: vaultAddress,
|
|
2996
|
+
abi: AxonVaultAbi,
|
|
2997
|
+
functionName: "globalDestinationBlacklist",
|
|
2998
|
+
args: [destination]
|
|
2999
|
+
});
|
|
3000
|
+
if (isBlacklisted) {
|
|
3001
|
+
return { allowed: false, reason: "Destination is on the global blacklist" };
|
|
3002
|
+
}
|
|
3003
|
+
const globalCount = await publicClient.readContract({
|
|
3004
|
+
address: vaultAddress,
|
|
3005
|
+
abi: AxonVaultAbi,
|
|
3006
|
+
functionName: "globalDestinationCount"
|
|
3007
|
+
});
|
|
3008
|
+
if (globalCount > 0n) {
|
|
3009
|
+
const isGlobalWhitelisted = await publicClient.readContract({
|
|
3010
|
+
address: vaultAddress,
|
|
3011
|
+
abi: AxonVaultAbi,
|
|
3012
|
+
functionName: "globalDestinationWhitelist",
|
|
3013
|
+
args: [destination]
|
|
3014
|
+
});
|
|
3015
|
+
if (!isGlobalWhitelisted) {
|
|
3016
|
+
return { allowed: false, reason: "Destination is not on the global whitelist" };
|
|
3017
|
+
}
|
|
3018
|
+
}
|
|
3019
|
+
const botCount = await publicClient.readContract({
|
|
3020
|
+
address: vaultAddress,
|
|
3021
|
+
abi: AxonVaultAbi,
|
|
3022
|
+
functionName: "botDestinationCount",
|
|
3023
|
+
args: [botAddress]
|
|
3024
|
+
});
|
|
3025
|
+
if (botCount > 0n) {
|
|
3026
|
+
const isBotWhitelisted = await publicClient.readContract({
|
|
3027
|
+
address: vaultAddress,
|
|
3028
|
+
abi: AxonVaultAbi,
|
|
3029
|
+
functionName: "botDestinationWhitelist",
|
|
3030
|
+
args: [botAddress, destination]
|
|
3031
|
+
});
|
|
3032
|
+
if (!isBotWhitelisted) {
|
|
3033
|
+
return { allowed: false, reason: "Destination is not on the bot whitelist" };
|
|
3034
|
+
}
|
|
3077
3035
|
}
|
|
3078
|
-
return
|
|
3036
|
+
return { allowed: true };
|
|
3079
3037
|
}
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3038
|
+
async function getRebalanceTokenCount(publicClient, vaultAddress) {
|
|
3039
|
+
const count = await publicClient.readContract({
|
|
3040
|
+
address: vaultAddress,
|
|
3041
|
+
abi: AxonVaultAbi,
|
|
3042
|
+
functionName: "rebalanceTokenCount"
|
|
3043
|
+
});
|
|
3044
|
+
return Number(count);
|
|
3085
3045
|
}
|
|
3086
|
-
function
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3046
|
+
async function isRebalanceTokenWhitelisted(publicClient, vaultAddress, token) {
|
|
3047
|
+
return publicClient.readContract({
|
|
3048
|
+
address: vaultAddress,
|
|
3049
|
+
abi: AxonVaultAbi,
|
|
3050
|
+
functionName: "rebalanceTokenWhitelist",
|
|
3051
|
+
args: [token]
|
|
3052
|
+
});
|
|
3053
|
+
}
|
|
3054
|
+
async function deployVault(walletClient, publicClient, relayerUrl) {
|
|
3055
|
+
if (!walletClient.account) {
|
|
3056
|
+
throw new Error("walletClient has no account attached");
|
|
3057
|
+
}
|
|
3058
|
+
const chainId = walletClient.chain?.id;
|
|
3059
|
+
if (!chainId) throw new Error("walletClient has no chain configured");
|
|
3060
|
+
const factoryAddress = await getFactoryAddress(chainId, relayerUrl);
|
|
3061
|
+
const hash = await walletClient.writeContract({
|
|
3062
|
+
address: factoryAddress,
|
|
3063
|
+
abi: AxonVaultFactoryAbi,
|
|
3064
|
+
functionName: "deployVault",
|
|
3065
|
+
args: [],
|
|
3066
|
+
account: walletClient.account,
|
|
3067
|
+
chain: walletClient.chain ?? null
|
|
3068
|
+
});
|
|
3069
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
3070
|
+
for (const log of receipt.logs) {
|
|
3071
|
+
try {
|
|
3072
|
+
if (log.topics.length >= 3 && log.topics[2]) {
|
|
3073
|
+
const vaultAddress = `0x${log.topics[2].slice(26)}`;
|
|
3074
|
+
return vaultAddress;
|
|
3075
|
+
}
|
|
3076
|
+
} catch {
|
|
3092
3077
|
}
|
|
3093
3078
|
}
|
|
3094
|
-
|
|
3095
|
-
}
|
|
3096
|
-
function getTokenSymbolByAddress(address) {
|
|
3097
|
-
return addressToSymbol.get(address.toLowerCase()) ?? null;
|
|
3079
|
+
throw new Error("VaultDeployed event not found in transaction receipt");
|
|
3098
3080
|
}
|
|
3099
|
-
function
|
|
3100
|
-
if (
|
|
3101
|
-
|
|
3102
|
-
throw new Error("Token address cannot be the zero address");
|
|
3103
|
-
}
|
|
3104
|
-
return token;
|
|
3081
|
+
async function addBot(walletClient, publicClient, vaultAddress, botAddress, config) {
|
|
3082
|
+
if (!walletClient.account) {
|
|
3083
|
+
throw new Error("walletClient has no account attached");
|
|
3105
3084
|
}
|
|
3106
|
-
const
|
|
3107
|
-
|
|
3108
|
-
|
|
3085
|
+
const params = toBotConfigParams(config);
|
|
3086
|
+
const hash = await walletClient.writeContract({
|
|
3087
|
+
address: vaultAddress,
|
|
3088
|
+
abi: AxonVaultAbi,
|
|
3089
|
+
functionName: "addBot",
|
|
3090
|
+
args: [botAddress, params],
|
|
3091
|
+
account: walletClient.account,
|
|
3092
|
+
chain: walletClient.chain ?? null
|
|
3093
|
+
});
|
|
3094
|
+
await publicClient.waitForTransactionReceipt({ hash });
|
|
3095
|
+
return hash;
|
|
3096
|
+
}
|
|
3097
|
+
async function updateBotConfig(walletClient, publicClient, vaultAddress, botAddress, config) {
|
|
3098
|
+
if (!walletClient.account) {
|
|
3099
|
+
throw new Error("walletClient has no account attached");
|
|
3109
3100
|
}
|
|
3110
|
-
const
|
|
3111
|
-
|
|
3112
|
-
|
|
3101
|
+
const params = toBotConfigParams(config);
|
|
3102
|
+
const hash = await walletClient.writeContract({
|
|
3103
|
+
address: vaultAddress,
|
|
3104
|
+
abi: AxonVaultAbi,
|
|
3105
|
+
functionName: "updateBotConfig",
|
|
3106
|
+
args: [botAddress, params],
|
|
3107
|
+
account: walletClient.account,
|
|
3108
|
+
chain: walletClient.chain ?? null
|
|
3109
|
+
});
|
|
3110
|
+
await publicClient.waitForTransactionReceipt({ hash });
|
|
3111
|
+
return hash;
|
|
3112
|
+
}
|
|
3113
|
+
async function removeBot(walletClient, publicClient, vaultAddress, botAddress) {
|
|
3114
|
+
if (!walletClient.account) {
|
|
3115
|
+
throw new Error("walletClient has no account attached");
|
|
3113
3116
|
}
|
|
3114
|
-
|
|
3117
|
+
const hash = await walletClient.writeContract({
|
|
3118
|
+
address: vaultAddress,
|
|
3119
|
+
abi: AxonVaultAbi,
|
|
3120
|
+
functionName: "removeBot",
|
|
3121
|
+
args: [botAddress],
|
|
3122
|
+
account: walletClient.account,
|
|
3123
|
+
chain: walletClient.chain ?? null
|
|
3124
|
+
});
|
|
3125
|
+
await publicClient.waitForTransactionReceipt({ hash });
|
|
3126
|
+
return hash;
|
|
3115
3127
|
}
|
|
3116
|
-
function
|
|
3117
|
-
if (
|
|
3118
|
-
|
|
3119
|
-
if (!symbol) {
|
|
3120
|
-
throw new Error(
|
|
3121
|
-
`Unknown token address ${token} \u2014 cannot determine decimals. Use a bigint amount instead, or pass a known token symbol.`
|
|
3122
|
-
);
|
|
3123
|
-
}
|
|
3124
|
-
const entry2 = KNOWN_TOKENS[symbol];
|
|
3125
|
-
return entry2.decimals;
|
|
3128
|
+
async function deposit(walletClient, publicClient, vaultAddress, token, amount, ref = "0x0000000000000000000000000000000000000000000000000000000000000000") {
|
|
3129
|
+
if (!walletClient.account) {
|
|
3130
|
+
throw new Error("walletClient has no account attached");
|
|
3126
3131
|
}
|
|
3127
|
-
const
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
+
const isEthSymbol = token === "ETH" || token === "eth";
|
|
3133
|
+
let tokenAddress;
|
|
3134
|
+
if (isEthSymbol) {
|
|
3135
|
+
tokenAddress = NATIVE_ETH;
|
|
3136
|
+
} else if (token.startsWith("0x")) {
|
|
3137
|
+
tokenAddress = token;
|
|
3138
|
+
} else {
|
|
3139
|
+
const chainId = walletClient.chain?.id;
|
|
3140
|
+
if (!chainId) throw new Error("walletClient has no chain \u2014 cannot resolve token symbol");
|
|
3141
|
+
tokenAddress = resolveToken(token, chainId);
|
|
3132
3142
|
}
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
function parseAmount(amount, token, chainId) {
|
|
3143
|
+
const isEth = tokenAddress.toLowerCase() === NATIVE_ETH.toLowerCase();
|
|
3144
|
+
let resolvedAmount;
|
|
3136
3145
|
if (typeof amount === "bigint") {
|
|
3137
|
-
|
|
3146
|
+
resolvedAmount = amount;
|
|
3147
|
+
} else if (isEth) {
|
|
3148
|
+
resolvedAmount = parseAmount(amount, "WETH");
|
|
3149
|
+
} else {
|
|
3150
|
+
resolvedAmount = parseAmount(amount, token);
|
|
3138
3151
|
}
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
}
|
|
3152
|
+
if (!isEth) {
|
|
3153
|
+
const approveTx = await walletClient.writeContract({
|
|
3154
|
+
address: tokenAddress,
|
|
3155
|
+
abi: erc20Abi,
|
|
3156
|
+
functionName: "approve",
|
|
3157
|
+
args: [vaultAddress, resolvedAmount],
|
|
3158
|
+
account: walletClient.account,
|
|
3159
|
+
chain: walletClient.chain ?? null
|
|
3160
|
+
});
|
|
3161
|
+
await publicClient.waitForTransactionReceipt({ hash: approveTx });
|
|
3149
3162
|
}
|
|
3150
|
-
|
|
3163
|
+
const hash = await walletClient.writeContract({
|
|
3164
|
+
address: vaultAddress,
|
|
3165
|
+
abi: AxonVaultAbi,
|
|
3166
|
+
functionName: "deposit",
|
|
3167
|
+
args: [tokenAddress, resolvedAmount, ref],
|
|
3168
|
+
account: walletClient.account,
|
|
3169
|
+
chain: walletClient.chain ?? null,
|
|
3170
|
+
...isEth ? { value: resolvedAmount } : {}
|
|
3171
|
+
});
|
|
3172
|
+
await publicClient.waitForTransactionReceipt({ hash });
|
|
3173
|
+
return hash;
|
|
3151
3174
|
}
|
|
3152
3175
|
|
|
3153
3176
|
// src/utils.ts
|