lp_ui: mvp complete

Change-Id: Ie054ec9963b05e13e2651e78a5c99b31addc2b88
This commit is contained in:
Evan Gray 2021-09-13 11:13:12 -04:00
parent 1230ea6fa3
commit 5698768112
9 changed files with 1450 additions and 99 deletions

1192
lp_ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -22,6 +22,7 @@
"@types/node": "^16.9.1", "@types/node": "^16.9.1",
"@types/react": "^17.0.20", "@types/react": "^17.0.20",
"@types/react-dom": "^17.0.9", "@types/react-dom": "^17.0.9",
"ethers": "^5.4.6",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-scripts": "4.0.3", "react-scripts": "4.0.3",

View File

@ -1,23 +1,13 @@
import { import { Button, Paper, Typography } from "@material-ui/core";
Container,
makeStyles,
Typography,
Paper,
Button,
} from "@material-ui/core";
import { useEffect } from "react"; import { useEffect } from "react";
import SolanaWalletKey from "../components/SolanaWalletKey";
import { useLogger } from "../contexts/Logger"; import { useLogger } from "../contexts/Logger";
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
const useStyles = makeStyles(() => ({}));
function LogWatcher() { function LogWatcher() {
const { logs, clear, log } = useLogger(); const { logs, clear, log } = useLogger();
useEffect(() => { useEffect(() => {
log("Instantiated the logger."); log("Instantiated the logger.");
}, []); }, [log]);
return ( return (
<Paper style={{ padding: "1rem", maxHeight: "600px", overflow: "auto" }}> <Paper style={{ padding: "1rem", maxHeight: "600px", overflow: "auto" }}>

View File

@ -1,4 +1,3 @@
import { ChainId, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
import { Typography } from "@material-ui/core"; import { Typography } from "@material-ui/core";
import { import {
ASSOCIATED_TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID,

View File

@ -23,11 +23,9 @@ export const LoggerProvider = ({ children }: { children: ReactChildren }) => {
const clear = useCallback(() => setLogs([]), [setLogs]); const clear = useCallback(() => setLogs([]), [setLogs]);
const log = useCallback( const log = useCallback(
(value: string) => { (value: string) => {
const newLogs = logs.slice(); setLogs((logs) => [...logs, value]);
newLogs.push(value);
setLogs(newLogs);
}, },
[logs, setLogs] [setLogs]
); );
const contextValue = useMemo( const contextValue = useMemo(

View File

@ -1,43 +1,40 @@
import addLiquidityTx from "@certusone/wormhole-sdk/lib/migration/addLiquidity";
import getAuthorityAddress from "@certusone/wormhole-sdk/lib/migration/authorityAddress";
import claimSharesTx from "@certusone/wormhole-sdk/lib/migration/claimShares";
import createPoolAccount from "@certusone/wormhole-sdk/lib/migration/createPool";
import getFromCustodyAddress from "@certusone/wormhole-sdk/lib/migration/fromCustodyAddress";
import migrateTokensTx from "@certusone/wormhole-sdk/lib/migration/migrateTokens";
import parsePool from "@certusone/wormhole-sdk/lib/migration/parsePool";
import getPoolAddress from "@certusone/wormhole-sdk/lib/migration/poolAddress";
import getShareMintAddress from "@certusone/wormhole-sdk/lib/migration/shareMintAddress";
import getToCustodyAddress from "@certusone/wormhole-sdk/lib/migration/toCustodyAddress";
import { import {
Button,
Container, Container,
Divider,
makeStyles, makeStyles,
Typography,
Paper, Paper,
TextField, TextField,
Button, Typography,
Divider,
} from "@material-ui/core"; } from "@material-ui/core";
//import { pool_address } from "@certusone/wormhole-sdk/lib/solana/migration/wormhole_migration";
import { useCallback, useEffect, useState } from "react";
import LogWatcher from "../components/LogWatcher";
import SolanaWalletKey from "../components/SolanaWalletKey";
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
import TabContext from "@material-ui/lab/TabContext";
import TabList from "@material-ui/lab/TabList";
import TabPanel from "@material-ui/lab/TabPanel";
import { MIGRATION_PROGRAM_ADDRESS, SOLANA_URL } from "../utils/consts";
import { PublicKey, Connection } from "@solana/web3.js";
import { useLogger } from "../contexts/Logger";
import { getMultipleAccounts, signSendAndConfirm } from "../utils/solana";
import getAuthorityAddress from "@certusone/wormhole-sdk/lib/migration/authorityAddress";
import createPoolAccount from "@certusone/wormhole-sdk/lib/migration/createPool";
import getPoolAddress from "@certusone/wormhole-sdk/lib/migration/poolAddress";
import getFromCustodyAddress from "@certusone/wormhole-sdk/lib/migration/fromCustodyAddress";
import getToCustodyAddress from "@certusone/wormhole-sdk/lib/migration/toCustodyAddress";
import getShareMintAddress from "@certusone/wormhole-sdk/lib/migration/shareMintAddress";
import parsePool from "@certusone/wormhole-sdk/lib/migration/parsePool";
import addLiquidityTx from "@certusone/wormhole-sdk/lib/migration/addLiquidity";
import claimSharesTx from "@certusone/wormhole-sdk/lib/migration/claimShares";
import migrateTokensTx from "@certusone/wormhole-sdk/lib/migration/migrateTokens";
import SolanaCreateAssociatedAddress, {
useAssociatedAccountExistsState,
} from "../components/SolanaCreateAssociatedAddress";
import { import {
ASSOCIATED_TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID,
Token, Token,
TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID,
} from "@solana/spl-token"; } from "@solana/spl-token";
import { Connection, PublicKey } from "@solana/web3.js";
//import { pool_address } from "@certusone/wormhole-sdk/lib/solana/migration/wormhole_migration";
import { parseUnits } from "ethers/lib/utils";
import { useCallback, useEffect, useMemo, useState } from "react";
import LogWatcher from "../components/LogWatcher";
import SolanaCreateAssociatedAddress, {
useAssociatedAccountExistsState,
} from "../components/SolanaCreateAssociatedAddress";
import SolanaWalletKey from "../components/SolanaWalletKey";
import { useLogger } from "../contexts/Logger";
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
import { MIGRATION_PROGRAM_ADDRESS, SOLANA_URL } from "../utils/consts";
import { getMultipleAccounts, signSendAndConfirm } from "../utils/solana";
const useStyles = makeStyles(() => ({ const useStyles = makeStyles(() => ({
rootContainer: {}, rootContainer: {},
@ -52,21 +49,68 @@ const useStyles = makeStyles(() => ({
}, },
})); }));
const getDecimals = async (
connection: Connection,
mint: string,
setter: (decimals: number | undefined) => void,
log: (value: string) => void
) => {
setter(undefined);
if (mint) {
try {
const pk = new PublicKey(mint);
const info = await connection.getParsedAccountInfo(pk);
// @ts-ignore
const decimals = info.value?.data.parsed.info.decimals;
log(`${mint} has ${decimals} decimals`);
setter(decimals);
} catch (e) {
log(`Unable to determine decimals of ${mint}`);
}
}
};
const getBalance = async (
connection: Connection,
address: string | undefined,
setter: (balance: string | undefined) => void,
log: (value: string) => void
) => {
setter(undefined);
if (address) {
try {
const pk = new PublicKey(address);
const info = await connection.getParsedAccountInfo(pk);
// @ts-ignore
const balance = info.value?.data.parsed.info.tokenAmount.uiAmountString;
log(`${address} has a balance of ${balance}`);
setter(balance);
} catch (e) {
log(`Unable to determine balance of ${address}`);
}
}
};
function Main() { function Main() {
const classes = useStyles(); const classes = useStyles();
const wallet = useSolanaWallet(); const wallet = useSolanaWallet();
const logger = useLogger(); const { log } = useLogger();
const connection = new Connection(SOLANA_URL, "finalized"); const connection = useMemo(() => new Connection(SOLANA_URL, "confirmed"), []);
const [fromMint, setFromMint] = useState(""); const [fromMint, setFromMint] = useState("");
const [fromMintDecimals, setFromMintDecimals] = useState<number | undefined>(
undefined
);
const [toMint, setToMint] = useState(""); const [toMint, setToMint] = useState("");
const [toMintDecimals, setToMintDecimals] = useState<any>(undefined);
const [shareMintAddress, setShareMintAddress] = useState<string | undefined>( const [shareMintAddress, setShareMintAddress] = useState<string | undefined>(
undefined undefined
); );
const [shareMintDecimals, setShareMintDecimals] = useState<any>(undefined);
const [poolAddress, setPoolAddress] = useState(""); const [poolAddress, setPoolAddress] = useState("");
const [poolExists, setPoolExists] = useState<boolean | undefined>(undefined); const [poolExists, setPoolExists] = useState<boolean | undefined>(undefined);
const [poolAccountInfo, setPoolAccountInfo] = useState(undefined); const [poolAccountInfo, setPoolAccountInfo] = useState<any>(undefined);
const [parsedPoolData, setParsedPoolData] = useState(undefined); const [parsedPoolData, setParsedPoolData] = useState(undefined);
//These are the user's personal token accounts corresponding to the mints for the connected wallet //These are the user's personal token accounts corresponding to the mints for the connected wallet
@ -101,9 +145,15 @@ function Main() {
const [fromCustodyAddress, setFromCustodyAddress] = useState< const [fromCustodyAddress, setFromCustodyAddress] = useState<
string | undefined string | undefined
>(undefined); >(undefined);
const [fromCustodyBalance, setFromCustodyBalance] = useState<
string | undefined
>(undefined);
const [toCustodyAddress, setToCustodyAddress] = useState<string | undefined>( const [toCustodyAddress, setToCustodyAddress] = useState<string | undefined>(
undefined undefined
); );
const [toCustodyBalance, setToCustodyBalance] = useState<string | undefined>(
undefined
);
const [toggleAllData, setToggleAllData] = useState(false); const [toggleAllData, setToggleAllData] = useState(false);
@ -117,6 +167,46 @@ function Main() {
These are generally data fetchers which fire when requisite data populates. These are generally data fetchers which fire when requisite data populates.
*/ */
//Retrieve from mint information when fromMint changes
useEffect(() => {
getDecimals(connection, fromMint, setFromMintDecimals, log);
}, [connection, fromMint, log]);
//Retrieve to mint information when fromMint changes
useEffect(() => {
getDecimals(connection, toMint, setToMintDecimals, log);
}, [connection, toMint, log]);
//Retrieve to mint information when shareMint changes
useEffect(() => {
// TODO: cancellable
if (shareMintAddress) {
getDecimals(connection, shareMintAddress, setShareMintDecimals, log);
} else {
setShareMintDecimals(undefined);
}
}, [connection, shareMintAddress, log]);
//Retrieve from custody balance when fromCustodyAccount changes
useEffect(() => {
// TODO: cancellable
if (fromCustodyAddress) {
getBalance(connection, fromCustodyAddress, setFromCustodyBalance, log);
} else {
setFromCustodyBalance(undefined);
}
}, [connection, fromCustodyAddress, log]);
//Retrieve from custody balance when toCustodyAccount changes
useEffect(() => {
// TODO: cancellable
if (toCustodyAddress) {
getBalance(connection, toCustodyAddress, setToCustodyBalance, log);
} else {
setFromCustodyBalance(undefined);
}
}, [connection, toCustodyAddress, log]);
//Retrieve pool address on selectedTokens change //Retrieve pool address on selectedTokens change
useEffect(() => { useEffect(() => {
if (toMint && fromMint) { if (toMint && fromMint) {
@ -124,18 +214,17 @@ function Main() {
getPoolAddress(MIGRATION_PROGRAM_ADDRESS, fromMint, toMint).then( getPoolAddress(MIGRATION_PROGRAM_ADDRESS, fromMint, toMint).then(
(result) => { (result) => {
const key = new PublicKey(result).toString(); const key = new PublicKey(result).toString();
logger.log("Calculated the pool address at: " + key); log("Calculated the pool address at: " + key);
setPoolAddress(key); setPoolAddress(key);
}, },
(error) => logger.log("ERROR, could not calculate pool address.") (error) => log("ERROR, could not calculate pool address.")
); );
} }
}, [toMint, fromMint, setPoolAddress]); }, [log, toMint, fromMint, setPoolAddress]);
//Retrieve the poolAccount every time the pool address changes. //Retrieve the poolAccount every time the pool address changes.
useEffect(() => { useEffect(() => {
if (poolAddress) { if (poolAddress && poolAccountInfo === undefined) {
setPoolAccountInfo(undefined);
setPoolExists(undefined); setPoolExists(undefined);
try { try {
getMultipleAccounts( getMultipleAccounts(
@ -148,26 +237,27 @@ function Main() {
parsePool(result[0].data).then( parsePool(result[0].data).then(
(parsed) => setParsedPoolData(parsed), (parsed) => setParsedPoolData(parsed),
(error) => { (error) => {
logger.log("Failed to parse the pool data."); log("Failed to parse the pool data.");
console.error(error); console.error(error);
} }
); );
setPoolExists(true); setPoolExists(true);
logger.log("Successfully found account info for the pool."); log("Successfully found account info for the pool.");
} else if (result.length && result[0] === null) { } else if (result.length && result[0] === null) {
logger.log("Confirmed that the pool does not exist."); log("Confirmed that the pool does not exist.");
setPoolExists(false); setPoolExists(false);
setPoolAccountInfo(null);
} else { } else {
logger.log( log(
"unexpected error in fetching pool address. Please reload and try again" "unexpected error in fetching pool address. Please reload and try again"
); );
} }
}); });
} catch (e) { } catch (e) {
logger.log("Could not fetch pool address"); log("Could not fetch pool address");
} }
} }
}, [poolAddress]); }, [connection, log, poolAddress, poolAccountInfo]);
//Set all the addresses which derive from poolAddress //Set all the addresses which derive from poolAddress
useEffect(() => { useEffect(() => {
@ -245,7 +335,7 @@ function Main() {
and then potentially update something on the state. and then potentially update something on the state.
*/ */
const createPool = async () => { const createPool = useCallback(async () => {
console.log( console.log(
"createPool with these args", "createPool with these args",
connection, connection,
@ -268,20 +358,20 @@ function Main() {
(transaction: any) => { (transaction: any) => {
setPoolExists(undefined); //Set these to null to force a fetch on them setPoolExists(undefined); //Set these to null to force a fetch on them
setPoolAccountInfo(undefined); setPoolAccountInfo(undefined);
logger.log("Successfully created the pool."); log("Successfully created the pool.");
}, },
(error) => { (error) => {
logger.log("Could not create the pool"); log("Could not create the pool");
console.error(error); console.error(error);
} }
); );
} catch (e) { } catch (e) {
logger.log("Failed to create the pool."); log("Failed to create the pool.");
console.error(e); console.error(e);
} }
}; }, [connection, fromMint, toMint, wallet, log]);
const addLiquidity = async () => { const addLiquidity = useCallback(async () => {
try { try {
const instruction = await addLiquidityTx( const instruction = await addLiquidityTx(
connection, connection,
@ -291,27 +381,43 @@ function Main() {
toMint, toMint,
toTokenAccount || "", toTokenAccount || "",
shareTokenAccount || "", shareTokenAccount || "",
BigInt(liquidityAmount) parseUnits(liquidityAmount, toMintDecimals).toBigInt()
); );
signSendAndConfirm(wallet, connection, instruction).then( signSendAndConfirm(wallet, connection, instruction).then(
(transaction: any) => { (transaction: any) => {
setPoolExists(undefined); //Set these to null to force a fetch on them log("Successfully added liquidity to the pool.");
setPoolAccountInfo(undefined); getBalance(
logger.log("Successfully added liquidity to the pool."); connection,
fromCustodyAddress,
setFromCustodyBalance,
log
);
getBalance(connection, toCustodyAddress, setToCustodyBalance, log);
}, },
(error) => { (error) => {
logger.log("Could not complete the addLiquidity transaction"); log("Could not complete the addLiquidity transaction");
console.error(error); console.error(error);
} }
); );
} catch (e) { } catch (e) {
logger.log("Could not complete the addLiquidity transaction"); log("Could not complete the addLiquidity transaction");
console.error(e); console.error(e);
} }
}; }, [
connection,
fromMint,
liquidityAmount,
shareTokenAccount,
toMint,
toTokenAccount,
wallet,
log,
toMintDecimals,
fromCustodyAddress,
toCustodyAddress,
]);
const migrateTokens = async () => { const migrateTokens = useCallback(async () => {
try { try {
const instruction = await migrateTokensTx( const instruction = await migrateTokensTx(
connection, connection,
@ -321,27 +427,44 @@ function Main() {
toMint, toMint,
fromTokenAccount || "", fromTokenAccount || "",
toTokenAccount || "", toTokenAccount || "",
BigInt(migrationAmount) parseUnits(migrationAmount, fromMintDecimals).toBigInt()
); );
signSendAndConfirm(wallet, connection, instruction).then( signSendAndConfirm(wallet, connection, instruction).then(
(transaction: any) => { (transaction: any) => {
setPoolExists(undefined); //Set these to null to force a fetch on them log("Successfully migrated the tokens.");
setPoolAccountInfo(undefined); getBalance(
logger.log("Successfully migrated the tokens."); connection,
fromCustodyAddress,
setFromCustodyBalance,
log
);
getBalance(connection, toCustodyAddress, setToCustodyBalance, log);
}, },
(error) => { (error) => {
logger.log("Could not complete the migrateTokens transaction."); log("Could not complete the migrateTokens transaction.");
console.error(error); console.error(error);
} }
); );
} catch (e) { } catch (e) {
logger.log("Could not complete the migrateTokens transaction."); log("Could not complete the migrateTokens transaction.");
console.error(e); console.error(e);
} }
}; }, [
connection,
fromMint,
fromTokenAccount,
log,
migrationAmount,
toMint,
toTokenAccount,
wallet,
fromMintDecimals,
fromCustodyAddress,
toCustodyAddress,
]);
const redeemShares = async () => { const redeemShares = useCallback(async () => {
try { try {
const instruction = await claimSharesTx( const instruction = await claimSharesTx(
connection, connection,
@ -349,27 +472,44 @@ function Main() {
MIGRATION_PROGRAM_ADDRESS, MIGRATION_PROGRAM_ADDRESS,
fromMint, fromMint,
toMint, toMint,
toTokenAccount || "", fromTokenAccount || "",
shareTokenAccount || "", shareTokenAccount || "",
BigInt(redeemAmount) parseUnits(redeemAmount, shareMintDecimals).toBigInt()
); );
signSendAndConfirm(wallet, connection, instruction).then( signSendAndConfirm(wallet, connection, instruction).then(
(transaction: any) => { (transaction: any) => {
setPoolExists(undefined); //Set these to null to force a fetch on them log("Successfully redeemed the shares.");
setPoolAccountInfo(undefined); getBalance(
logger.log("Successfully redeemed the shares."); connection,
fromCustodyAddress,
setFromCustodyBalance,
log
);
getBalance(connection, toCustodyAddress, setToCustodyBalance, log);
}, },
(error) => { (error) => {
logger.log("Could not complete the claimShares transaction."); log("Could not complete the claimShares transaction.");
console.error(error); console.error(error);
} }
); );
} catch (e) { } catch (e) {
logger.log("Could not complete the claimShares transaction."); log("Could not complete the claimShares transaction.");
console.error(e); console.error(e);
} }
}; }, [
connection,
fromMint,
log,
redeemAmount,
shareTokenAccount,
toMint,
fromTokenAccount,
wallet,
shareMintDecimals,
fromCustodyAddress,
toCustodyAddress,
]);
/* /*
End actions! End actions!
*/ */
@ -441,7 +581,7 @@ function Main() {
value={migrationAmount} value={migrationAmount}
type="number" type="number"
onChange={(event) => setMigrationAmount(event.target.value)} onChange={(event) => setMigrationAmount(event.target.value)}
label={"Amount to add"} label={"Amount to migrate"}
></TextField> ></TextField>
<Button variant="contained" onClick={migrateTokens}> <Button variant="contained" onClick={migrateTokens}>
Migrate Tokens Migrate Tokens
@ -460,7 +600,7 @@ function Main() {
type="number" type="number"
value={redeemAmount} value={redeemAmount}
onChange={(event) => setRedeemAmount(event.target.value)} onChange={(event) => setRedeemAmount(event.target.value)}
label={"Amount to add"} label={"Amount to redeem"}
></TextField> ></TextField>
<Button variant="contained" onClick={redeemShares}> <Button variant="contained" onClick={redeemShares}>
Redeem Shares Redeem Shares
@ -537,6 +677,10 @@ function Main() {
<Divider className={classes.divider} /> <Divider className={classes.divider} />
{poolInfo} {poolInfo}
{createPoolButton} {createPoolButton}
<Typography>'From' Balance In Pool</Typography>
<Typography>{fromCustodyBalance}</Typography>
<Typography>'To' Balance In Pool</Typography>
<Typography>{toCustodyBalance}</Typography>
<Divider className={classes.divider} /> <Divider className={classes.divider} />
{relevantTokenAccounts} {relevantTokenAccounts}
<Divider className={classes.divider} /> <Divider className={classes.divider} />

View File

@ -1,3 +1,4 @@
import { Token, TOKEN_PROGRAM_ID } from "@solana/spl-token";
import { Connection, PublicKey, Transaction } from "@solana/web3.js"; import { Connection, PublicKey, Transaction } from "@solana/web3.js";
import { ixFromRust } from "../solana"; import { ixFromRust } from "../solana";
@ -11,9 +12,17 @@ export default async function addLiquidity(
lp_share_token_account: string, lp_share_token_account: string,
amount: BigInt amount: BigInt
) { ) {
const { add_liquidity } = await import( const { authority_address, add_liquidity } = await import(
"../solana/migration/wormhole_migration" "../solana/migration/wormhole_migration"
); );
const approvalIx = Token.createApproveInstruction(
TOKEN_PROGRAM_ID,
new PublicKey(liquidity_token_account),
new PublicKey(authority_address(program_id)),
new PublicKey(payerAddress),
[],
Number(amount)
);
const ix = ixFromRust( const ix = ixFromRust(
add_liquidity( add_liquidity(
program_id, program_id,
@ -24,7 +33,7 @@ export default async function addLiquidity(
amount amount
) )
); );
const transaction = new Transaction().add(ix); const transaction = new Transaction().add(approvalIx, ix);
const { blockhash } = await connection.getRecentBlockhash(); const { blockhash } = await connection.getRecentBlockhash();
transaction.recentBlockhash = blockhash; transaction.recentBlockhash = blockhash;
transaction.feePayer = new PublicKey(payerAddress); transaction.feePayer = new PublicKey(payerAddress);

View File

@ -1,3 +1,4 @@
import { Token, TOKEN_PROGRAM_ID } from "@solana/spl-token";
import { Connection, PublicKey, Transaction } from "@solana/web3.js"; import { Connection, PublicKey, Transaction } from "@solana/web3.js";
import { ixFromRust } from "../solana"; import { ixFromRust } from "../solana";
@ -11,9 +12,17 @@ export default async function claimShares(
lp_share_token_account: string, lp_share_token_account: string,
amount: BigInt amount: BigInt
) { ) {
const { claim_shares } = await import( const { authority_address, claim_shares } = await import(
"../solana/migration/wormhole_migration" "../solana/migration/wormhole_migration"
); );
const approvalIx = Token.createApproveInstruction(
TOKEN_PROGRAM_ID,
new PublicKey(lp_share_token_account),
new PublicKey(authority_address(program_id)),
new PublicKey(payerAddress),
[],
Number(amount)
);
const ix = ixFromRust( const ix = ixFromRust(
claim_shares( claim_shares(
program_id, program_id,
@ -24,7 +33,7 @@ export default async function claimShares(
amount amount
) )
); );
const transaction = new Transaction().add(ix); const transaction = new Transaction().add(approvalIx, ix);
const { blockhash } = await connection.getRecentBlockhash(); const { blockhash } = await connection.getRecentBlockhash();
transaction.recentBlockhash = blockhash; transaction.recentBlockhash = blockhash;
transaction.feePayer = new PublicKey(payerAddress); transaction.feePayer = new PublicKey(payerAddress);

View File

@ -1,3 +1,4 @@
import { Token, TOKEN_PROGRAM_ID } from "@solana/spl-token";
import { Connection, PublicKey, Transaction } from "@solana/web3.js"; import { Connection, PublicKey, Transaction } from "@solana/web3.js";
import { ixFromRust } from "../solana"; import { ixFromRust } from "../solana";
@ -11,9 +12,17 @@ export default async function migrateTokens(
output_token_account: string, output_token_account: string,
amount: BigInt amount: BigInt
) { ) {
const { migrate_tokens } = await import( const { authority_address, migrate_tokens } = await import(
"../solana/migration/wormhole_migration" "../solana/migration/wormhole_migration"
); );
const approvalIx = Token.createApproveInstruction(
TOKEN_PROGRAM_ID,
new PublicKey(input_token_account),
new PublicKey(authority_address(program_id)),
new PublicKey(payerAddress),
[],
Number(amount)
);
const ix = ixFromRust( const ix = ixFromRust(
migrate_tokens( migrate_tokens(
program_id, program_id,
@ -24,7 +33,7 @@ export default async function migrateTokens(
amount amount
) )
); );
const transaction = new Transaction().add(ix); const transaction = new Transaction().add(approvalIx, ix);
const { blockhash } = await connection.getRecentBlockhash(); const { blockhash } = await connection.getRecentBlockhash();
transaction.recentBlockhash = blockhash; transaction.recentBlockhash = blockhash;
transaction.feePayer = new PublicKey(payerAddress); transaction.feePayer = new PublicKey(payerAddress);