@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
JavaScript
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