Skip to content

Commit

Permalink
Deployment scripts for Solana (#688)
Browse files Browse the repository at this point in the history
These changes include bash scripts and init scripts that are used to
build, deploy and init `tbtc` & `wormhole_gateway` programs on Solana.

Usage:
`./scripts/deploy.sh` - due to the nature of deployment on Solana, it is
very tricky to do everything in one shot. Please read the comments.
Certain parts of this script need to be uncommented one by one to deploy
programs properly

`./scripts/transfer_authority.sh` - same as above, please read the
comments and uncomment the parts one by one. For `mainnet` use only.

`tbtc` on mainnet
https://solscan.io/account/Gj93RRt6QB7FjmyokAD5rcMAku7pq3Fk2Aa8y6nNbwsV
`wormhole_gateway` on mainnet
https://solscan.io/account/87MEvHZCXE3ML5rrmh5uX1FbShHmRXXS32xJDGbQ7h5t

Commented-out commands in the bash scripts are intentional because it is
tricky to deploy on Solana in one shot.
  • Loading branch information
pdyraga authored Aug 21, 2023
2 parents d9b9a6c + ae73a0e commit 59af02b
Show file tree
Hide file tree
Showing 14 changed files with 5,134 additions and 337 deletions.
6 changes: 6 additions & 0 deletions cross-chain/solana/.env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export AUTHORITY=<path_to_keypair_deployer>
export THRESHOLD_COUNCIL_MULTISIG=<multisig_address>
export NETWORK=<solana-devnet, mainnet>
export CLUSTER=<devnet, mainnet>
export ANCHOR_PROVIDER_URL=<http://localhost:8899, https://api.devnet.solana.com, https://api.mainnet-beta.solana.com>
export ANCHOR_WALLET=<path_to_keypair_deployer>
7 changes: 5 additions & 2 deletions cross-chain/solana/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
.anchor
.prettierrc.json
.DS_Store
target
/target/*
**/*.rs.bk
node_modules
test-ledger
artifacts-mainnet
artifacts-testnet
artifacts-testnet
.env
!/target/idl/
!/target/idl/*
3 changes: 3 additions & 0 deletions cross-chain/solana/.prettierrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
...require("@thesis-co/prettier-config"),
}
8 changes: 7 additions & 1 deletion cross-chain/solana/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,10 @@ lint:
cargo fmt --check
cargo check --features "mainnet" --no-default-features
cargo check --features "solana-devnet" --no-default-features
cargo clippy --no-deps --all-targets -- -D warnings
cargo clippy --no-deps --all-targets -- -D warnings

init_programs:
ts-node --files ./deploy/init.ts

transfer_authority:
ts-node --files ./deploy/transfer_authority.ts
46 changes: 46 additions & 0 deletions cross-chain/solana/deploy/helpers/consts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { WRAPPED_TBTC_MINT } from "./../../tests/helpers/consts"
export const WH_ARBITRUM_CHAIN_ID = 23

export const WH_OPTIMISM_CHAIN_ID = 24

export const WH_POLYGON_CHAIN_ID = 5

export const WH_BASE_CHAIN_ID = 30

export const WH_SOLANA_CHAIN_ID = 1

// EVM addresses converted to 32 bytes. 0x is trimmed intentionally as the input
// param requires it without leading 0x.

export const ARBITRUM_GATEWAY_ADDRESS_TESTNET =
"00000000000000000000000031a15e213b59e230b45e8c5c99dafac3d1236ee2"

export const ARBITRUM_GATEWAY_ADDRESS_MAINNET =
"0000000000000000000000001293a54e160d1cd7075487898d65266081a15458"

export const OPTIMISM_GATEWAY_ADDRESS_TESTNET =
"0000000000000000000000006449F4381f3d63bDfb36B3bDc375724aD3cD4621"
export const OPTIMISM_GATEWAY_ADDRESS_MAINNET =
"0000000000000000000000001293a54e160D1cd7075487898d65266081A15458"

export const POLYGON_GATEWAY_ADDRESS_TESTNET =
"00000000000000000000000091Fe7128f74dBd4F031ea3D90FC5Ea4DCfD81818"
export const POLYGON_GATEWAY_ADDRESS_MAINNET =
"00000000000000000000000009959798B95d00a3183d20FaC298E4594E599eab"

export const BASE_GATEWAY_ADDRESS_TESTNET =
"000000000000000000000000e3e0511EEbD87F08FbaE4486419cb5dFB06e1343"
export const BASE_GATEWAY_ADDRESS_MAINNET =
"00000000000000000000000009959798B95d00a3183d20FaC298E4594E599eab"

export const SOLANA_GATEWAY_ADDRESS_TESTNET =
"87MEvHZCXE3ML5rrmh5uX1FbShHmRXXS32xJDGbQ7h5t"
export const SOLANA_GATEWAY_ADDRESS_MAINNET =
"87MEvHZCXE3ML5rrmh5uX1FbShHmRXXS32xJDGbQ7h5t"

// deriveWrappedMintKey("DZnkkTmCiFWfYTfT41X3Rd1kDgozqzxWaHqsw6W4x2oe", 2, "0x679874fbe6d4e7cc54a59e315ff1eb266686a937")
export const WRAPPED_TBTC_MINT_TESTNET =
"FMYvcyMJJ22whB9m3T5g1oPKwM6jpLnFBXnrY6eXmCrp"
// deriveWrappedMintKey("wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb", 2, "0x18084fbA666a33d37592fA2633fD49a74DD93a88")
export const WRAPPED_TBTC_MINT_MAINNET =
"25rXTx9zDZcHyTav5sRqM6YBvTGu9pPH9yv83uAEqbgG"
294 changes: 294 additions & 0 deletions cross-chain/solana/deploy/init.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
import * as anchor from "@coral-xyz/anchor"
import fs from "fs"
import { PublicKey, Keypair } from "@solana/web3.js"
import dotenv from "dotenv"
import { Program } from "@coral-xyz/anchor"
import { Tbtc } from "../target/types/tbtc"
import { WormholeGateway } from "../target/types/wormhole_gateway"
import { PROGRAM_ID as METADATA_PROGRAM_ID } from "@metaplex-foundation/mpl-token-metadata"
import * as consts from "./helpers/consts"

async function run(): Promise<void> {
dotenv.config({ path: "../solana.env" })

anchor.setProvider(anchor.AnchorProvider.env())

const tbtcProgram = anchor.workspace.Tbtc as Program<Tbtc>
const wormholeGatewayProgram = anchor.workspace
.WormholeGateway as Program<WormholeGateway>

// This wallet deployed the program and is also an authority
const authority = loadKey(process.env.AUTHORITY).publicKey

const mint = PublicKey.findProgramAddressSync(
[Buffer.from("tbtc-mint")],
tbtcProgram.programId
)[0]

const config = PublicKey.findProgramAddressSync(
[Buffer.from("config")],
tbtcProgram.programId
)[0]

const guardians = PublicKey.findProgramAddressSync(
[Buffer.from("guardians")],
tbtcProgram.programId
)[0]

const minters = PublicKey.findProgramAddressSync(
[Buffer.from("minters")],
tbtcProgram.programId
)[0]

const tbtcMetadata = PublicKey.findProgramAddressSync(
[Buffer.from("metadata"), METADATA_PROGRAM_ID.toBuffer(), mint.toBuffer()],
METADATA_PROGRAM_ID
)[0]

const mplTokenMetadataProgram = METADATA_PROGRAM_ID

// Initalize tbtc program
await tbtcProgram.methods
.initialize()
.accounts({
mint,
config,
guardians,
minters,
authority,
tbtcMetadata,
mplTokenMetadataProgram,
})
.rpc()

const minter = PublicKey.findProgramAddressSync(
[Buffer.from("redeemer")],
wormholeGatewayProgram.programId
)[0]

const mintingLimit = "18446744073709551615" // Max u64
let WRAPPED_TBTC = consts.WRAPPED_TBTC_MINT_TESTNET
if (process.env.CLUSTER === "mainnet") {
WRAPPED_TBTC = consts.WRAPPED_TBTC_MINT_MAINNET
}
const WRAPPED_TBTC_MINT = new PublicKey(WRAPPED_TBTC)

const gatewayWrappedTbtcToken = PublicKey.findProgramAddressSync(
[Buffer.from("wrapped-token")],
wormholeGatewayProgram.programId
)[0]

const tokenBridgeSender = PublicKey.findProgramAddressSync(
[Buffer.from("sender")],
wormholeGatewayProgram.programId
)[0]

// NOTE: It might happen on mainnet that tbtc won't be initialized if running this
// script in one shot.
// The simplest solution is just to wait a bit and then proceed with wormhole_gateway
// initializtion.

// Initialize wormhole gateway
await wormholeGatewayProgram.methods
.initialize(new anchor.BN(mintingLimit))
.accounts({
authority,
custodian: minter,
tbtcMint: mint,
wrappedTbtcMint: WRAPPED_TBTC_MINT,
wrappedTbtcToken: gatewayWrappedTbtcToken,
tokenBridgeSender,
})
.rpc()

console.log("Initialized wormhole gateway program..")

const minterInfo = PublicKey.findProgramAddressSync(
[Buffer.from("minter-info"), minter.toBuffer()],
tbtcProgram.programId
)[0]

// Adding a minter (wormholeGateway)
await tbtcProgram.methods
.addMinter()
.accounts({
config,
authority,
minters,
minterInfo,
minter,
})
.rpc()

console.log("Added a minter..")

// Point to devnet addresses by default
let ARBITRUM_GATEWAY = consts.ARBITRUM_GATEWAY_ADDRESS_TESTNET
let OPTIMISM_GATEWAY = consts.OPTIMISM_GATEWAY_ADDRESS_TESTNET
let POLYGON_GATEWAY = consts.POLYGON_GATEWAY_ADDRESS_TESTNET
let BASE_GATEWAY = consts.BASE_GATEWAY_ADDRESS_TESTNET
let SOLANA_GATEWAY = consts.SOLANA_GATEWAY_ADDRESS_TESTNET
if (process.env.CLUSTER === "mainnet") {
ARBITRUM_GATEWAY = consts.ARBITRUM_GATEWAY_ADDRESS_MAINNET
OPTIMISM_GATEWAY = consts.OPTIMISM_GATEWAY_ADDRESS_MAINNET
POLYGON_GATEWAY = consts.POLYGON_GATEWAY_ADDRESS_MAINNET
BASE_GATEWAY = consts.BASE_GATEWAY_ADDRESS_MAINNET
SOLANA_GATEWAY = consts.SOLANA_GATEWAY_ADDRESS_MAINNET
}

// Updating with Arbitrum
const arbiArgs = {
chain: consts.WH_ARBITRUM_CHAIN_ID,
address: Array.from(Buffer.alloc(32, ARBITRUM_GATEWAY, "hex")),
}

const encodedArbiChain = Buffer.alloc(2)
encodedArbiChain.writeUInt16LE(consts.WH_ARBITRUM_CHAIN_ID)
const gatewayArbiInfo = PublicKey.findProgramAddressSync(
[Buffer.from("gateway-info"), encodedArbiChain],
wormholeGatewayProgram.programId
)[0]

await wormholeGatewayProgram.methods
.updateGatewayAddress(arbiArgs)
.accounts({
custodian: minter,
gatewayInfo: gatewayArbiInfo,
authority,
})
.rpc()

console.log(
"Updated Solana gateway with Arbitrum..",
Array.from(new PublicKey(ARBITRUM_GATEWAY).toBuffer())
)

// Updating with Optimism
const optiArgs = {
chain: consts.WH_OPTIMISM_CHAIN_ID,
address: Array.from(Buffer.alloc(32, OPTIMISM_GATEWAY, "hex")),
}

const encodedOptiChain = Buffer.alloc(2)
encodedOptiChain.writeUInt16LE(consts.WH_OPTIMISM_CHAIN_ID)
const gatewayOptiInfo = PublicKey.findProgramAddressSync(
[Buffer.from("gateway-info"), encodedOptiChain],
wormholeGatewayProgram.programId
)[0]

await wormholeGatewayProgram.methods
.updateGatewayAddress(optiArgs)
.accounts({
custodian: minter,
gatewayInfo: gatewayOptiInfo,
authority,
})
.rpc()

console.log(
"Updated Solana gateway with Optimism..",
Array.from(new PublicKey(OPTIMISM_GATEWAY).toBuffer())
)

// Updating with Polygon
const polyArgs = {
chain: consts.WH_POLYGON_CHAIN_ID,
address: Array.from(Buffer.alloc(32, POLYGON_GATEWAY, "hex")),
}

const encodedPolyChain = Buffer.alloc(2)
encodedPolyChain.writeUInt16LE(consts.WH_POLYGON_CHAIN_ID)
const gatewayPolyInfo = PublicKey.findProgramAddressSync(
[Buffer.from("gateway-info"), encodedPolyChain],
wormholeGatewayProgram.programId
)[0]

await wormholeGatewayProgram.methods
.updateGatewayAddress(polyArgs)
.accounts({
custodian: minter,
gatewayInfo: gatewayPolyInfo,
authority,
})
.rpc()

console.log(
"Updated Solana gateway with Polygon..",
Array.from(new PublicKey(POLYGON_GATEWAY).toBuffer())
)

// Updating with BASE
const baseArgs = {
chain: consts.WH_BASE_CHAIN_ID,
address: Array.from(Buffer.alloc(32, BASE_GATEWAY, "hex")),
}

const encodedBaseChain = Buffer.alloc(2)
encodedBaseChain.writeUInt16LE(consts.WH_BASE_CHAIN_ID)
const gatewayBaseInfo = PublicKey.findProgramAddressSync(
[Buffer.from("gateway-info"), encodedBaseChain],
wormholeGatewayProgram.programId
)[0]

await wormholeGatewayProgram.methods
.updateGatewayAddress(baseArgs)
.accounts({
custodian: minter,
gatewayInfo: gatewayBaseInfo,
authority,
})
.rpc()

console.log(
"Updated Solana gateway with Base..",
Array.from(Buffer.alloc(32, BASE_GATEWAY, "hex"))
)

// Updating with self (SOLANA)
const solanaArgs = {
chain: consts.WH_SOLANA_CHAIN_ID,
address: Array.from(new PublicKey(SOLANA_GATEWAY).toBuffer()),
}

const encodedSolanaChain = Buffer.alloc(2)
encodedSolanaChain.writeUInt16LE(consts.WH_SOLANA_CHAIN_ID)
const gatewaySolanaInfo = PublicKey.findProgramAddressSync(
[Buffer.from("gateway-info"), encodedSolanaChain],
wormholeGatewayProgram.programId
)[0]

await wormholeGatewayProgram.methods
.updateGatewayAddress(solanaArgs)
.accounts({
custodian: minter,
gatewayInfo: gatewaySolanaInfo,
authority,
})
.rpc()

console.log(
"Updated Solana gateway with self (Solana)..",
Array.from(new PublicKey(SOLANA_GATEWAY).toBuffer())
)

console.log("Done initializing programs!")
}

;(async () => {
try {
await run()
} catch (e) {
console.log("Exception called:", e)
}
})()

function loadKey(filename: string): Keypair {
try {
const contents = fs.readFileSync(filename).toString()
const bs = Uint8Array.from(JSON.parse(contents))

return Keypair.fromSecretKey(bs)
} catch {
console.log("Unable to read keypair...", filename)
}
}
Loading

0 comments on commit 59af02b

Please sign in to comment.