-
Notifications
You must be signed in to change notification settings - Fork 348
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #44 from DefiLlama/feat/add-satellite-bridge
Feat/add satellite bridge
- Loading branch information
Showing
6 changed files
with
382 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { ethers } from "ethers"; | ||
|
||
export const gatewayAddresses = { | ||
ethereum: "0x4F4495243837681061C4743b74B3eEdf548D56A5", | ||
bsc: "0x304acf330bbE08d1e512eefaa92F6a57871fD895", | ||
polygon: "0x6f015F16De9fC8791b234eF68D486d2bF203FBA8", | ||
avax: "0x5029C0EFf6C34351a0CEc334542cDb22c7928f78", | ||
fantom: "0x304acf330bbE08d1e512eefaa92F6a57871fD895", | ||
arbitrum: "0xe432150cce91c13a887f7D836923d5597adD8E31", | ||
optimism: "0xe432150cce91c13a887f7D836923d5597adD8E31", | ||
base: "0xe432150cce91c13a887f7D836923d5597adD8E31", | ||
linea: "0xe432150cce91c13a887f7D836923d5597adD8E31", | ||
moonbeam: "0x4F4495243837681061C4743b74B3eEdf548D56A5", | ||
celo: "0xe432150cce91c13a887f7D836923d5597adD8E31", | ||
kava: "0xe432150cce91c13a887f7D836923d5597adD8E31", | ||
filecoin: "0xe432150cce91c13a887f7D836923d5597adD8E31", | ||
} as { | ||
[chain: string]: string; | ||
}; | ||
|
||
// The same address for all chains | ||
export const depositServiceAddress = "0xc1DCb196BA862B337Aa23eDA1Cb9503C0801b955"; | ||
|
||
export const withdrawParams = (from: string, to: string) => ({ | ||
target: "", | ||
topic: "Transfer(address,address,uint256)", | ||
topics: [ | ||
ethers.utils.id("Transfer(address,address,uint256)"), | ||
ethers.utils.hexZeroPad(from, 32), | ||
ethers.utils.hexZeroPad(to, 32), | ||
], | ||
abi: ["event Transfer(address indexed from, address indexed to, uint256 value)"], | ||
logKeys: { | ||
blockNumber: "blockNumber", | ||
txHash: "transactionHash", | ||
}, | ||
argKeys: { | ||
to: "to", | ||
amount: "value", | ||
}, | ||
fixedEventData: { | ||
token: "", | ||
from: "", | ||
}, | ||
functionSignatureFilter: { | ||
includeSignatures: ["0x09c5ea"], | ||
}, | ||
isDeposit: false, | ||
}); | ||
|
||
export const wrapParams = (from: string, to: string) => ({ | ||
...withdrawParams(from, to), | ||
functionSignatureFilter: { | ||
includeSignatures: ["0xcf85fb"], | ||
}, | ||
isDeposit: true, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,226 @@ | ||
import { Chain, ETHER_ADDRESS } from "@defillama/sdk/build/general"; | ||
import { getTimestamp, normalizeAddress } from "@defillama/sdk/build/util"; | ||
import { BridgeAdapter, PartialContractEventParams } from "../../helpers/bridgeAdapter.type"; | ||
import { constructTransferParams } from "../../helpers/eventParams"; | ||
import { getTxDataFromEVMEventLogs } from "../../helpers/processTransactions"; | ||
import { | ||
FetchDepositTransfersOptions, | ||
FetchDepositTransfersResponse, | ||
DepositAddressTransfer, | ||
WrapTransfer, | ||
} from "./type"; | ||
import { gatewayAddresses, withdrawParams, wrapParams } from "./constant"; | ||
import fetch from "node-fetch"; | ||
const retry = require("async-retry"); | ||
|
||
function mapChainToAxelarscanChain(chain: Chain) { | ||
switch (chain) { | ||
case "bsc": | ||
return "binance"; | ||
case "avax": | ||
return "avalanche"; | ||
default: | ||
return chain; | ||
} | ||
} | ||
|
||
function fetchAssets() { | ||
// fetch from axelarscan and pass {"method": "getAssets"} as json body | ||
return retry(() => | ||
fetch("https://api.axelarscan.io/", { | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/json", | ||
}, | ||
body: JSON.stringify({ | ||
method: "getAssets", | ||
}), | ||
}).then((res) => res.json()) | ||
); | ||
} | ||
|
||
async function fetchDepositAddressTransfers<T extends DepositAddressTransfer>( | ||
fromBlock: number, | ||
toBlock: number, | ||
chain: string, | ||
options: FetchDepositTransfersOptions | ||
): Promise<T[]> { | ||
const { isDeposit = true, type = "deposit_address", size = 2000 } = options; | ||
const typeToResponseObj = { | ||
deposit_address: "link", | ||
wrap: "wrap", | ||
}; | ||
|
||
// Fetch timestamp from block numbers | ||
const [fromTime, toTime] = await retry(() => | ||
Promise.all([getTimestamp(fromBlock, chain), getTimestamp(toBlock, chain)]) | ||
); | ||
|
||
// Get deposit addresses from the axelarscan API | ||
return retry(() => | ||
fetch("https://api.axelarscan.io/", { | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/json", | ||
}, | ||
body: JSON.stringify({ | ||
method: "searchTransfers", | ||
type, | ||
fromTime, | ||
toTime, | ||
size, | ||
sourceChain: isDeposit ? mapChainToAxelarscanChain(chain) : undefined, | ||
destinationChain: isDeposit ? undefined : mapChainToAxelarscanChain(chain), | ||
}), | ||
}) | ||
.then((res) => res.json()) | ||
.then((res: FetchDepositTransfersResponse) => | ||
res.data.map((d: any) => ({ | ||
...d[typeToResponseObj[type]], | ||
denom: d.send.denom.startsWith("ibc") ? d.link.denom : d.send.denom, | ||
})) | ||
) | ||
); | ||
} | ||
|
||
function constructDepositAddressTransfers(linkedDepositAddresses: DepositAddressTransfer[]) { | ||
const eventParams = [] as PartialContractEventParams[]; | ||
|
||
for (const linkedDepositAddress of linkedDepositAddresses) { | ||
eventParams.push(constructTransferParams(linkedDepositAddress.deposit_address, true)); | ||
} | ||
|
||
return eventParams; | ||
} | ||
|
||
function constructWrapTransfers(wrapTransfers: WrapTransfer[], gateway: string, chain: string, assets: any[]) { | ||
const eventParams = [] as PartialContractEventParams[]; | ||
if (wrapTransfers.length === 0) return eventParams; | ||
|
||
const asset = assets.find((asset) => asset.denom === wrapTransfers[0].denom); | ||
|
||
if (!asset || !asset?.addresses?.[chain]?.address) { | ||
// console.log(`[${chain}] Asset not found`, wrapTransfers[0].denom); | ||
return eventParams; | ||
} | ||
|
||
const token = normalizeAddress(asset?.addresses?.[chain]?.address); | ||
|
||
for (const wrapTransfer of wrapTransfers) { | ||
eventParams.push({ | ||
...wrapParams(wrapTransfer.deposit_address, gateway), | ||
target: token, | ||
fixedEventData: { | ||
token: token, | ||
from: token, | ||
}, | ||
}); | ||
} | ||
|
||
return eventParams; | ||
} | ||
|
||
function constructWithdrawAddressTransfers( | ||
linkedDepositAddresses: DepositAddressTransfer[], | ||
gateway: string, | ||
chain: string, | ||
assets: any[] | ||
) { | ||
const _chain = mapChainToAxelarscanChain(chain); | ||
const eventParams = [] as PartialContractEventParams[]; | ||
const withdrawTransfers = linkedDepositAddresses.map((d) => ({ | ||
recipient: normalizeAddress(d.recipient_address), | ||
denom: d.denom, | ||
})); | ||
|
||
for (const withdraw of withdrawTransfers) { | ||
const asset = assets.find((asset) => asset.denom === withdraw.denom); | ||
if (!asset) { | ||
// console.log(`[${_chain}] Asset not found`, withdraw.denom, withdraw.recipient); | ||
continue; | ||
} | ||
|
||
const token = normalizeAddress(asset?.addresses[_chain]?.address); | ||
const isNativeChain = asset.native_chain === _chain; | ||
|
||
if (isNativeChain) { | ||
// token will be transfered from gateway contract. | ||
const param = { | ||
...withdrawParams(gateway, normalizeAddress(withdraw.recipient)), | ||
target: token, | ||
fixedEventData: { | ||
token: token, | ||
from: token, | ||
}, | ||
}; | ||
eventParams.push(param); | ||
} else { | ||
// token will be minted from zero address. | ||
const param = { | ||
...withdrawParams(ETHER_ADDRESS, normalizeAddress(withdraw.recipient)), | ||
target: token, | ||
fixedEventData: { | ||
token: token, | ||
from: token, | ||
}, | ||
}; | ||
eventParams.push(param); | ||
} | ||
} | ||
|
||
return eventParams; | ||
} | ||
|
||
const constructParams = (chain: string) => { | ||
const gateway = normalizeAddress(gatewayAddresses[chain]); | ||
|
||
return async (fromBlock: number, toBlock: number) => { | ||
const [deposits, wraps, withdraws, assets] = await retry(() => | ||
Promise.all([ | ||
fetchDepositAddressTransfers<DepositAddressTransfer>(fromBlock, toBlock, chain, { | ||
isDeposit: true, | ||
}), | ||
fetchDepositAddressTransfers<WrapTransfer>(fromBlock, toBlock, chain, { | ||
isDeposit: true, | ||
type: "wrap", | ||
}), | ||
fetchDepositAddressTransfers<DepositAddressTransfer>(fromBlock, toBlock, chain, { | ||
isDeposit: false, | ||
}), | ||
fetchAssets(), | ||
]) | ||
); | ||
|
||
// console.log(`[${chain}] ${deposits.length} deposits`); | ||
// console.log(`[${chain}] ${wraps.length} wraps`); | ||
// console.log(`[${chain}] ${withdraws.length} withdraws`); | ||
|
||
const eventParams = [ | ||
...constructDepositAddressTransfers(deposits), | ||
...constructWrapTransfers(wraps, gateway, chain, assets), | ||
...constructWithdrawAddressTransfers(withdraws, gateway, chain, assets), | ||
]; | ||
|
||
// console.log("Total listened events:", eventParams.length); | ||
|
||
return getTxDataFromEVMEventLogs("axelar-satellite", chain as Chain, fromBlock, toBlock, eventParams); | ||
}; | ||
}; | ||
|
||
const adapter: BridgeAdapter = { | ||
fantom: constructParams("fantom"), | ||
avalanche: constructParams("avax"), | ||
ethereum: constructParams("ethereum"), | ||
arbitrum: constructParams("arbitrum"), | ||
optimism: constructParams("optimism"), | ||
linea: constructParams("linea"), | ||
base: constructParams("base"), | ||
moonbeam: constructParams("moonbeam"), | ||
kava: constructParams("kava"), | ||
filecoin: constructParams("filecoin"), | ||
polygon: constructParams("polygon"), | ||
bsc: constructParams("bsc"), | ||
// celo: constructParams("celo"), | ||
}; | ||
|
||
export default adapter; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
export interface AxelarTransfer { | ||
deposit_address: string; | ||
destination_chain: string; | ||
source_chain: string; | ||
sender_address: string; | ||
recipient_address: string; | ||
denom: string; | ||
} | ||
|
||
export interface DepositAddressTransfer extends AxelarTransfer {} | ||
|
||
export interface WrapTransfer extends AxelarTransfer { | ||
tx_hash_wrap: string; | ||
} | ||
|
||
export interface FetchDepositTransfersOptions { | ||
isDeposit: boolean; | ||
type?: "deposit_address" | "wrap"; | ||
size?: number; | ||
} | ||
|
||
export interface FetchDepositTransfersResponse { | ||
data: { | ||
link: DepositAddressTransfer; | ||
wrap: DepositAddressTransfer; | ||
}[]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.