Skip to content

Commit dd87737

Browse files
authored
Add firefly labs (#460)
1 parent 294314f commit dd87737

File tree

4 files changed

+308
-0
lines changed

4 files changed

+308
-0
lines changed

src/adapters/firefly/index.ts

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { BridgeAdapter } from "../../helpers/bridgeAdapter.type";
2+
import { getTxDataFromEVMEventLogs } from "../../helpers/processTransactions";
3+
import { bridgesAddress, processMulticallEvents, SupportedChains } from "./processTransaction";
4+
5+
const depositParams = (chain: SupportedChains) => {
6+
const bridgeAddress = bridgesAddress[chain];
7+
const baseConfig = {
8+
target: bridgeAddress,
9+
topic: "Deposit()",
10+
abi: ["event Deposit()"],
11+
logKeys: {
12+
blockNumber: "blockNumber",
13+
txHash: "transactionHash",
14+
},
15+
txKeys: {
16+
from: "from",
17+
},
18+
isDeposit: true,
19+
}
20+
return [
21+
{
22+
...baseConfig,
23+
functionSignatureFilter: {
24+
includeSignatures: ["0x8340f5"],
25+
},
26+
inputDataExtraction: {
27+
inputDataABI: ["function deposit(address token, address target, uint256 amount)"],
28+
inputDataFnName: "deposit",
29+
inputDataKeys: {
30+
token: "token",
31+
to: "target",
32+
amount: "amount",
33+
},
34+
},
35+
},
36+
{
37+
...baseConfig,
38+
functionSignatureFilter: {
39+
includeSignatures: ["0x376c14"],
40+
},
41+
},
42+
]
43+
};
44+
45+
const withdrawParams = (chain: SupportedChains) => {
46+
const bridgeAddress = bridgesAddress[chain];
47+
const baseConfig = {
48+
target: bridgeAddress,
49+
topic: "Withdraw()",
50+
abi: ["event Withdraw()"],
51+
isDeposit: false,
52+
txKeys: {
53+
from: "from",
54+
},
55+
}
56+
return [
57+
{
58+
...baseConfig,
59+
functionSignatureFilter: {
60+
includeSignatures: ["0xd9caed"],
61+
},
62+
inputDataExtraction: {
63+
inputDataABI: ["function withdraw(address token, address target, uint256 amount)"],
64+
inputDataFnName: "withdraw",
65+
inputDataKeys: {
66+
token: "token",
67+
to: "target",
68+
amount: "amount",
69+
},
70+
},
71+
},
72+
{
73+
target: bridgeAddress,
74+
topic: "Deposit()",
75+
abi: ["event Deposit()"],
76+
isDeposit: false,
77+
functionSignatureFilter: {
78+
includeSignatures: ["0xe4e1e1"],
79+
},
80+
txKeys: {
81+
from: "from",
82+
},
83+
},
84+
]
85+
}
86+
87+
const constructParams = (chain: SupportedChains) => {
88+
return async (fromBlock: number, toBlock: number) => {
89+
const eventParams = [
90+
...depositParams(chain),
91+
...withdrawParams(chain)
92+
];
93+
const logs = await getTxDataFromEVMEventLogs("firefly", chain, fromBlock, toBlock, eventParams);
94+
return processMulticallEvents(logs, chain);
95+
};
96+
};
97+
98+
const adapter: BridgeAdapter = {}
99+
100+
for (const chain of Object.keys(bridgesAddress) as SupportedChains[]) {
101+
adapter[chain] = constructParams(chain);
102+
}
103+
104+
export default adapter;
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import { ethers } from "ethers";
2+
import { PromisePool } from "@supercharge/promise-pool";
3+
import { getProvider } from "../../utils/provider";
4+
import { EventData } from "../../utils/types";
5+
6+
export const bridgesAddress = {
7+
ethereum: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
8+
optimism: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
9+
cronos: "0x3c167F5B05FA97d11601F6e187B0d100A95595Ba",
10+
xdc: "0x0061f8549e337be2a47d938f6fb3289be89bae97",
11+
bsc: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
12+
gnosis: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
13+
unichain: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
14+
polygon: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
15+
monad: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
16+
sonic: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
17+
manta: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
18+
hashkey: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
19+
"x layer": "0x5eec3660830fc0aa8053d6048d11276fcf29402f",
20+
opbnb: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
21+
"zksync era": "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
22+
"world chain": "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
23+
stable: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
24+
hyperevm: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
25+
conflux: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
26+
sei: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
27+
gravity: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
28+
soneium: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
29+
ronin: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
30+
abstract: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
31+
mantle: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
32+
klaytn: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
33+
base: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
34+
plasma: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
35+
arbitrum: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
36+
"arbitrum nova": "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
37+
celo: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
38+
avalanche: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
39+
ink: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
40+
linea: "0x3c167F5B05FA97d11601F6e187B0d100A95595Ba",
41+
berachain: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
42+
codex: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
43+
blast: "0xcbCD511CcF92Da77245C4aE936e574B630FF0001",
44+
} as const;
45+
46+
export type SupportedChains = keyof typeof bridgesAddress;
47+
48+
const CALL3VALUE_STRUCT = "tuple(address target, bool allowFailure, uint256 value, bytes callData)[]";
49+
50+
const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
51+
52+
const depositAndMulticallInterface = new ethers.utils.Interface([
53+
`function depositAndMulticall(
54+
address[] tokens,
55+
uint256[] amounts,
56+
${CALL3VALUE_STRUCT} calls,
57+
address refundTo,
58+
address nftRecipient)`
59+
]);
60+
const depositAndMulticallSighash = depositAndMulticallInterface.getSighash("depositAndMulticall");
61+
const withdrawAndMulticallInterface = new ethers.utils.Interface([
62+
`function withdrawAndMulticall(
63+
address[] tokens,
64+
uint256[] amounts,
65+
${CALL3VALUE_STRUCT} calls,
66+
address refundTo,
67+
address nftRecipient)`
68+
]);
69+
const withdrawAndMulticallSighash = withdrawAndMulticallInterface.getSighash("withdrawAndMulticall");
70+
71+
const decodeDepositAndMulticall = (tx: any, event: EventData) => {
72+
try {
73+
const decoded = depositAndMulticallInterface.decodeFunctionData("depositAndMulticall", tx.data);
74+
const tokens = decoded.tokens as string[];
75+
const amounts = decoded.amounts as ethers.BigNumber[];
76+
if (Array.isArray(tokens) && tokens.length) {
77+
event.token = tokens[0];
78+
}
79+
if (Array.isArray(amounts) && amounts.length) {
80+
event.amount = amounts[0]
81+
}
82+
if (amounts.length === 0 && tokens.length === 0) {
83+
event.amount = ethers.BigNumber.from(tx.value);
84+
event.token = ZERO_ADDRESS;
85+
}
86+
event.to = decoded.refundTo;
87+
} catch (error) {
88+
console.error(
89+
`firefly adapter: Unable to decode depositAndMulticall input for ${event.txHash}`,
90+
error
91+
);
92+
}
93+
return event;
94+
}
95+
96+
const decodeWithdrawAndMulticall = (tx: any, event: EventData) => {
97+
try {
98+
const decoded = withdrawAndMulticallInterface.decodeFunctionData("withdrawAndMulticall", tx.data);
99+
const tokens = decoded.tokens as string[];
100+
const amounts = decoded.amounts as ethers.BigNumber[];
101+
if (Array.isArray(tokens) && tokens.length) {
102+
event.token = tokens[0];
103+
}
104+
if (Array.isArray(amounts) && amounts.length) {
105+
event.amount = amounts[0]
106+
}
107+
if (amounts.length === 0 && tokens.length === 0) {
108+
event.amount = ethers.BigNumber.from(tx.value);
109+
event.token = ZERO_ADDRESS;
110+
}
111+
event.to = decoded.refundTo;
112+
} catch (error) {
113+
console.error(
114+
`firefly adapter: Unable to decode withdrawAndMulticall input for ${event.txHash}`,
115+
error
116+
);
117+
}
118+
return event;
119+
}
120+
121+
const decodeInputDataParams = [
122+
{
123+
sighash: depositAndMulticallSighash,
124+
decodeFn: decodeDepositAndMulticall
125+
},
126+
{
127+
sighash: withdrawAndMulticallSighash,
128+
decodeFn: decodeWithdrawAndMulticall
129+
}
130+
]
131+
132+
export const processMulticallEvents = async (
133+
eventData: EventData[],
134+
chain: SupportedChains
135+
) => {
136+
const provider = getProvider(chain) as any;
137+
const eventsToDecode = eventData.filter((event) => !event.token);
138+
139+
await PromisePool.withConcurrency(5)
140+
.for(eventsToDecode)
141+
.process(async (event) => {
142+
const tx = await provider.getTransaction(event.txHash);
143+
if (!tx)
144+
return event;
145+
146+
const decodeInputDataParam = decodeInputDataParams.find((decodeInputDataParam) => tx.data?.startsWith(decodeInputDataParam.sighash));
147+
if (!decodeInputDataParam)
148+
return event;
149+
150+
return decodeInputDataParam.decodeFn(tx, event);
151+
});
152+
console.log(eventData);
153+
return eventData.filter((event) => event.token && event.amount);
154+
};

src/adapters/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ import kaspabridge from "./kaspabridge";
101101
import hyperbridge from "./hyperbridge";
102102
import starkgate from "./starkgate";
103103
import onesec from "./1sec";
104+
import firefly from "./firefly";
104105

105106
export default {
106107
polygon,
@@ -205,6 +206,7 @@ export default {
205206
hyperbridge,
206207
starkgate,
207208
"1sec": onesec,
209+
firefly
208210
} as {
209211
[bridge: string]: BridgeAdapter | AsyncBridgeAdapter;
210212
};

src/data/bridgeNetworkData.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2783,4 +2783,52 @@ export default [
27832783
],
27842784
destinationChain: "kasplex",
27852785
},
2786+
{
2787+
id: 106,
2788+
displayName: "Firefly",
2789+
bridgeDbName: "firefly",
2790+
slug: "firefly",
2791+
iconLink: "icons:firefly",
2792+
largeTxThreshold: 10000,
2793+
url: "https://fireflylabs.app/?utm_source=defillama",
2794+
chains: [
2795+
"ethereum",
2796+
"base",
2797+
"arbitrum",
2798+
"optimism",
2799+
"binance",
2800+
"avalanche",
2801+
"hyperevm",
2802+
"polygon",
2803+
"cronos",
2804+
"xdc",
2805+
"xdai",
2806+
"unichain",
2807+
"monad",
2808+
"sonic",
2809+
"manta",
2810+
"hashkey",
2811+
"x layer",
2812+
"op_bnb",
2813+
"zksync era",
2814+
"World Chain",
2815+
"stable",
2816+
"conflux",
2817+
"sei",
2818+
"gravity",
2819+
"soneium",
2820+
"ronin",
2821+
"abstract",
2822+
"mantle",
2823+
"klaytn",
2824+
"plasma",
2825+
"arbitrum nova",
2826+
"celo",
2827+
"ink",
2828+
"linea",
2829+
"berachain",
2830+
"codex",
2831+
"blast",
2832+
],
2833+
}
27862834
] as BridgeNetwork[];

0 commit comments

Comments
 (0)