UNPKG

@wormhole-foundation/sdk-connect

Version:

The core package for the Connect SDK, used in conjunction with 1 or more of the chain packages

217 lines 8.69 kB
import { amount, encoding, toChain, toChainId } from "@wormhole-foundation/sdk-base"; import { deserialize } from "@wormhole-foundation/sdk-definitions"; import axios from "axios"; import { retry } from "./tasks.js"; export const WHSCAN_RETRY_INTERVAL = 2000; /** * Gets the bytes of a VAA for a given WormholeMessageId or `null` if the VAA is not available yet. * @param apiUrl the url of the wormholescan API * @param whm the WormholeMessageId * @returns a Uint8Array containing the VAA or `null` if it's not available yet * @throws Errors if the service throws an unrecoverable error (e.g. 500) */ export async function getVaaBytes(apiUrl, whm) { const { chain, emitter, sequence } = whm; const chainId = toChainId(chain); const emitterAddress = encoding.stripPrefix("0x", emitter.toString()); const url = `${apiUrl}/v1/signed_vaa/${chainId}/${emitterAddress}/${sequence}`; try { const { data: { vaaBytes }, } = await axios.get(url); return encoding.b64.decode(vaaBytes); } catch (error) { if (!error) return null; if (typeof error === "object") { // A 404 error means the VAA is not yet available // since its not available yet, we return null signaling it can be tried again if (axios.isAxiosError(error) && error.response?.status === 404) return null; if ("status" in error && error.status === 404) return null; } throw error; } } export async function getVaaBytesWithRetry(apiUrl, whm, timeout) { const task = () => getVaaBytes(apiUrl, whm); return await retry(task, WHSCAN_RETRY_INTERVAL, timeout, "Wormholescan:GetVaaBytes"); } export async function getVaa(apiUrl, whm, decodeAs) { const vaaBytes = await getVaaBytes(apiUrl, whm); if (!vaaBytes) return null; return deserialize(decodeAs, vaaBytes); } export async function getVaaWithRetry(apiUrl, whm, decodeAs, timeout) { const task = () => getVaa(apiUrl, whm, decodeAs); return await retry(task, WHSCAN_RETRY_INTERVAL, timeout, "Wormholescan:GetVaaBytes"); } /** * Gets the status for a transaction given WormholeMessageId or `null` if the VAA is not available yet. * @param rpcUrl the url of the wormholescan API * @param whm the WormholeMessageId * @returns a TransactionStatus or `null` if it's not available yet * @throws Errors if the service throws an unrecoverable error (e.g. 500) */ export async function getTransactionStatus(rpcUrl, whm) { const { chain, emitter, sequence } = whm; const chainId = toChainId(chain); const emitterAddress = emitter.toUniversalAddress().toString(); const url = `${rpcUrl}/api/v1/transactions/${chainId}/${emitterAddress}/${sequence}`; try { const response = await axios.get(url); return response.data; } catch (error) { if (!error) return null; if (typeof error === "object") { // A 404 error means the VAA is not yet available // since its not available yet, we return null signaling it can be tried again if (axios.isAxiosError(error) && error.response?.status === 404) return null; if ("status" in error && error.status === 404) return null; } throw error; } } export async function getTransactionStatusWithRetry(rpcUrl, whm, timeout) { const task = () => getTransactionStatus(rpcUrl, whm); return await retry(task, WHSCAN_RETRY_INTERVAL, timeout, "Wormholescan:GetTransactionStatus"); } export async function getRelayStatus(rpcUrl, txid) { const url = `${rpcUrl}/v1/relays?txHash=${txid}`; try { const response = await axios.get(url); if (response.data.data.to.txHash) return response.data.data; } catch (error) { if (!error) return null; if (typeof error === "object") { // A 404 error means the VAA is not yet available // since its not available yet, we return null signaling it can be tried again if (axios.isAxiosError(error) && error.response?.status === 404) return null; if ("status" in error && error.status === 404) return null; } throw error; } return null; } export async function getRelayStatusWithRetry(rpcUrl, txid, timeout) { const task = () => getRelayStatus(rpcUrl, txid); return retry(task, WHSCAN_RETRY_INTERVAL, timeout, "Wormholescan:GetRelayStatus"); } export async function getVaaByTxHash(rpcUrl, txid) { const url = `${rpcUrl}/api/v1/vaas?txHash=${txid}`; try { const response = await axios.get(url); if (response.data.data.length > 0) return response.data.data[0]; } catch (error) { if (!error) return null; if (typeof error === "object") { // A 404 error means the VAA is not yet available // since its not available yet, we return null signaling it can be tried again if (axios.isAxiosError(error) && error.response?.status === 404) return null; if ("status" in error && error.status === 404) return null; } throw error; } return null; } export async function getVaaByTxHashWithRetry(rpcUrl, txid, decodeAs, timeout) { const task = () => getVaaByTxHash(rpcUrl, txid); const vaa = await retry(task, WHSCAN_RETRY_INTERVAL, timeout, "Wormholescan:GetVaaByTxHash"); if (!vaa) return null; return deserialize(decodeAs, encoding.b64.decode(vaa.vaa)); } export async function getTxsByAddress(rpcUrl, address, pageSize = 50, page = 0) { const url = `${rpcUrl}/api/v1/transactions?address=${address}&pageSize=${pageSize}&page=${page}`; try { const response = await axios.get(url); if (response.data.transactions.length > 0) return response.data.transactions; } catch (error) { if (!error) return null; if (typeof error === "object") { // A 404 error means the VAA is not yet available // since its not available yet, we return null signaling it can be tried again if (axios.isAxiosError(error) && error.response?.status === 404) return null; if ("status" in error && error.status === 404) return null; } throw error; } return null; } export async function getGuardianHeartbeats(rpcUrl) { const url = `${rpcUrl}/v1/heartbeats`; try { const response = await axios.get(url); if (response.data && response.data.entries.length > 0) return response.data.entries; } catch { } return null; } export async function getGovernedTokens(rpcUrl) { const url = `${rpcUrl}/v1/governor/token_list`; try { const response = await axios.get(url); if (response.data && response.data.entries.length > 0) { return response.data.entries.reduce((acc, entry) => { const chain = toChain(entry.originChainId); acc[chain] = acc[chain] || {}; acc[chain][entry.originAddress] = entry.price; return acc; }, {}); } } catch { } return null; } export async function getGovernorLimits(rpcUrl) { const url = `${rpcUrl}/v1/governor/available_notional_by_chain`; try { const response = await axios.get(url); if (response.data && response.data.entries.length > 0) { return response.data.entries.reduce((acc, entry) => { // if 0 consider it no limit const maxSize = entry.bigTransactionSize === "0" ? undefined : amount.whole(amount.parse(entry.bigTransactionSize, 2)); acc[toChain(entry.chainId)] = { available: amount.whole(amount.parse(entry.remainingAvailableNotional, 2)), limit: amount.whole(amount.parse(entry.notionalLimit, 2)), maxSize, }; return acc; }, {}); } } catch { } return null; } export async function getIsVaaEnqueued(rpcUrl, whm) { const { chain, emitter, sequence } = whm; const chainId = toChainId(chain); const emitterAddress = emitter.toUniversalAddress().toString(); const url = `${rpcUrl}/v1/governor/is_vaa_enqueued/${chainId}/${emitterAddress}/${sequence}`; const response = await axios.get(url); return response.data.isEnqueued; } //# sourceMappingURL=whscan-api.js.map