Token Transfer

Migrate token via Token Portal from evm to solana

Token transfer can be done in following way.

const depositor = signer.address;
const sourceChain = CHAIN_ID_BSC;

const depositorAddrInSolana = ZebecSolBridgeClient.getProxyUserKey(
   depositor, 
   sourceChain, 
   SOL_ZEBEC_BRIDGE_ADDRESS
);

const tokenAddress = "<evm address of token>";

// use ui value for amount
const amount = "1";

// this may vary depending upon the token being transfered
const relayFee = "0.1";

// transfer from evm to solana
const transferReceipt = await transferEvm(
	signer,
	tokenAddress,
	sourceChain,
	amount,
	targetChain,
	proxyAccount.toString(),
	relayFee,
);

Note: The solana proxy address that is derived from the user's evm address must be initialized beforehand. To initialize see initialize proxy account part.

After this, it takes some time for your token to reach solana chain. During this time, a vaa is created which is then verified and signed by the wormhole validators called guardians. You can obtain the vaa in following way.

const sequence = parseSequenceFromLogEth(transferReceipt, getBridgeAddressForChain(sourceChain));
const transferEmitterAddress = getEmitterAddressEth(getTokenBridgeAddressForChain(sourceChain));
const { vaaBytes } = await getSignedVAAWithRetry(
	WORMHOLE_RPC_HOSTS,
	sourceChain,
	transferEmitterAddress,
	sequence,
);

The vaa then can be used to posted on solana chain and redeem the token transferred. Zebec provides specialized token bridge relayer supporting certain tokens as well that automatically relay your tokens using small amount of fee so this part may be optional. However, if you want to manually relay you can accomplish it in following way.

const payerAddress = wallet.publicKey.toString();
const bridgeAddress = getBridgeAddressForChain(targetChain);

const vaaBuf = Buffer.from(vaaBytes);

setDefaultWasm("node"); // use bundler for browser

// posting vaa in solana
await postVaaSolanaWithRetry(
   connection,
   wallet.signTransaction,
   bridgeAddress,
   payerAddress,
   vaaBuf,
   MAX_VAA_UPLOAD_RETRIES_SOLANA,
);

// redeeming token
const unsignedTransaction = await redeemOnSolana(
	connection,
	bridgeAddress,
	tokenBridgeAddress,
	payerAddress,
	vaaBytes,
);
unsignedTransaction.partialSign(keypair);

const txid = await connection.sendRawTransaction(unsignedTransaction.serialize());
await connection.confirmTransaction(txid);

If vaa is supposed to be relayed and token is redeemed by a relayer, in that case you can check and wait for token to be redeemed by the relayer in following way.

let success = false;
let retry = 0;

while(!success) {
   success = await getIsTransferCompletedSolana(tokenBridgeAddress, transferVaa, connection);
   await new Promise((r) => setTimeout(r, 5000));	
   if (retry > 13) throw new Error("Transfer failed!");
   retry++;
}

console.log("transfer successful");

Last updated