Skip to content

Commit

Permalink
Merge pull request #44 from DefiLlama/feat/add-satellite-bridge
Browse files Browse the repository at this point in the history
Feat/add satellite bridge
  • Loading branch information
vrtnd authored Sep 8, 2023
2 parents 8e00172 + 2292742 commit d551876
Show file tree
Hide file tree
Showing 6 changed files with 382 additions and 52 deletions.
57 changes: 57 additions & 0 deletions src/adapters/axelar-satellite/constant.ts
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,
});
226 changes: 226 additions & 0 deletions src/adapters/axelar-satellite/index.ts
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;
27 changes: 27 additions & 0 deletions src/adapters/axelar-satellite/type.ts
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;
}[];
}
2 changes: 2 additions & 0 deletions src/adapters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import meson from "./meson";
import base from "./base";
import mantle from "./mantle";
import neuron from "./neuron";
import axelarsatellite from "./axelar-satellite";
import squidrouter from "./squidrouter"

export default {
Expand Down Expand Up @@ -67,6 +68,7 @@ export default {
base,
mantle,
neuron,
axelarsatellite,
squidrouter,
} as {
[bridge: string]: BridgeAdapter;
Expand Down
Loading

0 comments on commit d551876

Please sign in to comment.