@persistenceone/bridgekitty 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +232 -0
  3. package/dist/backends/across.d.ts +10 -0
  4. package/dist/backends/across.js +285 -0
  5. package/dist/backends/debridge.d.ts +11 -0
  6. package/dist/backends/debridge.js +380 -0
  7. package/dist/backends/lifi.d.ts +19 -0
  8. package/dist/backends/lifi.js +295 -0
  9. package/dist/backends/persistence.d.ts +86 -0
  10. package/dist/backends/persistence.js +642 -0
  11. package/dist/backends/relay.d.ts +11 -0
  12. package/dist/backends/relay.js +292 -0
  13. package/dist/backends/squid.d.ts +31 -0
  14. package/dist/backends/squid.js +476 -0
  15. package/dist/backends/types.d.ts +125 -0
  16. package/dist/backends/types.js +11 -0
  17. package/dist/index.d.ts +2 -0
  18. package/dist/index.js +154 -0
  19. package/dist/routing/engine.d.ts +49 -0
  20. package/dist/routing/engine.js +336 -0
  21. package/dist/tools/check-status.d.ts +3 -0
  22. package/dist/tools/check-status.js +93 -0
  23. package/dist/tools/execute-bridge.d.ts +3 -0
  24. package/dist/tools/execute-bridge.js +428 -0
  25. package/dist/tools/get-chains.d.ts +3 -0
  26. package/dist/tools/get-chains.js +162 -0
  27. package/dist/tools/get-quote.d.ts +3 -0
  28. package/dist/tools/get-quote.js +534 -0
  29. package/dist/tools/get-tokens.d.ts +3 -0
  30. package/dist/tools/get-tokens.js +128 -0
  31. package/dist/tools/help.d.ts +2 -0
  32. package/dist/tools/help.js +204 -0
  33. package/dist/tools/multi-quote.d.ts +3 -0
  34. package/dist/tools/multi-quote.js +310 -0
  35. package/dist/tools/onboard.d.ts +3 -0
  36. package/dist/tools/onboard.js +218 -0
  37. package/dist/tools/wallet.d.ts +14 -0
  38. package/dist/tools/wallet.js +744 -0
  39. package/dist/tools/xprt-farm.d.ts +3 -0
  40. package/dist/tools/xprt-farm.js +1308 -0
  41. package/dist/tools/xprt-rewards.d.ts +2 -0
  42. package/dist/tools/xprt-rewards.js +177 -0
  43. package/dist/tools/xprt-staking.d.ts +2 -0
  44. package/dist/tools/xprt-staking.js +565 -0
  45. package/dist/utils/chains.d.ts +22 -0
  46. package/dist/utils/chains.js +154 -0
  47. package/dist/utils/circuit-breaker.d.ts +64 -0
  48. package/dist/utils/circuit-breaker.js +160 -0
  49. package/dist/utils/evm.d.ts +18 -0
  50. package/dist/utils/evm.js +46 -0
  51. package/dist/utils/fill-detector.d.ts +70 -0
  52. package/dist/utils/fill-detector.js +298 -0
  53. package/dist/utils/gas-estimator.d.ts +67 -0
  54. package/dist/utils/gas-estimator.js +340 -0
  55. package/dist/utils/sanitize-error.d.ts +23 -0
  56. package/dist/utils/sanitize-error.js +101 -0
  57. package/dist/utils/token-registry.d.ts +70 -0
  58. package/dist/utils/token-registry.js +669 -0
  59. package/dist/utils/tokens.d.ts +17 -0
  60. package/dist/utils/tokens.js +37 -0
  61. package/dist/utils/tx-simulator.d.ts +27 -0
  62. package/dist/utils/tx-simulator.js +105 -0
  63. package/package.json +75 -0
