From bc5a7f1e6dc7c62605df7204d822443865760bce Mon Sep 17 00:00:00 2001 From: muhammad abubakar <81152955+muhammadabubakar7526@users.noreply.github.com> Date: Mon, 1 Dec 2025 18:03:08 +0500 Subject: [PATCH 1/9] kaspabridge added --- src/adapters/index.ts | 2 + src/adapters/kaspabridge/index.ts | 130 ++++++++++++++++++++++++++++++ src/data/bridgeNetworkData.ts | 16 ++++ 3 files changed, 148 insertions(+) create mode 100644 src/adapters/kaspabridge/index.ts diff --git a/src/adapters/index.ts b/src/adapters/index.ts index 7a27c6d7..fa83be2a 100755 --- a/src/adapters/index.ts +++ b/src/adapters/index.ts @@ -97,6 +97,7 @@ import teleswap from "./teleswap"; import agglayer from "./agglayer"; import fxrp from "./flare/fxrp"; import snowbridge from "./snowbridge"; +import kaspabridge from "./kaspabridge"; export default { polygon, @@ -197,6 +198,7 @@ export default { agglayer, fxrp, snowbridge, + kaspabridge, } as { [bridge: string]: BridgeAdapter | AsyncBridgeAdapter; }; diff --git a/src/adapters/kaspabridge/index.ts b/src/adapters/kaspabridge/index.ts new file mode 100644 index 00000000..4d71c09c --- /dev/null +++ b/src/adapters/kaspabridge/index.ts @@ -0,0 +1,130 @@ +import { BridgeAdapter } from "../../helpers/bridgeAdapter.type"; +import { Contract, providers, utils, BigNumber } from "ethers"; + +// ---------- CONFIG ---------- + +const CHAIN_RPC: Record = { + kasplex: "https://evmrpc.kasplex.org", + bsc: "https://bsc-rpc.publicnode.com", +}; + +const MAILBOX: Record = { + kasplex: "0x01e6A1a37942b51b08B047DdfDf345507A818d4d", + bsc: "0x5f297B3A1e8154c4D58702F7a880b7631bBf5340", +}; + +// Minimal ABI for Dispatch / Process events +const MAILBOX_ABI = [ + "event Dispatch(bytes32 indexed id, uint32 indexed destinationDomain, bytes32 recipientAddress, bytes messageBody)", + "event Process(bytes32 indexed id, bool success)", +]; + +// ---------- TYPES ---------- + +export interface KasplexEvent { + txHash: string; + blockNumber: number; + from: string; + to: string; + token: string; + isDeposit: boolean; + amount: BigNumber; +} + +// ---------- HELPERS ---------- + +function decodeMessageBody(messageBody: string) { + try { + const abiCoder = new utils.AbiCoder(); + const decoded = abiCoder.decode(["address", "address", "address", "uint256"], messageBody); + return { + from: decoded[0] as string, + to: decoded[1] as string, + token: decoded[2] as string, + amount: BigNumber.from(decoded[3] as BigNumber), + }; + } catch (e) { + console.error("Failed to decode messageBody:", e); + return { + from: "0x0000000000000000000000000000000000000000", + to: "0x0000000000000000000000000000000000000000", + token: "0x0000000000000000000000000000000000000000", + amount: BigNumber.from(0), + }; + } +} + +// ---------- MAIN ADAPTER FUNCTION ---------- + +const BLOCK_CHUNK = 2_000; + +export const getKaspaBridgeEvents = + (chain: string) => + async (fromBlock: number, toBlock: number): Promise => { + const rpcUrl = CHAIN_RPC[chain]; + const mailboxAddress = MAILBOX[chain]; + if (!rpcUrl || !mailboxAddress) { + throw new Error(`Missing kaspabridge config for chain ${chain}`); + } + + const provider = new providers.JsonRpcProvider(rpcUrl); + const mailbox = new Contract(mailboxAddress, MAILBOX_ABI, provider); + const events: KasplexEvent[] = []; + + for (let start = fromBlock; start <= toBlock; start += BLOCK_CHUNK) { + const end = Math.min(start + BLOCK_CHUNK - 1, toBlock); + + const [dispatchLogs, processLogs] = await Promise.all([ + provider.getLogs({ ...mailbox.filters.Dispatch(), fromBlock: start, toBlock: end }), + provider.getLogs({ ...mailbox.filters.Process(), fromBlock: start, toBlock: end }), + ]); + + for (const log of dispatchLogs) { + const parsed = mailbox.interface.parseLog(log); + const decoded = decodeMessageBody(parsed.args["messageBody"] as string); + + events.push({ + txHash: log.transactionHash, + blockNumber: log.blockNumber, + from: decoded.from, + to: decoded.to, + token: decoded.token, + isDeposit: true, + amount: decoded.amount, + }); + } + + for (const log of processLogs) { + events.push({ + txHash: log.transactionHash, + blockNumber: log.blockNumber, + from: "0x0000000000000000000000000000000000000000", + to: "0x0000000000000000000000000000000000000000", + token: "0x0000000000000000000000000000000000000000", + isDeposit: false, + amount: BigNumber.from(0), + }); + } + } + + return events; + }; + +// ---------- BRIDGE ADAPTER ---------- + +export async function setUp(): Promise { + return Object.keys(CHAIN_RPC); +} + +export async function build(): Promise { + const adapter: BridgeAdapter = {}; + const chains = await setUp(); + + for (const chain of chains) { + adapter[chain] = getKaspaBridgeEvents(chain); + } + + return adapter; +} + +export default { isAsync: true, build }; diff --git a/src/data/bridgeNetworkData.ts b/src/data/bridgeNetworkData.ts index 755145d8..bfa03020 100644 --- a/src/data/bridgeNetworkData.ts +++ b/src/data/bridgeNetworkData.ts @@ -2597,4 +2597,20 @@ export default [ chains: ["Polkadot", "Ethereum"], destinationChain: "Ethereum", }, + { + id: 102, + displayName: "Kaspa Bridge", + bridgeDbName: "kaspabridge", + slug: "kaspabridge", + iconLink: "icons:hyperlane", + largeTxThreshold: 10000, + url: "https://www.kaspabridge.com/", + chains: [ + "kasplex", + "bsc", + ], + chainMapping: { + avalanche: "avax", + }, + }, ] as BridgeNetwork[]; From 761f8679f119578d5ff77c5689a64a22ecbf5bf7 Mon Sep 17 00:00:00 2001 From: muhammad abubakar <81152955+muhammadabubakar7526@users.noreply.github.com> Date: Fri, 5 Dec 2025 18:29:15 +0500 Subject: [PATCH 2/9] updated logo --- src/data/bridgeNetworkData.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/bridgeNetworkData.ts b/src/data/bridgeNetworkData.ts index bfa03020..d4f5c599 100644 --- a/src/data/bridgeNetworkData.ts +++ b/src/data/bridgeNetworkData.ts @@ -2602,7 +2602,7 @@ export default [ displayName: "Kaspa Bridge", bridgeDbName: "kaspabridge", slug: "kaspabridge", - iconLink: "icons:hyperlane", + iconLink: "https://app.kaspafinance.io/images/chains/0xf87e587ab945f7b111329a6ace6dc497d34f098b.png", largeTxThreshold: 10000, url: "https://www.kaspabridge.com/", chains: [ From 1297c08f0d686e97214a08663ad4bb30c6439dc1 Mon Sep 17 00:00:00 2001 From: muhammad abubakar <81152955+muhammadabubakar7526@users.noreply.github.com> Date: Fri, 5 Dec 2025 18:37:05 +0500 Subject: [PATCH 3/9] fixed starkgate data entery --- src/data/bridgeNetworkData.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/data/bridgeNetworkData.ts b/src/data/bridgeNetworkData.ts index e9da9f5b..5ff89cd7 100644 --- a/src/data/bridgeNetworkData.ts +++ b/src/data/bridgeNetworkData.ts @@ -2700,6 +2700,9 @@ export default [ chainMapping: { avalanche: "avax", }, + }, + { + id: 103, defillamaId: "3787", displayName: "StarkGate", bridgeDbName: "starkgate", From 6e0b35d4605de1a831bc13bf66f68a6905c9e39c Mon Sep 17 00:00:00 2001 From: muhammad abubakar <81152955+muhammadabubakar7526@users.noreply.github.com> Date: Fri, 5 Dec 2025 18:45:16 +0500 Subject: [PATCH 4/9] fixed id order as mentioned in comment --- src/data/bridgeNetworkData.ts | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/data/bridgeNetworkData.ts b/src/data/bridgeNetworkData.ts index 5ff89cd7..65cf5016 100644 --- a/src/data/bridgeNetworkData.ts +++ b/src/data/bridgeNetworkData.ts @@ -2684,9 +2684,20 @@ export default [ chains: ["Polkadot", "Ethereum"], destinationChain: "Ethereum", }, - { id: 102, + defillamaId: "3787", + displayName: "StarkGate", + bridgeDbName: "starkgate", + iconLink: "chain:starknet", + slug: "starkgate", + largeTxThreshold: 10000, + url: "https://starkgate.starknet.io/", + chains: ["Ethereum", "Starknet"], + destinationChain: "Starknet", + }, + { + id: 103, displayName: "Kaspa Bridge", bridgeDbName: "kaspabridge", slug: "kaspabridge", @@ -2701,16 +2712,4 @@ export default [ avalanche: "avax", }, }, - { - id: 103, - defillamaId: "3787", - displayName: "StarkGate", - bridgeDbName: "starkgate", - iconLink: "chain:starknet", - slug: "starkgate", - largeTxThreshold: 10000, - url: "https://starkgate.starknet.io/", - chains: ["Ethereum", "Starknet"], - destinationChain: "Starknet", - }, ] as BridgeNetwork[]; From e85ac9d707836af31e61eff7cae0c038b104380a Mon Sep 17 00:00:00 2001 From: muhammad abubakar <81152955+muhammadabubakar7526@users.noreply.github.com> Date: Fri, 5 Dec 2025 19:04:46 +0500 Subject: [PATCH 5/9] logo link updated from global repo of defilama icons --- src/data/bridgeNetworkData.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/bridgeNetworkData.ts b/src/data/bridgeNetworkData.ts index 65cf5016..dfab5bc3 100644 --- a/src/data/bridgeNetworkData.ts +++ b/src/data/bridgeNetworkData.ts @@ -2701,7 +2701,7 @@ export default [ displayName: "Kaspa Bridge", bridgeDbName: "kaspabridge", slug: "kaspabridge", - iconLink: "https://app.kaspafinance.io/images/chains/0xf87e587ab945f7b111329a6ace6dc497d34f098b.png", + iconLink: "icons:kaspabridge", largeTxThreshold: 10000, url: "https://www.kaspabridge.com/", chains: [ From 64d932259332443b090e83d089a3638b3b60f54a Mon Sep 17 00:00:00 2001 From: muhammad abubakar <81152955+muhammadabubakar7526@users.noreply.github.com> Date: Mon, 8 Dec 2025 12:08:48 +0500 Subject: [PATCH 6/9] update kaspa bridge like aribtrum --- src/adapters/kaspabridge/index.ts | 131 ++++++++++++++++-------------- 1 file changed, 68 insertions(+), 63 deletions(-) diff --git a/src/adapters/kaspabridge/index.ts b/src/adapters/kaspabridge/index.ts index 4d71c09c..28b56fd9 100644 --- a/src/adapters/kaspabridge/index.ts +++ b/src/adapters/kaspabridge/index.ts @@ -2,16 +2,11 @@ import { BridgeAdapter } from "../../helpers/bridgeAdapter.type"; import { Contract, providers, utils, BigNumber } from "ethers"; // ---------- CONFIG ---------- - const CHAIN_RPC: Record = { - kasplex: "https://evmrpc.kasplex.org", bsc: "https://bsc-rpc.publicnode.com", }; -const MAILBOX: Record = { - kasplex: "0x01e6A1a37942b51b08B047DdfDf345507A818d4d", - bsc: "0x5f297B3A1e8154c4D58702F7a880b7631bBf5340", -}; +const MAILBOX_BSC = "0x5f297B3A1e8154c4D58702F7a880b7631bBf5340"; // Minimal ABI for Dispatch / Process events const MAILBOX_ABI = [ @@ -20,7 +15,6 @@ const MAILBOX_ABI = [ ]; // ---------- TYPES ---------- - export interface KasplexEvent { txHash: string; blockNumber: number; @@ -32,11 +26,13 @@ export interface KasplexEvent { } // ---------- HELPERS ---------- - function decodeMessageBody(messageBody: string) { try { const abiCoder = new utils.AbiCoder(); - const decoded = abiCoder.decode(["address", "address", "address", "uint256"], messageBody); + const decoded = abiCoder.decode( + ["address", "address", "address", "uint256"], + messageBody + ); return { from: decoded[0] as string, to: decoded[1] as string, @@ -55,65 +51,70 @@ function decodeMessageBody(messageBody: string) { } // ---------- MAIN ADAPTER FUNCTION ---------- - -const BLOCK_CHUNK = 2_000; - -export const getKaspaBridgeEvents = - (chain: string) => - async (fromBlock: number, toBlock: number): Promise => { - const rpcUrl = CHAIN_RPC[chain]; - const mailboxAddress = MAILBOX[chain]; - if (!rpcUrl || !mailboxAddress) { - throw new Error(`Missing kaspabridge config for chain ${chain}`); +const BLOCK_CHUNK = 2000; + +const getKaspaBridgeEvents = (fromBlock: number, toBlock: number) => + (async (): Promise => { + const rpcUrl = CHAIN_RPC["bsc"]; + if (!rpcUrl || !MAILBOX_BSC) { + throw new Error(`Missing kaspabridge config for chain bsc`); + } + + const provider = new providers.JsonRpcProvider(rpcUrl); + const mailbox = new Contract(MAILBOX_BSC, MAILBOX_ABI, provider); + const events: KasplexEvent[] = []; + + for (let start = fromBlock; start <= toBlock; start += BLOCK_CHUNK) { + const end = Math.min(start + BLOCK_CHUNK - 1, toBlock); + + const [dispatchLogs, processLogs] = await Promise.all([ + provider.getLogs({ + ...mailbox.filters.Dispatch(), + fromBlock: start, + toBlock: end, + }), + provider.getLogs({ + ...mailbox.filters.Process(), + fromBlock: start, + toBlock: end, + }), + ]); + + for (const log of dispatchLogs) { + const parsed = mailbox.interface.parseLog(log); + const decoded = decodeMessageBody(parsed.args["messageBody"] as string); + + events.push({ + txHash: log.transactionHash, + blockNumber: log.blockNumber, + from: decoded.from, + to: decoded.to, + token: decoded.token, + isDeposit: true, + amount: decoded.amount, + }); } - const provider = new providers.JsonRpcProvider(rpcUrl); - const mailbox = new Contract(mailboxAddress, MAILBOX_ABI, provider); - const events: KasplexEvent[] = []; - - for (let start = fromBlock; start <= toBlock; start += BLOCK_CHUNK) { - const end = Math.min(start + BLOCK_CHUNK - 1, toBlock); - - const [dispatchLogs, processLogs] = await Promise.all([ - provider.getLogs({ ...mailbox.filters.Dispatch(), fromBlock: start, toBlock: end }), - provider.getLogs({ ...mailbox.filters.Process(), fromBlock: start, toBlock: end }), - ]); - - for (const log of dispatchLogs) { - const parsed = mailbox.interface.parseLog(log); - const decoded = decodeMessageBody(parsed.args["messageBody"] as string); - - events.push({ - txHash: log.transactionHash, - blockNumber: log.blockNumber, - from: decoded.from, - to: decoded.to, - token: decoded.token, - isDeposit: true, - amount: decoded.amount, - }); - } - - for (const log of processLogs) { - events.push({ - txHash: log.transactionHash, - blockNumber: log.blockNumber, - from: "0x0000000000000000000000000000000000000000", - to: "0x0000000000000000000000000000000000000000", - token: "0x0000000000000000000000000000000000000000", - isDeposit: false, - amount: BigNumber.from(0), - }); - } + for (const log of processLogs) { + events.push({ + txHash: log.transactionHash, + blockNumber: log.blockNumber, + from: "0x0000000000000000000000000000000000000000", + to: "0x0000000000000000000000000000000000000000", + token: "0x0000000000000000000000000000000000000000", + isDeposit: false, + amount: BigNumber.from(0), + }); } + } - return events; - }; + return events; + })(); // ---------- BRIDGE ADAPTER ---------- - export async function setUp(): Promise { - return Object.keys(CHAIN_RPC); + // Only track BSC events + return ["bsc"]; } export async function build(): Promise { @@ -121,10 +122,14 @@ export async function build(): Promise { const chains = await setUp(); for (const chain of chains) { - adapter[chain] = getKaspaBridgeEvents(chain); + adapter[chain] = getKaspaBridgeEvents; } return adapter; } -export default { isAsync: true, build }; +// Default export +export default { + isAsync: true, + build, +}; From 5a27fd5bcbe112ca3e41c9111710bbe8aae1ef07 Mon Sep 17 00:00:00 2001 From: muhammad abubakar <81152955+muhammadabubakar7526@users.noreply.github.com> Date: Mon, 8 Dec 2025 14:23:37 +0500 Subject: [PATCH 7/9] defilama sdk used as mentioned in comment --- src/adapters/kaspabridge/index.ts | 190 +++++++++++++++--------------- 1 file changed, 94 insertions(+), 96 deletions(-) diff --git a/src/adapters/kaspabridge/index.ts b/src/adapters/kaspabridge/index.ts index 28b56fd9..b3dc8908 100644 --- a/src/adapters/kaspabridge/index.ts +++ b/src/adapters/kaspabridge/index.ts @@ -1,46 +1,29 @@ +import * as sdk from "@defillama/sdk"; import { BridgeAdapter } from "../../helpers/bridgeAdapter.type"; -import { Contract, providers, utils, BigNumber } from "ethers"; +import { BigNumber, utils } from "ethers"; // ---------- CONFIG ---------- -const CHAIN_RPC: Record = { - bsc: "https://bsc-rpc.publicnode.com", -}; - +// KaspaBridge contract address on BSC const MAILBOX_BSC = "0x5f297B3A1e8154c4D58702F7a880b7631bBf5340"; -// Minimal ABI for Dispatch / Process events -const MAILBOX_ABI = [ +// ABI for events +const ABI = [ "event Dispatch(bytes32 indexed id, uint32 indexed destinationDomain, bytes32 recipientAddress, bytes messageBody)", "event Process(bytes32 indexed id, bool success)", ]; -// ---------- TYPES ---------- -export interface KasplexEvent { - txHash: string; - blockNumber: number; - from: string; - to: string; - token: string; - isDeposit: boolean; - amount: BigNumber; -} +const iface = new utils.Interface(ABI); -// ---------- HELPERS ---------- -function decodeMessageBody(messageBody: string) { +// Helper function to decode the message body from Dispatch events +function decodeMessageBody(body: string) { try { const abiCoder = new utils.AbiCoder(); - const decoded = abiCoder.decode( + const [from, to, token, amount] = abiCoder.decode( ["address", "address", "address", "uint256"], - messageBody + body ); - return { - from: decoded[0] as string, - to: decoded[1] as string, - token: decoded[2] as string, - amount: BigNumber.from(decoded[3] as BigNumber), - }; - } catch (e) { - console.error("Failed to decode messageBody:", e); + return { from, to, token, amount: BigNumber.from(amount) }; + } catch { return { from: "0x0000000000000000000000000000000000000000", to: "0x0000000000000000000000000000000000000000", @@ -50,85 +33,100 @@ function decodeMessageBody(messageBody: string) { } } -// ---------- MAIN ADAPTER FUNCTION ---------- -const BLOCK_CHUNK = 2000; - -const getKaspaBridgeEvents = (fromBlock: number, toBlock: number) => - (async (): Promise => { - const rpcUrl = CHAIN_RPC["bsc"]; - if (!rpcUrl || !MAILBOX_BSC) { - throw new Error(`Missing kaspabridge config for chain bsc`); - } - - const provider = new providers.JsonRpcProvider(rpcUrl); - const mailbox = new Contract(MAILBOX_BSC, MAILBOX_ABI, provider); - const events: KasplexEvent[] = []; - - for (let start = fromBlock; start <= toBlock; start += BLOCK_CHUNK) { - const end = Math.min(start + BLOCK_CHUNK - 1, toBlock); - - const [dispatchLogs, processLogs] = await Promise.all([ - provider.getLogs({ - ...mailbox.filters.Dispatch(), - fromBlock: start, - toBlock: end, - }), - provider.getLogs({ - ...mailbox.filters.Process(), - fromBlock: start, - toBlock: end, - }), - ]); - - for (const log of dispatchLogs) { - const parsed = mailbox.interface.parseLog(log); - const decoded = decodeMessageBody(parsed.args["messageBody"] as string); - - events.push({ - txHash: log.transactionHash, - blockNumber: log.blockNumber, - from: decoded.from, - to: decoded.to, - token: decoded.token, - isDeposit: true, - amount: decoded.amount, - }); - } +// Utility function to delay between retries (for exponential backoff) +function delay(ms: number) { + return new Promise(resolve => setTimeout(resolve, ms)); +} - for (const log of processLogs) { - events.push({ - txHash: log.transactionHash, - blockNumber: log.blockNumber, - from: "0x0000000000000000000000000000000000000000", - to: "0x0000000000000000000000000000000000000000", - token: "0x0000000000000000000000000000000000000000", - isDeposit: false, - amount: BigNumber.from(0), +// Retry logic to fetch logs with multiple RPC providers +async function getLogsWithRetry(rpcUrls: string[], fromBlock: number, toBlock: number, retries = 3) { + for (let attempt = 0; attempt < retries; attempt++) { + for (const url of rpcUrls) { + try { + const logs = await sdk.api.util.getLogs({ + target: MAILBOX_BSC, + topic: "Dispatch(bytes32,uint32,bytes32,bytes)", + keys: [], + fromBlock, + toBlock, + chain: "bsc", + //@ts-ignore + rpcUrl: url, // Optional: Pass different RPC URL for load balancing }); + return logs; + } catch (error) { + console.error(`Error with RPC URL ${url}: ${error}`); } } - return events; - })(); - -// ---------- BRIDGE ADAPTER ---------- -export async function setUp(): Promise { - // Only track BSC events - return ["bsc"]; + const delayTime = Math.pow(2, attempt) * 1000; // Exponential backoff (1s, 2s, 4s, ...) + console.log(`Retrying in ${delayTime / 1000}s...`); + await delay(delayTime); + } + throw new Error("All retries failed with all RPC providers"); } -export async function build(): Promise { - const adapter: BridgeAdapter = {}; - const chains = await setUp(); +// Main function to get KaspaBridge events (both Dispatch and Process) +async function getKaspaBridgeEvents(fromBlock: number, toBlock: number) { + const events = []; + + // List of RPC URLs to try in case one hits rate limits or fails + const rpcUrls = [ + "https://bsc-dataseed.binance.org", + "https://bsc.meowrpc.com", + "https://endpoints.omniatech.io/v1/bsc/mainnet/public", + "https://rpc.poolz.finance/bsc", + "https://bsc-dataseed.bnbchain.org", + "https://bsc-dataseed2.ninicoin.io", + "https://bsc-dataseed4.ninicoin.io" + ]; + + // Fetch Dispatch (deposit) logs with retry + const dispatch = await getLogsWithRetry(rpcUrls, fromBlock, toBlock); + for (const log of dispatch.output) { + const parsed = iface.parseLog({ + data: log.data, + //@ts-ignore + topics: log.topics, + }); + const decoded = decodeMessageBody(parsed.args.messageBody); + + events.push({ + txHash: log.transactionHash, + blockNumber: log.blockNumber, + from: decoded.from, + to: decoded.to, + token: decoded.token, + isDeposit: true, + amount: decoded.amount, + }); + } - for (const chain of chains) { - adapter[chain] = getKaspaBridgeEvents; + // Fetch Process (withdrawal) logs with retry + const process = await getLogsWithRetry(rpcUrls, fromBlock, toBlock); + for (const log of process.output) { + events.push({ + txHash: log.transactionHash, + blockNumber: log.blockNumber, + from: "0x0000000000000000000000000000000000000000", + to: "0x0000000000000000000000000000000000000000", + token: "0x0000000000000000000000000000000000000000", + isDeposit: false, + amount: BigNumber.from(0), + }); } - return adapter; + return events; +} + +// Bridge adapter construction function +export async function build(): Promise { + return { + bsc: getKaspaBridgeEvents, + }; } -// Default export +// Default export with async flag export default { isAsync: true, build, From 3a9fc413fda26ca7ee59d66684b7343da6782deb Mon Sep 17 00:00:00 2001 From: muhammad abubakar <81152955+muhammadabubakar7526@users.noreply.github.com> Date: Mon, 8 Dec 2025 14:46:58 +0500 Subject: [PATCH 8/9] updated zeor addresses conditionally --- src/adapters/kaspabridge/index.ts | 109 +++++++++++++----------------- src/data/bridgeNetworkData.ts | 29 ++++---- 2 files changed, 64 insertions(+), 74 deletions(-) diff --git a/src/adapters/kaspabridge/index.ts b/src/adapters/kaspabridge/index.ts index b3dc8908..3843fab5 100644 --- a/src/adapters/kaspabridge/index.ts +++ b/src/adapters/kaspabridge/index.ts @@ -2,19 +2,17 @@ import * as sdk from "@defillama/sdk"; import { BridgeAdapter } from "../../helpers/bridgeAdapter.type"; import { BigNumber, utils } from "ethers"; -// ---------- CONFIG ---------- -// KaspaBridge contract address on BSC const MAILBOX_BSC = "0x5f297B3A1e8154c4D58702F7a880b7631bBf5340"; -// ABI for events +// ABI for Dispatch and Process events const ABI = [ "event Dispatch(bytes32 indexed id, uint32 indexed destinationDomain, bytes32 recipientAddress, bytes messageBody)", - "event Process(bytes32 indexed id, bool success)", + "event Process(bytes32 indexed id, bool success)" ]; const iface = new utils.Interface(ABI); -// Helper function to decode the message body from Dispatch events +// Helper function to decode the message body of the Dispatch event function decodeMessageBody(body: string) { try { const abiCoder = new utils.AbiCoder(); @@ -33,56 +31,24 @@ function decodeMessageBody(body: string) { } } -// Utility function to delay between retries (for exponential backoff) -function delay(ms: number) { - return new Promise(resolve => setTimeout(resolve, ms)); -} - -// Retry logic to fetch logs with multiple RPC providers -async function getLogsWithRetry(rpcUrls: string[], fromBlock: number, toBlock: number, retries = 3) { - for (let attempt = 0; attempt < retries; attempt++) { - for (const url of rpcUrls) { - try { - const logs = await sdk.api.util.getLogs({ - target: MAILBOX_BSC, - topic: "Dispatch(bytes32,uint32,bytes32,bytes)", - keys: [], - fromBlock, - toBlock, - chain: "bsc", - //@ts-ignore - rpcUrl: url, // Optional: Pass different RPC URL for load balancing - }); - return logs; - } catch (error) { - console.error(`Error with RPC URL ${url}: ${error}`); - } - } - - const delayTime = Math.pow(2, attempt) * 1000; // Exponential backoff (1s, 2s, 4s, ...) - console.log(`Retrying in ${delayTime / 1000}s...`); - await delay(delayTime); - } - throw new Error("All retries failed with all RPC providers"); -} - -// Main function to get KaspaBridge events (both Dispatch and Process) +// Function to fetch Kaspa Bridge events async function getKaspaBridgeEvents(fromBlock: number, toBlock: number) { const events = []; - // List of RPC URLs to try in case one hits rate limits or fails - const rpcUrls = [ - "https://bsc-dataseed.binance.org", - "https://bsc.meowrpc.com", - "https://endpoints.omniatech.io/v1/bsc/mainnet/public", - "https://rpc.poolz.finance/bsc", - "https://bsc-dataseed.bnbchain.org", - "https://bsc-dataseed2.ninicoin.io", - "https://bsc-dataseed4.ninicoin.io" - ]; - - // Fetch Dispatch (deposit) logs with retry - const dispatch = await getLogsWithRetry(rpcUrls, fromBlock, toBlock); + // Map to store Dispatch event data for correlation with Process events + const dispatchMap = new Map(); + + // Fetch Dispatch logs + const dispatch = await sdk.api.util.getLogs({ + target: MAILBOX_BSC, + topic: "Dispatch(bytes32,uint32,bytes32,bytes)", + keys: [], + fromBlock, + toBlock, + chain: "bsc", + }); + + // Process Dispatch events for (const log of dispatch.output) { const parsed = iface.parseLog({ data: log.data, @@ -91,6 +57,15 @@ async function getKaspaBridgeEvents(fromBlock: number, toBlock: number) { }); const decoded = decodeMessageBody(parsed.args.messageBody); + // Store Dispatch event data in the map for use with Process events + dispatchMap.set(log.transactionHash, { + from: decoded.from, + to: decoded.to, + token: decoded.token, + amount: decoded.amount, + }); + + // Add the Dispatch event to the events list events.push({ txHash: log.transactionHash, blockNumber: log.blockNumber, @@ -102,31 +77,43 @@ async function getKaspaBridgeEvents(fromBlock: number, toBlock: number) { }); } - // Fetch Process (withdrawal) logs with retry - const process = await getLogsWithRetry(rpcUrls, fromBlock, toBlock); + // Fetch Process logs + const process = await sdk.api.util.getLogs({ + target: MAILBOX_BSC, + topic: "Process(bytes32,bool)", + keys: [], + fromBlock, + toBlock, + chain: "bsc", + }); + + // Process Process events and correlate with Dispatch events for (const log of process.output) { + const dispatchData = dispatchMap.get(log.transactionHash); + + // Here, we ensure we don't use hardcoded zero addresses and instead use null if no matching Dispatch event was found events.push({ txHash: log.transactionHash, blockNumber: log.blockNumber, - from: "0x0000000000000000000000000000000000000000", - to: "0x0000000000000000000000000000000000000000", - token: "0x0000000000000000000000000000000000000000", + from: dispatchData ? dispatchData.from : null, // Use Dispatch data for 'from' if available + to: dispatchData ? dispatchData.to : null, // Use Dispatch data for 'to' if available + token: dispatchData ? dispatchData.token : null, // Use Dispatch data for 'token' if available isDeposit: false, - amount: BigNumber.from(0), + amount: dispatchData ? dispatchData.amount : BigNumber.from(0), }); } return events; } -// Bridge adapter construction function +// Build function to return the BridgeAdapter object export async function build(): Promise { return { - bsc: getKaspaBridgeEvents, + bsc: getKaspaBridgeEvents, // Use the getKaspaBridgeEvents function for the BSC chain }; } -// Default export with async flag +// Default export export default { isAsync: true, build, diff --git a/src/data/bridgeNetworkData.ts b/src/data/bridgeNetworkData.ts index ff947d54..4132cda5 100644 --- a/src/data/bridgeNetworkData.ts +++ b/src/data/bridgeNetworkData.ts @@ -2699,19 +2699,6 @@ export default [ }, { id: 103, - displayName: "Kaspa Bridge", - bridgeDbName: "kaspabridge", - slug: "kaspabridge", - iconLink: "icons:kaspabridge", - largeTxThreshold: 10000, - url: "https://www.kaspabridge.com/", - chains: [ - "kasplex", - "bsc", - ], - chainMapping: { - avalanche: "avax", - }, displayName: "Hyperbridge", bridgeDbName: "hyperbridge", iconLink: "icons:hyperbridge", @@ -2730,4 +2717,20 @@ export default [ chains: ["Ethereum", "Arbitrum", "Base", "ICP"], destinationChain: "ICP" }, + { + id: 105, + displayName: "Kaspa Bridge", + bridgeDbName: "kaspabridge", + slug: "kaspabridge", + iconLink: "icons:kaspabridge", + largeTxThreshold: 10000, + url: "https://www.kaspabridge.com/", + chains: [ + "kasplex", + "bsc", + ], + chainMapping: { + avalanche: "avax", + } + }, ] as BridgeNetwork[]; From a2b6beb6d50e0b0f82a3e75db569f68c68d7676c Mon Sep 17 00:00:00 2001 From: muhammad abubakar <81152955+muhammadabubakar7526@users.noreply.github.com> Date: Wed, 10 Dec 2025 12:47:01 +0500 Subject: [PATCH 9/9] fildted events without from and to addresses --- src/adapters/kaspabridge/index.ts | 57 ++++++++++++------------------- 1 file changed, 22 insertions(+), 35 deletions(-) diff --git a/src/adapters/kaspabridge/index.ts b/src/adapters/kaspabridge/index.ts index 3843fab5..2b701667 100644 --- a/src/adapters/kaspabridge/index.ts +++ b/src/adapters/kaspabridge/index.ts @@ -4,7 +4,6 @@ import { BigNumber, utils } from "ethers"; const MAILBOX_BSC = "0x5f297B3A1e8154c4D58702F7a880b7631bBf5340"; -// ABI for Dispatch and Process events const ABI = [ "event Dispatch(bytes32 indexed id, uint32 indexed destinationDomain, bytes32 recipientAddress, bytes messageBody)", "event Process(bytes32 indexed id, bool success)" @@ -12,33 +11,28 @@ const ABI = [ const iface = new utils.Interface(ABI); -// Helper function to decode the message body of the Dispatch event function decodeMessageBody(body: string) { try { - const abiCoder = new utils.AbiCoder(); - const [from, to, token, amount] = abiCoder.decode( + const coder = new utils.AbiCoder(); + const [from, to, token, amount] = coder.decode( ["address", "address", "address", "uint256"], body ); return { from, to, token, amount: BigNumber.from(amount) }; } catch { - return { - from: "0x0000000000000000000000000000000000000000", - to: "0x0000000000000000000000000000000000000000", - token: "0x0000000000000000000000000000000000000000", - amount: BigNumber.from(0), - }; + return null; // Important: return null to indicate failure } } -// Function to fetch Kaspa Bridge events async function getKaspaBridgeEvents(fromBlock: number, toBlock: number) { - const events = []; + const events: any[] = []; - // Map to store Dispatch event data for correlation with Process events - const dispatchMap = new Map(); + const dispatchMap = new Map< + string, + { from: string; to: string; token: string; amount: BigNumber } + >(); - // Fetch Dispatch logs + // ---------------------- FETCH DISPATCH EVENTS ---------------------- const dispatch = await sdk.api.util.getLogs({ target: MAILBOX_BSC, topic: "Dispatch(bytes32,uint32,bytes32,bytes)", @@ -48,24 +42,18 @@ async function getKaspaBridgeEvents(fromBlock: number, toBlock: number) { chain: "bsc", }); - // Process Dispatch events for (const log of dispatch.output) { const parsed = iface.parseLog({ data: log.data, - //@ts-ignore + // @ts-ignore topics: log.topics, }); + const decoded = decodeMessageBody(parsed.args.messageBody); + if (!decoded) continue; // Skip invalid message bodies - // Store Dispatch event data in the map for use with Process events - dispatchMap.set(log.transactionHash, { - from: decoded.from, - to: decoded.to, - token: decoded.token, - amount: decoded.amount, - }); + dispatchMap.set(log.transactionHash, decoded); - // Add the Dispatch event to the events list events.push({ txHash: log.transactionHash, blockNumber: log.blockNumber, @@ -77,7 +65,7 @@ async function getKaspaBridgeEvents(fromBlock: number, toBlock: number) { }); } - // Fetch Process logs + // ---------------------- FETCH PROCESS EVENTS ---------------------- const process = await sdk.api.util.getLogs({ target: MAILBOX_BSC, topic: "Process(bytes32,bool)", @@ -87,33 +75,32 @@ async function getKaspaBridgeEvents(fromBlock: number, toBlock: number) { chain: "bsc", }); - // Process Process events and correlate with Dispatch events for (const log of process.output) { const dispatchData = dispatchMap.get(log.transactionHash); - // Here, we ensure we don't use hardcoded zero addresses and instead use null if no matching Dispatch event was found + // >>> Maintainer request: SKIP events without dispatch data + if (!dispatchData) continue; + events.push({ txHash: log.transactionHash, blockNumber: log.blockNumber, - from: dispatchData ? dispatchData.from : null, // Use Dispatch data for 'from' if available - to: dispatchData ? dispatchData.to : null, // Use Dispatch data for 'to' if available - token: dispatchData ? dispatchData.token : null, // Use Dispatch data for 'token' if available + from: dispatchData.from, + to: dispatchData.to, + token: dispatchData.token, isDeposit: false, - amount: dispatchData ? dispatchData.amount : BigNumber.from(0), + amount: dispatchData.amount, }); } return events; } -// Build function to return the BridgeAdapter object export async function build(): Promise { return { - bsc: getKaspaBridgeEvents, // Use the getKaspaBridgeEvents function for the BSC chain + bsc: getKaspaBridgeEvents, }; } -// Default export export default { isAsync: true, build,