-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Deployment scripts for Solana (#688)
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
Showing
14 changed files
with
5,134 additions
and
337 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module.exports = { | ||
...require("@thesis-co/prettier-config"), | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
Oops, something went wrong.