@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.
- package/LICENSE +21 -0
- package/README.md +232 -0
- package/dist/backends/across.d.ts +10 -0
- package/dist/backends/across.js +285 -0
- package/dist/backends/debridge.d.ts +11 -0
- package/dist/backends/debridge.js +380 -0
- package/dist/backends/lifi.d.ts +19 -0
- package/dist/backends/lifi.js +295 -0
- package/dist/backends/persistence.d.ts +86 -0
- package/dist/backends/persistence.js +642 -0
- package/dist/backends/relay.d.ts +11 -0
- package/dist/backends/relay.js +292 -0
- package/dist/backends/squid.d.ts +31 -0
- package/dist/backends/squid.js +476 -0
- package/dist/backends/types.d.ts +125 -0
- package/dist/backends/types.js +11 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +154 -0
- package/dist/routing/engine.d.ts +49 -0
- package/dist/routing/engine.js +336 -0
- package/dist/tools/check-status.d.ts +3 -0
- package/dist/tools/check-status.js +93 -0
- package/dist/tools/execute-bridge.d.ts +3 -0
- package/dist/tools/execute-bridge.js +428 -0
- package/dist/tools/get-chains.d.ts +3 -0
- package/dist/tools/get-chains.js +162 -0
- package/dist/tools/get-quote.d.ts +3 -0
- package/dist/tools/get-quote.js +534 -0
- package/dist/tools/get-tokens.d.ts +3 -0
- package/dist/tools/get-tokens.js +128 -0
- package/dist/tools/help.d.ts +2 -0
- package/dist/tools/help.js +204 -0
- package/dist/tools/multi-quote.d.ts +3 -0
- package/dist/tools/multi-quote.js +310 -0
- package/dist/tools/onboard.d.ts +3 -0
- package/dist/tools/onboard.js +218 -0
- package/dist/tools/wallet.d.ts +14 -0
- package/dist/tools/wallet.js +744 -0
- package/dist/tools/xprt-farm.d.ts +3 -0
- package/dist/tools/xprt-farm.js +1308 -0
- package/dist/tools/xprt-rewards.d.ts +2 -0
- package/dist/tools/xprt-rewards.js +177 -0
- package/dist/tools/xprt-staking.d.ts +2 -0
- package/dist/tools/xprt-staking.js +565 -0
- package/dist/utils/chains.d.ts +22 -0
- package/dist/utils/chains.js +154 -0
- package/dist/utils/circuit-breaker.d.ts +64 -0
- package/dist/utils/circuit-breaker.js +160 -0
- package/dist/utils/evm.d.ts +18 -0
- package/dist/utils/evm.js +46 -0
- package/dist/utils/fill-detector.d.ts +70 -0
- package/dist/utils/fill-detector.js +298 -0
- package/dist/utils/gas-estimator.d.ts +67 -0
- package/dist/utils/gas-estimator.js +340 -0
- package/dist/utils/sanitize-error.d.ts +23 -0
- package/dist/utils/sanitize-error.js +101 -0
- package/dist/utils/token-registry.d.ts +70 -0
- package/dist/utils/token-registry.js +669 -0
- package/dist/utils/tokens.d.ts +17 -0
- package/dist/utils/tokens.js +37 -0
- package/dist/utils/tx-simulator.d.ts +27 -0
- package/dist/utils/tx-simulator.js +105 -0
- package/package.json +75 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { getKey, getConfigDir } from "./wallet.js";
|
|
3
|
+
import { ethers } from "ethers";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
const REWARDS_API = "https://rewards.interop.persistence.one";
|
|
6
|
+
const PERSISTENCE_REST = "https://rest.cosmos.directory/persistence";
|
|
7
|
+
const TIMEOUT_MS = 15_000;
|
|
8
|
+
async function fetchJson(url) {
|
|
9
|
+
const controller = new AbortController();
|
|
10
|
+
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
11
|
+
try {
|
|
12
|
+
const res = await fetch(url, { signal: controller.signal });
|
|
13
|
+
if (!res.ok) {
|
|
14
|
+
const text = await res.text().catch(() => "");
|
|
15
|
+
throw new Error(`HTTP ${res.status}: ${text.slice(0, 200)}`);
|
|
16
|
+
}
|
|
17
|
+
return res.json();
|
|
18
|
+
}
|
|
19
|
+
finally {
|
|
20
|
+
clearTimeout(timer);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function registerXprtRewardsCheck(server) {
|
|
24
|
+
server.tool("xprt_rewards_check", "Check your XPRT reward accrual from Persistence Interop bridging. Shows earned rewards, pending rewards, current epoch, multiplier tier, and qualifying volume.", {
|
|
25
|
+
walletAddress: z
|
|
26
|
+
.string()
|
|
27
|
+
.optional()
|
|
28
|
+
.describe("EVM wallet address (0x...). Defaults to the configured wallet if omitted."),
|
|
29
|
+
}, async (params) => {
|
|
30
|
+
// Resolve wallet address
|
|
31
|
+
let evmAddress = params.walletAddress;
|
|
32
|
+
if (!evmAddress) {
|
|
33
|
+
const privateKey = getKey("privateKey");
|
|
34
|
+
if (!privateKey) {
|
|
35
|
+
const envPath = path.resolve(getConfigDir(), ".env");
|
|
36
|
+
return {
|
|
37
|
+
content: [{
|
|
38
|
+
type: "text",
|
|
39
|
+
text: `No wallet configured and no walletAddress provided. Add keys to ${envPath} (MNEMONIC=... / PRIVATE_KEY=0x...) or run wallet_setup, or pass walletAddress parameter.`,
|
|
40
|
+
}],
|
|
41
|
+
isError: true,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
evmAddress = new ethers.Wallet(privateKey).address;
|
|
45
|
+
}
|
|
46
|
+
// Validate address format
|
|
47
|
+
if (!/^0x[a-fA-F0-9]{40}$/.test(evmAddress)) {
|
|
48
|
+
return {
|
|
49
|
+
content: [{
|
|
50
|
+
type: "text",
|
|
51
|
+
text: `Invalid EVM address format: ${evmAddress}. Expected 0x-prefixed 40-character hex string.`,
|
|
52
|
+
}],
|
|
53
|
+
isError: true,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const result = {
|
|
57
|
+
wallet: evmAddress,
|
|
58
|
+
};
|
|
59
|
+
// Fetch reward data
|
|
60
|
+
try {
|
|
61
|
+
const rewardData = await fetchJson(`${REWARDS_API}/rewards/${evmAddress}`);
|
|
62
|
+
result.totalXprtEarned = rewardData.totalXprtEarned ?? "not_yet_tracked";
|
|
63
|
+
result.pendingXprtRewards = rewardData.pendingXprtRewards ?? "pending_epoch_close";
|
|
64
|
+
result.nextDistributionDate = rewardData.nextDistributionDate ?? "pending_epoch_close";
|
|
65
|
+
result.currentMultiplier = rewardData.currentMultiplier ?? "not_yet_determined";
|
|
66
|
+
result.qualifyingVolumeBtc = rewardData.qualifyingVolumeBtc ?? "no_qualifying_volume";
|
|
67
|
+
result.lifetimeVolumeBtc = rewardData.lifetimeVolumeBtc ?? "no_volume_recorded";
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
result.totalXprtEarned = "not_yet_tracked";
|
|
71
|
+
result.pendingXprtRewards = "pending_epoch_close";
|
|
72
|
+
result.nextDistributionDate = "pending_epoch_close";
|
|
73
|
+
result.currentMultiplier = "not_yet_determined";
|
|
74
|
+
result.qualifyingVolumeBtc = "no_qualifying_volume";
|
|
75
|
+
result.lifetimeVolumeBtc = "no_volume_recorded";
|
|
76
|
+
}
|
|
77
|
+
// Fetch current epoch data
|
|
78
|
+
try {
|
|
79
|
+
const epochData = await fetchJson(`${REWARDS_API}/epochs/current`);
|
|
80
|
+
const currentEpoch = {
|
|
81
|
+
epochNumber: epochData.epochNumber ?? "not_available",
|
|
82
|
+
startDate: epochData.startDate ?? "not_available",
|
|
83
|
+
endDate: epochData.endDate ?? "not_available",
|
|
84
|
+
status: epochData.status ?? "not_available",
|
|
85
|
+
rewardPoolXprt: epochData.rewardPoolXprt
|
|
86
|
+
? `~${Number(epochData.rewardPoolXprt).toFixed(2)} XPRT`
|
|
87
|
+
: "not_available",
|
|
88
|
+
};
|
|
89
|
+
// Calculate time remaining if endDate is available
|
|
90
|
+
if (epochData.endDate && epochData.endDate !== "not_available") {
|
|
91
|
+
try {
|
|
92
|
+
const endTime = new Date(epochData.endDate).getTime();
|
|
93
|
+
const now = Date.now();
|
|
94
|
+
if (endTime > now) {
|
|
95
|
+
const diffMs = endTime - now;
|
|
96
|
+
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
|
|
97
|
+
const diffMinutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));
|
|
98
|
+
currentEpoch.endsIn = `${diffHours}h ${diffMinutes}m`;
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
currentEpoch.endsIn = "epoch_closed";
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
currentEpoch.endsIn = "calculation_failed";
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
currentEpoch.endsIn = "end_time_unknown";
|
|
110
|
+
}
|
|
111
|
+
result.currentEpoch = currentEpoch;
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
result.currentEpoch = {
|
|
115
|
+
epochNumber: "api_unavailable",
|
|
116
|
+
status: "api_unavailable",
|
|
117
|
+
endsIn: "api_unavailable",
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
// Fetch address link status
|
|
121
|
+
try {
|
|
122
|
+
const linkData = await fetchJson(`${REWARDS_API}/address-verification/check/${evmAddress}`);
|
|
123
|
+
result.persistenceAddressLinked = linkData.isRegistered ?? false;
|
|
124
|
+
if (linkData.persistenceAddress) {
|
|
125
|
+
result.persistenceAddress = linkData.persistenceAddress;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
result.persistenceAddressLinked = "unknown";
|
|
130
|
+
}
|
|
131
|
+
// Try to fetch staking data if mnemonic is available to enhance currentMultiplier
|
|
132
|
+
const mnemonic = getKey("mnemonic");
|
|
133
|
+
if (mnemonic && (result.currentMultiplier === "not_yet_determined" || result.currentMultiplier === "unknown")) {
|
|
134
|
+
try {
|
|
135
|
+
const { Secp256k1HdWallet } = await import("@cosmjs/amino");
|
|
136
|
+
const wallet = await Secp256k1HdWallet.fromMnemonic(mnemonic, { prefix: "persistence" });
|
|
137
|
+
const [account] = await wallet.getAccounts();
|
|
138
|
+
const persistenceAddress = account.address;
|
|
139
|
+
// Fetch staking delegations
|
|
140
|
+
const delegationsData = await fetchJson(`${PERSISTENCE_REST}/cosmos/staking/v1beta1/delegations/${persistenceAddress}`);
|
|
141
|
+
let totalStaked = 0;
|
|
142
|
+
if (delegationsData.delegation_responses) {
|
|
143
|
+
for (const del of delegationsData.delegation_responses) {
|
|
144
|
+
totalStaked += parseInt(del.balance?.amount || "0");
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const stakedXprt = totalStaked / 1e6;
|
|
148
|
+
// Determine multiplier tier from staked amount
|
|
149
|
+
if (stakedXprt >= 1000000) {
|
|
150
|
+
result.currentMultiplier = "5x";
|
|
151
|
+
}
|
|
152
|
+
else if (stakedXprt >= 10000) {
|
|
153
|
+
result.currentMultiplier = "2x";
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
result.currentMultiplier = "1x";
|
|
157
|
+
}
|
|
158
|
+
result.stakingInfo = {
|
|
159
|
+
stakedXprt: stakedXprt.toFixed(2),
|
|
160
|
+
multiplierFromStaking: result.currentMultiplier,
|
|
161
|
+
note: "Multiplier determined from current staking position"
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
// If staking data fetch fails, keep the original value
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
result.estimatedRewardNote = "Rewards depend on total participation in each epoch. Estimates may change based on network-wide bridging volume and staking multipliers.";
|
|
169
|
+
result.disclaimer = "Rewards are estimated and not guaranteed. Actual rewards distributed after epoch close.";
|
|
170
|
+
return {
|
|
171
|
+
content: [{
|
|
172
|
+
type: "text",
|
|
173
|
+
text: JSON.stringify(result, null, 2),
|
|
174
|
+
}],
|
|
175
|
+
};
|
|
176
|
+
});
|
|
177
|
+
}
|