Skip to content

Commit

Permalink
solana: add positive and negative tests for receiving tbtc
Browse files Browse the repository at this point in the history
  • Loading branch information
gator-boi committed Aug 4, 2023
1 parent 31e245c commit 64d1dc1
Show file tree
Hide file tree
Showing 3 changed files with 291 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ pub struct ReceiveTbtc<'info> {
bump = custodian.bump,
has_one = wrapped_tbtc_token,
has_one = wrapped_tbtc_mint,
has_one = tbtc_mint,
//has_one = token_bridge_redeemer,
has_one = tbtc_mint
)]
custodian: Account<'info, Custodian>,

Expand Down Expand Up @@ -96,9 +95,6 @@ pub struct ReceiveTbtc<'info> {
/// CHECK: This account is needed for the Token Bridge program.
token_bridge_registered_emitter: UncheckedAccount<'info>,

/// CHECK: This account is needed for the Token Bridge program.
//token_bridge_redeemer: UncheckedAccount<'info>,

/// CHECK: This account is needed for the Token Bridge program.
token_bridge_wrapped_asset: UncheckedAccount<'info>,

Expand Down Expand Up @@ -133,7 +129,7 @@ impl<'info> ReceiveTbtc<'info> {
);

// There must be an encoded amount.
require_gte!(
require_gt!(
transfer.amount(),
0,
WormholeGatewayError::NoTbtcTransferred
Expand Down
286 changes: 284 additions & 2 deletions cross-chain/solana/tests/02__wormholeGateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
} from "./helpers";
import * as tbtc from "./helpers/tbtc";
import * as wormholeGateway from "./helpers/wormholeGateway";
import { PublicKey } from "@solana/web3.js";

async function setup(
program: Program<WormholeGateway>,
Expand Down Expand Up @@ -106,9 +107,13 @@ describe("wormhole-gateway", () => {
tbtc.getTokenPDA(),
commonTokenOwner.publicKey
);

// Give the impostor some lamports.
await transferLamports(authority, impostorKeys.publicKey, 100000000000);
});

it("update minting limit", async () => {
// Update minting limit as authority.
const newLimit = BigInt(20000);
const ix = await wormholeGateway.updateMintingLimitIx(
{
Expand All @@ -118,11 +123,19 @@ describe("wormhole-gateway", () => {
);
await expectIxSuccess([ix], [authority]);
await wormholeGateway.checkState(authority.publicKey, newLimit);

// Only the authority can update the minting limit.
const failingIx = await wormholeGateway.updateMintingLimitIx(
{
authority: impostorKeys.publicKey,
},
newLimit + BigInt(1)
);
await expectIxFail([failingIx], [impostorKeys], "IsNotAuthority");
await wormholeGateway.checkState(authority.publicKey, newLimit);
});

it("deposit wrapped tokens", async () => {
const custodian = wormholeGateway.getCustodianPDA();

// Set up new wallet
const payer = await generatePayer(authority);

Expand Down Expand Up @@ -282,6 +295,275 @@ describe("wormhole-gateway", () => {
// Check balance change.
expect(tbtcAfter.amount).to.equal(tbtcBefore.amount + sentAmount);
expect(gatewayAfter.amount).to.equal(gatewayBefore.amount + sentAmount);

// Cannot receive tbtc again.
await expectIxFail([ix], [payer], "TransferAlreadyRedeemed");
});

it("receive wrapped tbtc (ata doesn't exist)", async () => {
// Set up new wallet
const payer = await generatePayer(authority);

// Use common token account.
const recipient = commonTokenOwner.publicKey;
const recipientToken = getAssociatedTokenAddressSync(
tbtc.getTokenPDA(),
recipient
);
const recipientWrappedToken = getAssociatedTokenAddressSync(
WRAPPED_TBTC_MINT,
recipient
);

// Verify that the wrapped token account doesn't exist yet.
try {
await getAccount(connection, recipientWrappedToken);
} catch (e: any) {
expect(e.toString()).to.equal("TokenAccountNotFoundError");
}

// Get foreign gateway.
const fromGateway = await wormholeGateway
.getGatewayInfo(2)
.then((info) => info.address);

// Create transfer VAA.
const sentAmount = BigInt(5000);
const signedVaa = await ethereumGatewaySendTbtc(
payer,
ethereumTokenBridge,
sentAmount,
fromGateway,
WORMHOLE_GATEWAY_PROGRAM_ID,
recipient
);

// Set the mint limit to a value smaller than sentAmount.
const newLimit = sentAmount - BigInt(69);
const updateLimitIx = await wormholeGateway.updateMintingLimitIx(
{
authority: authority.publicKey,
},
newLimit
);
await expectIxSuccess([updateLimitIx], [authority]);
await wormholeGateway.checkState(authority.publicKey, newLimit);

// Balance check before receiving wrapped tbtc. We can't
// check the balance of the recipient's wrapped tbtc yet,
// since the contract will create the ATA.
const [tbtcBefore, gatewayBefore] = await Promise.all([
getAccount(connection, recipientToken),
getAccount(connection, gatewayWrappedTbtcToken),
]);

const ix = await wormholeGateway.receiveTbtcIx(
{
payer: payer.publicKey,
recipientToken,
recipient,
},
signedVaa
);
await expectIxSuccess([ix], [payer]);

// Check token accounts after receiving wrapped tbtc. We should
// be able to fetch the recipient's wrapped tbtc now.
const [tbtcAfter, wrappedTbtcAfter, gatewayAfter] = await Promise.all([
getAccount(connection, recipientToken),
getAccount(connection, recipientWrappedToken),
getAccount(connection, gatewayWrappedTbtcToken),
]);

// Check balance change.
expect(tbtcAfter.amount).to.equal(tbtcBefore.amount);
expect(gatewayAfter.amount).to.equal(gatewayBefore.amount);
expect(wrappedTbtcAfter.amount).to.equal(sentAmount);
});

it("receive wrapped tbtc (ata exists)", async () => {
// Set up new wallet
const payer = await generatePayer(authority);

// Use common token account.
const recipient = commonTokenOwner.publicKey;
const recipientToken = getAssociatedTokenAddressSync(
tbtc.getTokenPDA(),
recipient
);
const recipientWrappedToken = await getOrCreateAta(
payer,
WRAPPED_TBTC_MINT,
recipient
);

// Get foreign gateway.
const fromGateway = await wormholeGateway
.getGatewayInfo(2)
.then((info) => info.address);

// Create transfer VAA.
const sentAmount = BigInt(5000);
const signedVaa = await ethereumGatewaySendTbtc(
payer,
ethereumTokenBridge,
sentAmount,
fromGateway,
WORMHOLE_GATEWAY_PROGRAM_ID,
recipient
);

// Set the mint limit to a value smaller than sentAmount.
const newLimit = sentAmount - BigInt(69);
const updateLimitIx = await wormholeGateway.updateMintingLimitIx(
{
authority: authority.publicKey,
},
newLimit
);
await expectIxSuccess([updateLimitIx], [authority]);
await wormholeGateway.checkState(authority.publicKey, newLimit);

// Balance check before receiving wrapped tbtc. If this
// line successfully executes, then the recipient's
// wrapped tbtc account already exists.
const [tbtcBefore, wrappedTbtcBefore, gatewayBefore] = await Promise.all([
getAccount(connection, recipientToken),
getAccount(connection, recipientWrappedToken),
getAccount(connection, gatewayWrappedTbtcToken),
]);

const ix = await wormholeGateway.receiveTbtcIx(
{
payer: payer.publicKey,
recipientToken,
recipient,
},
signedVaa
);
await expectIxSuccess([ix], [payer]);

// Check token accounts after receiving wrapped tbtc.
const [tbtcAfter, wrappedTbtcAfter, gatewayAfter] = await Promise.all([
getAccount(connection, recipientToken),
getAccount(connection, recipientWrappedToken),
getAccount(connection, gatewayWrappedTbtcToken),
]);

// Check balance change.
expect(tbtcAfter.amount).to.equal(tbtcBefore.amount);
expect(gatewayAfter.amount).to.equal(gatewayBefore.amount);
expect(wrappedTbtcAfter.amount).to.equal(wrappedTbtcBefore.amount + sentAmount);
});

it("cannot receive non-tbtc transfers", async () => {
// Set up new wallet
const payer = await generatePayer(authority);

// Use common token account.
const recipient = commonTokenOwner.publicKey;
const recipientToken = getAssociatedTokenAddressSync(
tbtc.getTokenPDA(),
recipient
);

// Get foreign gateway.
const fromGateway = await wormholeGateway
.getGatewayInfo(2)
.then((info) => info.address);

const sentAmount = BigInt(5000);
const signedVaa = await ethereumGatewaySendTbtc(
payer,
ethereumTokenBridge,
sentAmount,
fromGateway,
WORMHOLE_GATEWAY_PROGRAM_ID,
recipient,
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH address
69 // hehe
);

const failingIx = await wormholeGateway.receiveTbtcIx(
{
payer: payer.publicKey,
recipientToken,
recipient,
},
signedVaa
);
await expectIxFail([failingIx], [payer], "InvalidEthereumTbtc");
});

it("cannot receive zero-amount tbtc transfers", async () => {
// Set up new wallet
const payer = await generatePayer(authority);

// Use common token account.
const recipient = commonTokenOwner.publicKey;
const recipientToken = getAssociatedTokenAddressSync(
tbtc.getTokenPDA(),
recipient
);

// Get foreign gateway.
const fromGateway = await wormholeGateway
.getGatewayInfo(2)
.then((info) => info.address);

const sentAmount = BigInt(0);
const signedVaa = await ethereumGatewaySendTbtc(
payer,
ethereumTokenBridge,
sentAmount,
fromGateway,
WORMHOLE_GATEWAY_PROGRAM_ID,
recipient
);

const failingIx = await wormholeGateway.receiveTbtcIx(
{
payer: payer.publicKey,
recipientToken,
recipient,
},
signedVaa
);
await expectIxFail([failingIx], [payer], "NoTbtcTransferred");
});

it("cannot receive tbtc transfer with zero address as recipient", async () => {
// Set up new wallet
const payer = await generatePayer(authority);

// Use common token account. Set the recipient to the zero address.
const recipient = PublicKey.default;
const defaultTokenAccount = await getOrCreateAta(payer, tbtc.getTokenPDA(), recipient);

// Get foreign gateway.
const fromGateway = await wormholeGateway
.getGatewayInfo(2)
.then((info) => info.address);

const sentAmount = BigInt(100);
const signedVaa = await ethereumGatewaySendTbtc(
payer,
ethereumTokenBridge,
sentAmount,
fromGateway,
WORMHOLE_GATEWAY_PROGRAM_ID,
recipient
);

const failingIx = await wormholeGateway.receiveTbtcIx(
{
payer: payer.publicKey,
recipientToken: defaultTokenAccount,
recipient
},
signedVaa
);
await expectIxFail([failingIx], [payer], "RecipientZeroAddress");
});

it("send tbtc to gateway", async () => {
Expand Down
8 changes: 5 additions & 3 deletions cross-chain/solana/tests/helpers/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,15 @@ export async function ethereumGatewaySendTbtc<T extends Idl>(
amount: bigint,
fromGateway: number[],
toGateway: PublicKey,
recipient: PublicKey
recipient: PublicKey,
tokenAddress?: string,
tokenChain?: number
) {
const program = workspace.WormholeGateway as Program<T>;

const published = ethereumTokenBridge.publishTransferTokensWithPayload(
tryNativeToHexString(ETHEREUM_TBTC_ADDRESS, "ethereum"),
2,
tryNativeToHexString(tokenAddress ?? ETHEREUM_TBTC_ADDRESS, "ethereum"),
tokenChain ?? 2,
amount,
1,
toGateway.toBuffer().toString("hex"),
Expand Down

0 comments on commit 64d1dc1

Please sign in to comment.