diff --git a/solana/.gitignore b/solana/.gitignore index 562f4e351..f28f724f0 100644 --- a/solana/.gitignore +++ b/solana/.gitignore @@ -9,3 +9,5 @@ snapshots /artifacts package-lock.json yarn.lock +/app/e2e/log/*.json +app/keypairs/deployer.json diff --git a/solana/Anchor.toml b/solana/Anchor.toml index 9cd77a2f2..4d26d2106 100644 --- a/solana/Anchor.toml +++ b/solana/Anchor.toml @@ -7,10 +7,10 @@ seeds = true members = ["programs/staking"] [programs.localnet] -staking = "DgCSKsLDXXufYeEkvf21YSX5DMnFK89xans5WdSsUbeY" +staking = "AFuHPdrQGsW8rNQ4oEFF35sm5fg36gwrxyqjkjKvi6ap" [programs.devnet] -staking = "DgCSKsLDXXufYeEkvf21YSX5DMnFK89xans5WdSsUbeY" +staking = "AFuHPdrQGsW8rNQ4oEFF35sm5fg36gwrxyqjkjKvi6ap" external_program = "eLUV8cwhgUC2Bcu4UA16uhuMwK8zPkx3XSzt4hd3JJ3" [registry] diff --git a/solana/app/StakeConnection.ts b/solana/app/StakeConnection.ts index a7047f9b1..badd6da26 100644 --- a/solana/app/StakeConnection.ts +++ b/solana/app/StakeConnection.ts @@ -25,7 +25,7 @@ import BN from "bn.js"; import { Staking } from "../target/types/staking"; import IDL from "../target/idl/staking.json"; import { WHTokenBalance } from "./whTokenBalance"; -import { CORE_BRIDGE_ADDRESS, STAKING_ADDRESS } from "./constants"; +import { contracts } from "@wormhole-foundation/sdk-base"; import { PriorityFeeConfig, sendTransactions, @@ -89,11 +89,7 @@ export class StakeConnection { connection: Connection, wallet: Wallet, ): Promise { - return await StakeConnection.createStakeConnection( - connection, - wallet, - STAKING_ADDRESS, - ); + return await StakeConnection.createStakeConnection(connection, wallet); } /** Creates a program connection and loads the staking config @@ -102,7 +98,6 @@ export class StakeConnection { public static async createStakeConnection( connection: Connection, wallet: Wallet, - stakingProgramAddress: PublicKey, addressLookupTable?: PublicKey, priorityFeeConfig?: PriorityFeeConfig, ): Promise { @@ -386,7 +381,6 @@ export class StakeConnection { /** Creates a new stake account and sends the transaction for confirmation. */ public async createStakeAccount(): Promise { const instructions: TransactionInstruction[] = []; - instructions.push( await this.program.methods .createStakeAccount() @@ -395,6 +389,7 @@ export class StakeConnection { }) .instruction(), ); + await this.sendAndConfirmAsVersionedTransaction(instructions); } @@ -754,7 +749,12 @@ export class StakeConnection { unoptimized?: boolean, ): Promise { const { proposalAccount } = await this.fetchProposalAccount(proposalId); - + const networkType = this.provider.connection.rpcEndpoint.includes("mainnet") + ? "Mainnet" + : "Testnet"; + const coreBridge = new PublicKey( + contracts.coreBridge.get(networkType, "Solana"), // Testnet - 3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5 + ); const methodsBuilder = this.program.methods .addProposal( Buffer.from(ethProposalResponseBytes), @@ -764,10 +764,7 @@ export class StakeConnection { .accountsPartial({ proposal: proposalAccount, guardianSignatures: guardianSignatures, - guardianSet: deriveGuardianSetKey( - CORE_BRIDGE_ADDRESS, - guardianSetIndex, - ), + guardianSet: deriveGuardianSetKey(coreBridge, guardianSetIndex), }); if (unoptimized) { diff --git a/solana/app/deploy/devnet.ts b/solana/app/deploy/devnet.ts deleted file mode 100644 index 6a6d4f8f3..000000000 --- a/solana/app/deploy/devnet.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { PublicKey } from "@solana/web3.js"; -import { homedir } from "os"; -import { loadKeypair } from "../../tests/utils/keys"; - -export const DEPLOYER_AUTHORITY_PATH = "/.config/solana/deployer.json"; -export const DEPLOYER_AUTHORITY_KEYPAIR = loadKeypair( - homedir() + DEPLOYER_AUTHORITY_PATH, -); - -export const USER_AUTHORITY_PATH = "/.config/solana/user.json"; -export const USER_AUTHORITY_KEYPAIR = loadKeypair( - homedir() + USER_AUTHORITY_PATH, -); - -export const USER2_AUTHORITY_PATH = "/.config/solana/user2.json"; -export const USER2_AUTHORITY_KEYPAIR = loadKeypair( - homedir() + USER2_AUTHORITY_PATH, -); - -export const WORMHOLE_TOKEN = new PublicKey( - "Exne2kdeGToBnC2WVSdt1gLy6fjnNftbPtsCPx8AuL7V", -); - -export const RPC_NODE = "https://api.devnet.solana.com"; - -export const AIRLOCK_PDA_ADDRESS = new PublicKey( - "2ejzW2eFPedskg1KcrjcFs9g1JorRVcMes1TBPpGbhdy", -); diff --git a/solana/app/constants.ts b/solana/app/deploy/devnet/constants.ts similarity index 52% rename from solana/app/constants.ts rename to solana/app/deploy/devnet/constants.ts index 5b21423b9..74098f3f0 100644 --- a/solana/app/constants.ts +++ b/solana/app/deploy/devnet/constants.ts @@ -1,11 +1,21 @@ import { PublicKey } from "@solana/web3.js"; +import { homedir } from "os"; +import { loadKeypair } from "../../../tests/utils/keys"; +import { contracts } from "@wormhole-foundation/sdk-base"; + +export const RPC_NODE = "https://api.devnet.solana.com"; + +/// Wormhole Token (W) (devnet solana address) +export const WORMHOLE_TOKEN = new PublicKey( + "Exne2kdeGToBnC2WVSdt1gLy6fjnNftbPtsCPx8AuL7V", +); export const STAKING_ADDRESS = new PublicKey( - "DgCSKsLDXXufYeEkvf21YSX5DMnFK89xans5WdSsUbeY", + "AFuHPdrQGsW8rNQ4oEFF35sm5fg36gwrxyqjkjKvi6ap", ); -export const CORE_BRIDGE_ADDRESS = new PublicKey( - "3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5", +export const CORE_BRIDGE_PID = new PublicKey( + contracts.coreBridge.get("Testnet", "Solana")!, // 3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5 ); /// Wormhole Hub Proposal Metadata Contract (sepolia ethereum address) @@ -19,11 +29,13 @@ export const hubProposalMetadataUint8Array = new Uint8Array( ); /// Wormhole Hub Chain ID +// https://wormhole.com/docs/build/reference/chain-ids/#__tabbed_1_2 export const HUB_CHAIN_ID = 10002; // SEPOLIA export const CHECKPOINTS_ACCOUNT_LIMIT = 654998; export const TEST_CHECKPOINTS_ACCOUNT_LIMIT = 15; +/// Wormhole hubSolanaMessageDispatcher Contract (sepolia ethereum address) export const HUB_SOLANA_MESSAGE_DISPATCHER_ADDRESS = "0xaeb78fb7ddedbbcab908e91e94f1fb04a23fbce5"; const hubSolanaMessageDispatcherHex20 = @@ -53,3 +65,35 @@ export const HUB_SOLANA_SPOKE_VOTE_DECODER_ADDRESS = /// Wormhole HubVotePool Contract (sepolia ethereum address) export const HUB_VOTE_POOL_ADDRESS = "0x1004c781763c70f5f11aa64b0e5b34e1442a3c02"; + +export const DEPLOYER_AUTHORITY_PATH = "/.config/solana/deployer.json"; +export const DEPLOYER_AUTHORITY_KEYPAIR = loadKeypair( + homedir() + DEPLOYER_AUTHORITY_PATH, +); + +export const GOVERNANCE_AUTHORITY_PATH = + "/.config/solana/governanceAuthority.json"; +export const GOVERNANCE_AUTHORITY_KEYPAIR = loadKeypair( + homedir() + GOVERNANCE_AUTHORITY_PATH, +); + +export const VESTING_ADMIN_PATH = "/.config/solana/vestingAdmin.json"; +export const VESTING_ADMIN_KEYPAIR = loadKeypair( + homedir() + VESTING_ADMIN_PATH, +); + +export const USER_AUTHORITY_PATH = "/.config/solana/user.json"; +export const USER_AUTHORITY_KEYPAIR = loadKeypair( + homedir() + USER_AUTHORITY_PATH, +); + +export const USER2_AUTHORITY_PATH = "/.config/solana/user2.json"; +export const USER2_AUTHORITY_KEYPAIR = loadKeypair( + homedir() + USER2_AUTHORITY_PATH, +); + +export const AIRLOCK_PDA_ADDRESS = new PublicKey( + "2ejzW2eFPedskg1KcrjcFs9g1JorRVcMes1TBPpGbhdy", +); + +export const VOTE_WEIGHT_WINDOW_LENGTHS = 10 * 60; // 10 minutes diff --git a/solana/app/deploy/02_create_account_lookup_table.ts b/solana/app/deploy/devnet/initialize/initAddressLookupTable.ts similarity index 65% rename from solana/app/deploy/02_create_account_lookup_table.ts rename to solana/app/deploy/devnet/initialize/initAddressLookupTable.ts index 3f2603e03..4af71f9df 100644 --- a/solana/app/deploy/02_create_account_lookup_table.ts +++ b/solana/app/deploy/devnet/initialize/initAddressLookupTable.ts @@ -1,9 +1,14 @@ -// Usage: npx ts-node app/deploy/02_create_account_lookup_table.ts +// Usage: npx ts-node app/deploy/devnet/initialize/initAddressLookupTable.ts import { Wallet, AnchorProvider } from "@coral-xyz/anchor"; import { Connection } from "@solana/web3.js"; -import { DEPLOYER_AUTHORITY_KEYPAIR, WORMHOLE_TOKEN, RPC_NODE } from "./devnet"; -import { initAddressLookupTable } from "../../tests/utils/utils"; +import { + DEPLOYER_AUTHORITY_KEYPAIR, + RPC_NODE, + STAKING_ADDRESS, + WORMHOLE_TOKEN, +} from "../constants"; +import { initAddressLookupTable } from "../../../helpers/utils/lookup_table"; async function main() { try { @@ -17,6 +22,7 @@ async function main() { const lookupTableAddress = await initAddressLookupTable( provider, WORMHOLE_TOKEN, + STAKING_ADDRESS, ); console.log("Lookup table address: ", lookupTableAddress.toBase58()); diff --git a/solana/app/deploy/01_init_staking.ts b/solana/app/deploy/devnet/initialize/initConfig.ts similarity index 66% rename from solana/app/deploy/01_init_staking.ts rename to solana/app/deploy/devnet/initialize/initConfig.ts index 68925f762..e1c640442 100644 --- a/solana/app/deploy/01_init_staking.ts +++ b/solana/app/deploy/devnet/initialize/initConfig.ts @@ -1,16 +1,19 @@ -// Usage: npx ts-node app/deploy/01_init_staking.ts +// Usage: npx ts-node app/deploy/devnet/initialize/initConfig.ts import { Wallet, AnchorProvider, Program } from "@coral-xyz/anchor"; import { Connection } from "@solana/web3.js"; -import { DEPLOYER_AUTHORITY_KEYPAIR, WORMHOLE_TOKEN, RPC_NODE } from "./devnet"; -import { CHECKPOINTS_ACCOUNT_LIMIT, STAKING_ADDRESS } from "../constants"; -import { Staking } from "../../target/types/staking"; -import fs from "fs"; +import { + CHECKPOINTS_ACCOUNT_LIMIT, + STAKING_ADDRESS, + DEPLOYER_AUTHORITY_KEYPAIR, + WORMHOLE_TOKEN, + RPC_NODE, +} from "../constants"; async function main() { - const client = new Connection(RPC_NODE); + const connection = new Connection(RPC_NODE); const provider = new AnchorProvider( - client, + connection, new Wallet(DEPLOYER_AUTHORITY_KEYPAIR), {}, ); diff --git a/solana/app/deploy/03_create_airlock.ts b/solana/app/deploy/devnet/initialize/initializeSpokeAirlock.ts similarity index 82% rename from solana/app/deploy/03_create_airlock.ts rename to solana/app/deploy/devnet/initialize/initializeSpokeAirlock.ts index dd53e0752..efb3d2286 100644 --- a/solana/app/deploy/03_create_airlock.ts +++ b/solana/app/deploy/devnet/initialize/initializeSpokeAirlock.ts @@ -1,10 +1,9 @@ -// Usage: npx ts-node app/deploy/03_create_airlock.ts +// Usage: npx ts-node app/deploy/devnet/initialize/initializeSpokeAirlock.ts -import * as anchor from "@coral-xyz/anchor"; import { AnchorProvider, Program, Wallet } from "@coral-xyz/anchor"; import { Connection, PublicKey, SystemProgram } from "@solana/web3.js"; -import { DEPLOYER_AUTHORITY_KEYPAIR, RPC_NODE } from "./devnet"; -import { Staking } from "../../target/types/staking"; +import { DEPLOYER_AUTHORITY_KEYPAIR, RPC_NODE } from "../constants"; +import { Staking } from "../../../../target/types/staking"; import fs from "fs"; async function main() { diff --git a/solana/app/deploy/06_create_message_executor.ts b/solana/app/deploy/devnet/initialize/initializeSpokeMessageExecutor.ts similarity index 80% rename from solana/app/deploy/06_create_message_executor.ts rename to solana/app/deploy/devnet/initialize/initializeSpokeMessageExecutor.ts index b5f970cf8..a109a9edd 100644 --- a/solana/app/deploy/06_create_message_executor.ts +++ b/solana/app/deploy/devnet/initialize/initializeSpokeMessageExecutor.ts @@ -1,20 +1,19 @@ -// Usage: npx ts-node app/deploy/06_create_message_executor.ts +// Usage: npx ts-node app/deploy/devnet/initialize/initializeSpokeMessageExecutor.ts -import * as anchor from "@coral-xyz/anchor"; import { AnchorProvider, Program, Wallet } from "@coral-xyz/anchor"; import { Connection, PublicKey, SystemProgram } from "@solana/web3.js"; import { + DEPLOYER_AUTHORITY_KEYPAIR, hubSolanaMessageDispatcherPublicKey, HUB_CHAIN_ID, + RPC_NODE, } from "../constants"; -import { DEPLOYER_AUTHORITY_KEYPAIR, RPC_NODE } from "./devnet"; -import { Staking } from "../../target/types/staking"; +import { Staking } from "../../../../target/types/staking"; import fs from "fs"; -import { wasm } from "../StakeConnection"; +import { wasm } from "../../../StakeConnection"; async function main() { try { - const DEBUG = true; const connection = new Connection(RPC_NODE); const provider = new AnchorProvider( connection, @@ -47,7 +46,7 @@ async function main() { hubDispatcher: hubSolanaMessageDispatcherPublicKey, systemProgram: SystemProgram.programId, }) - .rpc({ skipPreflight: DEBUG }); + .rpc(); } catch (err) { console.error("Error:", err); } diff --git a/solana/app/deploy/04_create_spoke_metadata_collector.ts b/solana/app/deploy/devnet/initialize/initializeSpokeMetadataCollector.ts similarity index 68% rename from solana/app/deploy/04_create_spoke_metadata_collector.ts rename to solana/app/deploy/devnet/initialize/initializeSpokeMetadataCollector.ts index c6dae61a7..123c43d74 100644 --- a/solana/app/deploy/04_create_spoke_metadata_collector.ts +++ b/solana/app/deploy/devnet/initialize/initializeSpokeMetadataCollector.ts @@ -1,16 +1,18 @@ -// Usage: npx ts-node app/deploy/04_create_spoke_metadata_collector.ts +// Usage: npx ts-node app/deploy/devnet/initialize/initializeSpokeMetadataCollector.ts -import * as anchor from "@coral-xyz/anchor"; import { AnchorProvider, Program, Wallet } from "@coral-xyz/anchor"; import { Connection } from "@solana/web3.js"; -import { HUB_CHAIN_ID, hubProposalMetadataUint8Array } from "../constants"; -import { DEPLOYER_AUTHORITY_KEYPAIR, RPC_NODE } from "./devnet"; -import { Staking } from "../../target/types/staking"; +import { + DEPLOYER_AUTHORITY_KEYPAIR, + HUB_CHAIN_ID, + hubProposalMetadataUint8Array, + RPC_NODE, +} from "../constants"; +import { Staking } from "../../../../target/types/staking"; import fs from "fs"; async function main() { try { - const DEBUG = true; const connection = new Connection(RPC_NODE); const provider = new AnchorProvider( connection, diff --git a/solana/app/deploy/05_initializeVoteWeightWindowLengths.ts b/solana/app/deploy/devnet/initialize/initializeVoteWeightWindowLengths.ts similarity index 61% rename from solana/app/deploy/05_initializeVoteWeightWindowLengths.ts rename to solana/app/deploy/devnet/initialize/initializeVoteWeightWindowLengths.ts index cc7c1f281..cf7f3033a 100644 --- a/solana/app/deploy/05_initializeVoteWeightWindowLengths.ts +++ b/solana/app/deploy/devnet/initialize/initializeVoteWeightWindowLengths.ts @@ -1,16 +1,18 @@ -// Usage: npx ts-node app/deploy/05_initializeVoteWeightWindowLengths.ts +// Usage: npx ts-node app/deploy/devnet/initialize/initializeVoteWeightWindowLengths.ts -import * as anchor from "@coral-xyz/anchor"; import { AnchorProvider, Program, Wallet } from "@coral-xyz/anchor"; import { Connection } from "@solana/web3.js"; -import { DEPLOYER_AUTHORITY_KEYPAIR, RPC_NODE } from "./devnet"; -import { Staking } from "../../target/types/staking"; +import { + DEPLOYER_AUTHORITY_KEYPAIR, + RPC_NODE, + VOTE_WEIGHT_WINDOW_LENGTHS, +} from "../constants"; +import { Staking } from "../../../../target/types/staking"; import fs from "fs"; import BN from "bn.js"; async function main() { try { - const DEBUG = true; const connection = new Connection(RPC_NODE); const provider = new AnchorProvider( connection, @@ -24,7 +26,9 @@ async function main() { provider, ); - await program.methods.initializeVoteWeightWindowLengths(new BN(10)).rpc(); + await program.methods + .initializeVoteWeightWindowLengths(new BN(VOTE_WEIGHT_WINDOW_LENGTHS)) + .rpc(); } catch (err) { console.error("Error:", err); } diff --git a/solana/app/deploy/08_create_stake_account.ts b/solana/app/deploy/devnet/tests/11_createStakeAccount.ts similarity index 57% rename from solana/app/deploy/08_create_stake_account.ts rename to solana/app/deploy/devnet/tests/11_createStakeAccount.ts index b648933f6..d1c735054 100644 --- a/solana/app/deploy/08_create_stake_account.ts +++ b/solana/app/deploy/devnet/tests/11_createStakeAccount.ts @@ -1,17 +1,12 @@ -// Usage: npx ts-node app/deploy/08_create_stake_account.ts +// Usage: npx ts-node app/deploy/devnet/tests/11_createStakeAccount.ts -import * as anchor from "@coral-xyz/anchor"; import { AnchorProvider, Wallet } from "@coral-xyz/anchor"; import { Connection } from "@solana/web3.js"; -import * as wasm from "@wormhole/staking-wasm"; -import { StakeConnection } from "../StakeConnection"; -import { STAKING_ADDRESS } from "../constants"; -import { USER2_AUTHORITY_KEYPAIR, RPC_NODE } from "./devnet"; -import { Staking } from "../../target/types/staking"; +import { StakeConnection } from "../../../StakeConnection"; +import { RPC_NODE, USER2_AUTHORITY_KEYPAIR } from "../constants"; async function main() { try { - const DEBUG = true; const connection = new Connection(RPC_NODE); const provider = new AnchorProvider( connection, @@ -22,7 +17,6 @@ async function main() { const stakeConnection = await StakeConnection.createStakeConnection( connection, provider.wallet as Wallet, - STAKING_ADDRESS, ); await stakeConnection.createStakeAccount(); diff --git a/solana/app/deploy/09_deposit.ts b/solana/app/deploy/devnet/tests/12_deposit.ts similarity index 85% rename from solana/app/deploy/09_deposit.ts rename to solana/app/deploy/devnet/tests/12_deposit.ts index ed06b5cee..260996bde 100644 --- a/solana/app/deploy/09_deposit.ts +++ b/solana/app/deploy/devnet/tests/12_deposit.ts @@ -1,4 +1,4 @@ -// Usage: npx ts-node app/deploy/09_deposit.ts +// Usage: npx ts-node app/deploy/devnet/tests/12_deposit.ts import * as anchor from "@coral-xyz/anchor"; import { AnchorProvider, Program, Wallet } from "@coral-xyz/anchor"; @@ -8,15 +8,19 @@ import { } from "@solana/spl-token"; import { PublicKey, Transaction, Connection } from "@solana/web3.js"; import * as wasm from "@wormhole/staking-wasm"; -import { STAKING_ADDRESS } from "../constants"; -import { USER_AUTHORITY_KEYPAIR, WORMHOLE_TOKEN, RPC_NODE } from "./devnet"; +import { + RPC_NODE, + STAKING_ADDRESS, + USER2_AUTHORITY_KEYPAIR, + WORMHOLE_TOKEN, +} from "../constants"; async function main() { try { const connection = new Connection(RPC_NODE); const provider = new AnchorProvider( connection, - new Wallet(USER_AUTHORITY_KEYPAIR), + new Wallet(USER2_AUTHORITY_KEYPAIR), {}, ); const user = provider.wallet.publicKey; @@ -42,7 +46,7 @@ async function main() { from_account, toAccount, provider.wallet.publicKey, - 50, + 500000000, ); transaction.add(ix); diff --git a/solana/app/deploy/10_withdraw.ts b/solana/app/deploy/devnet/tests/13_withdrawTokens.ts similarity index 70% rename from solana/app/deploy/10_withdraw.ts rename to solana/app/deploy/devnet/tests/13_withdrawTokens.ts index 564565bb9..95f477410 100644 --- a/solana/app/deploy/10_withdraw.ts +++ b/solana/app/deploy/devnet/tests/13_withdrawTokens.ts @@ -1,25 +1,22 @@ -// Usage: npx ts-node app/deploy/10_withdraw.ts +// Usage: npx ts-node app/deploy/devnet/tests/13_withdrawTokens.ts -import * as anchor from "@coral-xyz/anchor"; import { AnchorProvider, Wallet } from "@coral-xyz/anchor"; import { Connection } from "@solana/web3.js"; -import { StakeConnection } from "../StakeConnection"; -import { WHTokenBalance } from "../whTokenBalance"; -import { STAKING_ADDRESS } from "../constants"; -import { USER_AUTHORITY_KEYPAIR, RPC_NODE } from "./devnet"; +import { StakeConnection } from "../../../StakeConnection"; +import { WHTokenBalance } from "../../../whTokenBalance"; +import { RPC_NODE, USER2_AUTHORITY_KEYPAIR } from "../constants"; async function main() { try { const connection = new Connection(RPC_NODE); const provider = new AnchorProvider( connection, - new Wallet(USER_AUTHORITY_KEYPAIR), + new Wallet(USER2_AUTHORITY_KEYPAIR), {}, ); const stakeConnection = await StakeConnection.createStakeConnection( connection, provider.wallet as Wallet, - STAKING_ADDRESS, ); const user = provider.wallet.publicKey; @@ -30,6 +27,9 @@ async function main() { stakeAccountMetadataAddress, false, ); + if (!stakeAccountCheckpointsAddress) { + throw new Error(`stakeAccountCheckpointsAddress is not defined`); + } let stakeAccount = await stakeConnection.loadStakeAccount( stakeAccountCheckpointsAddress, ); diff --git a/solana/app/deploy/11_delegate.ts b/solana/app/deploy/devnet/tests/14_delegate.ts similarity index 77% rename from solana/app/deploy/11_delegate.ts rename to solana/app/deploy/devnet/tests/14_delegate.ts index 7ebaf612a..6bf7d9675 100644 --- a/solana/app/deploy/11_delegate.ts +++ b/solana/app/deploy/devnet/tests/14_delegate.ts @@ -1,16 +1,14 @@ -// Usage: npx ts-node app/deploy/11_delegate.ts +// Usage: npx ts-node app/deploy/devnet/tests/14_delegate.ts -import * as anchor from "@coral-xyz/anchor"; import { AnchorProvider, Wallet } from "@coral-xyz/anchor"; -import { PublicKey, Connection } from "@solana/web3.js"; -import { StakeConnection } from "../StakeConnection"; -import { WHTokenBalance } from "../whTokenBalance"; -import { STAKING_ADDRESS } from "../constants"; +import { Connection } from "@solana/web3.js"; +import { StakeConnection } from "../../../StakeConnection"; +import { WHTokenBalance } from "../../../whTokenBalance"; import { USER_AUTHORITY_KEYPAIR, USER2_AUTHORITY_KEYPAIR, RPC_NODE, -} from "./devnet"; +} from "../constants"; function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); @@ -27,7 +25,6 @@ async function main() { const stakeConnection = await StakeConnection.createStakeConnection( connection, provider.wallet as Wallet, - STAKING_ADDRESS, ); await stakeConnection.delegate( @@ -44,7 +41,6 @@ async function main() { const user2StakeConnection = await StakeConnection.createStakeConnection( connection, user2Provider.wallet as Wallet, - STAKING_ADDRESS, ); await user2StakeConnection.delegate( diff --git a/solana/app/deploy/12_addProposal.ts b/solana/app/deploy/devnet/tests/15_addProposal.ts similarity index 81% rename from solana/app/deploy/12_addProposal.ts rename to solana/app/deploy/devnet/tests/15_addProposal.ts index 1abf636df..b0f524e99 100644 --- a/solana/app/deploy/12_addProposal.ts +++ b/solana/app/deploy/devnet/tests/15_addProposal.ts @@ -1,12 +1,15 @@ -// Usage: npx ts-node app/deploy/12_addProposal.ts +// Usage: npx ts-node app/deploy/devnet/tests/15_addProposal.ts -import * as anchor from "@coral-xyz/anchor"; import { AnchorProvider, Wallet } from "@coral-xyz/anchor"; -import { Connection, Keypair } from "@solana/web3.js"; -import { StakeConnection } from "../StakeConnection"; -import { STAKING_ADDRESS, CORE_BRIDGE_ADDRESS } from "../constants"; -import { DEPLOYER_AUTHORITY_KEYPAIR, RPC_NODE } from "./devnet"; -import { getWormholeBridgeData } from "../helpers/wormholeBridgeConfig"; +import { Connection } from "@solana/web3.js"; +import { StakeConnection } from "../../../StakeConnection"; +import { + CORE_BRIDGE_PID, + DEPLOYER_AUTHORITY_KEYPAIR, + RPC_NODE, +} from "../constants"; +import { getWormholeBridgeData } from "../../../helpers/wormholeBridgeConfig"; +import input from "@inquirer/input"; async function main() { try { @@ -21,10 +24,9 @@ async function main() { const stakeConnection = await StakeConnection.createStakeConnection( connection, provider.wallet as Wallet, - STAKING_ADDRESS, ); - const info = await getWormholeBridgeData(connection, CORE_BRIDGE_ADDRESS); + const info = await getWormholeBridgeData(connection, CORE_BRIDGE_PID); let guardianSetIndex = info.guardianSetIndex; const sepoliaEthProposalResponse = { diff --git a/solana/app/deploy/14_castVote.ts b/solana/app/deploy/devnet/tests/16_castVote.ts similarity index 77% rename from solana/app/deploy/14_castVote.ts rename to solana/app/deploy/devnet/tests/16_castVote.ts index 2a1903726..a6ca4d4dd 100644 --- a/solana/app/deploy/14_castVote.ts +++ b/solana/app/deploy/devnet/tests/16_castVote.ts @@ -1,11 +1,9 @@ -// Usage: npx ts-node app/deploy/14_castVote.ts +// Usage: npx ts-node app/deploy/devnet/tests/16_castVote.ts -import * as anchor from "@coral-xyz/anchor"; import { AnchorProvider, Wallet } from "@coral-xyz/anchor"; import { Connection } from "@solana/web3.js"; -import { StakeConnection } from "../StakeConnection"; -import { STAKING_ADDRESS } from "../constants"; -import { USER2_AUTHORITY_KEYPAIR, RPC_NODE } from "./devnet"; +import { StakeConnection } from "../../../StakeConnection"; +import { USER2_AUTHORITY_KEYPAIR, RPC_NODE } from "../constants"; import BN from "bn.js"; import input from "@inquirer/input"; @@ -26,7 +24,6 @@ async function main() { const user2StakeConnection = await StakeConnection.createStakeConnection( connection, user2Provider.wallet as Wallet, - STAKING_ADDRESS, ); await user2StakeConnection.castVote( diff --git a/solana/app/deploy/10_proposalVotes.ts b/solana/app/deploy/devnet/tests/17_proposalVotes.ts similarity index 78% rename from solana/app/deploy/10_proposalVotes.ts rename to solana/app/deploy/devnet/tests/17_proposalVotes.ts index 0fef765b2..b4a298887 100644 --- a/solana/app/deploy/10_proposalVotes.ts +++ b/solana/app/deploy/devnet/tests/17_proposalVotes.ts @@ -1,11 +1,9 @@ -// Usage: npx ts-node app/deploy/10_proposalVotes.ts +// Usage: npx ts-node app/deploy/devnet/tests/17_proposalVotes.ts -import * as anchor from "@coral-xyz/anchor"; import { AnchorProvider, Wallet } from "@coral-xyz/anchor"; import { Connection } from "@solana/web3.js"; -import { StakeConnection } from "../StakeConnection"; -import { STAKING_ADDRESS } from "../constants"; -import { USER_AUTHORITY_KEYPAIR, RPC_NODE } from "./devnet"; +import { StakeConnection } from "../../../StakeConnection"; +import { USER_AUTHORITY_KEYPAIR, RPC_NODE } from "../constants"; async function main() { try { @@ -19,7 +17,6 @@ async function main() { const stakeConnection = await StakeConnection.createStakeConnection( connection, provider.wallet as Wallet, - STAKING_ADDRESS, ); const proposalIdHex = diff --git a/solana/app/deploy/devnet/tests/21_fetchGlobalConfig.ts b/solana/app/deploy/devnet/tests/21_fetchGlobalConfig.ts new file mode 100644 index 000000000..ee4f20021 --- /dev/null +++ b/solana/app/deploy/devnet/tests/21_fetchGlobalConfig.ts @@ -0,0 +1,55 @@ +// Usage: npx ts-node app/deploy/devnet/tests/21_fetchGlobalConfig.ts + +import { Wallet, AnchorProvider, utils } from "@coral-xyz/anchor"; +import { Connection, PublicKey } from "@solana/web3.js"; +import * as wasm from "@wormhole/staking-wasm"; +import { + CHECKPOINTS_ACCOUNT_LIMIT, + DEPLOYER_AUTHORITY_KEYPAIR, + WORMHOLE_TOKEN, + GOVERNANCE_AUTHORITY_KEYPAIR, + VESTING_ADMIN_KEYPAIR, + RPC_NODE, +} from "../constants"; +import { StakeConnection } from "../../../StakeConnection"; + +async function main() { + const connection = new Connection(RPC_NODE); + const provider = new AnchorProvider( + connection, + new Wallet(DEPLOYER_AUTHORITY_KEYPAIR), + {}, + ); + const stakeConnection = await StakeConnection.createStakeConnection( + connection, + provider.wallet as Wallet, + ); + + console.log( + "DEPLOYER_AUTHORITY_KEYPAIR.publicKey:", + DEPLOYER_AUTHORITY_KEYPAIR.publicKey, + ); + console.log("WORMHOLE_TOKEN.publicKey:", WORMHOLE_TOKEN); + console.log( + "GOVERNANCE_AUTHORITY_KEYPAIR.publicKey:", + GOVERNANCE_AUTHORITY_KEYPAIR.publicKey, + ); + console.log( + "VESTING_ADMIN_KEYPAIR.publicKey:", + VESTING_ADMIN_KEYPAIR.publicKey, + ); + console.log("CHECKPOINTS_ACCOUNT_LIMIT:", CHECKPOINTS_ACCOUNT_LIMIT); + + const [configAccount, bump] = PublicKey.findProgramAddressSync( + [utils.bytes.utf8.encode(wasm.Constants.CONFIG_SEED())], + stakeConnection.program.programId, + ); + console.log("bump:", bump); + console.log("configAccount:", configAccount); + + let configAccountData = + await stakeConnection.program.account.globalConfig.fetch(configAccount); + console.log("configAccountData:", configAccountData); +} + +main(); diff --git a/solana/app/deploy/devnet/tests/22_fetchSpokeMetadataCollector.ts b/solana/app/deploy/devnet/tests/22_fetchSpokeMetadataCollector.ts new file mode 100644 index 000000000..7f62ff124 --- /dev/null +++ b/solana/app/deploy/devnet/tests/22_fetchSpokeMetadataCollector.ts @@ -0,0 +1,45 @@ +// Usage: npx ts-node app/deploy/devnet/tests/22_fetchSpokeMetadataCollector.ts + +import { Wallet, AnchorProvider, utils } from "@coral-xyz/anchor"; +import { Connection, PublicKey } from "@solana/web3.js"; +import * as wasm from "@wormhole/staking-wasm"; +import { + USER2_AUTHORITY_KEYPAIR, + HUB_CHAIN_ID, + hubProposalMetadataUint8Array, + RPC_NODE, +} from "../constants"; +import { StakeConnection } from "../../../StakeConnection"; + +async function main() { + const connection = new Connection(RPC_NODE); + const provider = new AnchorProvider( + connection, + new Wallet(USER2_AUTHORITY_KEYPAIR), + {}, + ); + const stakeConnection = await StakeConnection.createStakeConnection( + connection, + provider.wallet as Wallet, + ); + + console.log("HUB_CHAIN_ID:", HUB_CHAIN_ID); + console.log("hubProposalMetadataUint8Array:", hubProposalMetadataUint8Array); + + const [spokeMetadataCollector, _] = PublicKey.findProgramAddressSync( + [utils.bytes.utf8.encode(wasm.Constants.SPOKE_METADATA_COLLECTOR_SEED())], + stakeConnection.program.programId, + ); + console.log("spokeMetadataCollector:", spokeMetadataCollector); + + let spokeMetadataCollectorAccountData = + await stakeConnection.program.account.spokeMetadataCollector.fetch( + spokeMetadataCollector, + ); + console.log( + "spokeMetadataCollectorAccountData:", + spokeMetadataCollectorAccountData, + ); +} + +main(); diff --git a/solana/app/deploy/devnet/tests/23_fetchVoteWeightWindowLengths.ts b/solana/app/deploy/devnet/tests/23_fetchVoteWeightWindowLengths.ts new file mode 100644 index 000000000..7f08cbbe0 --- /dev/null +++ b/solana/app/deploy/devnet/tests/23_fetchVoteWeightWindowLengths.ts @@ -0,0 +1,63 @@ +// Usage: npx ts-node app/deploy/devnet/tests/23_fetchVoteWeightWindowLengths.ts + +import { Wallet, AnchorProvider, utils } from "@coral-xyz/anchor"; +import { Connection, PublicKey } from "@solana/web3.js"; +import * as wasm from "@wormhole/staking-wasm"; +import { + USER2_AUTHORITY_KEYPAIR, + RPC_NODE, + VOTE_WEIGHT_WINDOW_LENGTHS, +} from "../constants"; +import { StakeConnection } from "../../../StakeConnection"; +import { + readWindowLengths, + WindowLengthsAccount, +} from "../../../vote_weight_window_lengths"; + +async function main() { + const connection = new Connection(RPC_NODE); + const provider = new AnchorProvider( + connection, + new Wallet(USER2_AUTHORITY_KEYPAIR), + {}, + ); + const stakeConnection = await StakeConnection.createStakeConnection( + connection, + provider.wallet as Wallet, + ); + + console.log("VOTE_WEIGHT_WINDOW_LENGTHS:", VOTE_WEIGHT_WINDOW_LENGTHS); + + const [voteWeightWindowLengthsAccountAddress, _] = + PublicKey.findProgramAddressSync( + [ + utils.bytes.utf8.encode( + wasm.Constants.VOTE_WEIGHT_WINDOW_LENGTHS_SEED(), + ), + ], + stakeConnection.program.programId, + ); + console.log( + "voteWeightWindowLengthsAccountAddress:", + voteWeightWindowLengthsAccountAddress, + ); + + let windowLengthsAccount: WindowLengthsAccount = await readWindowLengths( + connection, + voteWeightWindowLengthsAccountAddress, + ); + console.log( + "windowLengthsAccount.getWindowLengthCount():", + windowLengthsAccount.getWindowLengthCount(), + ); + console.log( + "windowLengthsAccount.voteWeightWindowLengths.nextIndex:", + windowLengthsAccount.voteWeightWindowLengths.nextIndex, + ); + console.log( + "windowLengthsAccount.getLastWindowLength().value.toString():", + windowLengthsAccount.getLastWindowLength()?.value.toString(), + ); +} + +main(); diff --git a/solana/app/deploy/13_fetchProposalAccountData.ts b/solana/app/deploy/devnet/tests/24_fetchProposalAccountData.ts similarity index 70% rename from solana/app/deploy/13_fetchProposalAccountData.ts rename to solana/app/deploy/devnet/tests/24_fetchProposalAccountData.ts index caae7876c..cd009b683 100644 --- a/solana/app/deploy/13_fetchProposalAccountData.ts +++ b/solana/app/deploy/devnet/tests/24_fetchProposalAccountData.ts @@ -1,32 +1,31 @@ -// Usage: npx ts-node app/deploy/13_fetchProposalAccountData.ts +// Usage: npx ts-node app/deploy/devnet/tests/24_fetchProposalAccountData.ts -import * as anchor from "@coral-xyz/anchor"; import { AnchorProvider, Wallet } from "@coral-xyz/anchor"; import { Connection } from "@solana/web3.js"; -import { StakeConnection } from "../StakeConnection"; -import { STAKING_ADDRESS } from "../constants"; -import { DEPLOYER_AUTHORITY_KEYPAIR, RPC_NODE } from "./devnet"; -import BN from "bn.js"; +import { StakeConnection } from "../../../StakeConnection"; +import { USER2_AUTHORITY_KEYPAIR, RPC_NODE } from "../constants"; import { ethers } from "ethers"; +import input from "@inquirer/input"; async function main() { try { const connection = new Connection(RPC_NODE); const provider = new AnchorProvider( connection, - new Wallet(DEPLOYER_AUTHORITY_KEYPAIR), + new Wallet(USER2_AUTHORITY_KEYPAIR), {}, ); - const proposalId = await input({ message: "Enter the proposal id:" }); - const proposalIdHex = BigInt(proposalId).toString(16).padStart(64, "0"); + const inputProposalId = await input({ message: "Enter the proposal id:" }); + const proposalIdHex = BigInt(inputProposalId) + .toString(16) + .padStart(64, "0"); // console.log("proposalIdHex:", proposalIdHex); const proposalIdArray = Buffer.from(proposalIdHex, "hex"); const stakeConnection = await StakeConnection.createStakeConnection( connection, provider.wallet as Wallet, - STAKING_ADDRESS, ); const { proposalAccountData } = diff --git a/solana/app/deploy/devnet/tests/25_fetchSpokeMessageExecutor.ts b/solana/app/deploy/devnet/tests/25_fetchSpokeMessageExecutor.ts new file mode 100644 index 000000000..f81c79cb7 --- /dev/null +++ b/solana/app/deploy/devnet/tests/25_fetchSpokeMessageExecutor.ts @@ -0,0 +1,52 @@ +// Usage: npx ts-node app/deploy/devnet/tests/25_fetchSpokeMessageExecutor.ts + +import { Wallet, AnchorProvider, utils } from "@coral-xyz/anchor"; +import { Connection, PublicKey } from "@solana/web3.js"; +import * as wasm from "@wormhole/staking-wasm"; +import { + USER2_AUTHORITY_KEYPAIR, + HUB_CHAIN_ID, + hubSolanaMessageDispatcherPublicKey, + RPC_NODE, +} from "../constants"; +import { StakeConnection } from "../../../StakeConnection"; + +async function main() { + const connection = new Connection(RPC_NODE); + const provider = new AnchorProvider( + connection, + new Wallet(USER2_AUTHORITY_KEYPAIR), + {}, + ); + const stakeConnection = await StakeConnection.createStakeConnection( + connection, + provider.wallet as Wallet, + ); + + console.log("HUB_CHAIN_ID:", HUB_CHAIN_ID); + console.log( + "hubSolanaMessageDispatcherPublicKey:", + hubSolanaMessageDispatcherPublicKey, + ); + + const [spokeMessageExecutorAccountAddress, _] = + PublicKey.findProgramAddressSync( + [utils.bytes.utf8.encode(wasm.Constants.SPOKE_MESSAGE_EXECUTOR_SEED())], + stakeConnection.program.programId, + ); + console.log( + "spokeMessageExecutorAccountAddress:", + spokeMessageExecutorAccountAddress, + ); + + let spokeMessageExecutorAccountData = + await stakeConnection.program.account.spokeMessageExecutor.fetch( + spokeMessageExecutorAccountAddress, + ); + console.log( + "spokeMessageExecutorAccountData:", + spokeMessageExecutorAccountData, + ); +} + +main(); diff --git a/solana/app/deploy/tx_list.ts b/solana/app/deploy/devnet/tests/31_tx_list.ts similarity index 78% rename from solana/app/deploy/tx_list.ts rename to solana/app/deploy/devnet/tests/31_tx_list.ts index 6605a3a8e..61d01f942 100644 --- a/solana/app/deploy/tx_list.ts +++ b/solana/app/deploy/devnet/tests/31_tx_list.ts @@ -1,10 +1,12 @@ +// Usage: npx ts-node app/deploy/devnet/tests/31_tx_list.ts + import { Connection } from "@solana/web3.js"; -import { RPC_NODE } from "./devnet"; -import { getProgramTransactions } from "../utils/parse_transactions"; +import { RPC_NODE } from "../constants"; +import { getProgramTransactions } from "../../../helpers/utils/parse_transactions"; async function main() { const connection = new Connection(RPC_NODE, "confirmed"); - const programId = "DgCSKsLDXXufYeEkvf21YSX5DMnFK89xans5WdSsUbeY"; + const programId = "AFuHPdrQGsW8rNQ4oEFF35sm5fg36gwrxyqjkjKvi6ap"; const limit = 15; getProgramTransactions(connection, programId, limit) diff --git a/solana/app/deploy/tx_events.ts b/solana/app/deploy/devnet/tests/32_tx_events.ts similarity index 82% rename from solana/app/deploy/tx_events.ts rename to solana/app/deploy/devnet/tests/32_tx_events.ts index 3bab75dc2..af8b10f08 100644 --- a/solana/app/deploy/tx_events.ts +++ b/solana/app/deploy/devnet/tests/32_tx_events.ts @@ -1,16 +1,18 @@ +// Usage: npx ts-node app/deploy/devnet/tests/32_tx_events.ts + import { Connection } from "@solana/web3.js"; -import { RPC_NODE } from "./devnet"; +import { RPC_NODE } from "../constants"; import { getProgramTransactions, printTransactionEvents, -} from "../utils/parse_transactions"; +} from "../../../helpers/utils/parse_transactions"; async function main() { const connection = new Connection(RPC_NODE, "confirmed"); // const transactionSignature = '3er1rCAdfVkKjmyURX4HJJxqFsY4EjnCdyC3M4NCqMb4RErKh7USgacYQAsb42W6Syf9Dx1hn4kk3wzSPQrcGpsw'; // await printTransactionEvents(connection, transactionSignature); - const programId = "DgCSKsLDXXufYeEkvf21YSX5DMnFK89xans5WdSsUbeY"; + const programId = "AFuHPdrQGsW8rNQ4oEFF35sm5fg36gwrxyqjkjKvi6ap"; const limit = 3; try { diff --git a/solana/app/deploy/tx_details.ts b/solana/app/deploy/devnet/tests/33_tx_details.ts similarity index 79% rename from solana/app/deploy/tx_details.ts rename to solana/app/deploy/devnet/tests/33_tx_details.ts index 7e2c6614a..cc24c3f60 100644 --- a/solana/app/deploy/tx_details.ts +++ b/solana/app/deploy/devnet/tests/33_tx_details.ts @@ -1,13 +1,15 @@ +// Usage: npx ts-node app/deploy/devnet/tests/33_tx_details.ts + import { Connection } from "@solana/web3.js"; -import { RPC_NODE } from "./devnet"; +import { RPC_NODE } from "../constants"; import { getProgramTransactions, printTransactionDetails, -} from "../utils/parse_transactions"; +} from "../../../helpers/utils/parse_transactions"; async function main() { const connection = new Connection(RPC_NODE, "confirmed"); - const programId = "DgCSKsLDXXufYeEkvf21YSX5DMnFK89xans5WdSsUbeY"; + const programId = "AFuHPdrQGsW8rNQ4oEFF35sm5fg36gwrxyqjkjKvi6ap"; const limit = 2; try { diff --git a/solana/app/deploy/bridge-script.ts b/solana/app/deploy/devnet/tests/bridge-script.ts similarity index 94% rename from solana/app/deploy/bridge-script.ts rename to solana/app/deploy/devnet/tests/bridge-script.ts index 8dcd78295..60b56cd8b 100644 --- a/solana/app/deploy/bridge-script.ts +++ b/solana/app/deploy/devnet/tests/bridge-script.ts @@ -1,4 +1,4 @@ -// Usage: npx ts-node app/deploy/bridge-script.ts +// Usage: npx ts-node app/deploy/devnet/tests/bridge-script.ts import { EthCallData, @@ -7,7 +7,6 @@ import { QueryRequest, signaturesToEvmStruct, QueryResponse, - QueryProxyQueryResponse, signaturesToSolanaArray, } from "@wormhole-foundation/wormhole-query-sdk"; import axios from "axios"; @@ -15,17 +14,15 @@ import { ethers } from "ethers"; import select from "@inquirer/select"; import input from "@inquirer/input"; import "dotenv/config"; - import { Keypair } from "@solana/web3.js"; import { Connection } from "@solana/web3.js"; import { - CORE_BRIDGE_ADDRESS, - HUB_CHAIN_ID, + CORE_BRIDGE_PID, HUB_PROPOSAL_METADATA_ADDRESS, + RPC_NODE, } from "../constants"; -import { RPC_NODE } from "./devnet"; -import { getWormholeBridgeData } from "../helpers/wormholeBridgeConfig"; -import { deriveGuardianSetKey } from "../helpers/guardianSet"; +import { getWormholeBridgeData } from "../../../helpers/wormholeBridgeConfig"; +import { deriveGuardianSetKey } from "../../../helpers/guardianSet"; const API_KEY = process.env.WORMHOLE_API_KEY as string; @@ -76,13 +73,10 @@ const scripts: { const connection = new Connection(RPC_NODE); - const info = await getWormholeBridgeData(connection, CORE_BRIDGE_ADDRESS); + const info = await getWormholeBridgeData(connection, CORE_BRIDGE_PID); let guardianSetIndex = info.guardianSetIndex; - const guardianSet = deriveGuardianSetKey( - CORE_BRIDGE_ADDRESS, - guardianSetIndex, - ); + const guardianSet = deriveGuardianSetKey(CORE_BRIDGE_PID, guardianSetIndex); console.log(` In Solana: diff --git a/solana/app/deploy/devnet/tests/new_wallet.ts b/solana/app/deploy/devnet/tests/new_wallet.ts new file mode 100644 index 000000000..aaf6013bf --- /dev/null +++ b/solana/app/deploy/devnet/tests/new_wallet.ts @@ -0,0 +1,19 @@ +// Usage: npx ts-node app/deploy/devnet/tests/new_wallet.ts + +import { Keypair } from "@solana/web3.js"; +import fs from "fs"; + +// Generate a new Keypair +const newWallet = Keypair.generate(); + +// Extract public and private keys +const publicKey = newWallet.publicKey.toString(); +const secretKey = Buffer.from(newWallet.secretKey).toString("base64"); // keep it secure + +console.log("Public Key:", publicKey); +console.log("Secret Key (base64):", secretKey); + +// Save the secret key to a file +const fileName = `app/keypairs/secretKey_${publicKey}.json`; +fs.writeFileSync(fileName, JSON.stringify([...newWallet.secretKey])); +console.log(`Wallet secret key saved to ${fileName}`); diff --git a/solana/app/deploy/receive_message.ts b/solana/app/deploy/devnet/tests/receive_message.ts similarity index 93% rename from solana/app/deploy/receive_message.ts rename to solana/app/deploy/devnet/tests/receive_message.ts index cde91eb5f..df07aaa50 100644 --- a/solana/app/deploy/receive_message.ts +++ b/solana/app/deploy/devnet/tests/receive_message.ts @@ -1,4 +1,3 @@ -import * as anchor from "@coral-xyz/anchor"; import { AnchorProvider, Program, Wallet } from "@coral-xyz/anchor"; import { AccountMeta, @@ -6,11 +5,14 @@ import { PublicKey, SystemProgram, } from "@solana/web3.js"; -import { DEPLOYER_AUTHORITY_KEYPAIR, RPC_NODE } from "./devnet"; -import { Staking } from "../../target/types/staking"; +import { + CORE_BRIDGE_PID, + DEPLOYER_AUTHORITY_KEYPAIR, + RPC_NODE, +} from "../constants"; +import { Staking } from "../../../../target/types/staking"; import fs from "fs"; import BN from "bn.js"; -import { CORE_BRIDGE_PID } from "../../tests/executor"; import { hubSolanaMessageDispatcherPublicKey, HUB_CHAIN_ID, diff --git a/solana/app/deploy/transfer_W_token.ts b/solana/app/deploy/devnet/tests/transfer_W_token.ts similarity index 96% rename from solana/app/deploy/transfer_W_token.ts rename to solana/app/deploy/devnet/tests/transfer_W_token.ts index 1412d3b73..bc9f28bed 100644 --- a/solana/app/deploy/transfer_W_token.ts +++ b/solana/app/deploy/devnet/tests/transfer_W_token.ts @@ -1,4 +1,3 @@ -import * as anchor from "@coral-xyz/anchor"; import { AnchorProvider, Wallet } from "@coral-xyz/anchor"; import { PublicKey, Connection, Transaction } from "@solana/web3.js"; import { @@ -7,7 +6,7 @@ import { USER2_AUTHORITY_KEYPAIR, WORMHOLE_TOKEN, RPC_NODE, -} from "./devnet"; +} from "../constants"; import { createTransferInstruction, getAssociatedTokenAddressSync, diff --git a/solana/app/deploy/devnet/update/updateGovernanceAuthority.ts b/solana/app/deploy/devnet/update/updateGovernanceAuthority.ts new file mode 100644 index 000000000..5808fdb3e --- /dev/null +++ b/solana/app/deploy/devnet/update/updateGovernanceAuthority.ts @@ -0,0 +1,48 @@ +// Usage: npx ts-node app/deploy/devnet/update/updateGovernanceAuthority.ts + +import { AnchorProvider, Program, Wallet } from "@coral-xyz/anchor"; +import { Connection } from "@solana/web3.js"; +import { + DEPLOYER_AUTHORITY_KEYPAIR, + GOVERNANCE_AUTHORITY_KEYPAIR, + RPC_NODE, +} from "../constants"; +import { Staking } from "../../../../target/types/staking"; +import fs from "fs"; + +async function main() { + try { + const connection = new Connection(RPC_NODE); + const provider = new AnchorProvider( + connection, + new Wallet(DEPLOYER_AUTHORITY_KEYPAIR), + {}, + ); + + let program: Program; + program = new Program( + JSON.parse(fs.readFileSync("./target/idl/staking.json").toString()), + provider, + ); + + await program.methods + .updateGovernanceAuthority() + .accounts({ + governanceSigner: DEPLOYER_AUTHORITY_KEYPAIR.publicKey, + newAuthority: GOVERNANCE_AUTHORITY_KEYPAIR.publicKey, + }) + .rpc(); + + await program.methods + .claimGovernanceAuthority() + .accounts({ + newAuthority: GOVERNANCE_AUTHORITY_KEYPAIR.publicKey, + }) + .signers([GOVERNANCE_AUTHORITY_KEYPAIR]) + .rpc(); + } catch (err) { + console.error("Error:", err); + } +} + +main(); diff --git a/solana/app/deploy/devnet/update/updateHubProposalMetadata.ts b/solana/app/deploy/devnet/update/updateHubProposalMetadata.ts new file mode 100644 index 000000000..9d87bab2c --- /dev/null +++ b/solana/app/deploy/devnet/update/updateHubProposalMetadata.ts @@ -0,0 +1,45 @@ +// Usage: npx ts-node app/deploy/devnet/update/updateHubProposalMetadata.ts + +import { AnchorProvider, Program, Wallet } from "@coral-xyz/anchor"; +import { Connection, PublicKey } from "@solana/web3.js"; +import { + DEPLOYER_AUTHORITY_KEYPAIR, + hubProposalMetadataUint8Array, + RPC_NODE, +} from "../constants"; +import { Staking } from "../../../../target/types/staking"; +import fs from "fs"; + +async function main() { + try { + const connection = new Connection(RPC_NODE); + const provider = new AnchorProvider( + connection, + new Wallet(DEPLOYER_AUTHORITY_KEYPAIR), + {}, + ); + + let program: Program; + program = new Program( + JSON.parse(fs.readFileSync("./target/idl/staking.json").toString()), + provider, + ); + + const airlockPDA: PublicKey = PublicKey.findProgramAddressSync( + [Buffer.from("airlock")], + program.programId, + )[0]; + + await program.methods + .updateHubProposalMetadata(Array.from(hubProposalMetadataUint8Array)) + .accounts({ + payer: DEPLOYER_AUTHORITY_KEYPAIR.publicKey, + airlock: airlockPDA, + }) + .rpc(); + } catch (err) { + console.error("Error:", err); + } +} + +main(); diff --git a/solana/app/deploy/devnet/update/updateVestingAdmin.ts b/solana/app/deploy/devnet/update/updateVestingAdmin.ts new file mode 100644 index 000000000..b1e26704b --- /dev/null +++ b/solana/app/deploy/devnet/update/updateVestingAdmin.ts @@ -0,0 +1,43 @@ +// Usage: npx ts-node app/deploy/devnet/update/updateVestingAdmin.ts + +import { AnchorProvider, Program, Wallet } from "@coral-xyz/anchor"; +import { Connection } from "@solana/web3.js"; +import { + DEPLOYER_AUTHORITY_KEYPAIR, + RPC_NODE, + VESTING_ADMIN_KEYPAIR, +} from "../constants"; +import { Staking } from "../../../../target/types/staking"; +import fs from "fs"; + +async function main() { + try { + const connection = new Connection(RPC_NODE); + const provider = new AnchorProvider( + connection, + new Wallet(DEPLOYER_AUTHORITY_KEYPAIR), + {}, + ); + + let program: Program; + program = new Program( + JSON.parse(fs.readFileSync("./target/idl/staking.json").toString()), + provider, + ); + + await program.methods + .updateVestingAdmin() + .accounts({ newVestingAdmin: VESTING_ADMIN_KEYPAIR.publicKey }) + .rpc(); + + await program.methods + .claimVestingAdmin() + .accounts({ newVestingAdmin: VESTING_ADMIN_KEYPAIR.publicKey }) + .signers([VESTING_ADMIN_KEYPAIR]) + .rpc(); + } catch (err) { + console.error("Error:", err); + } +} + +main(); diff --git a/solana/app/deploy/devnet/vesting/claimVesting.ts b/solana/app/deploy/devnet/vesting/claimVesting.ts new file mode 100644 index 000000000..0f2f02f1c --- /dev/null +++ b/solana/app/deploy/devnet/vesting/claimVesting.ts @@ -0,0 +1,138 @@ +// Usage: npx ts-node app/deploy/devnet/vesting/claimVesting.ts + +import { Wallet, AnchorProvider } from "@coral-xyz/anchor"; +import { Connection, PublicKey, SystemProgram } from "@solana/web3.js"; +import { + VESTING_ADMIN_KEYPAIR, + USER_AUTHORITY_KEYPAIR, + WORMHOLE_TOKEN, + RPC_NODE, +} from "../constants"; +import { StakeConnection } from "../../../StakeConnection"; +import BN from "bn.js"; +import * as wasm from "@wormhole/staking-wasm"; +import { + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + getAssociatedTokenAddressSync, +} from "@solana/spl-token"; + +async function main() { + const admin = VESTING_ADMIN_KEYPAIR; + const vester = USER_AUTHORITY_KEYPAIR; + + const connection = new Connection(RPC_NODE); + const vesterProvider = new AnchorProvider(connection, new Wallet(vester), {}); + const vesterStakeConnection = await StakeConnection.createStakeConnection( + connection, + vesterProvider.wallet as Wallet, + ); + + const confirm = async (signature: string): Promise => { + const block = + await vesterStakeConnection.provider.connection.getLatestBlockhash(); + await vesterStakeConnection.provider.connection.confirmTransaction({ + signature, + ...block, + }); + + return signature; + }; + + const NOW = new BN(1741270829); + + const seedBufferHex = "83cda9d4e2445bff"; + const seedBuffer = Buffer.from(seedBufferHex, "hex"); + const config = PublicKey.findProgramAddressSync( + [ + Buffer.from(wasm.Constants.VESTING_CONFIG_SEED()), + WORMHOLE_TOKEN.toBuffer(), + seedBuffer, + ], + vesterStakeConnection.program.programId, + )[0]; + // const config = new PublicKey("AHfPLNVnRGoACwMfoRCwWnCEJWjMX4x7Yq3ufg3tpjQQ"); + console.log("Vesting config account:", config); + + const vault = getAssociatedTokenAddressSync( + WORMHOLE_TOKEN, + config, + true, + TOKEN_PROGRAM_ID, + ); + const vesterTa = getAssociatedTokenAddressSync( + WORMHOLE_TOKEN, + vester.publicKey, + false, + TOKEN_PROGRAM_ID, + ); + const adminAta = getAssociatedTokenAddressSync( + WORMHOLE_TOKEN, + admin.publicKey, + false, + TOKEN_PROGRAM_ID, + ); + + const vestNow = PublicKey.findProgramAddressSync( + [ + Buffer.from(wasm.Constants.VEST_SEED()), + config.toBuffer(), + vesterTa.toBuffer(), + NOW.toBuffer("le", 8), + ], + vesterStakeConnection.program.programId, + )[0]; + + const vestingBalance = PublicKey.findProgramAddressSync( + [ + Buffer.from(wasm.Constants.VESTING_BALANCE_SEED()), + config.toBuffer(), + vester.publicKey.toBuffer(), + ], + vesterStakeConnection.program.programId, + )[0]; + + let stakeAccountMetadataAddress = + await vesterStakeConnection.getStakeMetadataAddress(vester.publicKey); + let stakeAccountMetadataData = + await vesterStakeConnection.fetchStakeAccountMetadata(vester.publicKey); + let delegateStakeAccountCheckpointsOwner = stakeAccountMetadataData.delegate; + let delegateStakeAccountMetadataAddress = + await vesterStakeConnection.getStakeMetadataAddress( + delegateStakeAccountCheckpointsOwner, + ); + let delegateStakeAccountCheckpointsAddress = + await vesterStakeConnection.getStakeAccountCheckpointsAddressByMetadata( + delegateStakeAccountMetadataAddress, + false, + ); + + let accounts = { + vester: vester.publicKey, + mint: WORMHOLE_TOKEN, + vault, + vesterTa, + config, + vest: vestNow, + vestingBalance, + delegateStakeAccountCheckpoints: delegateStakeAccountCheckpointsAddress, + delegateStakeAccountMetadata: stakeAccountMetadataAddress, + stakeAccountMetadata: stakeAccountMetadataAddress, + globalConfig: vesterStakeConnection.configAddress, + admin: admin.publicKey, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + tokenProgram: TOKEN_PROGRAM_ID, + systemProgram: SystemProgram.programId, + }; + + console.log("Starting claimVesting..."); + await vesterStakeConnection.program.methods + .claimVesting() + .accountsPartial({ ...accounts }) + .signers([vester]) + .rpc() + .then(confirm); + console.log("claimVesting completed successfully."); +} + +main(); diff --git a/solana/app/deploy/test_vesting.ts b/solana/app/deploy/devnet/vesting/createVestingConfig.ts similarity index 64% rename from solana/app/deploy/test_vesting.ts rename to solana/app/deploy/devnet/vesting/createVestingConfig.ts index 9e99cba3c..2def78a9d 100644 --- a/solana/app/deploy/test_vesting.ts +++ b/solana/app/deploy/devnet/vesting/createVestingConfig.ts @@ -1,3 +1,5 @@ +// Usage: npx ts-node app/deploy/devnet/vesting/createVestingConfig.ts + import { Wallet, AnchorProvider } from "@coral-xyz/anchor"; import { Connection, @@ -6,14 +8,12 @@ import { Transaction, } from "@solana/web3.js"; import { - DEPLOYER_AUTHORITY_KEYPAIR, + VESTING_ADMIN_KEYPAIR, USER_AUTHORITY_KEYPAIR, WORMHOLE_TOKEN, RPC_NODE, -} from "./devnet"; -import { STAKING_ADDRESS } from "../constants"; -import { StakeConnection } from "../StakeConnection"; -import { WHTokenBalance } from "../whTokenBalance"; +} from "../constants"; +import { StakeConnection } from "../../../StakeConnection"; import BN from "bn.js"; import { randomBytes } from "crypto"; import * as wasm from "@wormhole/staking-wasm"; @@ -24,23 +24,20 @@ import { getAssociatedTokenAddressSync, } from "@solana/spl-token"; +function sleep(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + async function main() { - const admin = DEPLOYER_AUTHORITY_KEYPAIR; + const admin = VESTING_ADMIN_KEYPAIR; const vester = USER_AUTHORITY_KEYPAIR; const connection = new Connection(RPC_NODE); const provider = new AnchorProvider(connection, new Wallet(admin), {}); - const vesterProvider = new AnchorProvider(connection, new Wallet(vester), {}); const stakeConnection = await StakeConnection.createStakeConnection( connection, provider.wallet as Wallet, - STAKING_ADDRESS, - ); - const vesterStakeConnection = await StakeConnection.createStakeConnection( - connection, - vesterProvider.wallet as Wallet, - STAKING_ADDRESS, ); const confirm = async (signature: string): Promise => { @@ -57,24 +54,39 @@ async function main() { const NOW = new BN(Math.floor(new Date().getTime() / 1000)); const LATER = NOW.add(new BN(1000)); const EVEN_LATER = LATER.add(new BN(1000)); + console.log("Vesting claim times:"); + console.log( + `NOW: ${NOW.toString()} (${new Date(NOW.toNumber() * 1000).toISOString()})`, + ); + console.log( + `LATER: ${LATER.toString()} (${new Date(LATER.toNumber() * 1000).toISOString()})`, + ); + console.log( + `EVEN_LATER: ${EVEN_LATER.toString()} (${new Date(EVEN_LATER.toNumber() * 1000).toISOString()})`, + ); const seed = new BN(randomBytes(8)); + console.log("Vesting config random seed:", seed); + const seedBuffer = seed.toBuffer("le", 8); + console.log("seedBuffer:", seedBuffer); + console.log("seedBufferHex:", seedBuffer.toString("hex")); + const config = PublicKey.findProgramAddressSync( [ Buffer.from(wasm.Constants.VESTING_CONFIG_SEED()), - admin.publicKey.toBuffer(), WORMHOLE_TOKEN.toBuffer(), - seed.toBuffer("le", 8), + seedBuffer, ], stakeConnection.program.programId, )[0]; + console.log("Vesting config account:", config); + const vault = getAssociatedTokenAddressSync( WORMHOLE_TOKEN, config, true, TOKEN_PROGRAM_ID, ); - const vesterTa = getAssociatedTokenAddressSync( WORMHOLE_TOKEN, vester.publicKey, @@ -88,25 +100,6 @@ async function main() { TOKEN_PROGRAM_ID, ); - const vestNow = PublicKey.findProgramAddressSync( - [ - Buffer.from(wasm.Constants.VEST_SEED()), - config.toBuffer(), - vesterTa.toBuffer(), - NOW.toBuffer("le", 8), - ], - stakeConnection.program.programId, - )[0]; - const vestLater = PublicKey.findProgramAddressSync( - [ - Buffer.from(wasm.Constants.VEST_SEED()), - config.toBuffer(), - vesterTa.toBuffer(), - LATER.toBuffer("le", 8), - ], - stakeConnection.program.programId, - )[0]; - const vestingBalance = PublicKey.findProgramAddressSync( [ Buffer.from(wasm.Constants.VESTING_BALANCE_SEED()), @@ -115,24 +108,10 @@ async function main() { ], stakeConnection.program.programId, )[0]; - - let adminStakeAccountCheckpointsAddress = - await stakeConnection.getStakeAccountCheckpointsAddress(admin.publicKey); - if (!adminStakeAccountCheckpointsAddress) { - await stakeConnection.createStakeAccount(); - } - - let vesterStakeAccountCheckpointsAddress = - await vesterStakeConnection.getStakeAccountCheckpointsAddress( - vester.publicKey, - ); - if (!vesterStakeAccountCheckpointsAddress) { - await vesterStakeConnection.createStakeAccount(); - } + console.log("Vesting balance account for vester:", vestingBalance); let accounts = { admin: admin.publicKey, - payer: admin.publicKey, mint: WORMHOLE_TOKEN, config, vault, @@ -143,53 +122,84 @@ async function main() { associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, tokenProgram: TOKEN_PROGRAM_ID, systemProgram: SystemProgram.programId, - vestingBalance: vestingBalance, + vestingBalance, }; + console.log("Initializing vesting config..."); await stakeConnection.program.methods .initializeVestingConfig(seed) .accounts({ ...accounts }) .signers([admin]) .rpc() .then(confirm); + console.log("Vesting config initialized"); + await sleep(3000); + console.log("Creating vesting balance for vester..."); await stakeConnection.program.methods .createVestingBalance() .accounts({ ...accounts }) .signers([admin]) .rpc() .then(confirm); + console.log("Vesting balance for vester created"); + await sleep(3000); + console.log(`Creating vest for vester at NOW (${NOW.toString()})...`); await stakeConnection.program.methods .createVesting(NOW, new BN(20e6)) .accounts({ ...accounts }) .signers([admin]) - .rpc({ - skipPreflight: true, - }) + .rpc() .then(confirm); + console.log(`Vest for vester at NOW (${NOW.toString()}) created`); + await sleep(3000); + console.log(`Creating vest for vester at LATER (${LATER.toString()})...`); await stakeConnection.program.methods .createVesting(LATER, new BN(20e6)) .accounts({ ...accounts }) .signers([admin]) .rpc() .then(confirm); + console.log(`Vest for vester at LATER (${LATER.toString()}) created`); + await sleep(3000); + console.log( + `Creating vest for vester at EVEN_LATER (${EVEN_LATER.toString()})...`, + ); await stakeConnection.program.methods .createVesting(EVEN_LATER, new BN(20e6)) .accounts({ ...accounts }) .signers([admin]) .rpc() .then(confirm); + console.log( + `Vest for vester at EVEN_LATER (${EVEN_LATER.toString()}) created`, + ); + await sleep(3000); + + const vestLater = PublicKey.findProgramAddressSync( + [ + Buffer.from(wasm.Constants.VEST_SEED()), + config.toBuffer(), + vesterTa.toBuffer(), + LATER.toBuffer("le", 8), + ], + stakeConnection.program.programId, + )[0]; + console.log(`Canceling vest for vester at LATER (${LATER.toString()})...`); await stakeConnection.program.methods .cancelVesting() .accountsPartial({ ...accounts, vest: vestLater }) .signers([admin]) .rpc() .then(confirm); + console.log(`Vest for vester at LATER (${LATER.toString()}) canceled`); + await sleep(3000); + console.log("Transferring WH tokens to Vault..."); const tx = new Transaction(); tx.add( createTransferCheckedInstruction( @@ -197,53 +207,34 @@ async function main() { WORMHOLE_TOKEN, vault, admin.publicKey, - 50e6, + 100e6, 6, undefined, TOKEN_PROGRAM_ID, ), ); await stakeConnection.provider.sendAndConfirm(tx, [admin]); + console.log("WH tokens transferred to Vault"); + await sleep(3000); + console.log("Withdrawing surplus..."); await stakeConnection.program.methods .withdrawSurplus() .accounts({ ...accounts }) .signers([admin]) .rpc() .then(confirm); + console.log("Surplus withdrawn"); + await sleep(3000); + console.log("Finalizing vesting config..."); await stakeConnection.program.methods .finalizeVestingConfig() .accounts({ ...accounts }) .signers([admin]) .rpc() .then(confirm); - - let stakeAccountCheckpointsAddress = - await vesterStakeConnection.delegateWithVest( - vester.publicKey, - WHTokenBalance.fromString("0"), - true, - config, - ); - - let stakeAccountMetadataAddress = - await vesterStakeConnection.getStakeMetadataAddress( - stakeAccountCheckpointsAddress, - ); - - await stakeConnection.program.methods - .claimVesting() - .accountsPartial({ - ...accounts, - vest: vestNow, - stakeAccountCheckpoints: stakeAccountCheckpointsAddress, - stakeAccountMetadata: stakeAccountMetadataAddress, - globalConfig: stakeConnection.configAddress, - }) - .signers([vester]) - .rpc({ skipPreflight: true }) - .then(confirm); + console.log("Vesting config finalized"); } main(); diff --git a/solana/app/deploy/devnet/vesting/delegateWithVest.ts b/solana/app/deploy/devnet/vesting/delegateWithVest.ts new file mode 100644 index 000000000..5de38fc6a --- /dev/null +++ b/solana/app/deploy/devnet/vesting/delegateWithVest.ts @@ -0,0 +1,31 @@ +// Usage: npx ts-node app/deploy/devnet/vesting/delegateWithVest.ts + +import { Wallet, AnchorProvider } from "@coral-xyz/anchor"; +import { Connection, PublicKey } from "@solana/web3.js"; +import { USER_AUTHORITY_KEYPAIR, RPC_NODE } from "../constants"; +import { StakeConnection } from "../../../StakeConnection"; +import { WHTokenBalance } from "../../../whTokenBalance"; + +async function main() { + const vester = USER_AUTHORITY_KEYPAIR; + + const connection = new Connection(RPC_NODE); + const vesterProvider = new AnchorProvider(connection, new Wallet(vester), {}); + const vesterStakeConnection = await StakeConnection.createStakeConnection( + connection, + vesterProvider.wallet as Wallet, + ); + + const config = new PublicKey("AHfPLNVnRGoACwMfoRCwWnCEJWjMX4x7Yq3ufg3tpjQQ"); + + console.log(`Delegate WH tokens with vests`); + await vesterStakeConnection.delegateWithVest( + vester.publicKey, + WHTokenBalance.fromString("10"), + true, + config, + ); + console.log(`WH tokens with vests successfully delegated`); +} + +main(); diff --git a/solana/app/deploy/devnet/vesting/result.md b/solana/app/deploy/devnet/vesting/result.md new file mode 100644 index 000000000..18fd44e0f --- /dev/null +++ b/solana/app/deploy/devnet/vesting/result.md @@ -0,0 +1,62 @@ +npx ts-node app/deploy/devnet/vesting/createVestingConfig.ts + +Vesting claim times: +NOW: 1741270829 (2025-03-06T14:20:29.000Z) +LATER: 1741271829 (2025-03-06T14:37:09.000Z) +EVEN_LATER: 1741272829 (2025-03-06T14:53:49.000Z) +Vesting config random seed: +seedBuffer: +seedBufferHex: 83cda9d4e2445bff +Vesting config account: PublicKey [PublicKey(AHfPLNVnRGoACwMfoRCwWnCEJWjMX4x7Yq3ufg3tpjQQ)] { + _bn: +} +Vesting balance account for vester: PublicKey [PublicKey(GCSubvJoUD5hDbzZeLVWzBGkbej22wUkT1aZgYPDLyzW)] { + _bn: +} +Initializing vesting config... +Vesting config initialized +Creating vesting balance for vester... +Vesting balance for vester created +Creating vest for vester at NOW (1741270829)... +Vest for vester at NOW (1741270829) created +Creating vest for vester at LATER (1741271829)... +Vest for vester at LATER (1741271829) created +Creating vest for vester at EVEN_LATER (1741272829)... +Vest for vester at EVEN_LATER (1741272829) created +Canceling vest for vester at LATER (1741271829)... +Vest for vester at LATER (1741271829) canceled +Transferring WH tokens to Vault... +WH tokens transferred to Vault +Withdrawing surplus... +Surplus withdrawn +Finalizing vesting config... +Vesting config finalized + +---------------- + +npx ts-node app/deploy/devnet/vesting/claimVesting.ts + +Vesting config account: PublicKey [PublicKey(AHfPLNVnRGoACwMfoRCwWnCEJWjMX4x7Yq3ufg3tpjQQ)] { + _bn: +} +Starting claimVesting... +claimVesting completed successfully. + +---------------- + +npx ts-node app/deploy/devnet/vesting/delegateWithVest.ts + +Delegate WH tokens with vests +WH tokens with vests successfully delegated + +---------------- + +npx ts-node app/deploy/devnet/vesting/transferVesting.ts + +vestingBalanceAccount.totalVestingBalance: 20000000 +Starting transferVesting... +transferVesting completed successfully. +vestingBalanceAccount.totalVestingBalance: 0 +newVestingBalanceAccount.totalVestingBalance: 20000000 +vesterStakeMetadata.recordedVestingBalance: 20000000 +vesterStakeMetadata.recordedBalance: 50000040000150 diff --git a/solana/app/deploy/devnet/vesting/transferVesting.ts b/solana/app/deploy/devnet/vesting/transferVesting.ts new file mode 100644 index 000000000..5e2f32339 --- /dev/null +++ b/solana/app/deploy/devnet/vesting/transferVesting.ts @@ -0,0 +1,285 @@ +// Usage: npx ts-node app/deploy/devnet/vesting/transferVesting.ts + +import { Wallet, AnchorProvider } from "@coral-xyz/anchor"; +import { + Connection, + PublicKey, + SystemProgram, + Transaction, +} from "@solana/web3.js"; +import { + VESTING_ADMIN_KEYPAIR, + USER_AUTHORITY_KEYPAIR, + USER2_AUTHORITY_KEYPAIR, + WORMHOLE_TOKEN, + RPC_NODE, +} from "../constants"; +import { + StakeAccountMetadata, + StakeConnection, +} from "../../../StakeConnection"; +import BN from "bn.js"; +import * as wasm from "@wormhole/staking-wasm"; +import { + ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + getAssociatedTokenAddressSync, +} from "@solana/spl-token"; +import { CheckpointAccount } from "../../../checkpoints"; +import { WHTokenBalance } from "../../../whTokenBalance"; + +function sleep(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +async function main() { + const admin = VESTING_ADMIN_KEYPAIR; + const vester = USER_AUTHORITY_KEYPAIR; + const newVester = USER2_AUTHORITY_KEYPAIR; + + const connection = new Connection(RPC_NODE); + const vesterProvider = new AnchorProvider(connection, new Wallet(vester), {}); + const vesterStakeConnection = await StakeConnection.createStakeConnection( + connection, + vesterProvider.wallet as Wallet, + ); + + const confirm = async (signature: string): Promise => { + const block = + await vesterStakeConnection.provider.connection.getLatestBlockhash(); + await vesterStakeConnection.provider.connection.confirmTransaction({ + signature, + ...block, + }); + + return signature; + }; + + const EVEN_LATER = new BN(1741272829); + const config = new PublicKey("AHfPLNVnRGoACwMfoRCwWnCEJWjMX4x7Yq3ufg3tpjQQ"); + + const vesterTa = getAssociatedTokenAddressSync( + WORMHOLE_TOKEN, + vester.publicKey, + false, + TOKEN_PROGRAM_ID, + ); + const newVesterTa = getAssociatedTokenAddressSync( + WORMHOLE_TOKEN, + newVester.publicKey, + false, + TOKEN_PROGRAM_ID, + ); + const adminAta = getAssociatedTokenAddressSync( + WORMHOLE_TOKEN, + admin.publicKey, + false, + TOKEN_PROGRAM_ID, + ); + + const vestEvenLater = PublicKey.findProgramAddressSync( + [ + Buffer.from(wasm.Constants.VEST_SEED()), + config.toBuffer(), + vesterTa.toBuffer(), + EVEN_LATER.toBuffer("le", 8), + ], + vesterStakeConnection.program.programId, + )[0]; + const newVestEvenLater = PublicKey.findProgramAddressSync( + [ + Buffer.from(wasm.Constants.VEST_SEED()), + config.toBuffer(), + newVesterTa.toBuffer(), + EVEN_LATER.toBuffer("le", 8), + ], + vesterStakeConnection.program.programId, + )[0]; + + const vestingBalance = PublicKey.findProgramAddressSync( + [ + Buffer.from(wasm.Constants.VESTING_BALANCE_SEED()), + config.toBuffer(), + vester.publicKey.toBuffer(), + ], + vesterStakeConnection.program.programId, + )[0]; + const newVestingBalance = PublicKey.findProgramAddressSync( + [ + Buffer.from(wasm.Constants.VESTING_BALANCE_SEED()), + config.toBuffer(), + newVester.publicKey.toBuffer(), + ], + vesterStakeConnection.program.programId, + )[0]; + + let stakeAccountMetadataAddress = + await vesterStakeConnection.getStakeMetadataAddress(vester.publicKey); + // console.log("stakeAccountMetadataAddress:", stakeAccountMetadataAddress) + let newStakeAccountMetadataAddress = + await vesterStakeConnection.getStakeMetadataAddress(newVester.publicKey); + + let vestingBalanceAccount = + await vesterStakeConnection.program.account.vestingBalance.fetch( + vestingBalance, + ); + console.log( + "vestingBalanceAccount.totalVestingBalance: ", + vestingBalanceAccount.totalVestingBalance.toString(), + ); + + let vesterDelegateStakeAccountOwner = await vesterStakeConnection.delegates( + vester.publicKey, + ); + let vesterDelegateStakeAccountMetadataAddress = + await vesterStakeConnection.getStakeMetadataAddress( + vesterDelegateStakeAccountOwner, + ); + let vesterDelegateStakeAccountCheckpointsAddress = + await vesterStakeConnection.getStakeAccountCheckpointsAddressByMetadata( + vesterDelegateStakeAccountMetadataAddress, + false, + ); + + let newVesterDelegateStakeAccountOwner = + await vesterStakeConnection.delegates(newVester.publicKey); + let newVesterDelegateStakeAccountMetadataAddress = + await vesterStakeConnection.getStakeMetadataAddress( + newVesterDelegateStakeAccountOwner, + ); + let newVesterDelegateStakeAccountCheckpointsAddress = + await vesterStakeConnection.getStakeAccountCheckpointsAddressByMetadata( + newVesterDelegateStakeAccountMetadataAddress, + false, + ); + + let accounts = { + vester: vester.publicKey, + mint: WORMHOLE_TOKEN, + vesterTa, + newVesterTa, + config, + vest: vestEvenLater, + newVest: newVestEvenLater, + vestingBalance, + newVestingBalance, + globalConfig: vesterStakeConnection.configAddress, + delegateStakeAccountCheckpoints: + newVesterDelegateStakeAccountCheckpointsAddress, + delegateStakeAccountMetadata: newVesterDelegateStakeAccountMetadataAddress, + stakeAccountMetadata: stakeAccountMetadataAddress, + newStakeAccountMetadata: null, + associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, + tokenProgram: TOKEN_PROGRAM_ID, + systemProgram: SystemProgram.programId, + }; + + console.log("Starting transferVesting..."); + let tx = new Transaction(); + tx.instructions = [ + await vesterStakeConnection.program.methods + .delegate( + newVesterDelegateStakeAccountOwner, + vesterDelegateStakeAccountOwner, + ) + .accountsPartial({ + payer: vester.publicKey, + currentDelegateStakeAccountCheckpoints: + vesterDelegateStakeAccountCheckpointsAddress, + delegateeStakeAccountCheckpoints: + newVesterDelegateStakeAccountCheckpointsAddress, + vestingConfig: config, + vestingBalance: vestingBalance, + mint: WORMHOLE_TOKEN, + }) + .instruction(), + await vesterStakeConnection.program.methods + .transferVesting() + .accountsPartial({ ...accounts }) + .instruction(), + await vesterStakeConnection.program.methods + .delegate( + vesterDelegateStakeAccountOwner, + newVesterDelegateStakeAccountOwner, + ) + .accountsPartial({ + payer: vester.publicKey, + currentDelegateStakeAccountCheckpoints: + newVesterDelegateStakeAccountCheckpointsAddress, + delegateeStakeAccountCheckpoints: + vesterDelegateStakeAccountCheckpointsAddress, + vestingConfig: config, + vestingBalance: vestingBalance, + mint: WORMHOLE_TOKEN, + }) + .instruction(), + ]; + await vesterStakeConnection.provider.sendAndConfirm(tx, [vester]); + console.log("transferVesting completed successfully."); + + // console.log(`Delegate WH tokens with vests`); + // await vesterStakeConnection.delegateWithVest( + // newVesterDelegateStakeAccountOwner, + // WHTokenBalance.fromString("0"), + // true, + // config, + // ); + // console.log(`WH tokens with vests successfully delegated`); + // await sleep(3000); + // + // console.log("Starting transferVesting..."); + // await vesterStakeConnection.program.methods + // .transferVesting() + // .accountsPartial({ + // ...accounts + // }) + // .rpc() + // .then(confirm); + // console.log("transferVesting completed successfully."); + // await sleep(3000); + // + // console.log(`Delegate WH tokens with vests`); + // await vesterStakeConnection.delegateWithVest( + // vesterDelegateStakeAccountOwner, + // WHTokenBalance.fromString("0"), + // true, + // config, + // ); + // console.log(`WH tokens with vests successfully delegated`); + + vestingBalanceAccount = + await vesterStakeConnection.program.account.vestingBalance.fetch( + vestingBalance, + ); + let newVestingBalanceAccount = + await vesterStakeConnection.program.account.vestingBalance.fetch( + newVestingBalance, + ); + console.log( + "vestingBalanceAccount.totalVestingBalance: ", + vestingBalanceAccount.totalVestingBalance.toString(), + ); + console.log( + "newVestingBalanceAccount.totalVestingBalance: ", + newVestingBalanceAccount.totalVestingBalance.toString(), + ); + + let vesterStakeMetadata: StakeAccountMetadata = + await vesterStakeConnection.fetchStakeAccountMetadata(vester.publicKey); + let stakeAccountCheckpointsAddress = + await vesterStakeConnection.getStakeAccountCheckpointsAddressByMetadata( + stakeAccountMetadataAddress, + false, + ); + + console.log( + "vesterStakeMetadata.recordedVestingBalance: ", + vesterStakeMetadata.recordedVestingBalance.toString(), + ); + console.log( + "vesterStakeMetadata.recordedBalance: ", + vesterStakeMetadata.recordedBalance.toString(), + ); +} + +main(); diff --git a/solana/app/deploy/mainnet/constants.ts b/solana/app/deploy/mainnet/constants.ts new file mode 100644 index 000000000..ea09623c5 --- /dev/null +++ b/solana/app/deploy/mainnet/constants.ts @@ -0,0 +1,73 @@ +import { PublicKey } from "@solana/web3.js"; +import { homedir } from "os"; +import { loadKeypair } from "../../../tests/utils/keys"; +import { contracts } from "@wormhole-foundation/sdk-base"; + +export const RPC_NODE = "https://api.mainnet-beta.solana.com"; + +/// Wormhole Token (W) (mainnet solana address) +export const WORMHOLE_TOKEN = new PublicKey( + "85VBFQZC9TZkfaptBWjvUw7YbZjy52A6mjtPGjstQAmQ", +); + +export const STAKING_ADDRESS = new PublicKey( + "AFuHPdrQGsW8rNQ4oEFF35sm5fg36gwrxyqjkjKvi6ap", +); + +export const CORE_BRIDGE_PID = new PublicKey( + contracts.coreBridge.get("Mainnet", "Solana")!, // worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth +); +console.log(CORE_BRIDGE_PID); + +/// Wormhole Hub Proposal Metadata Contract (mainnet ethereum address) +export const HUB_PROPOSAL_METADATA_ADDRESS = + "0x1a3e5624769c3dc9106347a239523e4a08d85c38"; // TODO: needs to be updated after a deployment to ethereum mainnet +export const hubProposalMetadataUint8Array = new Uint8Array( + HUB_PROPOSAL_METADATA_ADDRESS.slice(2) + .toLowerCase() + .match(/.{1,2}/g) + ?.map((byte) => parseInt(byte, 16)), +); + +/// Wormhole Hub Chain ID +// https://wormhole.com/docs/build/reference/chain-ids/#__tabbed_1_1 +export const HUB_CHAIN_ID = 2; // Mainnet + +export const CHECKPOINTS_ACCOUNT_LIMIT = 654998; + +/// Wormhole hubSolanaMessageDispatcher Contract (mainnet ethereum address) +export const HUB_SOLANA_MESSAGE_DISPATCHER_ADDRESS = + "0xaeb78fb7ddedbbcab908e91e94f1fb04a23fbce5"; // TODO: needs to be updated after a deployment to ethereum mainnet +const hubSolanaMessageDispatcherHex20 = + HUB_SOLANA_MESSAGE_DISPATCHER_ADDRESS.slice(2); +const hubSolanaMessageDispatcherHex32 = + "000000000000000000000000" + hubSolanaMessageDispatcherHex20.toLowerCase(); +const hubSolanaMessageDispatcherUint8Array32 = new Uint8Array( + hubSolanaMessageDispatcherHex32 + .match(/.{1,2}/g) + ?.map((byte) => parseInt(byte, 16)), +); +export const hubSolanaMessageDispatcherUint8Array20 = + hubSolanaMessageDispatcherUint8Array32.slice(-20); +export const hubSolanaMessageDispatcherPublicKey = new PublicKey( + hubSolanaMessageDispatcherUint8Array32, +); +// console.log("hubSolanaMessageDispatcherPublicKey:", hubSolanaMessageDispatcherPublicKey); + +export const DEPLOYER_AUTHORITY_PATH = "/.config/solana/deployer.json"; +export const DEPLOYER_AUTHORITY_KEYPAIR = loadKeypair( + homedir() + DEPLOYER_AUTHORITY_PATH, +); + +export const GOVERNANCE_AUTHORITY_PATH = + "/.config/solana/governanceAuthority.json"; +export const GOVERNANCE_AUTHORITY_KEYPAIR = loadKeypair( + homedir() + GOVERNANCE_AUTHORITY_PATH, +); + +export const VESTING_ADMIN_PATH = "/.config/solana/vestingAdmin.json"; +export const VESTING_ADMIN_KEYPAIR = loadKeypair( + homedir() + VESTING_ADMIN_PATH, +); + +export const VOTE_WEIGHT_WINDOW_LENGTHS = 10 * 60; // 10 minutes diff --git a/solana/app/deploy/mainnet/initialize/initAddressLookupTable.ts b/solana/app/deploy/mainnet/initialize/initAddressLookupTable.ts new file mode 100644 index 000000000..8af3de328 --- /dev/null +++ b/solana/app/deploy/mainnet/initialize/initAddressLookupTable.ts @@ -0,0 +1,34 @@ +// Usage: npx ts-node app/deploy/mainnet/initialize/initAddressLookupTable.ts + +import { Wallet, AnchorProvider } from "@coral-xyz/anchor"; +import { Connection } from "@solana/web3.js"; +import { + DEPLOYER_AUTHORITY_KEYPAIR, + RPC_NODE, + STAKING_ADDRESS, + WORMHOLE_TOKEN, +} from "../constants"; +import { initAddressLookupTable } from "../../../helpers/utils/lookup_table"; + +async function main() { + try { + const connection = new Connection(RPC_NODE); + const provider = new AnchorProvider( + connection, + new Wallet(DEPLOYER_AUTHORITY_KEYPAIR), + {}, + ); + + const lookupTableAddress = await initAddressLookupTable( + provider, + WORMHOLE_TOKEN, + STAKING_ADDRESS, + ); + + console.log("Lookup table address: ", lookupTableAddress.toBase58()); + } catch (err) { + console.error("Error:", err); + } +} + +main(); diff --git a/solana/app/deploy/mainnet/initialize/initConfig.ts b/solana/app/deploy/mainnet/initialize/initConfig.ts new file mode 100644 index 000000000..c43afb3aa --- /dev/null +++ b/solana/app/deploy/mainnet/initialize/initConfig.ts @@ -0,0 +1,36 @@ +// Usage: npx ts-node app/deploy/mainnet/initialize/initConfig.ts + +import { Wallet, AnchorProvider, Program } from "@coral-xyz/anchor"; +import { Connection } from "@solana/web3.js"; +import { + CHECKPOINTS_ACCOUNT_LIMIT, + STAKING_ADDRESS, + DEPLOYER_AUTHORITY_KEYPAIR, + GOVERNANCE_AUTHORITY_KEYPAIR, + VESTING_ADMIN_KEYPAIR, + WORMHOLE_TOKEN, + RPC_NODE, +} from "../constants"; + +async function main() { + const connection = new Connection(RPC_NODE); + const provider = new AnchorProvider( + connection, + new Wallet(DEPLOYER_AUTHORITY_KEYPAIR), + {}, + ); + + const idl = (await Program.fetchIdl(STAKING_ADDRESS, provider))!; + const program = new Program(idl, provider); + + const globalConfig = { + bump: 255, + governanceAuthority: GOVERNANCE_AUTHORITY_KEYPAIR.publicKey, + votingTokenMint: WORMHOLE_TOKEN, + vestingAdmin: VESTING_ADMIN_KEYPAIR.publicKey, + maxCheckpointsAccountLimit: CHECKPOINTS_ACCOUNT_LIMIT, + }; + await program.methods.initConfig(globalConfig).rpc(); +} + +main(); diff --git a/solana/app/deploy/07_update_HubProposalMetadata.ts b/solana/app/deploy/mainnet/initialize/initializeSpokeAirlock.ts similarity index 62% rename from solana/app/deploy/07_update_HubProposalMetadata.ts rename to solana/app/deploy/mainnet/initialize/initializeSpokeAirlock.ts index b12a5878f..c6d4b7886 100644 --- a/solana/app/deploy/07_update_HubProposalMetadata.ts +++ b/solana/app/deploy/mainnet/initialize/initializeSpokeAirlock.ts @@ -1,11 +1,9 @@ -// Usage: npx ts-node app/deploy/07_update_HubProposalMetadata.ts +// Usage: npx ts-node app/deploy/mainnet/initialize/initializeSpokeAirlock.ts -import * as anchor from "@coral-xyz/anchor"; import { AnchorProvider, Program, Wallet } from "@coral-xyz/anchor"; import { Connection, PublicKey, SystemProgram } from "@solana/web3.js"; -import { hubProposalMetadataUint8Array } from "../constants"; -import { DEPLOYER_AUTHORITY_KEYPAIR, RPC_NODE } from "./devnet"; -import { Staking } from "../../target/types/staking"; +import { DEPLOYER_AUTHORITY_KEYPAIR, RPC_NODE } from "../constants"; +import { Staking } from "../../../../target/types/staking"; import fs from "fs"; async function main() { @@ -30,9 +28,14 @@ async function main() { )[0]; await program.methods - .updateHubProposalMetadata(Array.from(hubProposalMetadataUint8Array)) - .accounts({ payer: DEPLOYER_AUTHORITY_KEYPAIR.publicKey, airlock: airlockPDA }) - .rpc(); + .initializeSpokeAirlock() + .accounts({ + payer: DEPLOYER_AUTHORITY_KEYPAIR.publicKey, + // @ts-ignore + airlock: airlockPDA, + systemProgram: SystemProgram.programId, + }) + .rpc({ skipPreflight: DEBUG }); } catch (err) { console.error("Error:", err); } diff --git a/solana/app/deploy/mainnet/initialize/initializeSpokeMessageExecutor.ts b/solana/app/deploy/mainnet/initialize/initializeSpokeMessageExecutor.ts new file mode 100644 index 000000000..bf78b5e8f --- /dev/null +++ b/solana/app/deploy/mainnet/initialize/initializeSpokeMessageExecutor.ts @@ -0,0 +1,55 @@ +// Usage: npx ts-node app/deploy/mainnet/initialize/initializeSpokeMessageExecutor.ts + +import { AnchorProvider, Program, Wallet } from "@coral-xyz/anchor"; +import { Connection, PublicKey, SystemProgram } from "@solana/web3.js"; +import { + DEPLOYER_AUTHORITY_KEYPAIR, + hubSolanaMessageDispatcherPublicKey, + HUB_CHAIN_ID, + RPC_NODE, +} from "../constants"; +import { Staking } from "../../../../target/types/staking"; +import fs from "fs"; +import { wasm } from "../../../StakeConnection"; + +async function main() { + try { + const connection = new Connection(RPC_NODE); + const provider = new AnchorProvider( + connection, + new Wallet(DEPLOYER_AUTHORITY_KEYPAIR), + {}, + ); + + let program: Program; + program = new Program( + JSON.parse(fs.readFileSync("./target/idl/staking.json").toString()), + provider, + ); + + const messageExecutorPDA: PublicKey = PublicKey.findProgramAddressSync( + [Buffer.from("spoke_message_executor")], + program.programId, + )[0]; + const config: PublicKey = PublicKey.findProgramAddressSync( + [Buffer.from(wasm.Constants.CONFIG_SEED())], + program.programId, + )[0]; + + await program.methods + .initializeSpokeMessageExecutor(HUB_CHAIN_ID) + .accounts({ + governanceAuthority: DEPLOYER_AUTHORITY_KEYPAIR.publicKey, + // @ts-ignore + executor: messageExecutorPDA, + config: config, + hubDispatcher: hubSolanaMessageDispatcherPublicKey, + systemProgram: SystemProgram.programId, + }) + .rpc(); + } catch (err) { + console.error("Error:", err); + } +} + +main(); diff --git a/solana/app/deploy/mainnet/initialize/initializeSpokeMetadataCollector.ts b/solana/app/deploy/mainnet/initialize/initializeSpokeMetadataCollector.ts new file mode 100644 index 000000000..2cba674b4 --- /dev/null +++ b/solana/app/deploy/mainnet/initialize/initializeSpokeMetadataCollector.ts @@ -0,0 +1,40 @@ +// Usage: npx ts-node app/deploy/mainnet/initialize/initializeSpokeMetadataCollector.ts + +import { AnchorProvider, Program, Wallet } from "@coral-xyz/anchor"; +import { Connection } from "@solana/web3.js"; +import { + DEPLOYER_AUTHORITY_KEYPAIR, + HUB_CHAIN_ID, + hubProposalMetadataUint8Array, + RPC_NODE, +} from "../constants"; +import { Staking } from "../../../../target/types/staking"; +import fs from "fs"; + +async function main() { + try { + const connection = new Connection(RPC_NODE); + const provider = new AnchorProvider( + connection, + new Wallet(DEPLOYER_AUTHORITY_KEYPAIR), + {}, + ); + + let program: Program; + program = new Program( + JSON.parse(fs.readFileSync("./target/idl/staking.json").toString()), + provider, + ); + + await program.methods + .initializeSpokeMetadataCollector( + HUB_CHAIN_ID, + Array.from(hubProposalMetadataUint8Array), + ) + .rpc(); + } catch (err) { + console.error("Error:", err); + } +} + +main(); diff --git a/solana/app/deploy/mainnet/initialize/initializeVoteWeightWindowLengths.ts b/solana/app/deploy/mainnet/initialize/initializeVoteWeightWindowLengths.ts new file mode 100644 index 000000000..493545090 --- /dev/null +++ b/solana/app/deploy/mainnet/initialize/initializeVoteWeightWindowLengths.ts @@ -0,0 +1,37 @@ +// Usage: npx ts-node app/deploy/mainnet/initialize/initializeVoteWeightWindowLengths.ts + +import { AnchorProvider, Program, Wallet } from "@coral-xyz/anchor"; +import { Connection } from "@solana/web3.js"; +import { + DEPLOYER_AUTHORITY_KEYPAIR, + RPC_NODE, + VOTE_WEIGHT_WINDOW_LENGTHS, +} from "../constants"; +import { Staking } from "../../../../target/types/staking"; +import fs from "fs"; +import BN from "bn.js"; + +async function main() { + try { + const connection = new Connection(RPC_NODE); + const provider = new AnchorProvider( + connection, + new Wallet(DEPLOYER_AUTHORITY_KEYPAIR), + {}, + ); + + let program: Program; + program = new Program( + JSON.parse(fs.readFileSync("./target/idl/staking.json").toString()), + provider, + ); + + await program.methods + .initializeVoteWeightWindowLengths(new BN(VOTE_WEIGHT_WINDOW_LENGTHS)) + .rpc(); + } catch (err) { + console.error("Error:", err); + } +} + +main(); diff --git a/solana/app/deploy/mainnet_beta.ts b/solana/app/deploy/mainnet_beta.ts deleted file mode 100644 index 4af5bb2c8..000000000 --- a/solana/app/deploy/mainnet_beta.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { PublicKey } from "@solana/web3.js"; -import { homedir } from "os"; -import { loadKeypair } from "../../tests/utils/keys"; -export const AUTHORITY_PATH = "/.config/solana/deployer.json"; -export const AUTHORITY_KEYPAIR = loadKeypair(homedir() + AUTHORITY_PATH); - -export const WORMHOLE_TOKEN = new PublicKey( - "Exne2kdeGToBnC2WVSdt1gLy6fjnNftbPtsCPx8AuL7V", -); - -export const RPC_NODE = "https://api.mainnet-beta.solana.com"; diff --git a/solana/app/deploy/new_wallet.ts b/solana/app/deploy/new_wallet.ts deleted file mode 100644 index 288d837ef..000000000 --- a/solana/app/deploy/new_wallet.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Keypair } from '@solana/web3.js'; -import fs from 'fs'; - -// Generate a new Keypair -const newWallet = Keypair.generate(); - -// Extract public and private keys -const publicKey = newWallet.publicKey.toString(); -const secretKey = Buffer.from(newWallet.secretKey).toString('base64'); // keep it secure - -console.log('Public Key:', publicKey); -console.log('Secret Key (base64):', secretKey); - -// Save the secret key to a file -fs.writeFileSync('app/deploy/user3.json', JSON.stringify([...newWallet.secretKey])); -console.log('Wallet saved to app/deploy/user3.json'); diff --git a/solana/app/e2e/01_createProposeWithSolanaExecution.ts b/solana/app/e2e/01_createProposeWithSolanaExecution.ts index c882d7cd3..dd70fbfe2 100644 --- a/solana/app/e2e/01_createProposeWithSolanaExecution.ts +++ b/solana/app/e2e/01_createProposeWithSolanaExecution.ts @@ -3,11 +3,6 @@ import { ethers } from "ethers"; import * as fs from "fs"; import "dotenv/config"; -import { - HUB_SOLANA_MESSAGE_DISPATCHER_ADDRESS, - HUB_GOVERNOR_ADDRESS, - HUB_CHAIN_ID, -} from "../constants"; import { Connection } from "@solana/web3.js"; import { AnchorProvider, Program, Wallet } from "@coral-xyz/anchor"; import { ExternalProgram } from "./external_program/idl/external_program"; @@ -16,7 +11,10 @@ import { DEPLOYER_AUTHORITY_KEYPAIR, RPC_NODE, AIRLOCK_PDA_ADDRESS, -} from "../deploy/devnet"; + HUB_SOLANA_MESSAGE_DISPATCHER_ADDRESS, + HUB_GOVERNOR_ADDRESS, + HUB_CHAIN_ID, +} from "../deploy/devnet/constants"; import { v4 as uuidv4 } from "uuid"; // Define the ABI types diff --git a/solana/app/e2e/02_addProposal.ts b/solana/app/e2e/02_addProposal.ts index 02cc17129..40faf0af3 100644 --- a/solana/app/e2e/02_addProposal.ts +++ b/solana/app/e2e/02_addProposal.ts @@ -14,14 +14,14 @@ import "dotenv/config"; import { HUB_CHAIN_ID, HUB_PROPOSAL_METADATA_ADDRESS, - STAKING_ADDRESS, - CORE_BRIDGE_ADDRESS, -} from "../constants"; -import * as anchor from "@coral-xyz/anchor"; + CORE_BRIDGE_PID, + DEPLOYER_AUTHORITY_KEYPAIR, + RPC_NODE, +} from "../deploy/devnet/constants"; + import { AnchorProvider, Wallet } from "@coral-xyz/anchor"; -import { Connection, Keypair } from "@solana/web3.js"; +import { Connection } from "@solana/web3.js"; import { StakeConnection } from "../StakeConnection"; -import { DEPLOYER_AUTHORITY_KEYPAIR, RPC_NODE } from "../deploy/devnet"; import { getWormholeBridgeData } from "../helpers/wormholeBridgeConfig"; import * as fs from "fs"; @@ -118,7 +118,7 @@ async function getProposalMetadata( data: encodeCalldata(encodedSignature, encodedParameters), }; - const latestFinalizedBlock = await getLatestFinalizedBlock(rpcUrl); + const latestFinalizedBlock = await getLatestFinalizedBlock(rpcUrl!); console.log( "latestFinalizedBlock:", parseInt(latestFinalizedBlock.toString(), 16), @@ -154,7 +154,6 @@ async function addProposal() { const stakeConnection = await StakeConnection.createStakeConnection( connection, provider.wallet as Wallet, - STAKING_ADDRESS, ); const proposalId = await input({ message: "Enter the proposal id:" }); @@ -169,7 +168,7 @@ async function addProposal() { while (true) { const latestFinalizedBlockInt = parseInt( - (await getLatestFinalizedBlock(rpcUrl)).toString(), + (await getLatestFinalizedBlock(rpcUrl!)).toString(), 16, ); console.log("Latest finalized block:", latestFinalizedBlockInt); @@ -203,7 +202,7 @@ async function addProposal() { sepoliaEthProposalResponse.signatures, ); - const info = await getWormholeBridgeData(connection, CORE_BRIDGE_ADDRESS); + const info = await getWormholeBridgeData(connection, CORE_BRIDGE_PID); const guardianSetIndex = info.guardianSetIndex; await stakeConnection.addProposal( diff --git a/solana/app/e2e/03_castVote.ts b/solana/app/e2e/03_castVote.ts index 16b6067fa..3dc86cf6b 100644 --- a/solana/app/e2e/03_castVote.ts +++ b/solana/app/e2e/03_castVote.ts @@ -1,12 +1,9 @@ // Usage: npx ts-node app/e2e/03_castVote.ts -import * as anchor from "@coral-xyz/anchor"; import { AnchorProvider, Wallet } from "@coral-xyz/anchor"; import { Connection } from "@solana/web3.js"; import { StakeConnection } from "../StakeConnection"; -import { STAKING_ADDRESS } from "../constants"; -import { USER2_AUTHORITY_KEYPAIR, RPC_NODE } from "../deploy/devnet"; -import BN from "bn.js"; +import { USER2_AUTHORITY_KEYPAIR, RPC_NODE } from "../deploy/devnet/constants"; import { WHTokenBalance } from "../whTokenBalance"; import input from "@inquirer/input"; @@ -27,7 +24,6 @@ async function castVote() { const user2StakeConnection = await StakeConnection.createStakeConnection( connection, user2Provider.wallet as Wallet, - STAKING_ADDRESS, ); await user2StakeConnection.castVote( diff --git a/solana/app/e2e/04_crossChainVoteSolana.ts b/solana/app/e2e/04_crossChainVoteSolana.ts index 3ec11e625..bc471f24f 100644 --- a/solana/app/e2e/04_crossChainVoteSolana.ts +++ b/solana/app/e2e/04_crossChainVoteSolana.ts @@ -3,7 +3,6 @@ import { ethers } from "ethers"; import * as fs from "fs"; import "dotenv/config"; -import { PublicKey } from "@solana/web3.js"; import axios from "axios"; import { PerChainQueryRequest, @@ -11,7 +10,6 @@ import { QueryRequest, QueryResponse, SolanaPdaQueryRequest, - SolanaPdaQueryResponse, signaturesToEvmStruct, } from "@wormhole-foundation/wormhole-query-sdk"; import { @@ -20,7 +18,7 @@ import { HUB_SOLANA_SPOKE_VOTE_DECODER_ADDRESS, HUB_VOTE_POOL_ADDRESS, HUB_CHAIN_ID, -} from "../constants"; +} from "../deploy/devnet/constants"; import * as wasm from "@wormhole/staking-wasm"; import input from "@inquirer/input"; @@ -76,7 +74,10 @@ async function getSolanaQueryResponse(): Promise<{ ).data; // console.log("queryResponse: ", queryResponse); - // const queryResponseHex = QueryResponse.from(Buffer.from(queryResponse.bytes, "hex")); + const queryResponseHex = QueryResponse.from( + Buffer.from(queryResponse.bytes, "hex"), + ); + console.log("queryResponseHex: ", queryResponseHex); const bytes = "0x" + queryResponse["bytes"]; const signatures = queryResponse["signatures"]; diff --git a/solana/app/e2e/05_proposalQueue.ts b/solana/app/e2e/05_proposalQueue.ts index 6c19faf8d..c2fca5140 100644 --- a/solana/app/e2e/05_proposalQueue.ts +++ b/solana/app/e2e/05_proposalQueue.ts @@ -3,12 +3,11 @@ import { ethers } from "ethers"; import * as fs from "fs"; import "dotenv/config"; -import { signaturesToEvmStruct } from "@wormhole-foundation/wormhole-query-sdk"; import { HUB_SOLANA_MESSAGE_DISPATCHER_ADDRESS, HUB_GOVERNOR_ADDRESS, HUB_CHAIN_ID, -} from "../constants"; +} from "../deploy/devnet/constants"; import input from "@inquirer/input"; const HubGovernorAbiPath = "./app/e2e/abi/HubGovernor.json"; diff --git a/solana/app/e2e/06_proposalExecute.ts b/solana/app/e2e/06_proposalExecute.ts index 5a660137f..73d60ba1f 100644 --- a/solana/app/e2e/06_proposalExecute.ts +++ b/solana/app/e2e/06_proposalExecute.ts @@ -3,12 +3,11 @@ import { ethers } from "ethers"; import * as fs from "fs"; import "dotenv/config"; -import { signaturesToEvmStruct } from "@wormhole-foundation/wormhole-query-sdk"; import { HUB_SOLANA_MESSAGE_DISPATCHER_ADDRESS, HUB_GOVERNOR_ADDRESS, HUB_CHAIN_ID, -} from "../constants"; +} from "../deploy/devnet/constants"; import input from "@inquirer/input"; const HubGovernorAbiPath = "./app/e2e/abi/HubGovernor.json"; diff --git a/solana/app/e2e/external_program/initializeConfigPDA.ts b/solana/app/e2e/external_program/initializeConfigPDA.ts index 4bd15452b..71f42ac57 100644 --- a/solana/app/e2e/external_program/initializeConfigPDA.ts +++ b/solana/app/e2e/external_program/initializeConfigPDA.ts @@ -1,11 +1,10 @@ -import * as anchor from "@coral-xyz/anchor"; import { AnchorProvider, Program, Wallet } from "@coral-xyz/anchor"; import { Connection, PublicKey, SystemProgram } from "@solana/web3.js"; import { USER2_AUTHORITY_KEYPAIR, RPC_NODE, AIRLOCK_PDA_ADDRESS, -} from "../../deploy/devnet"; +} from "../../deploy/devnet/constants"; import { ExternalProgram } from "./idl/external_program"; import externalProgramIdl from "./idl/external_program.json"; diff --git a/solana/app/helpers/utils/lookup_table.ts b/solana/app/helpers/utils/lookup_table.ts new file mode 100644 index 000000000..1536e63c2 --- /dev/null +++ b/solana/app/helpers/utils/lookup_table.ts @@ -0,0 +1,60 @@ +import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; +import { + PublicKey, + SystemProgram, + AddressLookupTableProgram, + ComputeBudgetProgram, + SYSVAR_RENT_PUBKEY, + VersionedTransaction, + TransactionMessage, +} from "@solana/web3.js"; +import * as anchor from "@coral-xyz/anchor"; +import * as wasm from "@wormhole/staking-wasm"; + +function getConfigAccount(programId: PublicKey): PublicKey { + return PublicKey.findProgramAddressSync( + [anchor.utils.bytes.utf8.encode(wasm.Constants.CONFIG_SEED())], + programId, + )[0]; +} + +export async function initAddressLookupTable( + provider: anchor.AnchorProvider, + mint: PublicKey, + stakingPID: PublicKey, +) { + const configAccount = getConfigAccount(stakingPID); + + const [loookupTableInstruction, lookupTableAddress] = + AddressLookupTableProgram.createLookupTable({ + authority: provider.publicKey, + payer: provider.publicKey, + recentSlot: await provider.connection.getSlot(), + }); + const extendInstruction = AddressLookupTableProgram.extendLookupTable({ + payer: provider.publicKey, + authority: provider.publicKey, + lookupTable: lookupTableAddress, + addresses: [ + ComputeBudgetProgram.programId, + SystemProgram.programId, + stakingPID, + mint, + configAccount, + SYSVAR_RENT_PUBKEY, + TOKEN_PROGRAM_ID, + ], + }); + const createLookupTableTx = new VersionedTransaction( + new TransactionMessage({ + instructions: [loookupTableInstruction, extendInstruction], + payerKey: provider.publicKey, + recentBlockhash: (await provider.connection.getLatestBlockhash()) + .blockhash, + }).compileToV0Message(), + ); + await provider.sendAndConfirm(createLookupTableTx, [], { + skipPreflight: true, + }); + return lookupTableAddress; +} diff --git a/solana/app/utils/parse_transactions.ts b/solana/app/helpers/utils/parse_transactions.ts similarity index 98% rename from solana/app/utils/parse_transactions.ts rename to solana/app/helpers/utils/parse_transactions.ts index 6402c78b2..b8076d85f 100644 --- a/solana/app/utils/parse_transactions.ts +++ b/solana/app/helpers/utils/parse_transactions.ts @@ -6,7 +6,7 @@ import { TransactionSignature, } from "@solana/web3.js"; import { BorshCoder } from "@coral-xyz/anchor"; -import IDL from "../../target/idl/staking.json"; +import IDL from "../../../target/idl/staking.json"; // Gets program transactions export async function getProgramTransactions( diff --git a/solana/app/index.ts b/solana/app/index.ts index 6f0c89c75..98a235ba7 100644 --- a/solana/app/index.ts +++ b/solana/app/index.ts @@ -1,3 +1,2 @@ export { StakeAccount, StakeConnection } from "./StakeConnection"; -export * from "./constants"; export { WH_TOKEN_DECIMALS, WHTokenBalance } from "./whTokenBalance"; diff --git a/solana/programs/staking/src/context.rs b/solana/programs/staking/src/context.rs index 8469c5f59..210415fb3 100644 --- a/solana/programs/staking/src/context.rs +++ b/solana/programs/staking/src/context.rs @@ -441,8 +441,8 @@ pub struct UpdateGovernanceAuthority<'info> { #[derive(Accounts)] pub struct ClaimGovernanceAuthority<'info> { #[account( - mut, - seeds = [CONFIG_SEED.as_bytes()], + mut, + seeds = [CONFIG_SEED.as_bytes()], bump = config.bump, constraint = ( config.pending_governance_authority == Some(new_authority.key()) @@ -466,8 +466,8 @@ pub struct UpdateVestingAdmin<'info> { #[derive(Accounts)] pub struct ClaimVestingAdmin<'info> { #[account( - mut, - seeds = [CONFIG_SEED.as_bytes()], + mut, + seeds = [CONFIG_SEED.as_bytes()], bump = config.bump, constraint = ( config.pending_vesting_admin == Some(new_vesting_admin.key()) diff --git a/solana/programs/staking/src/contexts/cancel_vesting.rs b/solana/programs/staking/src/contexts/cancel_vesting.rs index 49cfef0fb..588676b50 100644 --- a/solana/programs/staking/src/contexts/cancel_vesting.rs +++ b/solana/programs/staking/src/contexts/cancel_vesting.rs @@ -1,10 +1,10 @@ +use crate::context::{CONFIG_SEED, VESTING_BALANCE_SEED, VESTING_CONFIG_SEED, VEST_SEED}; +use crate::error::VestingError; +use crate::state::global_config::GlobalConfig; +use crate::state::{Vesting, VestingBalance, VestingConfig}; use anchor_lang::prelude::*; use anchor_spl::associated_token::AssociatedToken; use anchor_spl::token::{Mint, Token, TokenAccount}; -use crate::context::{VESTING_BALANCE_SEED, VESTING_CONFIG_SEED, VEST_SEED, CONFIG_SEED}; -use crate::error::VestingError; -use crate::state::{Vesting, VestingBalance, VestingConfig}; -use crate::state::global_config::GlobalConfig; #[derive(Accounts)] pub struct CancelVesting<'info> { diff --git a/solana/programs/staking/src/contexts/claim_vesting.rs b/solana/programs/staking/src/contexts/claim_vesting.rs index 1ae7b01b4..e4f806cfa 100644 --- a/solana/programs/staking/src/contexts/claim_vesting.rs +++ b/solana/programs/staking/src/contexts/claim_vesting.rs @@ -8,7 +8,7 @@ use crate::{ }; use anchor_lang::prelude::*; use anchor_spl::associated_token::AssociatedToken; -use anchor_spl::token::{Mint, Token, TokenAccount, transfer_checked, TransferChecked}; +use anchor_spl::token::{transfer_checked, Mint, Token, TokenAccount, TransferChecked}; use std::convert::TryInto; #[event_cpi] diff --git a/solana/programs/staking/src/contexts/create_vesting.rs b/solana/programs/staking/src/contexts/create_vesting.rs index 8e7f77196..673c807a8 100644 --- a/solana/programs/staking/src/contexts/create_vesting.rs +++ b/solana/programs/staking/src/contexts/create_vesting.rs @@ -1,10 +1,10 @@ -use anchor_lang::prelude::*; -use anchor_spl::associated_token::AssociatedToken; -use anchor_spl::token::{Mint, Token, TokenAccount}; use crate::context::{CONFIG_SEED, VESTING_BALANCE_SEED, VESTING_CONFIG_SEED, VEST_SEED}; use crate::error::VestingError; use crate::state::global_config::GlobalConfig; use crate::state::{Vesting, VestingBalance, VestingConfig}; +use anchor_lang::prelude::*; +use anchor_spl::associated_token::AssociatedToken; +use anchor_spl::token::{Mint, Token, TokenAccount}; #[derive(Accounts)] #[instruction(maturation: i64)] diff --git a/solana/programs/staking/src/contexts/finalize.rs b/solana/programs/staking/src/contexts/finalize.rs index 398fc53dd..41023ac86 100644 --- a/solana/programs/staking/src/contexts/finalize.rs +++ b/solana/programs/staking/src/contexts/finalize.rs @@ -1,9 +1,9 @@ -use crate::context::{VESTING_CONFIG_SEED, CONFIG_SEED}; +use crate::context::{CONFIG_SEED, VESTING_CONFIG_SEED}; use crate::error::VestingError; +use crate::state::global_config::GlobalConfig; use crate::state::VestingConfig; use anchor_lang::prelude::*; use anchor_spl::token::{Mint, Token, TokenAccount}; -use crate::state::global_config::GlobalConfig; #[derive(Accounts)] pub struct Finalize<'info> { diff --git a/solana/programs/staking/src/contexts/transfer_vesting.rs b/solana/programs/staking/src/contexts/transfer_vesting.rs index 74ae22316..65a259538 100644 --- a/solana/programs/staking/src/contexts/transfer_vesting.rs +++ b/solana/programs/staking/src/contexts/transfer_vesting.rs @@ -120,8 +120,10 @@ impl<'info> crate::contexts::TransferVesting<'info> { return err!(VestingError::TransferVestToMyself); } - let sender_has_delegated_vest = self.vesting_balance.stake_account_metadata != Pubkey::default(); - let recipient_has_delegated_vest = self.new_vesting_balance.stake_account_metadata != Pubkey::default(); + let sender_has_delegated_vest = + self.vesting_balance.stake_account_metadata != Pubkey::default(); + let recipient_has_delegated_vest = + self.new_vesting_balance.stake_account_metadata != Pubkey::default(); // There are only 3 valid input account permutations. let delegate_votes_changed = match ( @@ -134,7 +136,14 @@ impl<'info> crate::contexts::TransferVesting<'info> { ) { // If the sender and recipient have delegated their vests, then we don't need the delegate accounts as we enforce below that the delegates // have to be the same. Thus, no there is no net change in delegate voting power and no new checkpoints are needed - (true, true, None, None, Some(stake_account_metadata), Some(new_stake_account_metadata)) => { + ( + true, + true, + None, + None, + Some(stake_account_metadata), + Some(new_stake_account_metadata), + ) => { // The sender must be delegated to the same address as the recipient. require!( stake_account_metadata.delegate == new_stake_account_metadata.delegate, @@ -178,16 +187,23 @@ impl<'info> crate::contexts::TransferVesting<'info> { transfer_vesting_events.new_stake_account_metadata = Some(StakeAccountMetadataEvents { recorded_vesting_balance_changed, - delegate_votes_changed + delegate_votes_changed, }); - + Ok(None) - }, + } // If the sender has delegated their vest, but the recipient hasn't then all bar the new_stake_account_metadata account must be provided - (true, false, Some(delegate_stake_account_checkpoints), Some(delegate_stake_account_metadata), Some(stake_account_metadata), None) => { + ( + true, + false, + Some(delegate_stake_account_checkpoints), + Some(delegate_stake_account_metadata), + Some(stake_account_metadata), + None, + ) => { // In this case the current delegate will have their voting balance reduced. That's what we do below. // We don't update the recorded vesting balance of the sender here, we instead do it later outside of the match cases - + // Check if stake account checkpoints is out of bounds let loaded_checkpoints = delegate_stake_account_checkpoints.load()?; require!( @@ -201,7 +217,7 @@ impl<'info> crate::contexts::TransferVesting<'info> { stake_account_metadata.delegate.key() == loaded_checkpoints.owner, VestingError::InvalidStakeAccountCheckpoints ); - drop(loaded_checkpoints); + drop(loaded_checkpoints); // Verify that the actual delegate_stake_account_metadata address matches the expected one require!( @@ -254,10 +270,10 @@ impl<'info> crate::contexts::TransferVesting<'info> { } Ok(delegate_votes_changed) - }, + } // If neither the sender or receiver has delegated their vests, then none of the optional accounts are needed // No changes need to be made to any accounts apart from the vesting balance accounts - (false, false, None, None, None, None) => { Ok(None) }, + (false, false, None, None, None, None) => Ok(None), // Any other combination is invalid _ => err!(VestingError::ErrorOfAccountParsing), }?; diff --git a/solana/programs/staking/src/contexts/withdraw_surplus.rs b/solana/programs/staking/src/contexts/withdraw_surplus.rs index 52d6fc2c4..f9ab27a06 100644 --- a/solana/programs/staking/src/contexts/withdraw_surplus.rs +++ b/solana/programs/staking/src/contexts/withdraw_surplus.rs @@ -1,10 +1,10 @@ -use crate::context::{VESTING_CONFIG_SEED, CONFIG_SEED}; +use crate::context::{CONFIG_SEED, VESTING_CONFIG_SEED}; use crate::error::VestingError; +use crate::state::global_config::GlobalConfig; use crate::state::VestingConfig; use anchor_lang::prelude::*; use anchor_spl::associated_token::AssociatedToken; -use anchor_spl::token::{Mint, Token, TokenAccount, transfer_checked, TransferChecked}; -use crate::state::global_config::GlobalConfig; +use anchor_spl::token::{transfer_checked, Mint, Token, TokenAccount, TransferChecked}; #[derive(Accounts)] pub struct WithdrawSurplus<'info> { diff --git a/solana/programs/staking/src/lib.rs b/solana/programs/staking/src/lib.rs index badedaee3..7287590a1 100644 --- a/solana/programs/staking/src/lib.rs +++ b/solana/programs/staking/src/lib.rs @@ -75,7 +75,7 @@ pub struct ProposalCreated { pub vote_start: u64, } -declare_id!("DgCSKsLDXXufYeEkvf21YSX5DMnFK89xans5WdSsUbeY"); +declare_id!("AFuHPdrQGsW8rNQ4oEFF35sm5fg36gwrxyqjkjKvi6ap"); #[program] pub mod staking { /// Creates a global config for the program @@ -98,12 +98,16 @@ pub mod staking { // Solana accounts are 10MB maximum = 10485760 bytes // The checkpoint account contains 8 + 32 + 8 = 48 bytes of fixed data // Every checkpoint is 8 + 8 = 16 bytes, so we can fit in (10485760 - 48) / 16 = 655,357 checkpoints - require!(args.max_checkpoints_account_limit <= 655_000, ErrorCode::InvalidCheckpointAccountLimit); + require!( + args.max_checkpoints_account_limit <= 655_000, + ErrorCode::InvalidCheckpointAccountLimit + ); // Similarly make sure max_checkpoints_account_limit > MAX_VOTE_WEIGHT_WINDOW_LENGTH so we can't have // 3 checkpoint accounts fall across a window. We don't mind for our tests #[cfg(not(feature = "testing"))] { - require!(args.max_checkpoints_account_limit > state::vote_weight_window_lengths::VoteWeightWindowLengths::MAX_VOTE_WEIGHT_WINDOW_LENGTH as u32, ErrorCode::InvalidCheckpointAccountLimit); + require!(args.max_checkpoints_account_limit > state::vote_weight_window_lengths::VoteWeightWindowLengths::MAX_VOTE_WEIGHT_WINDOW_LENGTH as u32, + ErrorCode::InvalidCheckpointAccountLimit); } config_account.max_checkpoints_account_limit = args.max_checkpoints_account_limit; config_account.pending_governance_authority = None; @@ -491,14 +495,13 @@ pub mod staking { .current_delegate_stake_account_checkpoints .load()?; if loaded_checkpoints.next_index >= config.max_checkpoints_account_limit.into() { - if ctx.accounts.current_delegate_stake_account_metadata.key() + if ctx.accounts.current_delegate_stake_account_metadata.key() != ctx.accounts.stake_account_metadata.key() { ctx.accounts .current_delegate_stake_account_metadata .stake_account_checkpoints_last_index += 1; - } - else { + } else { ctx.accounts .stake_account_metadata .stake_account_checkpoints_last_index += 1; @@ -890,10 +893,15 @@ pub mod staking { let spoke_metadata_collector = &mut ctx.accounts.spoke_metadata_collector; if spoke_metadata_collector.updates_controlled_by_governance { - require!(ctx.accounts.payer.key() == ctx.accounts.config.governance_authority, ErrorCode::NotGovernanceAuthority); - } - else { - require!(ctx.accounts.airlock.to_account_info().is_signer, ErrorCode::AirlockNotSigner); + require!( + ctx.accounts.payer.key() == ctx.accounts.config.governance_authority, + ErrorCode::NotGovernanceAuthority + ); + } else { + require!( + ctx.accounts.airlock.to_account_info().is_signer, + ErrorCode::AirlockNotSigner + ); } let _ = spoke_metadata_collector.update_hub_proposal_metadata(new_hub_proposal_metadata); @@ -902,7 +910,7 @@ pub mod staking { } pub fn relinquish_admin_control_over_hub_proposal_metadata( - ctx: Context + ctx: Context, ) -> Result<()> { let spoke_metadata_collector = &mut ctx.accounts.spoke_metadata_collector; spoke_metadata_collector.updates_controlled_by_governance = false; diff --git a/solana/programs/staking/src/wasm.rs b/solana/programs/staking/src/wasm.rs index dee1e6247..69d732915 100644 --- a/solana/programs/staking/src/wasm.rs +++ b/solana/programs/staking/src/wasm.rs @@ -179,6 +179,7 @@ reexport_seed_const!(VEST_SEED); reexport_seed_const!(SPOKE_METADATA_COLLECTOR_SEED); reexport_seed_const!(VOTE_WEIGHT_WINDOW_LENGTHS_SEED); reexport_seed_const!(GUARDIAN_SIGNATURES_SEED); +reexport_seed_const!(SPOKE_MESSAGE_EXECUTOR_SEED); #[wasm_bindgen] impl Constants { diff --git a/solana/tests/api_test.ts b/solana/tests/api_test.ts index 51996f2d8..73eb4c20f 100644 --- a/solana/tests/api_test.ts +++ b/solana/tests/api_test.ts @@ -26,11 +26,8 @@ import { createProposalQueryResponseBytesWithInvalidChainSpecificResponse, createProposalQueryResponseBytesWithInvalidFunctionSignature, } from "./utils/api_utils"; -import { - StakeConnection, - WHTokenBalance, - TEST_CHECKPOINTS_ACCOUNT_LIMIT, -} from "../app"; +import { StakeConnection, WHTokenBalance } from "../app"; +import { TEST_CHECKPOINTS_ACCOUNT_LIMIT } from "./utils/constants"; import { CheckpointAccount } from "../app/checkpoints"; import crypto from "crypto"; import { @@ -196,12 +193,12 @@ describe("api", async () => { user8 = user8StakeConnection.provider.wallet.publicKey; user9StakeConnection = await newUserStakeConnection( - stakeConnection, - Keypair.generate(), - config, - whMintAccount, - whMintAuthority, - WHTokenBalance.fromString("1000"), + stakeConnection, + Keypair.generate(), + config, + whMintAccount, + whMintAuthority, + WHTokenBalance.fromString("1000"), ); user9 = user9StakeConnection.provider.wallet.publicKey; }); @@ -845,7 +842,7 @@ describe("api", async () => { ) .accounts({ currentDelegateStakeAccountCheckpoints: - currentDelegateStakeAccountCheckpointsAddress, + currentDelegateStakeAccountCheckpointsAddress, destination: toAccount, }) .rpc(); @@ -965,10 +962,15 @@ describe("api", async () => { currentStakeAccountCheckpointsAddress, ); - let currentCheckpointCount = currentStakeAccountCheckpoints.getCheckpointCount(); + let currentCheckpointCount = + currentStakeAccountCheckpoints.getCheckpointCount(); // Fill all bar 1 checkpoints in the limit. Leave 1 space for the withdraw checkpoint - for (currentCheckpointCount; currentCheckpointCount < TEST_CHECKPOINTS_ACCOUNT_LIMIT - 1; currentCheckpointCount++) { + for ( + currentCheckpointCount; + currentCheckpointCount < TEST_CHECKPOINTS_ACCOUNT_LIMIT - 1; + currentCheckpointCount++ + ) { await sleep(1000); await user8StakeConnection.delegate( user8StakeConnection.userPublicKey(), @@ -980,9 +982,13 @@ describe("api", async () => { currentStakeAccountCheckpointsAddress, ); - let stakeAccountMetadata = await user8StakeConnection.fetchStakeAccountMetadata(user8StakeConnection.userPublicKey()); + let stakeAccountMetadata = + await user8StakeConnection.fetchStakeAccountMetadata( + user8StakeConnection.userPublicKey(), + ); - let previousCheckpointAccountIndex = stakeAccountMetadata.stakeAccountCheckpointsLastIndex; + let previousCheckpointAccountIndex = + stakeAccountMetadata.stakeAccountCheckpointsLastIndex; let balanceBefore = stakeAccount.tokenBalance; // This withdraw action fills up the checkpoint account, which should increment the checkpoint account index @@ -996,19 +1002,21 @@ describe("api", async () => { currentStakeAccountCheckpointsAddress, ); - stakeAccountMetadata = await user8StakeConnection.fetchStakeAccountMetadata(user8StakeConnection.userPublicKey()); - + stakeAccountMetadata = + await user8StakeConnection.fetchStakeAccountMetadata( + user8StakeConnection.userPublicKey(), + ); - let newCheckpointAccountIndex = stakeAccountMetadata.stakeAccountCheckpointsLastIndex; + let newCheckpointAccountIndex = + stakeAccountMetadata.stakeAccountCheckpointsLastIndex; let balanceAfter = stakeAccount.tokenBalance; // Both the checkpoint index and the balance should be properly update - assert.equal(previousCheckpointAccountIndex + 1, newCheckpointAccountIndex); assert.equal( - balanceBefore - balanceAfter, - 5000000, + previousCheckpointAccountIndex + 1, + newCheckpointAccountIndex, ); - + assert.equal(balanceBefore - balanceAfter, 5000000); }); }); @@ -1503,19 +1511,19 @@ describe("api", async () => { let user9StakeAccountCheckpoints: CheckpointAccount = await user9StakeConnection.fetchCheckpointAccount( - user9StakeAccountCheckpointsAddress, + user9StakeAccountCheckpointsAddress, ); assert.equal( - user9StakeAccountCheckpoints.checkpoints[0].value.toString(), + user9StakeAccountCheckpoints.checkpoints[0].value.toString(), "0", ); assert.equal( - user9StakeAccountCheckpoints.checkpoints[1].value.toString(), + user9StakeAccountCheckpoints.checkpoints[1].value.toString(), "225000000", ); let proposalIdInput = await addTestProposal( - user9StakeConnection, + user9StakeConnection, Math.floor(Date.now() / 1000), ); const { proposalAccount } = diff --git a/solana/tests/config.ts b/solana/tests/config.ts index 9972b6327..bc60546df 100644 --- a/solana/tests/config.ts +++ b/solana/tests/config.ts @@ -10,18 +10,14 @@ import * as wasm from "@wormhole/staking-wasm"; import assert from "assert"; import BN from "bn.js"; import path from "path"; +import { StakeConnection, WHTokenBalance, WH_TOKEN_DECIMALS } from "../app"; import { - StakeConnection, - TEST_CHECKPOINTS_ACCOUNT_LIMIT, - WH_TOKEN_DECIMALS, - WHTokenBalance, -} from "../app"; -import { - CORE_BRIDGE_ADDRESS, + CORE_BRIDGE_PID, HUB_CHAIN_ID, hubProposalMetadataUint8Array, -} from "../app/constants"; -import { StakeAccountMetadata } from "../app/StakeConnection.ts"; + TEST_CHECKPOINTS_ACCOUNT_LIMIT, +} from "./utils/constants"; +import { StakeAccountMetadata } from "../app/StakeConnection"; import { readWindowLengths, WindowLengthsAccount, @@ -234,7 +230,7 @@ describe("config", async () => { ); assert.equal( spokeMetadataCollectorAccountData.wormholeCore.toString("hex"), - CORE_BRIDGE_ADDRESS.toString("hex"), + CORE_BRIDGE_PID.toString("hex"), ); }); @@ -344,7 +340,7 @@ describe("config", async () => { ); assert.equal( spokeMetadataCollectorAccountData.wormholeCore.toString("hex"), - CORE_BRIDGE_ADDRESS.toString("hex"), + CORE_BRIDGE_PID.toString("hex"), ); }); @@ -434,7 +430,6 @@ describe("config", async () => { const samConnection = await StakeConnection.createStakeConnection( program.provider.connection, new Wallet(sam), - program.programId, ); await samConnection.program.provider.connection.requestAirdrop( @@ -467,7 +462,6 @@ describe("config", async () => { const vestingAdminConnection = await StakeConnection.createStakeConnection( program.provider.connection, new Wallet(vestingAdminKeypair), - program.programId, ); await vestingAdminConnection.program.provider.connection.requestAirdrop( diff --git a/solana/tests/executor.ts b/solana/tests/executor.ts index bbf0406a8..60b194825 100644 --- a/solana/tests/executor.ts +++ b/solana/tests/executor.ts @@ -26,29 +26,24 @@ import { mocks } from "@wormhole-foundation/sdk-definitions/testing"; import { SolanaWormholeCore, utils as coreUtils, - derivePostedVaaKey, } from "@wormhole-foundation/sdk-solana-core"; import { SolanaSendSigner } from "@wormhole-foundation/sdk-solana"; import { signAndSendWait } from "@wormhole-foundation/sdk-connect"; -import { Chain, contracts } from "@wormhole-foundation/sdk-base"; +import { Chain } from "@wormhole-foundation/sdk-base"; import { AnchorError, Program, utils } from "@coral-xyz/anchor"; -import { ExternalProgram } from "./artifacts/external_program.ts"; +import { ExternalProgram } from "./artifacts/external_program"; import externalProgramIdl from "./artifacts/external_program.json"; import BN from "bn.js"; import * as wasm from "@wormhole/staking-wasm"; import { readWindowLengths, WindowLengthsAccount, -} from "../app/vote_weight_window_lengths.ts"; +} from "../app/vote_weight_window_lengths"; +import { CORE_BRIDGE_PID } from "./utils/constants"; // Define the port number for the test const portNumber = getPortNumber(path.basename(__filename)); -// Constants -export const CORE_BRIDGE_PID = new PublicKey( - contracts.coreBridge.get("Testnet", "Solana")!, -); - export const GUARDIAN_KEY = "cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0"; export const MOCK_GUARDIANS = new mocks.MockGuardians(0, [GUARDIAN_KEY]); diff --git a/solana/tests/utils/api_utils.ts b/solana/tests/utils/api_utils.ts index 09be7f199..326d8b2cf 100644 --- a/solana/tests/utils/api_utils.ts +++ b/solana/tests/utils/api_utils.ts @@ -1,10 +1,7 @@ -import { StakeAccount, StakeConnection } from "../../app/StakeConnection"; +import { StakeConnection } from "../../app/StakeConnection"; import { PublicKey } from "@solana/web3.js"; import { WHTokenBalance } from "../../app"; -import { - HUB_CHAIN_ID, - HUB_PROPOSAL_METADATA_ADDRESS, -} from "../../app/constants"; +import { HUB_CHAIN_ID, HUB_PROPOSAL_METADATA_ADDRESS } from "./constants"; import assert from "assert"; import { QueryRequest, @@ -42,8 +39,13 @@ export async function assertBalanceMatches( owner: PublicKey, expected: WHTokenBalance, ) { - const stakeAccountCheckpointsAddress = - await stakeConnection.getStakeAccountCheckpointsAddress(owner); + let stakeAccountMetadataAddress = + await stakeConnection.getStakeMetadataAddress(owner); + let stakeAccountCheckpointsAddress = + await stakeConnection.getStakeAccountCheckpointsAddressByMetadata( + stakeAccountMetadataAddress, + false, + ); let stakeAccount = await stakeConnection.loadStakeAccount( stakeAccountCheckpointsAddress, ); diff --git a/solana/tests/utils/before.ts b/solana/tests/utils/before.ts index d08b5943e..bedf3e10a 100644 --- a/solana/tests/utils/before.ts +++ b/solana/tests/utils/before.ts @@ -22,20 +22,17 @@ import BN from "bn.js"; import toml from "toml"; import path from "path"; import os from "os"; -import { - StakeConnection, - WHTokenBalance, - WH_TOKEN_DECIMALS, - CHECKPOINTS_ACCOUNT_LIMIT, - TEST_CHECKPOINTS_ACCOUNT_LIMIT, -} from "../../app"; +import { StakeConnection, WHTokenBalance, WH_TOKEN_DECIMALS } from "../../app"; import { GlobalConfig } from "../../app/StakeConnection"; -import { createMint, initAddressLookupTable } from "./utils"; +import { createMint } from "./utils"; +import { initAddressLookupTable } from "../../app/helpers/utils/lookup_table"; import { loadKeypair } from "./keys"; import { HUB_CHAIN_ID, hubProposalMetadataUint8Array, -} from "../../app/constants"; + CHECKPOINTS_ACCOUNT_LIMIT, + TEST_CHECKPOINTS_ACCOUNT_LIMIT, +} from "./constants"; export const ANCHOR_CONFIG_PATH = "./Anchor.toml"; export interface AnchorConfig { @@ -343,7 +340,6 @@ export async function newUserStakeConnection( const userStakeConnection = await StakeConnection.createStakeConnection( connection, provider.wallet as Wallet, - new PublicKey(config.programs.localnet.staking), ); await transferSolFromValidatorWallet( @@ -438,8 +434,9 @@ export async function standardSetup( const lookupTableAddress = await initAddressLookupTable( provider, whMintAccount.publicKey, + program.programId, ); - // console.log("Lookup table address: ", lookupTableAddress.toBase58()); + // console.log("Lookup table address: ", lookupTableAddress.toBase58()); await program.methods .initializeSpokeMetadataCollector( @@ -475,7 +472,7 @@ export async function standardSetup( const stakeConnection = await StakeConnection.createStakeConnection( connection, provider.wallet as Wallet, - new PublicKey(config.programs.localnet.staking), + lookupTableAddress, ); return { controller, stakeConnection }; diff --git a/solana/tests/utils/constants.ts b/solana/tests/utils/constants.ts new file mode 100644 index 000000000..f0a95f377 --- /dev/null +++ b/solana/tests/utils/constants.ts @@ -0,0 +1,22 @@ +import { contracts } from "@wormhole-foundation/sdk-base"; +import { PublicKey } from "@solana/web3.js"; + +/// Wormhole Hub Proposal Metadata Contract (sepolia ethereum address) +export const HUB_PROPOSAL_METADATA_ADDRESS = + "0x1a3e5624769c3dc9106347a239523e4a08d85c38"; +export const hubProposalMetadataUint8Array = new Uint8Array( + HUB_PROPOSAL_METADATA_ADDRESS.slice(2) + .toLowerCase() + .match(/.{1,2}/g) + ?.map((byte) => parseInt(byte, 16)), +); + +/// Wormhole Hub Chain ID +export const HUB_CHAIN_ID = 10002; // SEPOLIA + +export const CHECKPOINTS_ACCOUNT_LIMIT = 654998; +export const TEST_CHECKPOINTS_ACCOUNT_LIMIT = 15; + +export const CORE_BRIDGE_PID = new PublicKey( + contracts.coreBridge.get("Testnet", "Solana")!, // 3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5 +); diff --git a/solana/tests/utils/utils.ts b/solana/tests/utils/utils.ts index ebc282dfe..5a9257694 100644 --- a/solana/tests/utils/utils.ts +++ b/solana/tests/utils/utils.ts @@ -9,25 +9,10 @@ import { Keypair, Transaction, SystemProgram, - AddressLookupTableProgram, - ComputeBudgetProgram, - SYSVAR_RENT_PUBKEY, - VersionedTransaction, - TransactionMessage, } from "@solana/web3.js"; import * as anchor from "@coral-xyz/anchor"; import { AnchorError } from "@coral-xyz/anchor"; import assert from "assert"; -import * as wasm from "@wormhole/staking-wasm"; -import { Staking } from "../../target/types/staking"; -import { STAKING_ADDRESS } from "../../app"; - -export function getConfigAccount(programId: PublicKey): PublicKey { - return PublicKey.findProgramAddressSync( - [anchor.utils.bytes.utf8.encode(wasm.Constants.CONFIG_SEED())], - programId, - )[0]; -} /** * Creates new spl-token at a random keypair @@ -71,46 +56,6 @@ export async function createMint( }); } -export async function initAddressLookupTable( - provider: anchor.AnchorProvider, - mint: PublicKey, -) { - const configAccount = getConfigAccount(STAKING_ADDRESS); - - const [loookupTableInstruction, lookupTableAddress] = - AddressLookupTableProgram.createLookupTable({ - authority: provider.publicKey, - payer: provider.publicKey, - recentSlot: await provider.connection.getSlot(), - }); - const extendInstruction = AddressLookupTableProgram.extendLookupTable({ - payer: provider.publicKey, - authority: provider.publicKey, - lookupTable: lookupTableAddress, - addresses: [ - ComputeBudgetProgram.programId, - SystemProgram.programId, - STAKING_ADDRESS, - mint, - configAccount, - SYSVAR_RENT_PUBKEY, - TOKEN_PROGRAM_ID, - ], - }); - const createLookupTableTx = new VersionedTransaction( - new TransactionMessage({ - instructions: [loookupTableInstruction, extendInstruction], - payerKey: provider.publicKey, - recentBlockhash: (await provider.connection.getLatestBlockhash()) - .blockhash, - }).compileToV0Message(), - ); - await provider.sendAndConfirm(createLookupTableTx, [], { - skipPreflight: true, - }); - return lookupTableAddress; -} - /** * Sends the rpc call and check whether the error message matches the provided string * @param rpcCall : anchor rpc call diff --git a/solana/tests/vesting.ts b/solana/tests/vesting.ts index c38534952..b841b520f 100644 --- a/solana/tests/vesting.ts +++ b/solana/tests/vesting.ts @@ -385,7 +385,6 @@ describe("vesting", () => { ], stakeConnection.program.programId, )[0]; - vestingBalanceWithoutAccount = PublicKey.findProgramAddressSync( [ Buffer.from(wasm.Constants.VESTING_BALANCE_SEED()), diff --git a/solana/tests/voter_votes_test.ts b/solana/tests/voter_votes_test.ts index dcc103b75..1ad6bd97d 100644 --- a/solana/tests/voter_votes_test.ts +++ b/solana/tests/voter_votes_test.ts @@ -4,13 +4,11 @@ import { getPortNumber, makeDefaultConfig, readAnchorConfig, - requestWHTokenAirdrop, standardSetup, } from "./utils/before"; import path from "path"; import { Keypair, PublicKey } from "@solana/web3.js"; import { StakeConnection, WHTokenBalance } from "../app"; -import { BN, Wallet } from "@coral-xyz/anchor"; import assert from "assert"; const portNumber = getPortNumber(path.basename(__filename));