diff --git a/src/components/Recovery.tsx b/src/components/Recovery.tsx
index 5ea4124..bac0e25 100644
--- a/src/components/Recovery.tsx
+++ b/src/components/Recovery.tsx
@@ -5,6 +5,7 @@ import {
CHAIN_ID_INJECTIVE,
CHAIN_ID_KARURA,
CHAIN_ID_NEAR,
+ CHAIN_ID_SEI,
CHAIN_ID_SOLANA,
CHAIN_ID_SUI,
CHAIN_ID_TERRA2,
@@ -105,6 +106,12 @@ import {
} from "../utils/injective";
import { makeNearProvider } from "../utils/near";
import parseError from "../utils/parseError";
+import {
+ getSeiQueryClient,
+ getSeiWasmClient,
+ parseSequenceFromLogSei,
+ queryExternalIdSei,
+} from "../utils/sei";
import { getSuiProvider } from "../utils/sui";
import ButtonWithLoader from "./ButtonWithLoader";
import ChainSelect from "./ChainSelect";
@@ -320,6 +327,26 @@ async function injective(txHash: string, enqueueSnackbar: any) {
}
}
+async function sei(txHash: string, enqueueSnackbar: any) {
+ try {
+ const client = await getSeiQueryClient();
+ const tx = await client.cosmos.tx.v1beta1.getTx({ hash: txHash });
+ if (!tx || !tx.tx_response) {
+ throw new Error("Unable to fetch transaction");
+ }
+ const sequence = parseSequenceFromLogSei(tx.tx_response);
+ if (!sequence) {
+ throw new Error("Sequence not found");
+ }
+ const emitterAddress = await getEmitterAddressTerra(
+ getTokenBridgeAddressForChain(CHAIN_ID_SEI)
+ );
+ return await fetchSignedVAA(CHAIN_ID_SEI, emitterAddress, sequence);
+ } catch (e) {
+ return handleError(e, enqueueSnackbar);
+ }
+}
+
async function sui(digest: string, enqueueSnackbar: any) {
try {
const provider = getSuiProvider();
@@ -568,6 +595,19 @@ export default function Recovery() {
}
})();
}
+ if (parsedPayload && parsedPayload.targetChain === CHAIN_ID_SEI) {
+ (async () => {
+ const client = await getSeiWasmClient();
+ const tokenId = await queryExternalIdSei(
+ client,
+ getTokenBridgeAddressForChain(CHAIN_ID_SEI),
+ parsedPayload.originAddress
+ );
+ if (!cancelled) {
+ setTokenId(tokenId || "");
+ }
+ })();
+ }
if (parsedPayload && parsedPayload.targetChain === CHAIN_ID_SUI) {
(async () => {
const tokenId = await getForeignAssetSui(
@@ -777,6 +817,26 @@ export default function Recovery() {
setIsVAAPending(isPending);
}
})();
+ } else if (recoverySourceChain === CHAIN_ID_SEI) {
+ setRecoverySourceTxError("");
+ setRecoverySourceTxIsLoading(true);
+ setTokenId("");
+ (async () => {
+ const { vaa, isPending, error } = await sei(
+ recoverySourceTx,
+ enqueueSnackbar
+ );
+ if (!cancelled) {
+ setRecoverySourceTxIsLoading(false);
+ if (vaa) {
+ setRecoverySignedVAA(vaa);
+ }
+ if (error) {
+ setRecoverySourceTxError(error);
+ }
+ setIsVAAPending(isPending);
+ }
+ })();
} else if (recoverySourceChain === CHAIN_ID_SUI) {
setRecoverySourceTxError("");
setRecoverySourceTxIsLoading(true);
diff --git a/src/hooks/useHandleAttest.tsx b/src/hooks/useHandleAttest.tsx
index 12d06d9..ffa3120 100644
--- a/src/hooks/useHandleAttest.tsx
+++ b/src/hooks/useHandleAttest.tsx
@@ -4,6 +4,7 @@ import {
CHAIN_ID_INJECTIVE,
CHAIN_ID_KLAYTN,
CHAIN_ID_NEAR,
+ CHAIN_ID_SEI,
CHAIN_ID_SOLANA,
CHAIN_ID_SUI,
CHAIN_ID_XPLA,
@@ -40,9 +41,15 @@ import {
} from "@certusone/wormhole-sdk";
import { getOriginalPackageId } from "@certusone/wormhole-sdk/lib/cjs/sui";
import { getEmitterAddressAndSequenceFromResponseSui } from "@certusone/wormhole-sdk/lib/esm/sui";
+import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate";
+import { calculateFee } from "@cosmjs/stargate";
import { WalletStrategy } from "@injectivelabs/wallet-ts";
import { Alert } from "@material-ui/lab";
import { Wallet } from "@near-wallet-selector/core";
+import {
+ useSigningCosmWasmClient as useSeiSigningCosmWasmClient,
+ useWallet as useSeiWallet,
+} from "@sei-js/react";
import { WalletContextState } from "@solana/wallet-adapter-react";
import { Connection, PublicKey } from "@solana/web3.js";
import {
@@ -113,6 +120,7 @@ import { signSendAndConfirm } from "../utils/solana";
import { getSuiProvider } from "../utils/sui";
import { postWithFees, waitForTerraExecution } from "../utils/terra";
import { postWithFeesXpla, waitForXplaExecution } from "../utils/xpla";
+import { attestFromSeiMsg, parseSequenceFromLogSei } from "../utils/sei";
async function algo(
dispatch: any,
@@ -543,6 +551,56 @@ async function injective(
}
}
+async function sei(
+ dispatch: any,
+ enqueueSnackbar: any,
+ wallet: SigningCosmWasmClient,
+ walletAddress: string,
+ asset: string
+) {
+ dispatch(setIsSending(true));
+ try {
+ const msg = attestFromSeiMsg(asset);
+ const fee = calculateFee(600000, "0.1usei");
+ const tokenBridgeAddress = getTokenBridgeAddressForChain(CHAIN_ID_SEI);
+ const tx = await wallet.execute(
+ walletAddress,
+ tokenBridgeAddress,
+ msg,
+ fee,
+ "Wormhole - Create Wrapped"
+ );
+ dispatch(setAttestTx({ id: tx.transactionHash, block: tx.height }));
+ enqueueSnackbar(null, {
+ content: Transaction confirmed,
+ });
+ const sequence = parseSequenceFromLogSei(tx);
+ if (!sequence) {
+ throw new Error("Sequence not found");
+ }
+ const emitterAddress = await getEmitterAddressTerra(tokenBridgeAddress);
+ enqueueSnackbar(null, {
+ content: Fetching VAA,
+ });
+ const { vaaBytes } = await getSignedVAAWithRetry(
+ WORMHOLE_RPC_HOSTS,
+ CHAIN_ID_SEI,
+ emitterAddress,
+ sequence
+ );
+ dispatch(setSignedVAAHex(uint8ArrayToHex(vaaBytes)));
+ enqueueSnackbar(null, {
+ content: Fetched Signed VAA,
+ });
+ } catch (e) {
+ console.error(e);
+ enqueueSnackbar(null, {
+ content: {parseError(e)},
+ });
+ dispatch(setIsSending(false));
+ }
+}
+
async function sui(
dispatch: any,
enqueueSnackbar: any,
@@ -624,6 +682,10 @@ export function useHandleAttest() {
const { account: aptosAccount, signAndSubmitTransaction } = useAptosContext();
const aptosAddress = aptosAccount?.address?.toString();
const { wallet: injWallet, address: injAddress } = useInjectiveContext();
+ const { signingCosmWasmClient: seiSigningCosmWasmClient } =
+ useSeiSigningCosmWasmClient();
+ const { accounts: seiAccounts } = useSeiWallet();
+ const seiAddress = seiAccounts.length ? seiAccounts[0].address : null;
const { accountId: nearAccountId, wallet: nearWallet } = useNearContext();
const suiWallet = useWallet();
const disabled = !isTargetComplete || isSending || isSendComplete;
@@ -649,6 +711,18 @@ export function useHandleAttest() {
aptos(dispatch, enqueueSnackbar, sourceAsset, signAndSubmitTransaction);
} else if (sourceChain === CHAIN_ID_INJECTIVE && injWallet && injAddress) {
injective(dispatch, enqueueSnackbar, injWallet, injAddress, sourceAsset);
+ } else if (
+ sourceChain === CHAIN_ID_SEI &&
+ seiSigningCosmWasmClient &&
+ seiAddress
+ ) {
+ sei(
+ dispatch,
+ enqueueSnackbar,
+ seiSigningCosmWasmClient,
+ seiAddress,
+ sourceAsset
+ );
} else if (sourceChain === CHAIN_ID_NEAR && nearAccountId && nearWallet) {
near(dispatch, enqueueSnackbar, nearAccountId, sourceAsset, nearWallet);
} else if (
@@ -677,6 +751,8 @@ export function useHandleAttest() {
nearAccountId,
nearWallet,
suiWallet,
+ seiSigningCosmWasmClient,
+ seiAddress,
]);
return useMemo(
() => ({
diff --git a/src/hooks/useHandleRedeem.tsx b/src/hooks/useHandleRedeem.tsx
index 8300716..7dc5553 100644
--- a/src/hooks/useHandleRedeem.tsx
+++ b/src/hooks/useHandleRedeem.tsx
@@ -12,6 +12,8 @@ import {
TerraChainId,
isEVMChain,
isTerraChain,
+ parseTransferPayload,
+ parseVaa,
postVaaSolanaWithRetry,
redeemAndUnwrapOnSolana,
redeemOnAlgorand,
@@ -81,6 +83,7 @@ import {
ALGORAND_TOKEN_BRIDGE_ID,
MAX_VAA_UPLOAD_RETRIES_SOLANA,
NEAR_TOKEN_BRIDGE_ACCOUNT,
+ SEI_TRANSLATER_TARGET,
SEI_TRANSLATOR,
SOLANA_HOST,
SOL_BRIDGE_ADDRESS,
@@ -412,24 +415,45 @@ async function sei(
) {
dispatch(setIsRedeeming(true));
try {
- const msg = {
- complete_transfer_and_convert: {
- vaa: fromUint8Array(signedVAA),
- },
- };
- // TODO: is this right?
- const fee = calculateFee(800000, "0.1usei");
- const tx = await wallet.execute(
- walletAddress,
- SEI_TRANSLATOR,
- msg,
- fee,
- "Wormhole - Complete Transfer"
- );
- dispatch(setRedeemTx({ id: tx.transactionHash, block: tx.height }));
- enqueueSnackbar(null, {
- content: Transaction confirmed,
- });
+ const parsed = parseVaa(signedVAA);
+ const payload = parseTransferPayload(parsed.payload);
+ if (payload.targetAddress === uint8ArrayToHex(SEI_TRANSLATER_TARGET)) {
+ const msg = {
+ complete_transfer_and_convert: {
+ vaa: fromUint8Array(signedVAA),
+ },
+ };
+ const fee = calculateFee(800000, "0.1usei");
+ const tx = await wallet.execute(
+ walletAddress,
+ SEI_TRANSLATOR,
+ msg,
+ fee,
+ "Wormhole - Complete Transfer"
+ );
+ dispatch(setRedeemTx({ id: tx.transactionHash, block: tx.height }));
+ enqueueSnackbar(null, {
+ content: Transaction confirmed,
+ });
+ } else {
+ const msg = {
+ submit_vaa: {
+ data: fromUint8Array(signedVAA),
+ },
+ };
+ const fee = calculateFee(800000, "0.1usei");
+ const tx = await wallet.execute(
+ walletAddress,
+ getTokenBridgeAddressForChain(CHAIN_ID_SEI),
+ msg,
+ fee,
+ "Wormhole - Complete Transfer"
+ );
+ dispatch(setRedeemTx({ id: tx.transactionHash, block: tx.height }));
+ enqueueSnackbar(null, {
+ content: Transaction confirmed,
+ });
+ }
} catch (e) {
enqueueSnackbar(null, {
content: {parseError(e)},
diff --git a/src/hooks/useHandleTransfer.tsx b/src/hooks/useHandleTransfer.tsx
index 06d39bb..4536f92 100644
--- a/src/hooks/useHandleTransfer.tsx
+++ b/src/hooks/useHandleTransfer.tsx
@@ -130,7 +130,7 @@ import {
signAndSendTransactions,
} from "../utils/near";
import parseError from "../utils/parseError";
-import { parseSequenceFromLogSei } from "../utils/sei";
+import { isNativeDenomSei, parseSequenceFromLogSei } from "../utils/sei";
import { signSendAndConfirm } from "../utils/solana";
import { getSuiProvider } from "../utils/sui";
import { postWithFees, waitForTerraExecution } from "../utils/terra";
@@ -144,9 +144,10 @@ type AdditionalPayloadOverride = {
function maybeAdditionalPayload(
recipientChain: ChainId,
- recipientAddress: Uint8Array
+ recipientAddress: Uint8Array,
+ originChain?: ChainId
): AdditionalPayloadOverride | null {
- if (recipientChain === CHAIN_ID_SEI) {
+ if (recipientChain === CHAIN_ID_SEI && originChain !== CHAIN_ID_SEI) {
return {
receivingContract: SEI_TRANSLATER_TARGET,
payload: new Uint8Array(
@@ -216,6 +217,7 @@ async function algo(
recipientChain: ChainId,
recipientAddress: Uint8Array,
chainId: ChainId,
+ originChain?: ChainId,
relayerFee?: string
) {
dispatch(setIsSending(true));
@@ -225,7 +227,8 @@ async function algo(
const transferAmountParsed = baseAmountParsed.add(feeParsed);
const additionalPayload = maybeAdditionalPayload(
recipientChain,
- recipientAddress
+ recipientAddress,
+ originChain
);
const algodClient = new algosdk.Algodv2(
ALGORAND_HOST.algodToken,
@@ -283,6 +286,7 @@ async function aptos(
) => Promise<{
hash: string;
}>,
+ originChain?: ChainId,
relayerFee?: string
) {
dispatch(setIsSending(true));
@@ -293,7 +297,8 @@ async function aptos(
const transferAmountParsed = baseAmountParsed.add(feeParsed);
const additionalPayload = maybeAdditionalPayload(
recipientChain,
- recipientAddress
+ recipientAddress,
+ originChain
);
if (additionalPayload?.payload) {
throw new Error("Transfer with payload is unsupported on Aptos");
@@ -346,6 +351,7 @@ async function evm(
recipientAddress: Uint8Array,
isNative: boolean,
chainId: ChainId,
+ originChain?: ChainId,
relayerFee?: string
) {
dispatch(setIsSending(true));
@@ -355,7 +361,8 @@ async function evm(
const transferAmountParsed = baseAmountParsed.add(feeParsed);
const additionalPayload = maybeAdditionalPayload(
recipientChain,
- recipientAddress
+ recipientAddress,
+ originChain
);
// Klaytn requires specifying gasPrice
const overrides =
@@ -420,6 +427,7 @@ async function near(
recipientChain: ChainId,
recipientAddress: Uint8Array,
chainId: ChainId,
+ originChain?: ChainId,
relayerFee?: string
) {
dispatch(setIsSending(true));
@@ -429,7 +437,8 @@ async function near(
const transferAmountParsed = baseAmountParsed.add(feeParsed);
const additionalPayload = maybeAdditionalPayload(
recipientChain,
- recipientAddress
+ recipientAddress,
+ originChain
);
const account = await makeNearAccount(senderAddr);
const msgs =
@@ -513,7 +522,8 @@ async function solana(
const transferAmountParsed = baseAmountParsed.add(feeParsed);
const additionalPayload = maybeAdditionalPayload(
targetChain,
- targetAddress
+ targetAddress,
+ originChain
);
const originAddress = originAddressStr
? zeroPad(hexToUint8Array(originAddressStr), 32)
@@ -583,6 +593,7 @@ async function terra(
targetAddress: Uint8Array,
feeDenom: string,
chainId: TerraChainId,
+ originChain?: ChainId,
relayerFee?: string
) {
dispatch(setIsSending(true));
@@ -593,7 +604,8 @@ async function terra(
const tokenBridgeAddress = getTokenBridgeAddressForChain(chainId);
const additionalPayload = maybeAdditionalPayload(
targetChain,
- targetAddress
+ targetAddress,
+ originChain
);
const msgs = await transferFromTerra(
wallet.terraAddress,
@@ -645,6 +657,7 @@ async function xpla(
decimals: number,
targetChain: ChainId,
targetAddress: Uint8Array,
+ originChain?: ChainId,
relayerFee?: string
) {
dispatch(setIsSending(true));
@@ -655,7 +668,8 @@ async function xpla(
const tokenBridgeAddress = getTokenBridgeAddressForChain(CHAIN_ID_XPLA);
const additionalPayload = maybeAdditionalPayload(
targetChain,
- targetAddress
+ targetAddress,
+ originChain
);
const msgs = await transferFromXpla(
wallet.xplaAddress,
@@ -706,6 +720,7 @@ async function injective(
decimals: number,
targetChain: ChainId,
targetAddress: Uint8Array,
+ originChain?: ChainId,
relayerFee?: string
) {
dispatch(setIsSending(true));
@@ -717,7 +732,8 @@ async function injective(
getTokenBridgeAddressForChain(CHAIN_ID_INJECTIVE);
const additionalPayload = maybeAdditionalPayload(
targetChain,
- targetAddress
+ targetAddress,
+ originChain
);
const msgs = await transferFromInjective(
walletAddress,
@@ -774,28 +790,73 @@ async function sei(
const feeParsed = parseUnits(relayerFee || "0", decimals);
const transferAmountParsed = baseAmountParsed.add(feeParsed);
const tokenBridgeAddress = getTokenBridgeAddressForChain(CHAIN_ID_SEI);
- // NOTE: this only supports transferring out via the Sei CW20 <> Bank translator
- const msg = {
- convert_and_transfer: {
- recipient_chain: targetChain,
- recipient: Buffer.from(targetAddress).toString("base64"),
- fee: feeParsed.toString(),
- },
- };
- const fee = calculateFee(600000, "0.1usei");
- const tx = await wallet.execute(
- walletAddress,
- SEI_TRANSLATOR,
- msg,
- fee,
- "Wormhole - Initiate Transfer",
- [{ denom: asset, amount: transferAmountParsed.toString() }]
- );
- dispatch(setTransferTx({ id: tx.transactionHash, block: tx.height }));
- enqueueSnackbar(null, {
- content: Transaction confirmed,
- });
- const sequence = parseSequenceFromLogSei(tx);
+ let sequence: string = "";
+ if (asset.startsWith(`factory/${SEI_TRANSLATOR}/`)) {
+ const msg = {
+ convert_and_transfer: {
+ recipient_chain: targetChain,
+ recipient: Buffer.from(targetAddress).toString("base64"),
+ fee: feeParsed.toString(),
+ },
+ };
+ const fee = calculateFee(750000, "0.1usei");
+ const tx = await wallet.execute(
+ walletAddress,
+ SEI_TRANSLATOR,
+ msg,
+ fee,
+ "Wormhole - Initiate Transfer",
+ [{ denom: asset, amount: transferAmountParsed.toString() }]
+ );
+ dispatch(setTransferTx({ id: tx.transactionHash, block: tx.height }));
+ enqueueSnackbar(null, {
+ content: Transaction confirmed,
+ });
+ sequence = parseSequenceFromLogSei(tx);
+ } else if (isNativeDenomSei(asset)) {
+ const fee = calculateFee(600000, "0.1usei");
+ const nonce = Math.round(Math.random() * 100000);
+ const tx = await wallet.executeMultiple(
+ walletAddress,
+ [
+ {
+ contractAddress: tokenBridgeAddress,
+ msg: {
+ deposit_tokens: {},
+ },
+ funds: [{ denom: asset, amount: transferAmountParsed.toString() }],
+ },
+ {
+ contractAddress: tokenBridgeAddress,
+ msg: {
+ initiate_transfer: {
+ asset: {
+ amount: transferAmountParsed.toString(),
+ info: {
+ native_token: {
+ denom: asset,
+ },
+ },
+ },
+ recipient_chain: targetChain,
+ recipient: Buffer.from(targetAddress).toString("base64"),
+ fee: feeParsed.toString(),
+ nonce,
+ },
+ },
+ },
+ ],
+ fee,
+ "Wormhole - Initiate Transfer"
+ );
+ dispatch(setTransferTx({ id: tx.transactionHash, block: tx.height }));
+ enqueueSnackbar(null, {
+ content: Transaction confirmed,
+ });
+ sequence = parseSequenceFromLogSei(tx);
+ } else {
+ throw new Error("Unsupported asset");
+ }
if (!sequence) {
throw new Error("Sequence not found");
}
@@ -822,6 +883,7 @@ async function sui(
decimals: number,
targetChain: ChainId,
targetAddress: Uint8Array,
+ originChain?: ChainId,
relayerFee?: string
) {
dispatch(setIsSending(true));
@@ -947,6 +1009,7 @@ export function useHandleTransfer() {
targetAddress,
isNative,
sourceChain,
+ originChain,
relayerFee
);
} else if (
@@ -992,6 +1055,7 @@ export function useHandleTransfer() {
targetAddress,
terraFeeDenom,
sourceChain,
+ originChain,
relayerFee
);
} else if (
@@ -1010,6 +1074,7 @@ export function useHandleTransfer() {
decimals,
targetChain,
targetAddress,
+ originChain,
relayerFee
);
} else if (
@@ -1029,6 +1094,7 @@ export function useHandleTransfer() {
targetChain,
targetAddress,
sourceChain,
+ originChain,
relayerFee
);
} else if (
@@ -1048,6 +1114,7 @@ export function useHandleTransfer() {
targetAddress,
sourceChain,
signAndSubmitTransaction,
+ originChain,
relayerFee
);
} else if (
@@ -1068,6 +1135,7 @@ export function useHandleTransfer() {
decimals,
targetChain,
targetAddress,
+ originChain,
relayerFee
);
} else if (
@@ -1109,6 +1177,7 @@ export function useHandleTransfer() {
targetChain,
targetAddress,
sourceChain,
+ originChain,
relayerFee
);
} else if (
@@ -1128,6 +1197,7 @@ export function useHandleTransfer() {
decimals,
targetChain,
targetAddress,
+ originChain,
relayerFee
);
}
diff --git a/src/hooks/useSeiNativeBalances.ts b/src/hooks/useSeiNativeBalances.ts
index fb1c9fe..b2fee4e 100644
--- a/src/hooks/useSeiNativeBalances.ts
+++ b/src/hooks/useSeiNativeBalances.ts
@@ -2,7 +2,7 @@ import { cosmos } from "@certusone/wormhole-sdk";
import { base58, formatUnits } from "ethers/lib/utils";
import { MutableRefObject, useEffect, useMemo, useState } from "react";
import { NFTParsedTokenAccount } from "../store/nftSlice";
-import { SEI_TRANSLATOR } from "../utils/consts";
+import { SEI_DECIMALS, SEI_TRANSLATOR } from "../utils/consts";
import { getSeiQueryClient, getSeiWasmClient } from "../utils/sei";
export default function useSeiNativeBalances(
@@ -35,9 +35,9 @@ export default function useSeiNativeBalances(
address: walletAddress,
});
// NOTE: this UI only handles the translator factory tokens for now
- // const seiCoin = response.balances.find(
- // (coin) => coin.denom === "usei"
- // );
+ const seiCoin = response.balances.find(
+ (coin) => coin.denom === "usei"
+ );
const translatedCoins = response.balances.filter((coin) =>
coin.denom.startsWith(`factory/${SEI_TRANSLATOR}/`)
);
@@ -59,26 +59,26 @@ export default function useSeiNativeBalances(
)
);
const tokenAccounts: NFTParsedTokenAccount[] = [
- // ...(seiCoin
- // ? [
- // {
- // amount: seiCoin.amount,
- // decimals: SEI_DECIMALS,
- // mintKey: seiCoin.denom,
- // publicKey: walletAddress,
- // uiAmount: Number(
- // formatUnits(BigInt(seiCoin.amount), SEI_DECIMALS)
- // ),
- // uiAmountString: formatUnits(
- // BigInt(seiCoin.amount),
- // SEI_DECIMALS
- // ),
- // isNativeAsset: true,
- // symbol: "SEI",
- // name: "Sei",
- // },
- // ]
- // : []),
+ ...(seiCoin
+ ? [
+ {
+ amount: seiCoin.amount,
+ decimals: SEI_DECIMALS,
+ mintKey: seiCoin.denom,
+ publicKey: walletAddress,
+ uiAmount: Number(
+ formatUnits(BigInt(seiCoin.amount), SEI_DECIMALS)
+ ),
+ uiAmountString: formatUnits(
+ BigInt(seiCoin.amount),
+ SEI_DECIMALS
+ ),
+ isNativeAsset: true,
+ symbol: "SEI",
+ name: "Sei",
+ },
+ ]
+ : []),
...translatedCoins.map((coin, idx) => ({
amount: coin.amount,
decimals: translatedCoinInfos[idx].decimals,
diff --git a/src/utils/sei.ts b/src/utils/sei.ts
index f10f332..e7d8b85 100644
--- a/src/utils/sei.ts
+++ b/src/utils/sei.ts
@@ -1,14 +1,21 @@
import {
+ CHAIN_ID_INJECTIVE,
CHAIN_ID_SEI,
+ CHAIN_ID_TERRA,
+ CHAIN_ID_XPLA,
ChainId,
ChainName,
+ CosmWasmChainId,
+ CosmWasmChainName,
WormholeWrappedInfo,
- buildTokenId,
coalesceChainId,
+ coalesceCosmWasmChainId,
hexToUint8Array,
- isNativeCosmWasmDenom,
+ isTerraChain,
} from "@certusone/wormhole-sdk";
+import { isNativeDenom } from "@certusone/wormhole-sdk/lib/esm/terra";
import { getCosmWasmClient, getQueryClient } from "@sei-js/core";
+import { keccak256 } from "ethers/lib/utils";
import { fromUint8Array } from "js-base64";
import { SEI_CHAIN_CONFIGURATION } from "./consts";
@@ -22,6 +29,57 @@ export type CosmWasmClient = {
queryContractSmart: (address: string, queryMsg: any) => Promise;
};
+// START SDK PATCH
+export const isNativeDenomInjective = (denom: string) => denom === "inj";
+export const isNativeDenomXpla = (denom: string) => denom === "axpla";
+export const isNativeDenomSei = (denom: string) => denom === "usei";
+
+export function isNativeCosmWasmDenom(
+ chainId: CosmWasmChainId,
+ address: string
+) {
+ return (
+ (isTerraChain(chainId) && isNativeDenom(address)) ||
+ (chainId === CHAIN_ID_INJECTIVE && isNativeDenomInjective(address)) ||
+ (chainId === CHAIN_ID_XPLA && isNativeDenomXpla(address)) ||
+ (chainId === CHAIN_ID_SEI && isNativeDenomSei(address))
+ );
+}
+
+export function buildTokenId(
+ chain: Exclude<
+ CosmWasmChainId | CosmWasmChainName,
+ typeof CHAIN_ID_TERRA | "terra"
+ >,
+ address: string
+) {
+ const chainId: CosmWasmChainId = coalesceCosmWasmChainId(chain);
+ return (
+ (isNativeCosmWasmDenom(chainId, address) ? "01" : "00") +
+ keccak256(Buffer.from(address, "utf-8")).substring(4)
+ );
+}
+// END SDK PATCH
+
+export function attestFromSeiMsg(asset: string) {
+ const nonce = Math.round(Math.random() * 100000);
+ const isNativeAsset = isNativeDenomSei(asset);
+ return {
+ create_asset_meta: {
+ asset_info: isNativeAsset
+ ? {
+ native_token: { denom: asset },
+ }
+ : {
+ token: {
+ contract_addr: asset,
+ },
+ },
+ nonce: nonce,
+ },
+ };
+}
+
/**
* Returns the address of the foreign asset
* @param tokenBridgeAddress Address of token bridge contact