-
Notifications
You must be signed in to change notification settings - Fork 737
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* wormchain: testnet wasm deploy * wormchain: testnet contract upgrade script * wormchain: global accountant registration script
- Loading branch information
Showing
3 changed files
with
539 additions
and
0 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,63 @@ | ||
import { toUtf8 } from "@cosmjs/encoding"; | ||
import { | ||
getWallet, | ||
getWormchainSigningClient, | ||
} from "@wormhole-foundation/wormchain-sdk"; | ||
import { ZERO_FEE } from "@wormhole-foundation/wormchain-sdk/lib/core/consts"; | ||
import "dotenv/config"; | ||
|
||
async function main() { | ||
/* Set up cosmos client & wallet */ | ||
|
||
const wallet = await getWallet(process.env.MNEMONIC); | ||
const client = await getWormchainSigningClient( | ||
process.env.WORMCHAIN_HOST, | ||
wallet | ||
); | ||
|
||
// there are several Cosmos chains in devnet, so check the config is as expected | ||
let id = await client.getChainId(); | ||
if (id !== "wormchain") { | ||
throw new Error( | ||
`Wormchain CosmWasmClient connection produced an unexpected chainID: ${id}` | ||
); | ||
} | ||
|
||
const signers = await wallet.getAccounts(); | ||
const signer = signers[0].address; | ||
console.log("wormchain contract deployer is: ", signer); | ||
|
||
const msg = client.wasm.msgExecuteContract({ | ||
sender: signer, | ||
contract: | ||
"wormhole14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9srrg465", | ||
msg: toUtf8( | ||
JSON.stringify({ | ||
submit_vaas: { | ||
vaas: [ | ||
Buffer.from( | ||
// This is the mainnet governance VAA for registering the Sui Token Bridge signed by the Guardians https://api.wormscan.io/api/v1/vaas/1/0000000000000000000000000000000000000000000000000000000000000004/8526464727442833848 | ||
"01000000030d00a42d36048a27a763413bfa1d261daeb86f7feed7a9850d9915d8c44acee97dd4327c0351648ac4d496050e6af6534badd2b86416a5675f373e5a5ebf876505700002349ec8ae9aeef147e87b2b06a475d6ec760c20680285ecdd6b7b400cbd67504232e0cae965d27dc9f0a83fbed40ec0a759bd7ad9a014a69c90364a1df88048dc000323d01a78c01887d981e22ffb0f91d02c1d7f015393513db3f215af64bd9cb74829eb08fc9895f8b1247933dd23c2b71ac1bf5bbefb8bea829ab2f6b94317eaa901040305b91b17227313395a861365c8b110414e961ccf25a2b645226e6a307e488f6dace0896c354425f5dfb6b0cb968ef1f752653cd85179b9344f215b555601dd01060dcfd80b54b6f43e502b9628a5a2b92b453fa96718397ecbe0e279495ff37c0835fff5ee7f3b749de287a0c3440105ddd705d06975d56384792373645b7787df000715985ff2cf28e3a8062d9ffc7ef69fbd7c56082f0938cc564586d0dbcacf14986579e7d7a8629dc8a9cfcbf0c97c46ae6492d05c5fba193400746a43b6f1123d000a5db8adfca6d43dd345e130fa0cae250ac7cfa364a29d47cf219ce2b50d6f930a0e1bc399b5b92cdec3d00fcf6f2c7f0732996344812dae85afcfa077c67d94b8010bf68fe7c2ed3aaa180b01ba28052fa63d72509e642bf45f8b5c14c582d8e6eb99514d41ab7fd3f6451470e02a054a3630e347020b6330a8ec23efdc3e4da4550b010d7f3ec58dbb8ae21a2fb71941ed80d646469f1992e7fdc32706c327bfbe01b98011e8b377fce487237f9238fe9af09991f5da11d85aba5a4a81e99df8d066aead010e18e7de979a55bd568b26754fdd7d9e7b03572d742e5657f944ab35b44398a40e07a0c2399e13a244277138375e7e980bf6b666f39bc2f86afd2605f0249a5a53000f77d089279a354b7faa1f3fdc084f6e0ef684d9bcce8d9fb11b5568c0d0b215f15d54cc4383e1b7112fadc238f750b885f5f81a21f84e00ec4487a8064386cc2e01106d9d3067e19413e985f76852eb0cdd071fef659540ddf3a9d5610d492a68a13c61cd109f64c977c1274f9782dcbddfa46ee94331e02f98ba8fb37e22300bd63e0111819a499e30feb82190736054d2993918aeb591e3098b4df77630e93512fec4c122f2cccfe88f2b735f42a06571944d800f3dfcb07de7956330515ddb3c9a41360000000000764b7752000100000000000000000000000000000000000000000000000000000000000000047654167e9520c1b820000000000000000000000000000000000000000000546f6b656e4272696467650100000015ccceeb29348f71bdd22ffef43a2a19c1f5b5e17c5cca5411529120182672ade5", | ||
"hex" | ||
).toString("base64"), | ||
], | ||
}, | ||
}) | ||
), | ||
funds: [], | ||
}); | ||
const res = await client.signAndBroadcast(signer, [msg], { | ||
...ZERO_FEE, | ||
gas: "10000000", | ||
}); | ||
console.log(res); | ||
} | ||
|
||
try { | ||
main(); | ||
} catch (e: any) { | ||
if (e?.message) { | ||
console.error(e.message); | ||
} | ||
throw e; | ||
} |
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,246 @@ | ||
import { | ||
CHAIN_ID_WORMCHAIN, | ||
hexToUint8Array, | ||
Other, | ||
Payload, | ||
serialiseVAA, | ||
sign, | ||
VAA, | ||
} from "@certusone/wormhole-sdk"; | ||
import { toBinary } from "@cosmjs/cosmwasm-stargate"; | ||
import { fromBase64 } from "@cosmjs/encoding"; | ||
import { | ||
getWallet, | ||
getWormchainSigningClient, | ||
} from "@wormhole-foundation/wormchain-sdk"; | ||
import { ZERO_FEE } from "@wormhole-foundation/wormchain-sdk/lib/core/consts"; | ||
import "dotenv/config"; | ||
import * as fs from "fs"; | ||
import { readdirSync } from "fs"; | ||
import { keccak256 } from "js-sha3"; | ||
import * as util from "util"; | ||
|
||
if (process.env.INIT_SIGNERS_KEYS_CSV === "undefined") { | ||
let msg = `.env is missing. run "make contracts-tools-deps" to fetch.`; | ||
console.error(msg); | ||
throw msg; | ||
} | ||
const VAA_SIGNERS = process.env.INIT_SIGNERS_KEYS_CSV.split(","); | ||
const WORMCHAIN_HOST = process.env.WORMCHAIN_HOST; | ||
if (!WORMCHAIN_HOST) { | ||
throw "WORMCHAIN_HOST unset"; | ||
} | ||
let MNEMONIC = process.env.MNEMONIC; | ||
if (!MNEMONIC) { | ||
throw "MNEMONIC unset"; | ||
} | ||
|
||
const GOVERNANCE_CHAIN = 1; | ||
const GOVERNANCE_EMITTER = | ||
"0000000000000000000000000000000000000000000000000000000000000004"; | ||
|
||
const readFileAsync = util.promisify(fs.readFile); | ||
|
||
/* | ||
NOTE: Only append to this array: keeping the ordering is crucial, as the | ||
contracts must be imported in a deterministic order so their addresses remain | ||
deterministic. | ||
*/ | ||
type ContractName = string; | ||
const artifacts: ContractName[] = ["wormchain_ibc_receiver.wasm"]; | ||
|
||
const ARTIFACTS_PATH = "../artifacts/"; | ||
/* Check that the artifact folder contains all the wasm files we expect and nothing else */ | ||
|
||
try { | ||
const actual_artifacts = readdirSync(ARTIFACTS_PATH).filter((a) => | ||
a.endsWith(".wasm") | ||
); | ||
|
||
const missing_artifacts = artifacts.filter( | ||
(a) => !actual_artifacts.includes(a) | ||
); | ||
if (missing_artifacts.length) { | ||
console.log( | ||
"Error during wormchain deployment. The following files are expected to be in the artifacts folder:" | ||
); | ||
missing_artifacts.forEach((file) => console.log(` - ${file}`)); | ||
console.log( | ||
"Hint: the deploy script needs to run after the contracts have been built." | ||
); | ||
console.log( | ||
"External binary blobs need to be manually added in tools/Dockerfile." | ||
); | ||
process.exit(1); | ||
} | ||
} catch (err) { | ||
console.error( | ||
`${ARTIFACTS_PATH} cannot be read. Do you need to run "make contracts-deploy-setup"?` | ||
); | ||
process.exit(1); | ||
} | ||
|
||
async function main() { | ||
/* Set up cosmos client & wallet */ | ||
|
||
const wallet = await getWallet(MNEMONIC); | ||
const client = await getWormchainSigningClient(WORMCHAIN_HOST, wallet); | ||
|
||
// there are several Cosmos chains in devnet, so check the config is as expected | ||
let id = await client.getChainId(); | ||
if (id !== "wormchain-testnet-0") { | ||
throw new Error( | ||
`Wormchain CosmWasmClient connection produced an unexpected chainID: ${id}` | ||
); | ||
} | ||
|
||
const signers = await wallet.getAccounts(); | ||
const signer = signers[0].address; | ||
console.log("wormchain contract deployer is: ", signer); | ||
|
||
/* Deploy artifacts */ | ||
|
||
const codeIds: { [name: ContractName]: number } = await artifacts.reduce( | ||
async (prev, file) => { | ||
// wait for the previous to finish, to avoid the race condition of wallet sequence mismatch. | ||
const accum = await prev; | ||
|
||
const contract_bytes = await readFileAsync(`${ARTIFACTS_PATH}${file}`); | ||
|
||
const payload = keccak256(contract_bytes); | ||
let vaa: VAA<Other> = { | ||
version: 1, | ||
guardianSetIndex: 0, | ||
signatures: [], | ||
timestamp: 0, | ||
nonce: 0, | ||
emitterChain: GOVERNANCE_CHAIN, | ||
emitterAddress: GOVERNANCE_EMITTER, | ||
sequence: BigInt(Math.floor(Math.random() * 100000000)), | ||
consistencyLevel: 0, | ||
payload: { | ||
type: "Other", | ||
// 32-byte id `WasmdModule`, action = 1 (ActionStoreCode), chain = Wormchain, payload = wasm hash | ||
// https://github.com/wormhole-foundation/wormhole/blob/main/sdk/vaa/payloads.go#L55 | ||
hex: `0000000000000000000000000000000000000000005761736D644D6F64756C65010${CHAIN_ID_WORMCHAIN.toString( | ||
16 | ||
)}${payload}`, | ||
}, | ||
}; | ||
vaa.signatures = sign(VAA_SIGNERS, vaa as unknown as VAA<Payload>); | ||
console.log("uploading", file); | ||
const msg = client.core.msgStoreCode({ | ||
signer, | ||
wasm_byte_code: new Uint8Array(contract_bytes), | ||
vaa: hexToUint8Array(serialiseVAA(vaa as unknown as VAA<Payload>)), | ||
}); | ||
const result = await client.signAndBroadcast(signer, [msg], { | ||
...ZERO_FEE, | ||
gas: "10000000", | ||
}); | ||
const codeId = Number( | ||
JSON.parse(result.rawLog)[0] | ||
.events.find(({ type }) => type === "store_code") | ||
.attributes.find(({ key }) => key === "code_id").value | ||
); | ||
console.log( | ||
`uploaded ${file}, codeID: ${codeId}, tx: ${result.transactionHash}` | ||
); | ||
|
||
accum[file] = codeId; | ||
return accum; | ||
}, | ||
Object() | ||
); | ||
|
||
// Instantiate contracts. | ||
|
||
async function instantiate(code_id: number, inst_msg: any, label: string) { | ||
const instMsgBinary = toBinary(inst_msg); | ||
const instMsgBytes = fromBase64(instMsgBinary); | ||
|
||
// see /sdk/vaa/governance.go | ||
const codeIdBuf = Buffer.alloc(8); | ||
codeIdBuf.writeBigInt64BE(BigInt(code_id)); | ||
const codeIdHash = keccak256(codeIdBuf); | ||
const codeIdLabelHash = keccak256( | ||
Buffer.concat([ | ||
Buffer.from(codeIdHash, "hex"), | ||
Buffer.from(label, "utf8"), | ||
]) | ||
); | ||
const fullHash = keccak256( | ||
Buffer.concat([Buffer.from(codeIdLabelHash, "hex"), instMsgBytes]) | ||
); | ||
|
||
console.log(fullHash); | ||
|
||
let vaa: VAA<Other> = { | ||
version: 1, | ||
guardianSetIndex: 0, | ||
signatures: [], | ||
timestamp: 0, | ||
nonce: 0, | ||
emitterChain: GOVERNANCE_CHAIN, | ||
emitterAddress: GOVERNANCE_EMITTER, | ||
sequence: BigInt(Math.floor(Math.random() * 100000000)), | ||
consistencyLevel: 0, | ||
payload: { | ||
type: "Other", | ||
hex: `0000000000000000000000000000000000000000005761736D644D6F64756C65020${CHAIN_ID_WORMCHAIN.toString( | ||
16 | ||
)}${fullHash}`, | ||
}, | ||
}; | ||
// TODO: check for number of guardians in set and use the corresponding keys | ||
vaa.signatures = sign(VAA_SIGNERS, vaa as unknown as VAA<Payload>); | ||
const msg = client.core.msgInstantiateContract({ | ||
signer, | ||
code_id, | ||
label, | ||
msg: instMsgBytes, | ||
vaa: hexToUint8Array(serialiseVAA(vaa as unknown as VAA<Payload>)), | ||
}); | ||
const result = await client.signAndBroadcast(signer, [msg], { | ||
...ZERO_FEE, | ||
gas: "10000000", | ||
}); | ||
console.log("contract instantiation msg: ", msg); | ||
console.log("contract instantiation result: ", result); | ||
const addr = JSON.parse(result.rawLog)[0] | ||
.events.find(({ type }) => type === "instantiate") | ||
.attributes.find(({ key }) => key === "_contract_address").value; | ||
console.log( | ||
`deployed contract ${label}, codeID: ${code_id}, address: ${addr}, txHash: ${result.transactionHash}` | ||
); | ||
|
||
return addr; | ||
} | ||
|
||
// Instantiate contracts. | ||
// NOTE: Only append at the end, the ordering must be deterministic. | ||
|
||
const addresses: { | ||
[contractName: string]: string; | ||
} = {}; | ||
|
||
const wormchainIbcReceiverInstantiateMsg = {}; | ||
addresses["wormchain_ibc_receiver.wasm"] = await instantiate( | ||
codeIds["wormchain_ibc_receiver.wasm"], | ||
wormchainIbcReceiverInstantiateMsg, | ||
"wormchainIbcReceiver" | ||
); | ||
console.log( | ||
"instantiated wormchain ibc receiver contract: ", | ||
addresses["wormchain_ibc_receiver.wasm"] | ||
); | ||
} | ||
|
||
try { | ||
main(); | ||
} catch (e: any) { | ||
if (e?.message) { | ||
console.error(e.message); | ||
} | ||
throw e; | ||
} |
Oops, something went wrong.