Skip to content

Commit

Permalink
Fix portal
Browse files Browse the repository at this point in the history
  • Loading branch information
vrtnd committed Sep 27, 2024
1 parent f4afea1 commit b7c29e6
Show file tree
Hide file tree
Showing 9 changed files with 135 additions and 78 deletions.
2 changes: 2 additions & 0 deletions src/adapters/portal/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,5 @@ export const contractAddresses = {
coreBridge: string;
};
};

export const wormholeChains = Object.keys(contractAddresses).filter((chain) => chain !== "solana");
50 changes: 44 additions & 6 deletions src/adapters/portal/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { getTxsBlockRangeEtherscan, getLock } from "../../helpers/etherscan";
import { EventData } from "../../utils/types";
import { ethers } from "ethers";
import { PromisePool } from "@supercharge/promise-pool";
import { contractAddresses } from "./consts";
import { contractAddresses, wormholeChains } from "./consts";
import { getProvider } from "@defillama/sdk/build/general";
import axios from "axios";
const retry = require("async-retry");
Expand Down Expand Up @@ -251,6 +251,7 @@ const constructParams = (chain: string) => {
const events = await getTxDataFromEVMEventLogs("portal", chain as Chain, fromBlock, toBlock, [
logMessagePublishedEventParams,
]);
const solanaLogs = await getSolanaEventsDestChain(fromBlock, toBlock, chain);
let hashes = events.map((e) => e.txHash);
// The token bridge doesn't emit events on withdrawals/inbound token transfers,
// only able to get from subgraph, Etherscan API, etc.
Expand All @@ -271,7 +272,7 @@ const constructParams = (chain: string) => {

const transfers = await portalNativeAndWrappedTransfersFromHashes(chain, hashes, tokenBridge);
// console.log(`transfers: ${JSON.stringify(transfers, null, 2)}`);
return transfers;
return [...transfers, ...solanaLogs];
};
};

Expand All @@ -294,16 +295,53 @@ const getSolanaEvents = async (fromSlot: number, toSlot: number): Promise<EventD
const response = await axios.get<SolanaEvent[]>(
`https://europe-west3-wormhole-message-db-mainnet.cloudfunctions.net/get-solana-events?fromSlot=${fromSlot}&toSlot=${toSlot}`
);
return response.data.map((event) => ({
...event,
amount: ethers.BigNumber.from(event.amount),
}));

const destChainsEvents = (
await Promise.all(
wormholeChains.map(async (chain) => {
if (chain === "solana") {
return [];
}
const result = await getSolanaEventsDestChain(fromSlot, toSlot, chain);
return result;
})
)
).flat();
console.log(`[INFO] Found ${response.data.length} Solana events from slots ${fromSlot} to ${toSlot}`);
return response.data
.map((event) => ({
...event,
amount: ethers.BigNumber.from(event.amount),
}))
.concat(...destChainsEvents);
} catch (e) {
console.error("Error fetching Solana events", e);
return [];
}
};

const getSolanaEventsDestChain = async (fromBlock: number, toBlock: number, destChain: string) => {
return retry(
async () => {
const response = await axios.get<SolanaEvent[]>(
`https://europe-west3-wormhole-message-db-mainnet.cloudfunctions.net/get-solana-events?fromSlot=${fromBlock}&toSlot=${toBlock}&destChain=${destChain}`
);
return response.data.map((event) => ({
...event,
amount: ethers.BigNumber.from(event.amount),
originChain: "solana",
isDeposit: true,
chainOverride: destChain === "avax" ? "avalanche" : destChain,
}));
},
{
retries: 3,
factor: 1,
minTimeout: 1000,
}
);
};

const adapter: BridgeAdapter = {
ethereum: constructParams("ethereum"),
polygon: constructParams("polygon"),
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/solana.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Connection } from "@solana/web3.js";

