sdk/js: fix transfer and redeem for native SOL
sdk/js: add optional argument for MockTokenBridge testing: add tests
This commit is contained in:
parent
68fbc1e77d
commit
5d9ce7227e
|
@ -6,8 +6,13 @@ import { MockEmitter } from "./wormhole";
|
|||
export class MockTokenBridge extends MockEmitter {
|
||||
consistencyLevel: number;
|
||||
|
||||
constructor(emitterAddress: string, chain: number, consistencyLevel: number) {
|
||||
super(emitterAddress, chain);
|
||||
constructor(
|
||||
emitterAddress: string,
|
||||
chain: number,
|
||||
consistencyLevel: number,
|
||||
startSequence?: number
|
||||
) {
|
||||
super(emitterAddress, chain, startSequence);
|
||||
this.consistencyLevel = consistencyLevel;
|
||||
}
|
||||
|
||||
|
@ -156,9 +161,14 @@ export class MockTokenBridge extends MockEmitter {
|
|||
}
|
||||
|
||||
export class MockEthereumTokenBridge extends MockTokenBridge {
|
||||
constructor(emitterAddress: string) {
|
||||
constructor(emitterAddress: string, startSequence?: number) {
|
||||
const chain = 2;
|
||||
super(tryNativeToHexString(emitterAddress, chain as ChainId), chain, 15);
|
||||
super(
|
||||
tryNativeToHexString(emitterAddress, chain as ChainId),
|
||||
chain,
|
||||
15,
|
||||
startSequence
|
||||
);
|
||||
}
|
||||
|
||||
publishAttestMeta(
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import {
|
||||
AccountLayout,
|
||||
ACCOUNT_SIZE,
|
||||
createCloseAccountInstruction,
|
||||
createInitializeAccountInstruction,
|
||||
createTransferInstruction,
|
||||
getMinimumBalanceForRentExemptMint,
|
||||
getMinimumBalanceForRentExemptAccount,
|
||||
getMint,
|
||||
NATIVE_MINT,
|
||||
TOKEN_PROGRAM_ID,
|
||||
|
@ -135,7 +135,10 @@ export async function redeemAndUnwrapOnSolana(
|
|||
(info) =>
|
||||
parsed.amount * BigInt(Math.pow(10, info.decimals - MAX_VAA_DECIMALS))
|
||||
);
|
||||
const rentBalance = await getMinimumBalanceForRentExemptMint(connection);
|
||||
const rentBalance = await getMinimumBalanceForRentExemptAccount(
|
||||
connection,
|
||||
commitment
|
||||
);
|
||||
if (Buffer.compare(parsed.tokenAddress, NATIVE_MINT.toBuffer()) != 0) {
|
||||
return Promise.reject("tokenAddress != NATIVE_MINT");
|
||||
}
|
||||
|
@ -154,12 +157,12 @@ export async function redeemAndUnwrapOnSolana(
|
|||
fromPubkey: payerPublicKey,
|
||||
newAccountPubkey: ancillaryKeypair.publicKey,
|
||||
lamports: rentBalance, //spl token accounts need rent exemption
|
||||
space: AccountLayout.span,
|
||||
space: ACCOUNT_SIZE,
|
||||
programId: TOKEN_PROGRAM_ID,
|
||||
});
|
||||
|
||||
//Initialize the account as a WSOL account, with the original payerAddress as owner
|
||||
const initAccountIx = await createInitializeAccountInstruction(
|
||||
const initAccountIx = createInitializeAccountInstruction(
|
||||
ancillaryKeypair.publicKey,
|
||||
NATIVE_MINT,
|
||||
payerPublicKey
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import {
|
||||
AccountLayout,
|
||||
ACCOUNT_SIZE,
|
||||
createCloseAccountInstruction,
|
||||
createInitializeAccountInstruction,
|
||||
getMinimumBalanceForRentExemptMint,
|
||||
getMinimumBalanceForRentExemptAccount,
|
||||
NATIVE_MINT,
|
||||
TOKEN_PROGRAM_ID,
|
||||
} from "@solana/spl-token";
|
||||
|
@ -43,7 +43,6 @@ import {
|
|||
Bridge__factory,
|
||||
TokenImplementation__factory,
|
||||
} from "../ethers-contracts";
|
||||
import { createBridgeFeeTransferInstruction } from "../solana";
|
||||
import {
|
||||
createApproveAuthoritySignerInstruction,
|
||||
createTransferNativeInstruction,
|
||||
|
@ -452,8 +451,10 @@ export async function transferNativeSol(
|
|||
payload: Uint8Array | Buffer | null = null,
|
||||
commitment?: Commitment
|
||||
) {
|
||||
const rentBalance = await getMinimumBalanceForRentExemptMint(connection);
|
||||
const mintPublicKey = NATIVE_MINT;
|
||||
const rentBalance = await getMinimumBalanceForRentExemptAccount(
|
||||
connection,
|
||||
commitment
|
||||
);
|
||||
const payerPublicKey = new PublicKey(payerAddress);
|
||||
const ancillaryKeypair = Keypair.generate();
|
||||
|
||||
|
@ -462,30 +463,24 @@ export async function transferNativeSol(
|
|||
fromPubkey: payerPublicKey,
|
||||
newAccountPubkey: ancillaryKeypair.publicKey,
|
||||
lamports: rentBalance, //spl token accounts need rent exemption
|
||||
space: AccountLayout.span,
|
||||
space: ACCOUNT_SIZE,
|
||||
programId: TOKEN_PROGRAM_ID,
|
||||
});
|
||||
|
||||
//Send in the amount of SOL which we want converted to wSOL
|
||||
const initialBalanceTransferIx = SystemProgram.transfer({
|
||||
fromPubkey: payerPublicKey,
|
||||
lamports: Number(amount),
|
||||
lamports: amount,
|
||||
toPubkey: ancillaryKeypair.publicKey,
|
||||
});
|
||||
//Initialize the account as a WSOL account, with the original payerAddress as owner
|
||||
const initAccountIx = await createInitializeAccountInstruction(
|
||||
const initAccountIx = createInitializeAccountInstruction(
|
||||
ancillaryKeypair.publicKey,
|
||||
mintPublicKey,
|
||||
NATIVE_MINT,
|
||||
payerPublicKey
|
||||
);
|
||||
|
||||
//Normal approve & transfer instructions, except that the wSOL is sent from the ancillary account.
|
||||
const nonce = createNonce().readUInt32LE(0);
|
||||
const transferIx = await createBridgeFeeTransferInstruction(
|
||||
connection,
|
||||
bridgeAddress,
|
||||
payerAddress
|
||||
);
|
||||
const approvalIx = createApproveAuthoritySignerInstruction(
|
||||
tokenBridgeAddress,
|
||||
ancillaryKeypair.publicKey,
|
||||
|
@ -494,6 +489,7 @@ export async function transferNativeSol(
|
|||
);
|
||||
|
||||
const message = Keypair.generate();
|
||||
const nonce = createNonce().readUInt32LE(0);
|
||||
const tokenBridgeTransferIx =
|
||||
payload !== null
|
||||
? createTransferNativeWithPayloadInstruction(
|
||||
|
@ -538,7 +534,6 @@ export async function transferNativeSol(
|
|||
createAncillaryAccountIx,
|
||||
initialBalanceTransferIx,
|
||||
initAccountIx,
|
||||
transferIx,
|
||||
approvalIx,
|
||||
tokenBridgeTransferIx,
|
||||
closeAccountIx
|
||||
|
@ -571,11 +566,6 @@ export async function transferFromSolana(
|
|||
fromOwnerAddress = payerAddress;
|
||||
}
|
||||
const nonce = createNonce().readUInt32LE(0);
|
||||
const transferIx = await createBridgeFeeTransferInstruction(
|
||||
connection,
|
||||
bridgeAddress,
|
||||
payerAddress
|
||||
);
|
||||
const approvalIx = createApproveAuthoritySignerInstruction(
|
||||
tokenBridgeAddress,
|
||||
fromAddress,
|
||||
|
@ -650,7 +640,6 @@ export async function transferFromSolana(
|
|||
coalesceChainId(targetChain)
|
||||
);
|
||||
const transaction = new SolanaTransaction().add(
|
||||
transferIx,
|
||||
approvalIx,
|
||||
tokenBridgeTransferIx
|
||||
);
|
||||
|
|
|
@ -52,6 +52,7 @@ import {
|
|||
getTransferNativeWithPayloadCpiAccounts,
|
||||
getTransferWrappedWithPayloadCpiAccounts,
|
||||
NodeWallet,
|
||||
signSendAndConfirmTransaction,
|
||||
SplTokenMetadataProgram,
|
||||
} from "../../../sdk/js/src/solana";
|
||||
import {
|
||||
|
@ -86,6 +87,10 @@ import {
|
|||
getOriginalAssetSolana,
|
||||
} from "../../../sdk/js/src/token_bridge";
|
||||
import { ChainId } from "../../../sdk/js/src";
|
||||
import {
|
||||
transferNativeSol,
|
||||
redeemAndUnwrapOnSolana,
|
||||
} from "../../../sdk/js/src/token_bridge";
|
||||
|
||||
describe("Token Bridge", () => {
|
||||
const connection = new web3.Connection(LOCALHOST, "processed");
|
||||
|
@ -1886,10 +1891,11 @@ describe("Token Bridge", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("Asset Queries", () => {
|
||||
describe("SDK Methods", () => {
|
||||
// nft bridge on Ethereum
|
||||
const ethereumTokenBridge = new MockEthereumTokenBridge(
|
||||
ETHEREUM_TOKEN_BRIDGE_ADDRESS
|
||||
ETHEREUM_TOKEN_BRIDGE_ADDRESS,
|
||||
3 // startSequence
|
||||
);
|
||||
|
||||
describe("getOriginalAssetSolana", () => {
|
||||
|
@ -2014,5 +2020,118 @@ describe("Token Bridge", () => {
|
|||
expect(isWrapped).is.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe("transferNativeSol", () => {
|
||||
it("Send SOL To Ethereum", async () => {
|
||||
const balanceBefore = await connection
|
||||
.getBalance(wallet.key())
|
||||
.then((num) => BigInt(num));
|
||||
|
||||
const amount = 6969696969n;
|
||||
const targetAddress = Buffer.alloc(32, "deadbeef", "hex");
|
||||
|
||||
const transferNativeSolTx = await transferNativeSol(
|
||||
connection,
|
||||
CORE_BRIDGE_ADDRESS,
|
||||
TOKEN_BRIDGE_ADDRESS,
|
||||
wallet.key(),
|
||||
amount,
|
||||
targetAddress,
|
||||
"ethereum"
|
||||
)
|
||||
.then((transaction) =>
|
||||
signSendAndConfirmTransaction(
|
||||
connection,
|
||||
wallet.key(),
|
||||
wallet.signTransaction,
|
||||
transaction
|
||||
)
|
||||
)
|
||||
.then((response) => response.signature);
|
||||
//console.log(`transferNativeSolTx: ${transferNativeSolTx}`);
|
||||
|
||||
const balanceAfter = await connection
|
||||
.getBalance(wallet.key())
|
||||
.then((num) => BigInt(num));
|
||||
|
||||
const transactionCost = 4601551n;
|
||||
expect(balanceBefore - balanceAfter - transactionCost).to.equal(amount);
|
||||
});
|
||||
});
|
||||
|
||||
describe("redeemAndUnwrapOnSolana", () => {
|
||||
it("Receive SOL From Ethereum", async () => {
|
||||
const balanceBefore = await connection
|
||||
.getBalance(wallet.key())
|
||||
.then((num) => BigInt(num));
|
||||
|
||||
const tokenChain = 1;
|
||||
const mintAta = await getOrCreateAssociatedTokenAccount(
|
||||
connection,
|
||||
wallet.signer(),
|
||||
NATIVE_MINT,
|
||||
wallet.key()
|
||||
).then((account) => account.address);
|
||||
|
||||
const amount = 42042042n;
|
||||
const recipientChain = 1;
|
||||
const fee = 0n;
|
||||
const nonce = 420;
|
||||
const message = ethereumTokenBridge.publishTransferTokens(
|
||||
NATIVE_MINT.toBuffer().toString("hex"),
|
||||
tokenChain,
|
||||
amount,
|
||||
recipientChain,
|
||||
mintAta.toBuffer().toString("hex"),
|
||||
fee,
|
||||
nonce
|
||||
);
|
||||
|
||||
const signedVaa = guardians.addSignatures(
|
||||
message,
|
||||
[0, 1, 2, 3, 5, 7, 8, 9, 10, 12, 15, 16, 18]
|
||||
);
|
||||
|
||||
const txSignatures = await postVaa(
|
||||
connection,
|
||||
wallet.signTransaction,
|
||||
CORE_BRIDGE_ADDRESS,
|
||||
wallet.key(),
|
||||
signedVaa
|
||||
).then((results) => results.map((result) => result.signature));
|
||||
const postTx = txSignatures.pop()!;
|
||||
for (const verifyTx of txSignatures) {
|
||||
// console.log(`verifySignatures: ${verifyTx}`);
|
||||
}
|
||||
// console.log(`postVaa: ${postTx}`);
|
||||
|
||||
const transferNativeSolTx = await redeemAndUnwrapOnSolana(
|
||||
connection,
|
||||
CORE_BRIDGE_ADDRESS,
|
||||
TOKEN_BRIDGE_ADDRESS,
|
||||
wallet.key(),
|
||||
signedVaa
|
||||
)
|
||||
.then((transaction) =>
|
||||
signSendAndConfirmTransaction(
|
||||
connection,
|
||||
wallet.key(),
|
||||
wallet.signTransaction,
|
||||
transaction
|
||||
)
|
||||
)
|
||||
.then((response) => response.signature);
|
||||
//console.log(`transferNativeSolTx: ${transferNativeSolTx}`);
|
||||
|
||||
const balanceAfter = await connection
|
||||
.getBalance(wallet.key())
|
||||
.then((num) => BigInt(num));
|
||||
|
||||
const transactionCost = 6821400n;
|
||||
expect(balanceAfter - (balanceBefore - transactionCost)).to.equal(
|
||||
amount * 10n
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue