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 {
|
export class MockTokenBridge extends MockEmitter {
|
||||||
consistencyLevel: number;
|
consistencyLevel: number;
|
||||||
|
|
||||||
constructor(emitterAddress: string, chain: number, consistencyLevel: number) {
|
constructor(
|
||||||
super(emitterAddress, chain);
|
emitterAddress: string,
|
||||||
|
chain: number,
|
||||||
|
consistencyLevel: number,
|
||||||
|
startSequence?: number
|
||||||
|
) {
|
||||||
|
super(emitterAddress, chain, startSequence);
|
||||||
this.consistencyLevel = consistencyLevel;
|
this.consistencyLevel = consistencyLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,9 +161,14 @@ export class MockTokenBridge extends MockEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MockEthereumTokenBridge extends MockTokenBridge {
|
export class MockEthereumTokenBridge extends MockTokenBridge {
|
||||||
constructor(emitterAddress: string) {
|
constructor(emitterAddress: string, startSequence?: number) {
|
||||||
const chain = 2;
|
const chain = 2;
|
||||||
super(tryNativeToHexString(emitterAddress, chain as ChainId), chain, 15);
|
super(
|
||||||
|
tryNativeToHexString(emitterAddress, chain as ChainId),
|
||||||
|
chain,
|
||||||
|
15,
|
||||||
|
startSequence
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
publishAttestMeta(
|
publishAttestMeta(
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import {
|
import {
|
||||||
AccountLayout,
|
ACCOUNT_SIZE,
|
||||||
createCloseAccountInstruction,
|
createCloseAccountInstruction,
|
||||||
createInitializeAccountInstruction,
|
createInitializeAccountInstruction,
|
||||||
createTransferInstruction,
|
createTransferInstruction,
|
||||||
getMinimumBalanceForRentExemptMint,
|
getMinimumBalanceForRentExemptAccount,
|
||||||
getMint,
|
getMint,
|
||||||
NATIVE_MINT,
|
NATIVE_MINT,
|
||||||
TOKEN_PROGRAM_ID,
|
TOKEN_PROGRAM_ID,
|
||||||
|
@ -135,7 +135,10 @@ export async function redeemAndUnwrapOnSolana(
|
||||||
(info) =>
|
(info) =>
|
||||||
parsed.amount * BigInt(Math.pow(10, info.decimals - MAX_VAA_DECIMALS))
|
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) {
|
if (Buffer.compare(parsed.tokenAddress, NATIVE_MINT.toBuffer()) != 0) {
|
||||||
return Promise.reject("tokenAddress != NATIVE_MINT");
|
return Promise.reject("tokenAddress != NATIVE_MINT");
|
||||||
}
|
}
|
||||||
|
@ -154,12 +157,12 @@ export async function redeemAndUnwrapOnSolana(
|
||||||
fromPubkey: payerPublicKey,
|
fromPubkey: payerPublicKey,
|
||||||
newAccountPubkey: ancillaryKeypair.publicKey,
|
newAccountPubkey: ancillaryKeypair.publicKey,
|
||||||
lamports: rentBalance, //spl token accounts need rent exemption
|
lamports: rentBalance, //spl token accounts need rent exemption
|
||||||
space: AccountLayout.span,
|
space: ACCOUNT_SIZE,
|
||||||
programId: TOKEN_PROGRAM_ID,
|
programId: TOKEN_PROGRAM_ID,
|
||||||
});
|
});
|
||||||
|
|
||||||
//Initialize the account as a WSOL account, with the original payerAddress as owner
|
//Initialize the account as a WSOL account, with the original payerAddress as owner
|
||||||
const initAccountIx = await createInitializeAccountInstruction(
|
const initAccountIx = createInitializeAccountInstruction(
|
||||||
ancillaryKeypair.publicKey,
|
ancillaryKeypair.publicKey,
|
||||||
NATIVE_MINT,
|
NATIVE_MINT,
|
||||||
payerPublicKey
|
payerPublicKey
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import {
|
import {
|
||||||
AccountLayout,
|
ACCOUNT_SIZE,
|
||||||
createCloseAccountInstruction,
|
createCloseAccountInstruction,
|
||||||
createInitializeAccountInstruction,
|
createInitializeAccountInstruction,
|
||||||
getMinimumBalanceForRentExemptMint,
|
getMinimumBalanceForRentExemptAccount,
|
||||||
NATIVE_MINT,
|
NATIVE_MINT,
|
||||||
TOKEN_PROGRAM_ID,
|
TOKEN_PROGRAM_ID,
|
||||||
} from "@solana/spl-token";
|
} from "@solana/spl-token";
|
||||||
|
@ -43,7 +43,6 @@ import {
|
||||||
Bridge__factory,
|
Bridge__factory,
|
||||||
TokenImplementation__factory,
|
TokenImplementation__factory,
|
||||||
} from "../ethers-contracts";
|
} from "../ethers-contracts";
|
||||||
import { createBridgeFeeTransferInstruction } from "../solana";
|
|
||||||
import {
|
import {
|
||||||
createApproveAuthoritySignerInstruction,
|
createApproveAuthoritySignerInstruction,
|
||||||
createTransferNativeInstruction,
|
createTransferNativeInstruction,
|
||||||
|
@ -452,8 +451,10 @@ export async function transferNativeSol(
|
||||||
payload: Uint8Array | Buffer | null = null,
|
payload: Uint8Array | Buffer | null = null,
|
||||||
commitment?: Commitment
|
commitment?: Commitment
|
||||||
) {
|
) {
|
||||||
const rentBalance = await getMinimumBalanceForRentExemptMint(connection);
|
const rentBalance = await getMinimumBalanceForRentExemptAccount(
|
||||||
const mintPublicKey = NATIVE_MINT;
|
connection,
|
||||||
|
commitment
|
||||||
|
);
|
||||||
const payerPublicKey = new PublicKey(payerAddress);
|
const payerPublicKey = new PublicKey(payerAddress);
|
||||||
const ancillaryKeypair = Keypair.generate();
|
const ancillaryKeypair = Keypair.generate();
|
||||||
|
|
||||||
|
@ -462,30 +463,24 @@ export async function transferNativeSol(
|
||||||
fromPubkey: payerPublicKey,
|
fromPubkey: payerPublicKey,
|
||||||
newAccountPubkey: ancillaryKeypair.publicKey,
|
newAccountPubkey: ancillaryKeypair.publicKey,
|
||||||
lamports: rentBalance, //spl token accounts need rent exemption
|
lamports: rentBalance, //spl token accounts need rent exemption
|
||||||
space: AccountLayout.span,
|
space: ACCOUNT_SIZE,
|
||||||
programId: TOKEN_PROGRAM_ID,
|
programId: TOKEN_PROGRAM_ID,
|
||||||
});
|
});
|
||||||
|
|
||||||
//Send in the amount of SOL which we want converted to wSOL
|
//Send in the amount of SOL which we want converted to wSOL
|
||||||
const initialBalanceTransferIx = SystemProgram.transfer({
|
const initialBalanceTransferIx = SystemProgram.transfer({
|
||||||
fromPubkey: payerPublicKey,
|
fromPubkey: payerPublicKey,
|
||||||
lamports: Number(amount),
|
lamports: amount,
|
||||||
toPubkey: ancillaryKeypair.publicKey,
|
toPubkey: ancillaryKeypair.publicKey,
|
||||||
});
|
});
|
||||||
//Initialize the account as a WSOL account, with the original payerAddress as owner
|
//Initialize the account as a WSOL account, with the original payerAddress as owner
|
||||||
const initAccountIx = await createInitializeAccountInstruction(
|
const initAccountIx = createInitializeAccountInstruction(
|
||||||
ancillaryKeypair.publicKey,
|
ancillaryKeypair.publicKey,
|
||||||
mintPublicKey,
|
NATIVE_MINT,
|
||||||
payerPublicKey
|
payerPublicKey
|
||||||
);
|
);
|
||||||
|
|
||||||
//Normal approve & transfer instructions, except that the wSOL is sent from the ancillary account.
|
//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(
|
const approvalIx = createApproveAuthoritySignerInstruction(
|
||||||
tokenBridgeAddress,
|
tokenBridgeAddress,
|
||||||
ancillaryKeypair.publicKey,
|
ancillaryKeypair.publicKey,
|
||||||
|
@ -494,6 +489,7 @@ export async function transferNativeSol(
|
||||||
);
|
);
|
||||||
|
|
||||||
const message = Keypair.generate();
|
const message = Keypair.generate();
|
||||||
|
const nonce = createNonce().readUInt32LE(0);
|
||||||
const tokenBridgeTransferIx =
|
const tokenBridgeTransferIx =
|
||||||
payload !== null
|
payload !== null
|
||||||
? createTransferNativeWithPayloadInstruction(
|
? createTransferNativeWithPayloadInstruction(
|
||||||
|
@ -538,7 +534,6 @@ export async function transferNativeSol(
|
||||||
createAncillaryAccountIx,
|
createAncillaryAccountIx,
|
||||||
initialBalanceTransferIx,
|
initialBalanceTransferIx,
|
||||||
initAccountIx,
|
initAccountIx,
|
||||||
transferIx,
|
|
||||||
approvalIx,
|
approvalIx,
|
||||||
tokenBridgeTransferIx,
|
tokenBridgeTransferIx,
|
||||||
closeAccountIx
|
closeAccountIx
|
||||||
|
@ -571,11 +566,6 @@ export async function transferFromSolana(
|
||||||
fromOwnerAddress = payerAddress;
|
fromOwnerAddress = payerAddress;
|
||||||
}
|
}
|
||||||
const nonce = createNonce().readUInt32LE(0);
|
const nonce = createNonce().readUInt32LE(0);
|
||||||
const transferIx = await createBridgeFeeTransferInstruction(
|
|
||||||
connection,
|
|
||||||
bridgeAddress,
|
|
||||||
payerAddress
|
|
||||||
);
|
|
||||||
const approvalIx = createApproveAuthoritySignerInstruction(
|
const approvalIx = createApproveAuthoritySignerInstruction(
|
||||||
tokenBridgeAddress,
|
tokenBridgeAddress,
|
||||||
fromAddress,
|
fromAddress,
|
||||||
|
@ -650,7 +640,6 @@ export async function transferFromSolana(
|
||||||
coalesceChainId(targetChain)
|
coalesceChainId(targetChain)
|
||||||
);
|
);
|
||||||
const transaction = new SolanaTransaction().add(
|
const transaction = new SolanaTransaction().add(
|
||||||
transferIx,
|
|
||||||
approvalIx,
|
approvalIx,
|
||||||
tokenBridgeTransferIx
|
tokenBridgeTransferIx
|
||||||
);
|
);
|
||||||
|
|
|
@ -52,6 +52,7 @@ import {
|
||||||
getTransferNativeWithPayloadCpiAccounts,
|
getTransferNativeWithPayloadCpiAccounts,
|
||||||
getTransferWrappedWithPayloadCpiAccounts,
|
getTransferWrappedWithPayloadCpiAccounts,
|
||||||
NodeWallet,
|
NodeWallet,
|
||||||
|
signSendAndConfirmTransaction,
|
||||||
SplTokenMetadataProgram,
|
SplTokenMetadataProgram,
|
||||||
} from "../../../sdk/js/src/solana";
|
} from "../../../sdk/js/src/solana";
|
||||||
import {
|
import {
|
||||||
|
@ -86,6 +87,10 @@ import {
|
||||||
getOriginalAssetSolana,
|
getOriginalAssetSolana,
|
||||||
} from "../../../sdk/js/src/token_bridge";
|
} from "../../../sdk/js/src/token_bridge";
|
||||||
import { ChainId } from "../../../sdk/js/src";
|
import { ChainId } from "../../../sdk/js/src";
|
||||||
|
import {
|
||||||
|
transferNativeSol,
|
||||||
|
redeemAndUnwrapOnSolana,
|
||||||
|
} from "../../../sdk/js/src/token_bridge";
|
||||||
|
|
||||||
describe("Token Bridge", () => {
|
describe("Token Bridge", () => {
|
||||||
const connection = new web3.Connection(LOCALHOST, "processed");
|
const connection = new web3.Connection(LOCALHOST, "processed");
|
||||||
|
@ -1886,10 +1891,11 @@ describe("Token Bridge", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Asset Queries", () => {
|
describe("SDK Methods", () => {
|
||||||
// nft bridge on Ethereum
|
// nft bridge on Ethereum
|
||||||
const ethereumTokenBridge = new MockEthereumTokenBridge(
|
const ethereumTokenBridge = new MockEthereumTokenBridge(
|
||||||
ETHEREUM_TOKEN_BRIDGE_ADDRESS
|
ETHEREUM_TOKEN_BRIDGE_ADDRESS,
|
||||||
|
3 // startSequence
|
||||||
);
|
);
|
||||||
|
|
||||||
describe("getOriginalAssetSolana", () => {
|
describe("getOriginalAssetSolana", () => {
|
||||||
|
@ -2014,5 +2020,118 @@ describe("Token Bridge", () => {
|
||||||
expect(isWrapped).is.true;
|
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