export const getConnection = (): Connection => {
const rpc = "https://api.mainnet-beta.solana.com";
const rpc = process.env.SOLANA_RPC as string;
const connection = new Connection(rpc);
const getBlock = async (block: number) => {
return new Connection(rpc).getBlock(block, {
Expand Down
27 changes: 22 additions & 5 deletions src/utils/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -502,12 +502,27 @@ export const runAdapterHistorical = async (
let latestSolanaBlock = null;
let averageBlockTimestamp = null;

let solanaTimestampsMap = {} as { [blockNumber: number]: number };

if (chain === "solana" && bridgeDbName !== "debridgedln") {
latestSolanaBlock = await getLatestBlock("solana");
const connection = getConnection();
const medianBlockNumber = txBlocks?.sort((a, b) => a - b)?.[Math.floor(txBlocks.length / 2)];
averageBlockTimestamp = await connection.getBlockTime(medianBlockNumber);

const blockTimePromises = eventLogs.map(async (event: any) => {
const blockTime = await retry(async () => connection.getBlockTime(event.blockNumber), { retries: 3 });
return { blockNumber: event.blockNumber, blockTime, chainOverride: event.chainOverride };
});

const results = await Promise.all(blockTimePromises);

results.forEach(({ blockNumber, blockTime, chainOverride }) => {
solanaTimestampsMap[blockNumber] = blockTime ?? 0;
console.log(
`[INFO] Block time for block ${blockNumber}: ${blockTime}, destination chain: ${chainOverride}`
);
});
}

for (let i = 0; i < 10; i++) {
const blockNumber = Math.floor(minBlock + i * (blockRange / 10));
for (let j = 0; j < 4; j++) {
Expand Down Expand Up @@ -560,6 +575,7 @@ export const runAdapterHistorical = async (
chainOverride,
isUSDVolume,
txsCountedAs,
originChain,
timestamp: realBlockTimestamp,
} = log;
const bucket = Math.floor(((blockNumber - minBlock) * 9) / blockRange);
Expand All @@ -573,7 +589,7 @@ export const runAdapterHistorical = async (
if (storedBridgeID) {
bridgeIdOverride = storedBridgeID;
} else {
const overrideID = await retry(async () => getBridgeID(bridgeDbName, chainOverride))?.id;
const overrideID = (await retry(async () => getBridgeID(bridgeDbName, chainOverride)))?.id;
bridgeIdOverride = overrideID;
storedBridgeIds[chainOverride] = overrideID;
if (!overrideID) {
Expand Down Expand Up @@ -602,9 +618,9 @@ export const runAdapterHistorical = async (
allowNullTxValues,
{
bridge_id: bridgeIdOverride,
chain: chainContractsAreOn,
chain: chainOverride ?? chainContractsAreOn,
tx_hash: txHash ?? null,
ts: realBlockTimestamp ?? timestamp,
ts: solanaTimestampsMap[blockNumber] ?? realBlockTimestamp ?? timestamp,
tx_block: blockNumber ?? null,
tx_from: from ?? null,
tx_to: to ?? null,
Expand All @@ -613,6 +629,7 @@ export const runAdapterHistorical = async (
is_deposit: isDeposit,
is_usd_volume: isUSDVolume ?? false,
txs_counted_as: txsCountedAs ?? 0,
origin_chain: originChain ?? null,
},
onConflict
);
Expand Down
44 changes: 29 additions & 15 deletions src/utils/aggregate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@ import {
getTimestampAtStartOfDayUTC,
} from "./date";
import { getLlamaPrices } from "./prices";
import {
getBridgeID,
getLargeTransaction,
} from "./wrappa/postgres/query";
import { getBridgeID, getLargeTransaction } from "./wrappa/postgres/query";
import {
insertHourlyAggregatedRow,
insertDailyAggregatedRow,
Expand All @@ -27,6 +24,8 @@ import { importBridgeNetwork } from "../data/importBridgeNetwork";
import { defaultConfidenceThreshold } from "./constants";
import { transformTokenDecimals, transformTokens } from "../helpers/tokenMappings";
import { blacklist } from "../data/blacklist";
import { PublicKey } from "@solana/web3.js";
import { sendDiscordText } from "./discord";

const nullPriceCountThreshold = 10; // insert error when there are more than this many prices missing per hour/day for a bridge

Expand Down Expand Up @@ -54,7 +53,7 @@ export const runAggregateDataHistorical = async (
const currentTimestamp = getCurrentUnixTimestamp() * 1000;
const bridgeNetwork = importBridgeNetwork(undefined, bridgeNetworkId);

if(!bridgeNetwork) {
if (!bridgeNetwork) {
const errString = `Bridge network with id ${bridgeNetworkId} not found.`;
await insertErrorRow({
ts: currentTimestamp,
Expand All @@ -79,9 +78,8 @@ export const runAggregateDataHistorical = async (
throw new Error(errString);
}


const chains = Object.keys(adapter);

let timestamp = endTimestamp;
while (timestamp > startTimestamp) {
if (chainToRestrictTo) {
Expand Down Expand Up @@ -151,6 +149,14 @@ export const runAggregateDataAllAdapters = async (timestamp: number, hourly: boo
console.log("Finished aggregating job.");
};

const checkSolanaAddress = (address: string) => {
try {
return PublicKey.isOnCurve(address);
} catch (e) {
return false;
}
};

/*
Couldn't figure out how to insert unknown usd values as null into composite sql types
and didn't spend time to fix it, so unknown usd values are 0 instead.
Expand Down Expand Up @@ -256,10 +262,12 @@ export const aggregateData = async (

const uniqueTokenPromises = Promise.all(
txs.map(async (tx) => {
const { token, chain, is_usd_volume } = tx;
let { token, chain, is_usd_volume, origin_chain } = tx;
const isSolanaAddress = checkSolanaAddress(token);
chain = isSolanaAddress ? "solana" : origin_chain ?? chain;
if (!is_usd_volume) {
if (!token || !chain) return;
const tokenL = token.toLowerCase();
const tokenL = isSolanaAddress ? token : token.toLowerCase();
uniqueTokens[transformTokens[chain]?.[tokenL] ?? `${chain}:${tokenL}`] = true;
}
})
Expand All @@ -281,7 +289,8 @@ export const aggregateData = async (
let tokensWithNullPrices = new Set();
const txsPromises = Promise.all(
txs.map(async (tx) => {
const { id, chain, token, amount, ts, is_deposit, tx_to, tx_from, is_usd_volume, txs_counted_as } = tx;
const { id, chain, token, amount, ts, is_deposit, tx_to, tx_from, is_usd_volume, txs_counted_as, origin_chain } =
tx;
if (
tx_from?.toLowerCase() === "0x0000000000000000000000000000000000000000" ||
tx_to?.toLowerCase() === "0x0000000000000000000000000000000000000000"
Expand All @@ -296,9 +305,9 @@ export const aggregateData = async (
let tokenKey = null;
if (is_usd_volume) {
usdValue = rawBnAmount.toNumber();
tokenKey = `${chain}:${token}`
tokenKey = `${chain}:${token}`;
} else {
const tokenL = token.toLowerCase();
const tokenL = chain === "solana" || origin_chain === "solana" ? token : token.toLowerCase();
const transformedDecimals = transformTokenDecimals[chain]?.[tokenL] ?? null;
tokenKey = transformTokens[chain]?.[tokenL] ?? `${chain}:${tokenL}`;
if (blacklist.includes(tokenKey)) {
Expand All @@ -307,7 +316,7 @@ export const aggregateData = async (
}
const priceData = llamaPrices[tokenKey];
if (!priceData && !tokensWithNullPrices.has(tokenKey)) {
console.log(`No price data for ${tokenKey}`);
console.log(`No price data for ${tokenKey}`, tx);
tokensWithNullPrices.add(tokenKey);
}
if (priceData && (priceData.confidence === undefined || priceData.confidence >= defaultConfidenceThreshold)) {
Expand All @@ -323,7 +332,7 @@ export const aggregateData = async (
}
}

if(usdValue) {
if (usdValue) {
if (usdValue > 10 ** 9) {
const errString = `USD value of tx id ${id} is ${usdValue}.`;
await insertErrorRow({
Expand All @@ -345,7 +354,7 @@ export const aggregateData = async (
});
}
}

if (is_deposit) {
totalDepositedUsd += usdValue ?? 0;
if (txs_counted_as) {
Expand Down Expand Up @@ -392,6 +401,11 @@ export const aggregateData = async (
})
);
await txsPromises;
if (tokensWithNullPrices.size) {
await sendDiscordText(
`There are ${tokensWithNullPrices.size} tokens with no price: \n ${Array.from(tokensWithNullPrices).join("\n")}`
);
}
if (tokensWithNullPrices.size > nullPriceCountThreshold) {
const errString = `There are ${tokensWithNullPrices.size} missing prices for ${bridgeID} from ${startTimestamp} to ${endTimestamp}`;
await insertErrorRow({
Expand Down
Loading

0 comments on commit b7c29e6

Please sign in to comment.