@@ -0,0 +1,218 @@
1
+ import { getKey, getConfigDir } from "./wallet.js";
2
+ import { ethers } from "ethers";
3
+ import { getProvider } from "../utils/gas-estimator.js";
4
+ import * as path from "path";
5
+ const REWARDS_API = "https://rewards.interop.persistence.one";
6
+ const TIMEOUT_MS = 15_000;
7
+ const CBTCB_BASE = "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf"; // cbBTC on Base (8 decimals)
8
+ const BTCB_BSC = "0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c"; // BTCB on BSC (18 decimals)
9
+ const ERC20_BALANCE_ABI = [
10
+ "function balanceOf(address) view returns (uint256)",
11
+ ];
12
+ async function fetchJson(url) {
13
+ const controller = new AbortController();
14
+ const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
15
+ try {
16
+ const res = await fetch(url, { signal: controller.signal });
17
+ if (!res.ok) {
18
+ const text = await res.text().catch(() => "");
19
+ throw new Error(`HTTP ${res.status}: ${text.slice(0, 200)}`);
20
+ }
21
+ return res.json();
22
+ }
23
+ finally {
24
+ clearTimeout(timer);
25
+ }
26
+ }
27
+ export function registerOnboardTool(server, engine) {
28
+ server.tool("xprt_onboard", "Personalized onboarding for XPRT farming. Detects current wallet state and returns an ordered " +
29
+ "action plan to go from your current position to actively farming XPRT rewards.", {}, async () => {
30
+ // Step 0: Check if wallet is configured
31
+ const privateKey = getKey("privateKey");
32
+ if (!privateKey) {
33
+ const envPath = path.resolve(getConfigDir(), ".env");
34
+ return {
35
+ content: [{
36
+ type: "text",
37
+ text: JSON.stringify({
38
+ status: "wallet_needed",
39
+ steps: [{
40
+ step: 1,
41
+ action: "Set up a wallet",
42
+ tool: "wallet_setup",
43
+ suggestedParams: {},
44
+ explanation: "You need a wallet before you can start farming. wallet_setup will generate keys for EVM, Cosmos, and Solana chains and save them securely.",
45
+ }],
46
+ nextAction: "Run wallet_setup to create your wallet, then run xprt_onboard again.",
47
+ configPath: envPath,
48
+ }, null, 2),
49
+ }],
50
+ };
51
+ }
52
+ const wallet = new ethers.Wallet(privateKey);
53
+ const evmAddress = wallet.address;
54
+ const steps = [];
55
+ let stepNum = 1;
56
+ // Gather wallet state in parallel
57
+ const balanceChecks = await Promise.allSettled([
58
+ // ETH on Base (for gas)
59
+ (async () => {
60
+ const provider = await getProvider(8453);
61
+ const bal = await provider.getBalance(evmAddress);
62
+ return { key: "ethBase", value: parseFloat(ethers.formatEther(bal)) };
63
+ })(),
64
+ // cbBTC on Base
65
+ (async () => {
66
+ const provider = await getProvider(8453);
67
+ const contract = new ethers.Contract(CBTCB_BASE, ERC20_BALANCE_ABI, provider);
68
+ const bal = await contract.balanceOf(evmAddress);
69
+ return { key: "cbbtcBase", value: parseFloat(ethers.formatUnits(bal, 8)) };
70
+ })(),
71
+ // BTCB on BSC
72
+ (async () => {
73
+ const provider = await getProvider(56);
74
+ const contract = new ethers.Contract(BTCB_BSC, ERC20_BALANCE_ABI, provider);
75
+ const bal = await contract.balanceOf(evmAddress);
76
+ return { key: "btcbBsc", value: parseFloat(ethers.formatUnits(bal, 18)) };
77
+ })(),
78
+ // BNB on BSC (for gas)
79
+ (async () => {
80
+ const provider = await getProvider(56);
81
+ const bal = await provider.getBalance(evmAddress);
82
+ return { key: "bnbBsc", value: parseFloat(ethers.formatEther(bal)) };
83
+ })(),
84
+ ]);
85
+ const balances = {
86
+ ethBase: 0,
87
+ cbbtcBase: 0,
88
+ btcbBsc: 0,
89
+ bnbBsc: 0,
90
+ };
91
+ for (const result of balanceChecks) {
92
+ if (result.status === "fulfilled") {
93
+ balances[result.value.key] = result.value.value;
94
+ }
95
+ }
96
+ // Check Persistence address link status
97
+ let isLinked = false;
98
+ let persistenceAddress;
99
+ try {
100
+ const linkData = await fetchJson(`${REWARDS_API}/address-verification/check/${evmAddress}`);
101
+ isLinked = linkData.isRegistered ?? false;
102
+ persistenceAddress = linkData.persistenceAddress;
103
+ }
104
+ catch {
105
+ // Non-fatal -- we'll suggest linking anyway
106
+ }
107
+ // Build personalized steps based on state
108
+ // Step: Need ETH on Base for gas
109
+ const needsBaseGas = balances.ethBase < 0.003;
110
+ const hasCbbtc = balances.cbbtcBase >= 0.00005;
111
+ const hasBtcb = balances.btcbBsc >= 0.00005;
112
+ const needsBscGas = balances.bnbBsc < 0.001;
113
+ const hasBtcAnywhere = hasCbbtc || hasBtcb;
114
+ if (needsBaseGas && !hasBtcAnywhere) {
115
+ // User has nothing -- need to fund wallet first
116
+ steps.push({
117
+ step: stepNum++,
118
+ action: "Fund your wallet with ETH on Base",
119
+ tool: "wallet_balance",
120
+ suggestedParams: { chains: ["base"] },
121
+ explanation: `Send at least 0.005 ETH to ${evmAddress} on Base (chain ID 8453). This covers gas fees and will be partially converted to cbBTC for farming. Current balance: ${balances.ethBase.toFixed(6)} ETH.`,
122
+ });
123
+ }
124
+ if (!needsBaseGas && !hasBtcAnywhere) {
125
+ // Has ETH but no BTC -- needs to prepare
126
+ steps.push({
127
+ step: stepNum++,
128
+ action: "Convert ETH to cbBTC and bridge gas to BSC",
129
+ tool: "xprt_farm_prepare",
130
+ suggestedParams: {},
131
+ explanation: `You have ${balances.ethBase.toFixed(6)} ETH on Base. xprt_farm_prepare will swap some to cbBTC for farming and bridge a small amount to BSC for gas.`,
132
+ });
133
+ }
134
+ if (hasBtcAnywhere && needsBscGas) {
135
+ // Has BTC but no BSC gas
136
+ steps.push({
137
+ step: stepNum++,
138
+ action: "Bridge some ETH to BSC for gas",
139
+ tool: "bridge_get_quote",
140
+ suggestedParams: {
141
+ fromChain: "base",
142
+ toChain: "bsc",
143
+ fromToken: "ETH",
144
+ toToken: "BNB",
145
+ amount: "0.0002",
146
+ fromAddress: evmAddress,
147
+ },
148
+ explanation: `You need BNB on BSC for gas fees during farming round-trips. Current BNB balance: ${balances.bnbBsc.toFixed(6)}. Bridge a small amount of ETH to BNB.`,
149
+ });
150
+ }
151
+ if (hasBtcAnywhere && !needsBscGas) {
152
+ // Ready to farm
153
+ steps.push({
154
+ step: stepNum++,
155
+ action: "Start XPRT farming",
156
+ tool: "xprt_farm_start",
157
+ suggestedParams: {
158
+ rounds: 5,
159
+ startFrom: "auto",
160
+ },
161
+ explanation: `You have ${hasCbbtc ? balances.cbbtcBase.toFixed(8) + " cbBTC on Base" : ""}${hasCbbtc && hasBtcb ? " and " : ""}${hasBtcb ? balances.btcbBsc.toFixed(8) + " BTCB on BSC" : ""}. Ready to start round-trip farming to earn XPRT rewards.`,
162
+ });
163
+ }
164
+ if (!isLinked) {
165
+ steps.push({
166
+ step: stepNum++,
167
+ action: "Link your Persistence address for rewards",
168
+ tool: "xprt_farm_status",
169
+ suggestedParams: {},
170
+ explanation: "Link your EVM wallet to a Persistence address so XPRT rewards can be distributed to your Cosmos wallet. Run xprt_farm_status to check link status and follow the instructions.",
171
+ });
172
+ }
173
+ // Always suggest boost as optional final step
174
+ steps.push({
175
+ step: stepNum++,
176
+ action: "Optional: Stake XPRT for multiplier boost",
177
+ tool: "xprt_farm_boost",
178
+ suggestedParams: {},
179
+ explanation: "Stake XPRT on Persistence chain to increase your farming multiplier. Tiers: Explorer (1x, 0 XPRT), Voyager (2x, 10K XPRT), Pioneer (5x, 1M XPRT).",
180
+ });
181
+ // Determine overall readiness
182
+ let readiness;
183
+ if (!needsBaseGas && hasBtcAnywhere && !needsBscGas) {
184
+ readiness = "ready_to_farm";
185
+ }
186
+ else if (!needsBaseGas && !hasBtcAnywhere) {
187
+ readiness = "needs_btc_conversion";
188
+ }
189
+ else if (hasBtcAnywhere && needsBscGas) {
190
+ readiness = "needs_bsc_gas";
191
+ }
192
+ else {
193
+ readiness = "needs_funding";
194
+ }
195
+ const response = {
196
+ status: readiness,
197
+ wallet: evmAddress,
198
+ currentBalances: {
199
+ "ETH (Base)": `${balances.ethBase.toFixed(6)} ETH`,
200
+ "cbBTC (Base)": `${balances.cbbtcBase.toFixed(8)} BTC`,
201
+ "BTCB (BSC)": `${balances.btcbBsc.toFixed(8)} BTC`,
202
+ "BNB (BSC)": `${balances.bnbBsc.toFixed(6)} BNB`,
203
+ },
204
+ persistenceLinked: isLinked,
205
+ steps,
206
+ estimatedYield: "XPRT rewards are distributed daily based on your bridging volume and multiplier tier. Estimated, not guaranteed.",
207
+ };
208
+ if (persistenceAddress) {
209
+ response.persistenceAddress = persistenceAddress;
210
+ }
211
+ return {
212
+ content: [{
213
+ type: "text",
214
+ text: JSON.stringify(response, null, 2),
215
+ }],
216
+ };
217
+ });
218
+ }
@@ -0,0 +1,14 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ /** Read a key: keyStore first, then process.env fallback (clearing env after read) */
3
+ export declare function getKey(name: "privateKey" | "mnemonic" | "solanaKey"): string | undefined;
4
+ /**
5
+ * Returns the BridgeKitty config directory. Resolution order (highest priority first):
6
+ * 1. BRIDGEKITTY_CONFIG env var → use as explicit path
7
+ * 2. ./.bridgekitty/ (local directory) → preferred for sandboxed agents
8
+ * 3. BRIDGEKITTY_HOME env var (if set)
9
+ * 4. ~/.bridgekitty/ (home directory) → traditional default
10
+ *
11
+ * Creates the directory if it doesn't exist (mode 0o700).
12
+ */
13
+ export declare function getConfigDir(): string;
14
+ export declare function registerWalletTools(server: McpServer): void;