From 66c39c6b6b289be379557322f80f59b9551c3805 Mon Sep 17 00:00:00 2001 From: ByeongSu Hong Date: Fri, 20 Oct 2023 20:28:34 +0900 Subject: [PATCH 01/53] some scripts (#50) * feat: deploy scripts * refactor * filter wasm event * feat: utilties --------- Co-authored-by: hashableric --- scripts/.env.example | 2 - scripts/.gitignore | 1 + scripts/{ => action}/deploy.ts | 29 ++-- scripts/action/fetch.ts | 25 ++++ scripts/action/link.ts | 46 ++++++ scripts/action/mailbox.ts | 82 ++++++++++ scripts/action/multisig.ts | 175 ++++++++++++++++++++++ scripts/action/warp.ts | 35 +++++ scripts/config.example.yaml | 8 + scripts/fill.ts | 10 +- scripts/package.json | 4 + scripts/pnpm-lock.yaml | 163 +++++++++++++++++--- scripts/src/config.ts | 79 ++++++++++ scripts/src/conv.ts | 4 + scripts/src/index.ts | 47 ++---- scripts/src/load_wasm.ts | 2 +- scripts/src/migrations/initialize.ts | 6 +- scripts/src/migrations/mailbox.ts | 3 +- scripts/src/migrations/mailbox_related.ts | 34 ++--- scripts/src/types.ts | 41 ++++- scripts/tsconfig.json | 10 +- scripts/warp.ts | 90 ----------- 22 files changed, 704 insertions(+), 192 deletions(-) delete mode 100644 scripts/.env.example rename scripts/{ => action}/deploy.ts (85%) create mode 100644 scripts/action/fetch.ts create mode 100644 scripts/action/link.ts create mode 100644 scripts/action/mailbox.ts create mode 100644 scripts/action/multisig.ts create mode 100644 scripts/action/warp.ts create mode 100644 scripts/config.example.yaml create mode 100644 scripts/src/config.ts create mode 100644 scripts/src/conv.ts delete mode 100644 scripts/warp.ts diff --git a/scripts/.env.example b/scripts/.env.example deleted file mode 100644 index c2c3b2df..00000000 --- a/scripts/.env.example +++ /dev/null @@ -1,2 +0,0 @@ -SIGNING_ADDRESS="" -SIGNING_MNEMONIC="" diff --git a/scripts/.gitignore b/scripts/.gitignore index c9358322..70264f89 100644 --- a/scripts/.gitignore +++ b/scripts/.gitignore @@ -1,4 +1,5 @@ /dist /context .env +config.yaml /node_modules diff --git a/scripts/deploy.ts b/scripts/action/deploy.ts similarity index 85% rename from scripts/deploy.ts rename to scripts/action/deploy.ts index 584dc5ee..1d845432 100644 --- a/scripts/deploy.ts +++ b/scripts/action/deploy.ts @@ -1,21 +1,18 @@ -import { - ExecuteResult, - SigningCosmWasmClient, -} from "@cosmjs/cosmwasm-stargate"; +import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate"; import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"; import { GasPrice } from "@cosmjs/stargate"; -import { loadContext } from "./src/load_context"; -import HplMailbox from "./src/contracts/hpl_mailbox"; -import { BaseContract, Context } from "./src/types"; -import HplHookMerkle from "./src/contracts/hpl_hook_merkle"; -import HplTestMockHook from "./src/contracts/hpl_test_mock_hook"; -import HplIgpGasOracle from "./src/contracts/hpl_igp_oracle"; -import HplIgp from "./src/contracts/hpl_igp"; -import HplIsmMultisig from "./src/contracts/hpl_ism_multisig"; +import { loadContext } from "../src/load_context"; +import HplMailbox from "../src/contracts/hpl_mailbox"; +import { Context } from "../src/types"; +import HplHookMerkle from "../src/contracts/hpl_hook_merkle"; +import HplTestMockHook from "../src/contracts/hpl_test_mock_hook"; +import HplIgpGasOracle from "../src/contracts/hpl_igp_oracle"; +import HplIgp from "../src/contracts/hpl_igp"; +import HplIsmMultisig from "../src/contracts/hpl_ism_multisig"; import { writeFileSync } from "fs"; -import HplValidatorAnnounce from "./src/contracts/hpl_validator_announce"; -import HplTestMockMsgReceiver from "./src/contracts/hpl_test_mock_msg_receiver"; +import HplValidatorAnnounce from "../src/contracts/hpl_validator_announce"; +import HplTestMockMsgReceiver from "../src/contracts/hpl_test_mock_msg_receiver"; const NETWORK_ID = process.env.NETWORK_ID || "osmo-test-5"; const NETWORK_HRP = process.env.NETWORK_HRP || "osmo"; @@ -115,7 +112,9 @@ async function main() { ); // init igp oracle - ctx.contracts[igp_oracle.contractName] = await igp_oracle.instantiate({}); + ctx.contracts[igp_oracle.contractName] = await igp_oracle.instantiate({ + owner: ctx.address!, + }); // init igp ctx.contracts[igp.contractName] = await igp.instantiate({ diff --git a/scripts/action/fetch.ts b/scripts/action/fetch.ts new file mode 100644 index 00000000..f6d4ef72 --- /dev/null +++ b/scripts/action/fetch.ts @@ -0,0 +1,25 @@ +import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate"; +import { Context } from "../src/types"; +import { Client } from "../src/config"; + +type Const = new ( + address: string | undefined, + codeId: number | undefined, + digest: string, + signer: string, + client: SigningCosmWasmClient +) => T; + +export class ContractFetcher { + constructor(private ctx: Context, private client: Client) {} + + public get(f: Const, name: string): T { + return new f( + this.ctx.contracts[name].address, + this.ctx.contracts[name].codeId, + this.ctx.contracts[name].digest, + this.client.signer, + this.client.wasm + ); + } +} diff --git a/scripts/action/link.ts b/scripts/action/link.ts new file mode 100644 index 00000000..bf6acda4 --- /dev/null +++ b/scripts/action/link.ts @@ -0,0 +1,46 @@ +import { loadContext } from "../src/load_context"; +import { config, getSigningClient } from "../src/config"; + +import HplWarpNative from "../src/contracts/hpl_warp_native"; +import HplIsmMultisig from "../src/contracts/hpl_ism_multisig"; +import { ContractFetcher } from "./fetch"; +import { addPad } from "../src/conv"; + +async function main() { + const client = await getSigningClient(config); + + const ctx = loadContext(config.network.id); + + const fetcher = new ContractFetcher(ctx, client); + + const ism_multisig = fetcher.get(HplIsmMultisig, "hpl_ism_multisig"); + + let res; + + res = await ism_multisig.execute({ + enroll_validator: { + set: { + domain: 5, + validator: client.signer, + validator_pubkey: client.signer_pubkey, + }, + }, + }); + console.log(res.events.filter((v) => v.type.startsWith("wasm"))); + + const warp_native_ibc = fetcher.get(HplWarpNative, "hpl_warp_native_ibc"); + + res = await warp_native_ibc.execute({ + router: { + set_route: { + set: { + domain: 5, + route: addPad("0xaB7011fa44868E023C869635eE33875629Aec8db"), + }, + }, + }, + }); + console.log(res.events.filter((v) => v.type.startsWith("wasm"))); +} + +main().catch(console.error); diff --git a/scripts/action/mailbox.ts b/scripts/action/mailbox.ts new file mode 100644 index 00000000..afdbc005 --- /dev/null +++ b/scripts/action/mailbox.ts @@ -0,0 +1,82 @@ +import { Command } from "commander"; + +import { version } from "../package.json"; +import { config, getSigningClient } from "../src/config"; +import HplMailbox from "../src/contracts/hpl_mailbox"; +import { addPad } from "../src/conv"; +import { loadContext } from "../src/load_context"; +import { ContractFetcher } from "./fetch"; +import { ExecuteResult } from "@cosmjs/cosmwasm-stargate"; + +const program = new Command(); + +program.name("Mailbox CLI").version(version); + +program + .command("dispatch") + .argument("", 'destination domain, e.g. "5"') + .argument("", "recipient address in hex") + .argument("", "message body in utf-8") + .action(makeHandler("dispatch")); + +program + .command("process") + .argument("", "metadata in hex") + .argument("", "message body in hex") + .action(makeHandler("process")); + +program.parseAsync(process.argv).catch(console.error); + +const parseWasmEventLog = (res: ExecuteResult) => { + return res.events + .filter((v) => v.type.startsWith("wasm")) + .map((v) => ({ + "@type": v.type.slice(5), + ...Object.fromEntries(v.attributes.map((x) => [x.key, x.value])), + })); +}; + +function makeHandler( + action: "dispatch" | "process" +): (...args: any[]) => void | Promise { + const ctx = loadContext(config.network.id); + + const loadDeps = async () => { + const client = await getSigningClient(config); + const fetcher = new ContractFetcher(ctx, client); + const mailbox = fetcher.get(HplMailbox, "hpl_mailbox"); + return { mailbox }; + }; + + switch (action) { + case "dispatch": + return async ( + dest_domain: string, + recipient_addr: string, + msg_body: string + ) => { + const { mailbox } = await loadDeps(); + + const res = await mailbox.execute({ + dispatch: { + dest_domain: Number(dest_domain), + recipient_addr: addPad(recipient_addr), + msg_body: Buffer.from(msg_body, "utf-8").toString("hex"), + }, + }); + console.log(parseWasmEventLog(res)); + }; + case "process": + return async (metadata: string, msg_body: string) => { + const { mailbox } = await loadDeps(); + + const res = await mailbox.execute({ + process: { + metadata, + msg_body, + }, + }); + console.log(parseWasmEventLog(res)); + }; + } +} diff --git a/scripts/action/multisig.ts b/scripts/action/multisig.ts new file mode 100644 index 00000000..f53ae1b7 --- /dev/null +++ b/scripts/action/multisig.ts @@ -0,0 +1,175 @@ +import { Command } from "commander"; + +import { version } from "../package.json"; +import { loadContext } from "../src/load_context"; +import { config } from "../src/config"; +import { fromBech32 } from "@cosmjs/encoding"; +import { Secp256k1, keccak256 } from "@cosmjs/crypto"; +import { readFileSync, writeFileSync } from "fs"; +import { DirectSecp256k1Wallet } from "@cosmjs/proto-signing"; + +type CheckpointInfo = { + origin_domain: number; + origin_merkle_tree: string; + merkle_root: string; + merkle_index: number; +}; + +const toHex = (v: Uint8Array): string => { + return Buffer.from(v).toString("hex"); +}; + +const fromHex = (v: string): Uint8Array => { + return Buffer.from(v, "hex"); +}; + +const u8 = (v: string): Uint8Array => { + return fromHex(Number(v).toString(16).padStart(8, "0")); +}; + +const program = new Command(); +const common = { output: `${process.cwd()}/signature.json` }; + +program.name("Multisig CLI").version(version); + +program + .command("sign") + .argument("", 'origin domain, e.g. "5"') + .argument("", "merkle root in hex") + .argument("", "merkle index") + .argument("", "message id in hex") + .option("-o, --output ", "output file", common.output) + .option("-p --prefix ", "bech32 prefix", "dual") + .requiredOption("-k --key ", "private key") + .action(sign); + +program + .command("join") + .argument("", "signature files (comma separated)") + .option("-o, --output ", "output file", common.output) + .option("-p --prefix ", "bech32 prefix", "dual") + .action(join); + +program.parseAsync(process.argv).catch(console.error); + +async function sign( + origin_domain_str: string, + merkle_root: string, + merkle_index_str: string, + message_id: string, + options: { output: string; prefix: string; key: string } +) { + const ctx = loadContext(config.network.id); + const origin_domain = u8(origin_domain_str); + const merkle_index = u8(merkle_index_str); + + const origin_merkle_str = ctx.contracts.hpl_hook_merkle.address!; + const origin_merkle = Buffer.from(fromBech32(origin_merkle_str).data); + + const domain_hash = keccak256( + Buffer.concat([ + origin_domain, + origin_merkle, + Buffer.from("HYPERLANE", "utf-8"), + ]) + ); + + const multisig_hash = keccak256( + Buffer.concat([ + domain_hash, + fromHex(merkle_root), + merkle_index, + fromHex(message_id), + ]) + ); + + const verify_digest = keccak256( + Buffer.concat([ + Buffer.from(`\x19Ethereum Signed Message:\n${multisig_hash.length}`), + multisig_hash, + ]) + ); + + const keypair = await Secp256k1.makeKeypair(fromHex(options.key)); + const key = await DirectSecp256k1Wallet.fromKey( + keypair.privkey, + options.prefix + ); + + const [{ address }] = await key.getAccounts(); + + const signature = await Secp256k1.createSignature( + verify_digest, + keypair.privkey + ); + + type Output = { + address: string; + signature: string; + } & CheckpointInfo; + + const output: Output = { + origin_domain: Number(origin_domain_str), + origin_merkle_tree: origin_merkle.toString("hex"), + merkle_root, + merkle_index: Number(merkle_index_str), + address: address, + signature: toHex(signature.toFixedLength()), + }; + + writeFileSync(options.output, JSON.stringify(output, null, 2)); +} + +async function join( + signature_paths: string[], + options: { output: string; prefix: string } +) { + const ctx = loadContext(config.network.id); + + const origin_merkle_str = ctx.contracts.hpl_hook_merkle.address!; + const origin_merkle = Buffer.from(fromBech32(origin_merkle_str).data); + + type Output = { + address: string; + signature: string; + } & CheckpointInfo; + + type Joined = { + signatures: Record; + } & CheckpointInfo; + + let joined: Joined | null = null; + + for (const path of signature_paths) { + const output: Output = JSON.parse(readFileSync(path, "utf-8")); + + if (joined) { + joined.signatures[output.address!] = output.signature!; + continue; + } + + joined = { + origin_domain: output.origin_domain, + origin_merkle_tree: output.origin_merkle_tree, + merkle_root: output.merkle_root, + merkle_index: output.merkle_index, + signatures: { + [output.address]: output.signature, + }, + }; + } + + if (!joined) { + console.error("no signature given"); + return; + } + + const metadata = Buffer.concat([ + fromHex(joined.origin_merkle_tree), + fromHex(joined.merkle_root), + u8(joined.merkle_index.toString()), + Buffer.concat(Object.values(joined.signatures).map((v) => fromHex(v))), + ]); + + console.log(metadata.toString("hex")); +} diff --git a/scripts/action/warp.ts b/scripts/action/warp.ts new file mode 100644 index 00000000..4ed62aff --- /dev/null +++ b/scripts/action/warp.ts @@ -0,0 +1,35 @@ +import { loadContext } from "../src/load_context"; +import HplMailbox from "../src/contracts/hpl_mailbox"; +import HplWarpNative from "../src/contracts/hpl_warp_native"; +import { config, getSigningClient } from "../src/config"; +import { ContractFetcher } from "./fetch"; + +async function main() { + const client = await getSigningClient(config); + + const ctx = loadContext(config.network.id); + + const fetcher = new ContractFetcher(ctx, client); + + const mailbox = fetcher.get(HplMailbox, "hpl_mailbox"); + + const warp_native = fetcher.get(HplWarpNative, "hpl_warp_native"); + + const target_denom = + "ibc/B5CB286F69D48B2C4F6F8D8CF59011C40590DCF8A91617A5FBA9FF0A7B21307F"; + + const ibc_route = await warp_native.instantiate({ + token: { + collateral: { + denom: target_denom, + }, + }, + hrp: "dual", + owner: client.signer, + mailbox: mailbox.address!, + }); + + console.log("ibc_route", ibc_route); +} + +main().catch(console.error); diff --git a/scripts/config.example.yaml b/scripts/config.example.yaml new file mode 100644 index 00000000..b4a213c6 --- /dev/null +++ b/scripts/config.example.yaml @@ -0,0 +1,8 @@ +network: + id: "localosmosis" + hrp: "osmo" + url: "http://localhost:26657" + gas: "0.025uosmo" + domain: "2303" + +signer: deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef diff --git a/scripts/fill.ts b/scripts/fill.ts index 9f165773..e8232353 100644 --- a/scripts/fill.ts +++ b/scripts/fill.ts @@ -10,6 +10,7 @@ import { ARTIFACTS } from "./artifacts"; async function getSigningClient(): Promise<{ client: SigningCosmWasmClient; address: string; + pubkey: string; }> { const mnemonic = process.env["SIGNING_MNEMONIC"] as string; const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, { @@ -24,11 +25,14 @@ async function getSigningClient(): Promise<{ gasPrice: GasPrice.fromString("0.025uosmo"), } ); - return { client, address }; + + const pubkey = process.env["SIGNING_PUBKEY"] as string; + + return { client, address, pubkey }; } async function main() { - const { client, address: owner } = await getSigningClient(); + const { client, address: owner, pubkey } = await getSigningClient(); const { hpl_hub: { address: hpl_hub }, @@ -43,6 +47,8 @@ async function main() { let execRes: ExecuteResult; + // =========================== hpl_routing + // =========================== hpl_hub { const originDomain = await client.queryContractSmart(hpl_hub, { diff --git a/scripts/package.json b/scripts/package.json index aac1f75a..8b6796ec 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -18,15 +18,19 @@ "@cosmjs/encoding": "^0.31.0", "@cosmjs/proto-signing": "^0.31.0", "@cosmjs/stargate": "^0.31.0", + "@cosmjs/tendermint-rpc": "0.31.0", "axios": "^1.4.0", "colors": "^1.4.0", + "commander": "^11.1.0", "inversify": "^6.0.1", "readline": "^1.3.0", "reflect-metadata": "^0.1.13" }, "devDependencies": { + "@types/js-yaml": "^4.0.8", "@types/node": "^20.4.4", "ts-node": "^10.9.1", + "ts-yaml": "^1.0.0", "tsx": "^3.13.0", "typescript": "^5.1.6" } diff --git a/scripts/pnpm-lock.yaml b/scripts/pnpm-lock.yaml index cfbbe4b7..3ef0d902 100644 --- a/scripts/pnpm-lock.yaml +++ b/scripts/pnpm-lock.yaml @@ -26,12 +26,18 @@ dependencies: '@cosmjs/stargate': specifier: ^0.31.0 version: 0.31.0 + '@cosmjs/tendermint-rpc': + specifier: 0.31.0 + version: 0.31.0 axios: specifier: ^1.4.0 version: 1.4.0 colors: specifier: ^1.4.0 version: 1.4.0 + commander: + specifier: ^11.1.0 + version: 11.1.0 inversify: specifier: ^6.0.1 version: 6.0.1 @@ -43,12 +49,18 @@ dependencies: version: 0.1.13 devDependencies: + '@types/js-yaml': + specifier: ^4.0.8 + version: 4.0.8 '@types/node': specifier: ^20.4.4 version: 20.4.4 ts-node: specifier: ^10.9.1 version: 10.9.1(@types/node@20.4.4)(typescript@5.1.6) + ts-yaml: + specifier: ^1.0.0 + version: 1.0.0 tsx: specifier: ^3.13.0 version: 3.13.0 @@ -77,10 +89,10 @@ packages: /@cosmjs/amino@0.31.0: resolution: {integrity: sha512-xJ5CCEK7H79FTpOuEmlpSzVI+ZeYESTVvO3wHDgbnceIyAne3C68SvyaKqLUR4uJB0Z4q4+DZHbqW6itUiv4lA==} dependencies: - '@cosmjs/crypto': 0.31.0 - '@cosmjs/encoding': 0.31.0 - '@cosmjs/math': 0.31.0 - '@cosmjs/utils': 0.31.0 + '@cosmjs/crypto': 0.31.1 + '@cosmjs/encoding': 0.31.1 + '@cosmjs/math': 0.31.1 + '@cosmjs/utils': 0.31.1 dev: false /@cosmjs/cosmwasm-launchpad@0.25.6: @@ -151,6 +163,18 @@ packages: libsodium-wrappers-sumo: 0.7.11 dev: false + /@cosmjs/crypto@0.31.1: + resolution: {integrity: sha512-4R/SqdzdVzd4E5dpyEh1IKm5GbTqwDogutyIyyb1bcOXiX/x3CrvPI9Tb4WSIMDLvlb5TVzu2YnUV51Q1+6mMA==} + dependencies: + '@cosmjs/encoding': 0.31.1 + '@cosmjs/math': 0.31.1 + '@cosmjs/utils': 0.31.1 + '@noble/hashes': 1.3.1 + bn.js: 5.2.1 + elliptic: 6.5.4 + libsodium-wrappers-sumo: 0.7.11 + dev: false + /@cosmjs/encoding@0.25.6: resolution: {integrity: sha512-0imUOB8XkUstI216uznPaX1hqgvLQ2Xso3zJj5IV5oJuNlsfDj9nt/iQxXWbJuettc6gvrFfpf+Vw2vBZSZ75g==} dependencies: @@ -167,10 +191,18 @@ packages: readonly-date: 1.0.0 dev: false - /@cosmjs/json-rpc@0.31.0: - resolution: {integrity: sha512-Ix2Cil2qysiLNrX+E0w3vtwCrqxGVq8jklpLA7B2vtMrw7tru/rS65fdFSy8ep0wUNLL6Ud32VXa5K0YObDOMA==} + /@cosmjs/encoding@0.31.1: + resolution: {integrity: sha512-IuxP6ewwX6vg9sUJ8ocJD92pkerI4lyG8J5ynAM3NaX3q+n+uMoPRSQXNeL9bnlrv01FF1kIm8if/f5F7ZPtkA==} dependencies: - '@cosmjs/stream': 0.31.0 + base64-js: 1.5.1 + bech32: 1.1.4 + readonly-date: 1.0.0 + dev: false + + /@cosmjs/json-rpc@0.31.1: + resolution: {integrity: sha512-gIkCj2mUDHAxvmJnHtybXtMLZDeXrkDZlujjzhvJlWsIuj1kpZbKtYqh+eNlfwhMkMMAlQa/y4422jDmizW+ng==} + dependencies: + '@cosmjs/stream': 0.31.1 xstream: 11.14.0 dev: false @@ -200,6 +232,12 @@ packages: bn.js: 5.2.1 dev: false + /@cosmjs/math@0.31.1: + resolution: {integrity: sha512-kiuHV6m6DSB8/4UV1qpFhlc4ul8SgLXTGRlYkYiIIP4l0YNeJ+OpPYaOlEgx4Unk2mW3/O2FWYj7Jc93+BWXng==} + dependencies: + bn.js: 5.2.1 + dev: false + /@cosmjs/proto-signing@0.31.0: resolution: {integrity: sha512-JNlyOJRkn8EKB9mCthkjr6lVX6eyVQ09PFdmB4/DR874E62dFTvQ+YvyKMAgN7K7Dcjj26dVlAD3f6Xs7YOGDg==} dependencies: @@ -212,10 +250,10 @@ packages: long: 4.0.0 dev: false - /@cosmjs/socket@0.31.0: - resolution: {integrity: sha512-WDh9gTyiP3OCXvSAJJn33+Ef3XqMWag+bpR1TdMBxTmlTxuvU+kPy4cf6P2OF+jkkUBEA5Se2EAju0eFbJMT+w==} + /@cosmjs/socket@0.31.1: + resolution: {integrity: sha512-XTtEr+x3WGbqkzoGX0sCkwVqf5n+bBqDwqNgb+DWaBABQxHVRuuainrTVp0Yc91D3Iy2twLQzeBA9OrRxDSerw==} dependencies: - '@cosmjs/stream': 0.31.0 + '@cosmjs/stream': 0.31.1 isomorphic-ws: 4.0.1(ws@7.5.9) ws: 7.5.9 xstream: 11.14.0 @@ -251,16 +289,22 @@ packages: xstream: 11.14.0 dev: false + /@cosmjs/stream@0.31.1: + resolution: {integrity: sha512-xsIGD9bpBvYYZASajCyOevh1H5pDdbOWmvb4UwGZ78doGVz3IC3Kb9BZKJHIX2fjq9CMdGVJHmlM+Zp5aM8yZA==} + dependencies: + xstream: 11.14.0 + dev: false + /@cosmjs/tendermint-rpc@0.31.0: resolution: {integrity: sha512-yo9xbeuI6UoEKIhFZ9g0dvUKLqnBzwdpEc/uldQygQc51j38gQVwFko+6sjmhieJqRYYvrYumcbJMiV6GFM9aA==} dependencies: - '@cosmjs/crypto': 0.31.0 - '@cosmjs/encoding': 0.31.0 - '@cosmjs/json-rpc': 0.31.0 - '@cosmjs/math': 0.31.0 - '@cosmjs/socket': 0.31.0 - '@cosmjs/stream': 0.31.0 - '@cosmjs/utils': 0.31.0 + '@cosmjs/crypto': 0.31.1 + '@cosmjs/encoding': 0.31.1 + '@cosmjs/json-rpc': 0.31.1 + '@cosmjs/math': 0.31.1 + '@cosmjs/socket': 0.31.1 + '@cosmjs/stream': 0.31.1 + '@cosmjs/utils': 0.31.1 axios: 0.21.4 readonly-date: 1.0.0 xstream: 11.14.0 @@ -278,6 +322,10 @@ packages: resolution: {integrity: sha512-nNcycZWUYLNJlrIXgpcgVRqdl6BXjF4YlXdxobQWpW9Tikk61bEGeAFhDYtC0PwHlokCNw0KxWiHGJL4nL7Q5A==} dev: false + /@cosmjs/utils@0.31.1: + resolution: {integrity: sha512-n4Se1wu4GnKwztQHNFfJvUeWcpvx3o8cWhSbNs9JQShEuB3nv3R5lqFBtDCgHZF/emFQAP+ZjF8bTfCs9UBGhA==} + dev: false + /@cspotcode/source-map-support@0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} @@ -563,6 +611,10 @@ packages: resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} dev: true + /@types/js-yaml@4.0.8: + resolution: {integrity: sha512-m6jnPk1VhlYRiLFm3f8X9Uep761f+CK8mHyS65LutH2OhmBF0BeMEjHgg05usH8PLZMWWc/BUR9RPmkvpWnyRA==} + dev: true + /@types/long@4.0.2: resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} dev: false @@ -585,6 +637,17 @@ packages: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} dev: true + /argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + dependencies: + sprintf-js: 1.0.3 + dev: true + + /arrify@1.0.1: + resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} + engines: {node: '>=0.10.0'} + dev: true + /asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} dev: false @@ -649,6 +712,11 @@ packages: delayed-stream: 1.0.0 dev: false + /commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} + dev: false + /cosmjs-types@0.8.0: resolution: {integrity: sha512-Q2Mj95Fl0PYMWEhA2LuGEIhipF7mQwd9gTQ85DdP9jjjopeoGaDxvmPa5nakNzsq7FnO1DMTatXTAx6bxMH7Lg==} dependencies: @@ -673,6 +741,11 @@ packages: engines: {node: '>=0.4.0'} dev: false + /diff@3.5.0: + resolution: {integrity: sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==} + engines: {node: '>=0.3.1'} + dev: true + /diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} @@ -720,6 +793,12 @@ packages: '@esbuild/win32-x64': 0.18.20 dev: true + /esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + dev: true + /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: false @@ -844,6 +923,14 @@ packages: resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} dev: false + /js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + dev: true + /libsodium-sumo@0.7.11: resolution: {integrity: sha512-bY+7ph7xpk51Ez2GbE10lXAQ5sJma6NghcIDaSPbM/G9elfrjLa0COHl/7P6Wb/JizQzl5UQontOOP1z0VwbLA==} dev: false @@ -892,6 +979,17 @@ packages: resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} dev: false + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: true + + /mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + dependencies: + minimist: 1.2.8 + dev: true + /object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} @@ -981,6 +1079,10 @@ packages: engines: {node: '>=0.10.0'} dev: true + /sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + dev: true + /string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} dependencies: @@ -1023,6 +1125,28 @@ packages: yn: 3.1.1 dev: true + /ts-node@6.2.0: + resolution: {integrity: sha512-ZNT+OEGfUNVMGkpIaDJJ44Zq3Yr0bkU/ugN1PHbU+/01Z7UV1fsELRiTx1KuQNvQ1A3pGh3y25iYF6jXgxV21A==} + engines: {node: '>=4.2.0'} + hasBin: true + dependencies: + arrify: 1.0.1 + buffer-from: 1.1.2 + diff: 3.5.0 + make-error: 1.3.6 + minimist: 1.2.8 + mkdirp: 0.5.6 + source-map-support: 0.5.21 + yn: 2.0.0 + dev: true + + /ts-yaml@1.0.0: + resolution: {integrity: sha512-g5D8X+8VhhljTWT5M4A9O8+ONj6WNB34E/WUAMr7DBxyC6S8lFJ2tgv24hfOcwz2x0hUIifBlPSf4nnh2NGY6A==} + dependencies: + js-yaml: 3.14.1 + ts-node: 6.2.0 + dev: true + /tsx@3.13.0: resolution: {integrity: sha512-rjmRpTu3as/5fjNq/kOkOtihgLxuIz6pbKdj9xwP4J5jOLkBxw/rjN5ANw+KyrrOXV5uB7HC8+SrrSJxT65y+A==} hasBin: true @@ -1068,6 +1192,11 @@ packages: symbol-observable: 2.0.3 dev: false + /yn@2.0.0: + resolution: {integrity: sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==} + engines: {node: '>=4'} + dev: true + /yn@3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} diff --git a/scripts/src/config.ts b/scripts/src/config.ts new file mode 100644 index 00000000..f79d74f6 --- /dev/null +++ b/scripts/src/config.ts @@ -0,0 +1,79 @@ +import yaml from "js-yaml"; +import { readFileSync } from "fs"; +import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate"; +import { + Tendermint34Client, + Tendermint37Client, + TendermintClient, +} from "@cosmjs/tendermint-rpc"; +import { DirectSecp256k1Wallet } from "@cosmjs/proto-signing"; +import { GasPrice, SigningStargateClient } from "@cosmjs/stargate"; + +export type Config = { + network: { + id: string; + hrp: string; + url: string; + gas: string; + domain: number; + tm_version?: "34" | "37"; + }; + + signer: string; +}; + +export type Client = { + wasm: SigningCosmWasmClient; + stargate: SigningStargateClient; + signer: string; + signer_pubkey: string; +}; + +const path = process.env.CONFIG || `${process.cwd()}/config.yaml`; + +export const config = yaml.load(readFileSync(path, "utf-8")) as Config; + +export async function getSigningClient({ + network, + signer, +}: Config): Promise { + const wallet = await DirectSecp256k1Wallet.fromKey( + Buffer.from(signer, "hex"), + network.hrp + ); + + const [account] = await wallet.getAccounts(); + + let clientBase: TendermintClient; + + switch (network.tm_version || "37") { + case "34": + clientBase = await Tendermint34Client.connect(network.url); + break; + case "37": + clientBase = await Tendermint37Client.connect(network.url); + break; + } + + const wasm = await SigningCosmWasmClient.createWithSigner( + clientBase, + wallet, + { + gasPrice: GasPrice.fromString(network.gas), + } + ); + const stargate = await SigningStargateClient.createWithSigner( + clientBase, + wallet, + { + gasPrice: GasPrice.fromString(network.gas), + } + ); + + return { + wasm, + stargate, + signer: account.address, + signer_pubkey: Buffer.from(account.pubkey).toString("hex"), + }; +} diff --git a/scripts/src/conv.ts b/scripts/src/conv.ts new file mode 100644 index 00000000..ac1b9488 --- /dev/null +++ b/scripts/src/conv.ts @@ -0,0 +1,4 @@ +export const addPad = (v: string): string => { + const s = v.startsWith("0x") ? v.slice(2) : v; + return s.padStart(64, "0"); +}; diff --git a/scripts/src/index.ts b/scripts/src/index.ts index 9cf672ba..f2317383 100644 --- a/scripts/src/index.ts +++ b/scripts/src/index.ts @@ -4,19 +4,13 @@ import { loadContext, saveContext } from "./load_context"; import { getTargetContract, getTargetContractName } from "./contracts"; import { CodeUpdate, CodeCreate, Context } from "./types"; import * as readline from "readline"; -import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate"; -import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"; -import { GasPrice } from "@cosmjs/stargate"; + import { AxiosError } from "axios"; import { CONTAINER } from "./ioc"; import { runMigrations } from "./migrations"; +import { config, getSigningClient } from "./config"; colors.enable(); -const NETWORK_ID = process.env.NETWORK_ID || "osmo-test-5"; -const NETWORK_HRP = process.env.NETWORK_HRP || "osmo"; -const NETWORK_URL = - process.env.NETWORK_URL || "https://rpc.osmotest5.osmosis.zone"; -const NETWORK_GAS = process.env.NETWORK_GAS || "0.025uosmo"; function askQuestion(query: string) { const rl = readline.createInterface({ @@ -32,36 +26,21 @@ function askQuestion(query: string) { ); } -async function getSigningClient(): Promise<{ - client: SigningCosmWasmClient; - address: string; -}> { - const mnemonic = process.env["SIGNING_MNEMONIC"] as string; - const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, { - prefix: NETWORK_HRP, - }); - const [{ address }] = await wallet.getAccounts(); - - const client = await SigningCosmWasmClient.connectWithSigner( - NETWORK_URL, - wallet, - { - gasPrice: GasPrice.fromString(NETWORK_GAS), - } - ); - return { client, address }; -} - async function main() { const digest = await loadWasmFileDigest(); - const context = await loadContext(NETWORK_ID); + const context = loadContext(config.network.id); const targetContractName = getTargetContractName(); - const { client, address } = await getSigningClient(); - context.address = address; + const client = await getSigningClient(config); + context.address = client.signer; CONTAINER.bind(Context).toConstantValue(context); - const contracts = getTargetContract(context, client, address, CONTAINER); + const contracts = getTargetContract( + context, + client.wasm, + client.signer, + CONTAINER + ); console.log("check exist contracts...."); const codeChanges = targetContractName @@ -128,7 +107,7 @@ async function main() { contract.digest = v.digest; const contractContext = await contract.upload(); context.contracts[v.contractName] = contractContext; - saveContext(NETWORK_ID, context); + saveContext(config.network.id, context); console.log("OK".green, "as", contractContext.codeId); } catch (e) { @@ -141,7 +120,7 @@ async function main() { console.log("No contracts to upload."); } - runMigrations(NETWORK_ID, false); + runMigrations(config.network.id, false); } main(); diff --git a/scripts/src/load_wasm.ts b/scripts/src/load_wasm.ts index e269aab5..a1f9cdeb 100644 --- a/scripts/src/load_wasm.ts +++ b/scripts/src/load_wasm.ts @@ -53,5 +53,5 @@ export async function loadWasmFileDigest() { } export function getWasmPath(contractName: string): string { - return path.join(directoryPath, `${contractName}-aarch64.wasm`); + return path.join(directoryPath, `${contractName}.wasm`); } diff --git a/scripts/src/migrations/initialize.ts b/scripts/src/migrations/initialize.ts index 9dfe6eab..9e48f4f1 100644 --- a/scripts/src/migrations/initialize.ts +++ b/scripts/src/migrations/initialize.ts @@ -44,7 +44,9 @@ export default class InitializeStandalone implements Migration { // init igp oracle this.ctx.contracts[this.igp_oracle.contractName] = - await this.igp_oracle.instantiate({}); + await this.igp_oracle.instantiate({ + owner: this.ctx.address!, + }); // init igp this.ctx.contracts[this.igp.contractName] = await this.igp.instantiate({ @@ -52,7 +54,7 @@ export default class InitializeStandalone implements Migration { owner: this.ctx.address!, mailbox: this.ctx.contracts[this.mailbox.contractName].address, gas_token: "token", - beneficairy: this.ctx.address!, + beneficiary: this.ctx.address!, }); // init ism multisig diff --git a/scripts/src/migrations/mailbox.ts b/scripts/src/migrations/mailbox.ts index 1016733c..05d86b2c 100644 --- a/scripts/src/migrations/mailbox.ts +++ b/scripts/src/migrations/mailbox.ts @@ -18,7 +18,8 @@ export default class MailboxMigration implements Migration { run = async (): Promise => { const mailboxInit: HplMailboxInstantiateMsg = { owner: this.ctx.address!, - default_ism: this.ism_multisig.address!, + hrp: "dual", + domain: 33333, } this.ctx.contracts[this.mailbox.contractName] = await this.mailbox.instantiate(mailboxInit); return this.ctx; diff --git a/scripts/src/migrations/mailbox_related.ts b/scripts/src/migrations/mailbox_related.ts index 7b2d61cc..ef9a383f 100644 --- a/scripts/src/migrations/mailbox_related.ts +++ b/scripts/src/migrations/mailbox_related.ts @@ -23,24 +23,24 @@ export default class MailboxMigration implements Migration { ) {} run = async (): Promise => { - const routingMsgs: HplIsmRoutingInstantiateMsg = { - owner: this.ctx.address!, - isms: [ - { - domain: 4337, - address: this.mailbox.address!, - }, - ], - }; - this.ctx.contracts[this.ismRouting.contractName] = - await this.ismRouting.instantiate(routingMsgs); + // const routingMsgs: HplIsmRoutingInstantiateMsg = { + // owner: this.ctx.address!, + // isms: [ + // { + // domain: 4337, + // address: this.mailbox.address!, + // }, + // ], + // }; + // this.ctx.contracts[this.ismRouting.contractName] = + // await this.ismRouting.instantiate(routingMsgs); - const vaMsg: HplValidatorAnnounceInstantiateMsg = { - addr_prefix: "osmo", - mailbox: this.mailbox.address!, - local_domain: 4337, - }; - this.ctx.contracts[this.va.contractName] = await this.va.instantiate(vaMsg); + // const vaMsg: HplValidatorAnnounceInstantiateMsg = { + // addr_prefix: "osmo", + // mailbox: this.mailbox.address!, + // local_domain: 4337, + // }; + // this.ctx.contracts[this.va.contractName] = await this.va.instantiate(vaMsg); return this.ctx; }; diff --git a/scripts/src/types.ts b/scripts/src/types.ts index 811ad22b..d3467ca8 100644 --- a/scripts/src/types.ts +++ b/scripts/src/types.ts @@ -1,4 +1,7 @@ -import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate"; +import { + ExecuteResult, + SigningCosmWasmClient, +} from "@cosmjs/cosmwasm-stargate"; import { getWasmPath } from "./load_wasm"; import fs from "fs"; @@ -83,11 +86,10 @@ export abstract class BaseContract implements Contract { } public async instantiate(msg: any): Promise { - const instantiateMsg = msg as HplMailboxInstantiateMsg; const contract = await this.client.instantiate( this.signer, this.codeId!, - instantiateMsg, + msg, this.contractName, "auto", { admin: this.signer } @@ -96,6 +98,36 @@ export abstract class BaseContract implements Contract { this.address = contract.contractAddress; return this.getContractContext(); } + + // overloads + public async execute(msg: any): Promise; + public async execute( + msg: any, + funds: { denom: string; amount: string }[] + ): Promise; + + // implementation + public async execute( + msg: any, + funds?: { denom: string; amount: string }[] + ): Promise { + const res = await this.client.execute( + this.signer, + this.address!, + msg, + "auto", + undefined, + funds + ); + + return res; + } + + public async query(msg: any): Promise { + const res = await this.client.queryContractSmart(this.address!, msg); + + return res; + } } export interface ContractConstructor { @@ -144,7 +176,8 @@ export interface HplIsmRoutingInstantiateMsg { export interface HplMailboxInstantiateMsg { owner: string; - default_ism: string; + hrp: string; + domain: number; } export interface HplMulticallInstantiateMsg { diff --git a/scripts/tsconfig.json b/scripts/tsconfig.json index 0f802280..25615870 100644 --- a/scripts/tsconfig.json +++ b/scripts/tsconfig.json @@ -1,9 +1,6 @@ { "compilerOptions": { - "lib": [ - "es2021", - "webworker" - ], + "lib": ["es2021", "webworker"], "module": "commonjs", "target": "es2021", "strict": true, @@ -16,8 +13,7 @@ "emitDecoratorMetadata": true, "strictPropertyInitialization": false, "outDir": "dist", - "declaration": true /* Skip type checking all .d.ts files. */ + "declaration": true /* Skip type checking all .d.ts files. */ }, - "include": [ "src/**/*.ts", "*.ts" ], + "include": ["src/**/*.ts", "*.ts", "action/**/*.ts"] } - diff --git a/scripts/warp.ts b/scripts/warp.ts deleted file mode 100644 index 51c9c31b..00000000 --- a/scripts/warp.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate"; -import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"; -import { GasPrice } from "@cosmjs/stargate"; - -import { loadContext } from "./src/load_context"; -import HplMailbox from "./src/contracts/hpl_mailbox"; -import { Context } from "./src/types"; -import HplWarpNative from "./src/contracts/hpl_warp_native"; - -const NETWORK_ID = process.env.NETWORK_ID || "osmo-test-5"; -const NETWORK_HRP = process.env.NETWORK_HRP || "osmo"; -const NETWORK_URL = - process.env.NETWORK_URL || "https://rpc.osmotest5.osmosis.zone"; -const NETWORK_GAS = process.env.NETWORK_GAS || "0.025uosmo"; - -async function getSigningClient(): Promise<{ - client: SigningCosmWasmClient; - address: string; -}> { - const mnemonic = process.env["SIGNING_MNEMONIC"] as string; - const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, { - prefix: NETWORK_HRP, - }); - const [{ address }] = await wallet.getAccounts(); - - const client = await SigningCosmWasmClient.connectWithSigner( - NETWORK_URL, - wallet, - { - gasPrice: GasPrice.fromString(NETWORK_GAS), - } - ); - return { client, address }; -} - -type Const = new ( - address: string | undefined, - codeId: number | undefined, - digest: string, - signer: string, - client: SigningCosmWasmClient -) => T; - -class ContractFetcher { - constructor( - private ctx: Context, - private owner: string, - private client: SigningCosmWasmClient - ) {} - - public get(f: Const, name: string): T { - return new f( - this.ctx.contracts[name].address, - this.ctx.contracts[name].codeId, - this.ctx.contracts[name].digest, - this.owner, - this.client - ); - } -} - -async function main() { - const { client, address: owner } = await getSigningClient(); - - const ctx = loadContext(NETWORK_ID); - - const fetcher = new ContractFetcher(ctx, owner, client); - - const mailbox = fetcher.get(HplMailbox, "hpl_mailbox"); - - const warp_native = fetcher.get(HplWarpNative, "hpl_warp_native"); - - const target_denom = - "ibc/B5CB286F69D48B2C4F6F8D8CF59011C40590DCF8A91617A5FBA9FF0A7B21307F"; - - const ibc_route = await warp_native.instantiate({ - token: { - collateral: { - denom: target_denom, - }, - }, - hrp: "dual", - owner, - mailbox: mailbox.address!, - }); - - console.log("ibc_route", ibc_route); -} - -main().catch(console.error); From f4027eba797cd60c7ee717a2a0cb76b095a6d474 Mon Sep 17 00:00:00 2001 From: ByeongSu Hong Date: Fri, 20 Oct 2023 20:28:51 +0900 Subject: [PATCH 02/53] fix: coverage ci (#49) * try: use llvm cov * fix * asdf * nightly --- .github/workflows/test.yaml | 83 ++++++++----------------------------- 1 file changed, 17 insertions(+), 66 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index f159d439..7959e3cd 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -9,50 +9,14 @@ on: - "main" jobs: - deps: - name: dependencies - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - with: - fetch-depth: 1 - - - name: Cache dependencies - uses: actions/cache@v3 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - - name: Load toolchain - uses: dtolnay/rust-toolchain@stable - with: - toolchain: stable - targets: wasm32-unknown-unknown - - - run: cargo fetch --verbose - - run: cargo build - - run: cargo wasm - unit-test: strategy: fail-fast: true - needs: deps - name: unit-test runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v3 - with: - fetch-depth: 1 + - uses: actions/checkout@v4 - name: Cache dependencies uses: actions/cache@v3 @@ -65,45 +29,32 @@ jobs: target/ key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - name: Load toolchain - uses: dtolnay/rust-toolchain@stable - with: - toolchain: stable - targets: wasm32-unknown-unknown + - name: Install Rust + run: rustup update stable + + - name: Install target + run: rustup target add wasm32-unknown-unknown - run: cargo test --workspace --exclude hpl-tests coverage: - needs: deps - - name: coverage runs-on: ubuntu-latest - container: - image: xd009642/tarpaulin:develop-nightly - options: --security-opt seccomp=unconfined + env: + CARGO_TERM_COLOR: always steps: - - name: Checkout repository - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - name: Cache dependencies - uses: actions/cache@v3 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + - name: Install Rust + run: rustup update nightly + + - name: Install target + run: rustup target add wasm32-unknown-unknown - - run: rustup target add wasm32-unknown-unknown + - name: Install cargo-llvm-cov + uses: taiki-e/install-action@cargo-llvm-cov - name: Generate code coverage - run: | - cargo +nightly tarpaulin \ - --verbose \ - --workspace --exclude hpl-tests \ - --timeout 120 --out Xml + run: cargo llvm-cov --all-features --workspace --exclude hpl-tests --codecov --output-path codecov.json - name: Upload to codecov.io uses: codecov/codecov-action@v3 From fbcc64128d227ce0aa10e8c5374c210f3c634d67 Mon Sep 17 00:00:00 2001 From: ByeongSu Hong Date: Tue, 24 Oct 2023 00:14:21 +0900 Subject: [PATCH 03/53] test: warp route (#51) * Revert "revert: aggregate" This reverts commit 00fe61588e4dd1c8b9057a7709222dabfab02973. * test(cw20): cw20 init * test(warp): unit test for cw20 * test(warp): add query test for cw20 * test(warp): remove existing tests for native * feat(warp): add ownable message * wip * test(warp): unit test for native denom * test: use u32 directly * fix!(ism): use parsed u32 merkle index * test: modularize * test: add empty warp * test: runnable mailbox * test: warp placeholder * test: deployable * fix(warp): inject minter to cw20 * fix(warp): use consistant event name * test: warp cw20 / native denom creation * chore: prune * feat: aggregation * schema * fix: evm equivalence * fix(hook): pass entire metadata * fix: aggregate works * chore: remove debugger * fix: tests * clippy * use aggregate ism on integration test * feat: migrate to aggregations (#53) --- contracts/hooks/aggregate/Cargo.toml | 42 ++ contracts/hooks/aggregate/src/error.rs | 16 + contracts/hooks/aggregate/src/lib.rs | 186 +++++++++ contracts/igps/core/src/contract.rs | 4 +- contracts/igps/core/src/execute.rs | 8 +- contracts/igps/core/src/lib.rs | 3 - contracts/igps/core/src/query.rs | 8 +- contracts/igps/core/src/tests/contract.rs | 17 +- contracts/igps/core/src/tests/mod.rs | 2 - contracts/isms/aggregate/Cargo.toml | 43 ++ contracts/isms/aggregate/src/error.rs | 17 + contracts/isms/aggregate/src/lib.rs | 149 +++++++ contracts/isms/multisig/src/lib.rs | 4 +- contracts/isms/multisig/src/query.rs | 4 +- contracts/warp/cw20/Cargo.toml | 2 + contracts/warp/cw20/src/contract.rs | 389 +++++++++++++++++- contracts/warp/cw20/src/error.rs | 12 +- contracts/warp/cw20/src/lib.rs | 3 - contracts/warp/cw20/src/tests/contracts.rs | 292 ------------- contracts/warp/cw20/src/tests/mod.rs | 115 ------ contracts/warp/native/Cargo.toml | 1 + contracts/warp/native/src/contract.rs | 315 +++++++++++++- contracts/warp/native/src/error.rs | 8 +- contracts/warp/native/src/lib.rs | 3 - contracts/warp/native/src/tests/contracts.rs | 279 ------------- contracts/warp/native/src/tests/mod.rs | 132 ------ integration-test/Cargo.toml | 3 + integration-test/tests/contracts/cw/deploy.rs | 116 +++--- integration-test/tests/contracts/cw/hook.rs | 55 ++- integration-test/tests/contracts/cw/igp.rs | 2 - integration-test/tests/contracts/cw/ism.rs | 67 ++- integration-test/tests/contracts/cw/mod.rs | 4 +- integration-test/tests/contracts/cw/setup.rs | 33 +- integration-test/tests/contracts/cw/store.rs | 4 +- integration-test/tests/contracts/cw/types.rs | 2 + integration-test/tests/mailbox.rs | 159 ++++--- integration-test/tests/validator.rs | 4 +- integration-test/tests/warp.rs | 288 +++++++++++++ packages/interface/src/core/mailbox.rs | 1 + packages/interface/src/core/mod.rs | 1 + packages/interface/src/core/va.rs | 2 + packages/interface/src/hook/aggregate.rs | 72 ++++ packages/interface/src/hook/mod.rs | 1 + packages/interface/src/igp/core.rs | 1 - packages/interface/src/ism/aggregate.rs | 54 +++ packages/interface/src/ism/mod.rs | 1 + packages/interface/src/types/metadata.rs | 114 ++++- packages/interface/src/warp/cw20.rs | 3 +- packages/interface/src/warp/native.rs | 3 +- scripts/action/deploy.ts | 71 +--- scripts/action/migrate.ts | 70 ++++ scripts/src/contracts/hpl_hook_aggregate.ts | 5 + scripts/src/contracts/hpl_ism_aggregate.ts | 5 + scripts/src/load_wasm.ts | 2 +- 54 files changed, 2056 insertions(+), 1141 deletions(-) create mode 100644 contracts/hooks/aggregate/Cargo.toml create mode 100644 contracts/hooks/aggregate/src/error.rs create mode 100644 contracts/hooks/aggregate/src/lib.rs create mode 100644 contracts/isms/aggregate/Cargo.toml create mode 100644 contracts/isms/aggregate/src/error.rs create mode 100644 contracts/isms/aggregate/src/lib.rs delete mode 100644 contracts/warp/cw20/src/tests/contracts.rs delete mode 100644 contracts/warp/cw20/src/tests/mod.rs delete mode 100644 contracts/warp/native/src/tests/contracts.rs delete mode 100644 contracts/warp/native/src/tests/mod.rs create mode 100644 integration-test/tests/warp.rs create mode 100644 packages/interface/src/hook/aggregate.rs create mode 100644 packages/interface/src/ism/aggregate.rs create mode 100644 scripts/action/migrate.ts create mode 100644 scripts/src/contracts/hpl_hook_aggregate.ts create mode 100644 scripts/src/contracts/hpl_ism_aggregate.ts diff --git a/contracts/hooks/aggregate/Cargo.toml b/contracts/hooks/aggregate/Cargo.toml new file mode 100644 index 00000000..7d196235 --- /dev/null +++ b/contracts/hooks/aggregate/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "hpl-hook-aggregate" +version.workspace = true +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true +documentation.workspace = true +keywords.workspace = true + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +# for more explicit tests, cargo test --features=backtraces +backtraces = ["cosmwasm-std/backtraces"] +# use library feature to disable all instantiate/execute/query exports +library = [] + +[dependencies] +cosmwasm-std.workspace = true +cosmwasm-storage.workspace = true +cosmwasm-schema.workspace = true + +cw-storage-plus.workspace = true +cw2.workspace = true +cw-utils.workspace = true + +schemars.workspace = true +serde-json-wasm.workspace = true + +thiserror.workspace = true + +hpl-ownable.workspace = true +hpl-interface.workspace = true + +[dev-dependencies] +rstest.workspace = true +ibcx-test-utils.workspace = true + +anyhow.workspace = true diff --git a/contracts/hooks/aggregate/src/error.rs b/contracts/hooks/aggregate/src/error.rs new file mode 100644 index 00000000..babe75a3 --- /dev/null +++ b/contracts/hooks/aggregate/src/error.rs @@ -0,0 +1,16 @@ +use cosmwasm_std::StdError; + +#[derive(thiserror::Error, Debug, PartialEq)] +pub enum ContractError { + #[error("{0}")] + Std(#[from] StdError), + + #[error("{0}")] + PaymentError(#[from] cw_utils::PaymentError), + + #[error("unauthorized")] + Unauthorized {}, + + #[error("hook paused")] + Paused {}, +} diff --git a/contracts/hooks/aggregate/src/lib.rs b/contracts/hooks/aggregate/src/lib.rs new file mode 100644 index 00000000..4ffcb579 --- /dev/null +++ b/contracts/hooks/aggregate/src/lib.rs @@ -0,0 +1,186 @@ +mod error; + +#[cfg(not(feature = "library"))] +use cosmwasm_std::entry_point; +use cosmwasm_std::{ + ensure_eq, Addr, Coin, CosmosMsg, Deps, DepsMut, Env, Event, HexBinary, MessageInfo, + QueryResponse, Response, StdResult, +}; +use cw_storage_plus::Item; +use error::ContractError; +use hpl_interface::{ + hook::{ + aggregate::{AggregateHookQueryMsg, ExecuteMsg, HooksResponse, InstantiateMsg, QueryMsg}, + post_dispatch, HookQueryMsg, MailboxResponse, PostDispatchMsg, QuoteDispatchMsg, + QuoteDispatchResponse, + }, + to_binary, + types::Message, +}; +use hpl_ownable::get_owner; + +// version info for migration info +pub const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); +pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); + +pub const HOOKS_KEY: &str = "hooks"; +pub const HOOKS: Item> = Item::new(HOOKS_KEY); + +fn new_event(name: &str) -> Event { + Event::new(format!("hpl_hook_aggregate::{}", name)) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn instantiate( + deps: DepsMut, + _env: Env, + info: MessageInfo, + msg: InstantiateMsg, +) -> Result { + cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + + let owner = deps.api.addr_validate(&msg.owner)?; + let hooks = msg + .hooks + .iter() + .map(|v| deps.api.addr_validate(v)) + .collect::>()?; + + hpl_ownable::initialize(deps.storage, &owner)?; + + HOOKS.save(deps.storage, &hooks)?; + + Ok(Response::new().add_event( + new_event("initialize") + .add_attribute("sender", info.sender) + .add_attribute("owner", owner) + .add_attribute("hooks", msg.hooks.join(",")), + )) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: ExecuteMsg, +) -> Result { + match msg { + ExecuteMsg::Ownable(msg) => Ok(hpl_ownable::handle(deps, env, info, msg)?), + ExecuteMsg::PostDispatch(PostDispatchMsg { message, metadata }) => { + // aggregate it + let hooks = HOOKS.load(deps.storage)?; + + let msgs: Vec = hooks + .into_iter() + .map(|v| { + let quote = hpl_interface::hook::quote_dispatch( + &deps.querier, + &v, + metadata.clone(), + message.clone(), + )?; + let msg = post_dispatch( + v, + metadata.clone(), + message.clone(), + quote.gas_amount.map(|v| vec![v]), + )? + .into(); + + Ok(msg) + }) + .collect::>()?; + + let decoded_msg: Message = message.into(); + + // do nothing + Ok(Response::new().add_messages(msgs).add_event( + new_event("post_dispatch").add_attribute("message_id", decoded_msg.id().to_hex()), + )) + } + ExecuteMsg::SetHooks { hooks } => { + ensure_eq!( + get_owner(deps.storage)?, + info.sender, + ContractError::Unauthorized {} + ); + + let parsed_hooks = hooks + .iter() + .map(|v| deps.api.addr_validate(v)) + .collect::>()?; + + HOOKS.save(deps.storage, &parsed_hooks)?; + + Ok(Response::new().add_event( + new_event("set_hooks") + .add_attribute("sender", info.sender) + .add_attribute("hooks", hooks.join(",")), + )) + } + } +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result { + match msg { + QueryMsg::Ownable(msg) => Ok(hpl_ownable::handle_query(deps, env, msg)?), + QueryMsg::Hook(msg) => match msg { + HookQueryMsg::Mailbox {} => to_binary(get_mailbox(deps)), + HookQueryMsg::QuoteDispatch(QuoteDispatchMsg { metadata, message }) => { + to_binary(quote_dispatch(deps, metadata, message)) + } + }, + QueryMsg::AggregateHook(msg) => match msg { + AggregateHookQueryMsg::Hooks {} => to_binary(get_hooks(deps)), + }, + } +} + +fn get_mailbox(_deps: Deps) -> Result { + Ok(MailboxResponse { + mailbox: "unrestricted".to_string(), + }) +} + +fn quote_dispatch( + deps: Deps, + metadata: HexBinary, + message: HexBinary, +) -> Result { + let hooks = HOOKS.load(deps.storage)?; + + let mut total: Option = None; + + for hook in hooks { + let res = hpl_interface::hook::quote_dispatch( + &deps.querier, + hook, + metadata.clone(), + message.clone(), + )?; + + if let Some(gas_amount) = res.gas_amount { + total = match total { + Some(mut v) => { + v.amount += gas_amount.amount; + Some(v) + } + None => Some(gas_amount), + }; + } + } + + Ok(QuoteDispatchResponse { gas_amount: total }) +} + +fn get_hooks(deps: Deps) -> Result { + Ok(HooksResponse { + hooks: HOOKS + .load(deps.storage)? + .into_iter() + .map(|v| v.into()) + .collect(), + }) +} diff --git a/contracts/igps/core/src/contract.rs b/contracts/igps/core/src/contract.rs index fbd4a3f7..e9df7605 100644 --- a/contracts/igps/core/src/contract.rs +++ b/contracts/igps/core/src/contract.rs @@ -7,7 +7,7 @@ use hpl_interface::igp::core::{ExecuteMsg, IgpQueryMsg, InstantiateMsg, QueryMsg use hpl_interface::igp::oracle::IgpGasOracleQueryMsg; use hpl_interface::to_binary; -use crate::{ContractError, BENEFICIARY, CONTRACT_NAME, CONTRACT_VERSION, GAS_TOKEN, HRP, MAILBOX}; +use crate::{ContractError, BENEFICIARY, CONTRACT_NAME, CONTRACT_VERSION, GAS_TOKEN, HRP}; fn new_event(name: &str) -> Event { Event::new(format!("hpl_igp_core::{}", name)) @@ -23,13 +23,11 @@ pub fn instantiate( cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; let owner = deps.api.addr_validate(&msg.owner)?; - let mailbox = deps.api.addr_validate(&msg.mailbox)?; let beneficiary = deps.api.addr_validate(&msg.beneficiary)?; hpl_ownable::initialize(deps.storage, &owner)?; BENEFICIARY.save(deps.storage, &beneficiary)?; - MAILBOX.save(deps.storage, &mailbox)?; GAS_TOKEN.save(deps.storage, &msg.gas_token)?; HRP.save(deps.storage, &msg.hrp)?; diff --git a/contracts/igps/core/src/execute.rs b/contracts/igps/core/src/execute.rs index c8dd1c64..49097b7e 100644 --- a/contracts/igps/core/src/execute.rs +++ b/contracts/igps/core/src/execute.rs @@ -1,6 +1,6 @@ use crate::event::{emit_claim, emit_pay_for_gas, emit_post_dispatch, emit_set_beneficiary}; use crate::query::quote_gas_price; -use crate::{ContractError, BENEFICIARY, DEFAULT_GAS_USAGE, GAS_TOKEN, HRP, MAILBOX}; +use crate::{ContractError, BENEFICIARY, DEFAULT_GAS_USAGE, GAS_TOKEN, HRP}; use cosmwasm_std::{ coins, ensure, ensure_eq, BankMsg, DepsMut, Env, HexBinary, MessageInfo, Response, Uint128, @@ -55,12 +55,6 @@ pub fn post_dispatch( info: MessageInfo, req: PostDispatchMsg, ) -> Result { - ensure_eq!( - info.sender, - MAILBOX.load(deps.storage)?, - ContractError::Unauthorized {} - ); - let message: Message = req.message.clone().into(); let hrp = HRP.load(deps.storage)?; diff --git a/contracts/igps/core/src/lib.rs b/contracts/igps/core/src/lib.rs index d6b60c4d..02d0d3ff 100644 --- a/contracts/igps/core/src/lib.rs +++ b/contracts/igps/core/src/lib.rs @@ -22,9 +22,6 @@ pub const DEFAULT_GAS_USAGE: u128 = 25_000; pub const HRP_KEY: &str = "hrp"; pub const HRP: Item = Item::new(HRP_KEY); -pub const MAILBOX_KEY: &str = "mailbox"; -pub const MAILBOX: Item = Item::new(MAILBOX_KEY); - pub const GAS_TOKEN_KEY: &str = "gas_token"; pub const GAS_TOKEN: Item = Item::new(GAS_TOKEN_KEY); diff --git a/contracts/igps/core/src/query.rs b/contracts/igps/core/src/query.rs index 5eb14279..db736d56 100644 --- a/contracts/igps/core/src/query.rs +++ b/contracts/igps/core/src/query.rs @@ -1,5 +1,5 @@ use crate::error::ContractError; -use crate::{BENEFICIARY, DEFAULT_GAS_USAGE, GAS_TOKEN, MAILBOX, TOKEN_EXCHANGE_RATE_SCALE}; +use crate::{BENEFICIARY, DEFAULT_GAS_USAGE, GAS_TOKEN, TOKEN_EXCHANGE_RATE_SCALE}; use cosmwasm_std::{coin, Addr, Deps, QuerierWrapper, Storage, Uint256}; use hpl_interface::hook::{MailboxResponse, QuoteDispatchMsg, QuoteDispatchResponse}; @@ -7,11 +7,9 @@ use hpl_interface::igp::core::{BeneficiaryResponse, QuoteGasPaymentResponse}; use hpl_interface::igp::oracle::{self, GetExchangeRateAndGasPriceResponse, IgpGasOracleQueryMsg}; use hpl_interface::types::{IGPMetadata, Message}; -pub fn get_mailbox(deps: Deps) -> Result { - let mailbox = MAILBOX.load(deps.storage)?; - +pub fn get_mailbox(_deps: Deps) -> Result { Ok(MailboxResponse { - mailbox: mailbox.into(), + mailbox: "unrestricted".to_string(), }) } diff --git a/contracts/igps/core/src/tests/contract.rs b/contracts/igps/core/src/tests/contract.rs index 3ac63e3e..e539cfc8 100644 --- a/contracts/igps/core/src/tests/contract.rs +++ b/contracts/igps/core/src/tests/contract.rs @@ -13,7 +13,7 @@ use hpl_router::get_routes; use ibcx_test_utils::{addr, gen_bz}; use rstest::{fixture, rstest}; -use crate::{BENEFICIARY, DEFAULT_GAS_USAGE, GAS_TOKEN, HRP, MAILBOX}; +use crate::{BENEFICIARY, DEFAULT_GAS_USAGE, GAS_TOKEN, HRP}; use super::IGP; @@ -62,22 +62,14 @@ macro_rules! arg_fixture { arg_fixture!(deployer, Addr, addr("deployer")); arg_fixture!(hrp, &'static str, "test"); arg_fixture!(owner, Addr, addr("owner")); -arg_fixture!(mailbox, Addr, addr("mailbox")); arg_fixture!(gas_token, &'static str, "utest"); arg_fixture!(beneficiary, Addr, addr("beneficiary")); #[fixture] -fn igp( - deployer: Addr, - hrp: &str, - owner: Addr, - mailbox: Addr, - gas_token: &str, - beneficiary: Addr, -) -> IGP { +fn igp(deployer: Addr, hrp: &str, owner: Addr, gas_token: &str, beneficiary: Addr) -> IGP { let mut igp = IGP::new(mock_dependencies(), mock_env()); - igp.init(&deployer, hrp, &owner, &mailbox, gas_token, &beneficiary) + igp.init(&deployer, hrp, &owner, gas_token, &beneficiary) .unwrap(); igp @@ -101,7 +93,6 @@ fn test_init(igp: IGP) { assert_eq!(get_owner(storage).unwrap(), "owner"); assert_eq!(BENEFICIARY.load(storage).unwrap(), "beneficiary"); assert_eq!(GAS_TOKEN.load(storage).unwrap(), "utest"); - assert_eq!(MAILBOX.load(storage).unwrap(), "mailbox"); assert_eq!(HRP.load(storage).unwrap(), "test"); } @@ -249,8 +240,6 @@ fn test_pay_for_gas( #[case(addr("mailbox"), true, Some(300_000))] #[case(addr("mailbox"), true, None)] #[case(addr("mailbox"), false, None)] -#[should_panic(expected = "unauthorized")] -#[case(addr("owner"), true, Some(300_000))] fn test_post_dispatch( #[values("osmo", "neutron")] hrp: &str, #[with(vec![(1, "oracle/2/150".into())])] igp_routes: (IGP, Vec<(u32, String)>), diff --git a/contracts/igps/core/src/tests/mod.rs b/contracts/igps/core/src/tests/mod.rs index 5abdd987..d0dd96f3 100644 --- a/contracts/igps/core/src/tests/mod.rs +++ b/contracts/igps/core/src/tests/mod.rs @@ -42,7 +42,6 @@ impl IGP { sender: &Addr, hrp: &str, owner: &Addr, - mailbox: &Addr, gas_token: &str, beneficiary: &Addr, ) -> Result { @@ -53,7 +52,6 @@ impl IGP { InstantiateMsg { hrp: hrp.to_string(), owner: owner.to_string(), - mailbox: mailbox.to_string(), gas_token: gas_token.to_string(), beneficiary: beneficiary.to_string(), }, diff --git a/contracts/isms/aggregate/Cargo.toml b/contracts/isms/aggregate/Cargo.toml new file mode 100644 index 00000000..e5c9f663 --- /dev/null +++ b/contracts/isms/aggregate/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "hpl-ism-aggregate" +version.workspace = true +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true +documentation.workspace = true +keywords.workspace = true + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +# for more explicit tests, cargo test --features=backtraces +backtraces = ["cosmwasm-std/backtraces"] +# use library feature to disable all instantiate/execute/query exports +library = [] + +[dependencies] +cosmwasm-std.workspace = true +cosmwasm-storage.workspace = true +cosmwasm-schema.workspace = true + +cw-storage-plus.workspace = true +cw2.workspace = true + +sha2.workspace = true +ripemd.workspace = true + +bech32.workspace = true +schemars.workspace = true + +thiserror.workspace = true + +hpl-ownable.workspace = true +hpl-interface.workspace = true + +[dev-dependencies] + +serde.workspace = true +anyhow.workspace = true diff --git a/contracts/isms/aggregate/src/error.rs b/contracts/isms/aggregate/src/error.rs new file mode 100644 index 00000000..6ceb0a7d --- /dev/null +++ b/contracts/isms/aggregate/src/error.rs @@ -0,0 +1,17 @@ +use cosmwasm_std::{StdError, VerificationError}; +use thiserror::Error; + +#[derive(Error, Debug, PartialEq)] +pub enum ContractError { + #[error("{0}")] + Std(#[from] StdError), + + #[error("{0}")] + VerificationError(#[from] VerificationError), + + #[error("unauthorized")] + Unauthorized, + + #[error("route not found")] + RouteNotFound {}, +} diff --git a/contracts/isms/aggregate/src/lib.rs b/contracts/isms/aggregate/src/lib.rs new file mode 100644 index 00000000..3d8b32d6 --- /dev/null +++ b/contracts/isms/aggregate/src/lib.rs @@ -0,0 +1,149 @@ +mod error; + +pub use crate::error::ContractError; +#[cfg(not(feature = "library"))] +use cosmwasm_std::entry_point; +use cosmwasm_std::{ + ensure_eq, Addr, Deps, DepsMut, Env, Event, HexBinary, MessageInfo, QueryResponse, Response, + StdResult, +}; +use cw2::set_contract_version; +use cw_storage_plus::Item; +use hpl_interface::{ + ism::{ + aggregate::{ExecuteMsg, InstantiateMsg, QueryMsg}, + IsmQueryMsg, IsmType, ModuleTypeResponse, VerifyInfoResponse, VerifyResponse, + }, + to_binary, + types::AggregateMetadata, +}; +use hpl_ownable::get_owner; + +// version info for migration info +pub const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); +pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); + +const ISMS_KEY: &str = "isms"; +const ISMS: Item> = Item::new(ISMS_KEY); + +const THRESHOLD_KEY: &str = "threshold"; +const THRESHOLD: Item = Item::new(THRESHOLD_KEY); + +fn new_event(name: &str) -> Event { + Event::new(format!("hpl_ism_aggregate::{}", name)) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn instantiate( + deps: DepsMut, + _env: Env, + info: MessageInfo, + msg: InstantiateMsg, +) -> Result { + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + + let owner = deps.api.addr_validate(&msg.owner)?; + let isms = msg + .isms + .iter() + .map(|v| deps.api.addr_validate(v)) + .collect::>()?; + + hpl_ownable::initialize(deps.storage, &owner)?; + + ISMS.save(deps.storage, &isms)?; + THRESHOLD.save(deps.storage, &msg.threshold)?; + + Ok(Response::new().add_event( + new_event("instantiate") + .add_attribute("sender", info.sender) + .add_attribute("owner", owner) + .add_attribute("isms", msg.isms.join(",")) + .add_attribute("threshold", msg.threshold.to_string()), + )) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: ExecuteMsg, +) -> Result { + match msg { + ExecuteMsg::Ownable(msg) => Ok(hpl_ownable::handle(deps, env, info, msg)?), + ExecuteMsg::SetIsms { isms } => { + ensure_eq!( + get_owner(deps.storage)?, + info.sender, + ContractError::Unauthorized + ); + + let parsed_isms = isms + .iter() + .map(|v| deps.api.addr_validate(v)) + .collect::>()?; + + ISMS.save(deps.storage, &parsed_isms)?; + + Ok(Response::new() + .add_event(new_event("set_isms").add_attribute("isms", isms.join(",")))) + } + } +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result { + use IsmQueryMsg::*; + + match msg { + QueryMsg::Ownable(msg) => Ok(hpl_ownable::handle_query(deps, env, msg)?), + + QueryMsg::Ism(msg) => match msg { + ModuleType {} => to_binary({ + Ok::<_, ContractError>(ModuleTypeResponse { + typ: IsmType::Aggregation, + }) + }), + Verify { metadata, message } => to_binary(verify(deps, metadata, message)), + VerifyInfo { message } => to_binary(verify_info(deps, message)), + }, + } +} + +fn verify( + deps: Deps, + metadata: HexBinary, + message: HexBinary, +) -> Result { + let isms = ISMS.load(deps.storage)?; + + let mut threshold = THRESHOLD.load(deps.storage)?; + + for (ism, meta) in AggregateMetadata::from_hex(metadata, isms) { + let verified = hpl_interface::ism::verify(&deps.querier, ism, meta, message.clone())?; + + if verified { + threshold -= 1; + } + + if threshold == 0 { + break; + } + } + + Ok(VerifyResponse { + verified: threshold == 0, + }) +} + +fn verify_info(deps: Deps, _message: HexBinary) -> Result { + Ok(VerifyInfoResponse { + threshold: THRESHOLD.load(deps.storage)?, + validators: ISMS + .load(deps.storage)? + .into_iter() + .map(|v| v.to_string()) + .collect(), + }) +} diff --git a/contracts/isms/multisig/src/lib.rs b/contracts/isms/multisig/src/lib.rs index 34f484b8..3330d8f6 100644 --- a/contracts/isms/multisig/src/lib.rs +++ b/contracts/isms/multisig/src/lib.rs @@ -39,14 +39,14 @@ pub fn eth_hash(message: HexBinary) -> Result { pub fn multisig_hash( mut domain_hash: Vec, mut root: Vec, - mut index: Vec, + index: u32, mut message_id: Vec, ) -> Result { let mut bz = vec![]; bz.append(&mut domain_hash); bz.append(&mut root); - bz.append(&mut index); + bz.append(&mut index.to_be_bytes().to_vec()); bz.append(&mut message_id); let hash = keccak256_hash(&bz); diff --git a/contracts/isms/multisig/src/query.rs b/contracts/isms/multisig/src/query.rs index b060a925..2f1529d8 100644 --- a/contracts/isms/multisig/src/query.rs +++ b/contracts/isms/multisig/src/query.rs @@ -38,6 +38,8 @@ pub fn verify_message( let threshold = THRESHOLD.load(deps.storage, message.origin_domain)?; let validators = VALIDATORS.load(deps.storage, message.origin_domain)?; + let merkle_index = metadata.merkle_index(); + let verifiable_cases = product( validators.0.into_iter().map(|v| v.signer_pubkey).collect(), metadata.signatures, @@ -46,7 +48,7 @@ pub fn verify_message( let multisig_hash = multisig_hash( domain_hash(message.origin_domain, metadata.origin_merkle_tree)?.to_vec(), metadata.merkle_root.to_vec(), - metadata.merkle_index.to_vec(), + merkle_index, message.id().to_vec(), )?; diff --git a/contracts/warp/cw20/Cargo.toml b/contracts/warp/cw20/Cargo.toml index 593cc1ba..4ed1c449 100644 --- a/contracts/warp/cw20/Cargo.toml +++ b/contracts/warp/cw20/Cargo.toml @@ -43,6 +43,8 @@ hpl-router.workspace = true hpl-interface.workspace = true [dev-dependencies] +osmosis-test-tube.workspace = true +ibcx-test-utils.workspace = true rstest.workspace = true anyhow.workspace = true k256.workspace = true diff --git a/contracts/warp/cw20/src/contract.rs b/contracts/warp/cw20/src/contract.rs index aade209e..2b06b440 100644 --- a/contracts/warp/cw20/src/contract.rs +++ b/contracts/warp/cw20/src/contract.rs @@ -1,8 +1,8 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - ensure_eq, from_binary, CosmosMsg, Deps, DepsMut, Env, Event, HexBinary, MessageInfo, - QueryResponse, Reply, Response, SubMsg, Uint256, WasmMsg, + ensure_eq, from_binary, CosmosMsg, Deps, DepsMut, Env, HexBinary, MessageInfo, QueryResponse, + Reply, Response, SubMsg, Uint256, WasmMsg, }; use cw20::Cw20ReceiveMsg; @@ -34,33 +34,39 @@ pub fn instantiate( let mode: TokenMode = msg.token.clone().into(); let owner = deps.api.addr_validate(&msg.owner)?; + let mailbox = deps.api.addr_validate(&msg.mailbox)?; HRP.save(deps.storage, &msg.hrp)?; MODE.save(deps.storage, &mode)?; - MAILBOX.save(deps.storage, &deps.api.addr_validate(&msg.mailbox)?)?; + MAILBOX.save(deps.storage, &mailbox)?; hpl_ownable::initialize(deps.storage, &owner)?; - let mut denom = "".into(); - - let msgs = match msg.token { + let (msgs, denom) = match msg.token { TokenModeMsg::Bridged(token) => { - vec![SubMsg::reply_on_success( + let mut token_init_msg = token.init_msg; + token_init_msg.mint = Some(cw20::MinterResponse { + minter: env.contract.address.to_string(), + cap: None, + }); + + let msgs = vec![SubMsg::reply_on_success( WasmMsg::Instantiate { admin: Some(env.contract.address.to_string()), code_id: token.code_id, - msg: cosmwasm_std::to_binary(&token.init_msg)?, + msg: cosmwasm_std::to_binary(&token_init_msg)?, funds: vec![], label: "token warp cw20".to_string(), }, REPLY_ID_CREATE_DENOM, - )] + )]; + + (msgs, token_init_msg.name) } TokenModeMsg::Collateral(token) => { let token_addr = deps.api.addr_validate(&token.address)?; TOKEN.save(deps.storage, &token_addr)?; - denom = token_addr.to_string(); - vec![] + (vec![], token_addr.into()) } }; @@ -83,6 +89,7 @@ pub fn execute( use ExecuteMsg::*; match msg { + Ownable(msg) => Ok(hpl_ownable::handle(deps, env, info, msg)?), Router(msg) => Ok(hpl_router::handle(deps, env, info, msg)?), Handle(msg) => mailbox_handle(deps, info, msg), Receive(msg) => { @@ -112,9 +119,8 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result Result { Ok(TokenModeResponse { mode }) } + +#[cfg(test)] +mod test { + use cosmwasm_std::{ + testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, + Empty, OwnedDeps, Uint128, + }; + use hpl_interface::{ + build_test_executor, build_test_querier, + core::HandleMsg, + router::DomainRouteSet, + warp::cw20::{Cw20ModeBridged, Cw20ModeCollateral}, + }; + use hpl_router::set_routes; + use ibcx_test_utils::{addr, gen_bz}; + use rstest::{fixture, rstest}; + + use super::*; + + build_test_querier!(super::query); + build_test_executor!(super::execute); + + const DEPLOYER: &str = "sender"; + const OWNER: &str = "owner"; + const MAILBOX: &str = "mailbox"; + const TOKEN: &str = "token"; + + const CW20_BRIDGED_CODE_ID: u64 = 1; + const CW20_BRIDGED_NAME: &str = "cw20-created"; + const CW20_COLLATERAL_ADDRESS: &str = "cw20-exisiting"; + + type Cw20TokenMode = TokenModeMsg; + type TestDeps = OwnedDeps; + + #[fixture] + fn token_mode_bridged() -> Cw20TokenMode { + TokenModeMsg::Bridged(Cw20ModeBridged { + code_id: CW20_BRIDGED_CODE_ID, + init_msg: cw20_base::msg::InstantiateMsg { + name: CW20_BRIDGED_NAME.to_string(), + symbol: CW20_BRIDGED_NAME.to_string(), + decimals: 1, + initial_balances: vec![], + mint: None, + marketing: None, + } + .into(), + }) + } + + #[fixture] + fn token_mode_collateral() -> Cw20TokenMode { + TokenModeMsg::Collateral(Cw20ModeCollateral { + address: CW20_COLLATERAL_ADDRESS.to_string(), + }) + } + + #[fixture] + fn deps( + #[default(vec![])] routes: Vec<(u32, HexBinary)>, + #[default("osmo")] hrp: &str, + #[default(Some(TOKEN))] token: Option<&str>, + token_mode_collateral: Cw20TokenMode, + ) -> (TestDeps, Response) { + let mut deps = mock_dependencies(); + + let res = instantiate( + deps.as_mut(), + mock_env(), + mock_info(DEPLOYER, &[]), + InstantiateMsg { + token: token_mode_collateral, + hrp: hrp.to_string(), + owner: OWNER.to_string(), + mailbox: MAILBOX.to_string(), + }, + ) + .unwrap(); + + if let Some(token) = token { + super::TOKEN + .save(deps.as_mut().storage, &addr(token)) + .unwrap(); + } + + if !routes.is_empty() { + set_routes( + deps.as_mut().storage, + &addr(OWNER), + routes + .into_iter() + .map(|v| DomainRouteSet { + domain: v.0, + route: Some(v.1), + }) + .collect(), + ) + .unwrap(); + } + + (deps, res) + } + + #[rstest] + #[case(token_mode_bridged())] + #[case(token_mode_collateral())] + fn test_queries(#[values("osmo", "neutron")] hrp: &str, #[case] token_mode: Cw20TokenMode) { + let (deps, _) = deps(vec![], hrp, Some(TOKEN), token_mode.clone()); + + let res: warp::TokenTypeResponse = test_query( + deps.as_ref(), + QueryMsg::TokenDefault(warp::TokenWarpDefaultQueryMsg::TokenType {}), + ); + assert_eq!( + res.typ, + warp::TokenType::CW20 { + contract: TOKEN.into() + } + ); + + let res: warp::TokenModeResponse = test_query( + deps.as_ref(), + QueryMsg::TokenDefault(warp::TokenWarpDefaultQueryMsg::TokenMode {}), + ); + assert_eq!(res.mode, token_mode.into()); + } + + #[rstest] + #[case(token_mode_bridged())] + #[case(token_mode_collateral())] + fn test_init(#[values("osmo", "neutron")] hrp: &str, #[case] token_mode: Cw20TokenMode) { + let (deps, res) = deps(vec![], hrp, None, token_mode.clone()); + + let storage = deps.as_ref().storage; + let mode = token_mode.clone().into(); + + assert_eq!(super::HRP.load(storage).unwrap(), hrp); + assert_eq!(super::MODE.load(storage).unwrap(), mode); + assert_eq!(super::MAILBOX.load(storage).unwrap(), MAILBOX); + + match token_mode { + TokenModeMsg::Bridged(mut v) => { + v.init_msg.mint = Some(cw20::MinterResponse { + minter: mock_env().contract.address.into(), + cap: None, + }); + + assert!(!super::TOKEN.exists(storage)); + + let reply = res.messages.get(0).unwrap(); + assert_eq!(reply.id, REPLY_ID_CREATE_DENOM); + assert_eq!( + reply.msg, + CosmosMsg::Wasm(WasmMsg::Instantiate { + admin: Some(mock_env().contract.address.to_string()), + code_id: v.code_id, + msg: cosmwasm_std::to_binary(&v.init_msg).unwrap(), + funds: vec![], + label: "token warp cw20".to_string() + }) + ) + } + TokenModeMsg::Collateral(v) => { + assert_eq!(super::TOKEN.load(storage).unwrap(), v.address); + assert!(res.messages.is_empty()) + } + } + } + + enum Method { + Handle, + Receive, + } + + fn default_cw20_receive_msg() -> Cw20ReceiveMsg { + Cw20ReceiveMsg { + sender: Default::default(), + amount: Default::default(), + msg: Default::default(), + } + } + + #[rstest] + #[should_panic(expected = "unauthorized")] + #[case(MAILBOX, Method::Receive)] + #[should_panic(expected = "unauthorized")] + #[case(TOKEN, Method::Handle)] + fn test_execute_authority( + deps: (TestDeps, Response), + #[case] sender: &str, + #[case] method: Method, + ) { + let (mut deps, _) = deps; + + let msg = match method { + Method::Handle => ExecuteMsg::Handle(HandleMsg::default()), + Method::Receive => ExecuteMsg::Receive(default_cw20_receive_msg()), + }; + + test_execute(deps.as_mut(), &addr(sender), msg, vec![]); + } + + #[rstest] + #[case(1, gen_bz(32), token_mode_bridged())] + #[case(1, gen_bz(32), token_mode_collateral())] + #[should_panic(expected = "route not found")] + #[case(2, gen_bz(32), token_mode_collateral())] + fn test_mailbox_handle( + #[values("osmo", "neutron")] hrp: &str, + #[case] domain: u32, + #[case] route: HexBinary, + #[case] token_mode: Cw20TokenMode, + ) { + let (mut deps, _) = deps( + vec![(1, route.clone())], + hrp, + Some(TOKEN), + token_mode.clone(), + ); + + let sender = MAILBOX; + + let warp_msg = warp::Message { + recipient: gen_bz(32), + amount: Uint256::from_u128(100), + metadata: HexBinary::default(), + }; + + let handle_msg = HandleMsg { + origin: domain, + sender: route, + body: warp_msg.clone().into(), + }; + + let res = test_execute( + deps.as_mut(), + &addr(sender), + ExecuteMsg::Handle(handle_msg), + vec![], + ); + let msg = &res.messages.get(0).unwrap().msg; + + match token_mode { + TokenModeMsg::Bridged(_) => { + assert_eq!( + cosmwasm_std::to_binary(msg).unwrap(), + cosmwasm_std::to_binary(&CosmosMsg::::Wasm( + conv::to_mint_msg( + TOKEN, + bech32_encode(hrp, warp_msg.recipient.as_slice()).unwrap(), + warp_msg.amount + ) + .unwrap() + )) + .unwrap() + ) + } + TokenModeMsg::Collateral(_) => { + assert_eq!( + cosmwasm_std::to_binary(msg).unwrap(), + cosmwasm_std::to_binary(&CosmosMsg::::Wasm( + conv::to_send_msg( + TOKEN, + bech32_encode(hrp, warp_msg.recipient.as_slice()).unwrap(), + warp_msg.amount + ) + .unwrap() + )) + .unwrap() + ); + } + } + } + + #[rstest] + #[case(1, gen_bz(32), token_mode_bridged())] + #[case(1, gen_bz(32), token_mode_collateral())] + #[should_panic(expected = "route not found")] + #[case(2, gen_bz(32), token_mode_collateral())] + fn test_transfer_remote( + #[values("osmo", "neutron")] hrp: &str, + #[case] domain: u32, + #[case] route: HexBinary, + #[case] token_mode: Cw20TokenMode, + ) { + let (mut deps, _) = deps( + vec![(1, route.clone())], + hrp, + Some(TOKEN), + token_mode.clone(), + ); + + let sender = addr("sender"); + let recipient = gen_bz(32); + + let receive_msg = Cw20ReceiveMsg { + sender: sender.to_string(), + amount: Uint128::new(100), + msg: cosmwasm_std::to_binary(&ReceiveMsg::TransferRemote { + dest_domain: domain, + recipient: recipient.clone(), + }) + .unwrap(), + }; + + let res = test_execute( + deps.as_mut(), + &addr(TOKEN), + ExecuteMsg::Receive(receive_msg), + vec![], + ); + let msgs = res.messages.into_iter().map(|v| v.msg).collect::>(); + + let warp_msg = warp::Message { + recipient, + amount: Uint256::from_u128(100), + metadata: HexBinary::default(), + }; + + let dispatch_msg = + mailbox::dispatch(MAILBOX, domain, route, warp_msg.into(), None, None).unwrap(); + + match token_mode { + TokenModeMsg::Bridged(_) => { + assert_eq!( + cosmwasm_std::to_binary(&msgs).unwrap(), + cosmwasm_std::to_binary(&vec![ + CosmosMsg::from(conv::to_burn_msg(TOKEN, Uint128::new(100)).unwrap()), + dispatch_msg, + ]) + .unwrap(), + ); + } + TokenModeMsg::Collateral(_) => { + assert_eq!( + cosmwasm_std::to_binary(&msgs).unwrap(), + cosmwasm_std::to_binary(&vec![dispatch_msg]).unwrap(), + ); + } + } + } +} diff --git a/contracts/warp/cw20/src/error.rs b/contracts/warp/cw20/src/error.rs index a4fef9b4..fc498fa4 100644 --- a/contracts/warp/cw20/src/error.rs +++ b/contracts/warp/cw20/src/error.rs @@ -9,21 +9,21 @@ pub enum ContractError { #[error("{0}")] ParseReplyError(#[from] cw_utils::ParseReplyError), - #[error("Unauthorized")] + #[error("unauthorized")] Unauthorized, - #[error("WrongLength")] + #[error("wrong length")] WrongLength {}, - #[error("InvalidTokenOption")] + #[error("invalid token option")] InvalidTokenOption, - #[error("InvalidReplyId")] + #[error("invalid reply id")] InvalidReplyId, - #[error("InvalidReceiveMsg")] + #[error("invalid receive msg")] InvalidReceiveMsg, - #[error("NoRouter domain:{domain:?}")] + #[error("no router for domain {domain:?}")] NoRouter { domain: u32 }, } diff --git a/contracts/warp/cw20/src/lib.rs b/contracts/warp/cw20/src/lib.rs index 02e02014..0924bda9 100644 --- a/contracts/warp/cw20/src/lib.rs +++ b/contracts/warp/cw20/src/lib.rs @@ -6,9 +6,6 @@ pub mod contract; mod conv; pub mod error; -#[cfg(test)] -mod tests; - // reply message pub const REPLY_ID_CREATE_DENOM: u64 = 0; diff --git a/contracts/warp/cw20/src/tests/contracts.rs b/contracts/warp/cw20/src/tests/contracts.rs deleted file mode 100644 index dbf24122..00000000 --- a/contracts/warp/cw20/src/tests/contracts.rs +++ /dev/null @@ -1,292 +0,0 @@ -// use std::str::FromStr; - -// use cosmwasm_std::{testing::mock_env, to_binary, Addr, Binary, CosmosMsg, Uint256, WasmMsg}; -// use hpl_interface::{ -// types::bech32_encode, -// warp::{self, cw20::TokenOption}, -// }; -// use rstest::rstest; - -// use crate::{error::ContractError, tests::TokenCW20, TOKEN}; - -// #[rstest] -// #[case("osmo")] -// #[case("neutron")] -// fn test_router_role(#[case] hrp: &str) -> anyhow::Result<()> { -// let deployer = Addr::unchecked("deployer"); -// let mailbox = Addr::unchecked("mailbox"); -// let owner = Addr::unchecked("owner"); - -// let token = Addr::unchecked("token-native"); -// let domain = 999; -// let router = Binary(b"hello".to_vec()); - -// let mut warp = TokenCW20::default(); - -// warp.init( -// &deployer, -// &owner, -// &mailbox, -// Some(TokenOption::Reuse { -// contract: token.to_string(), -// }), -// TokenMode::Bridged, -// hrp, -// )?; - -// // err -// let err = warp -// .router_enroll(&mailbox, domain, router.clone()) -// .unwrap_err(); -// assert_eq!(err, ContractError::Unauthorized); - -// // ok -// warp.router_enroll(&owner, domain, router)?; - -// Ok(()) -// } - -// #[rstest] -// #[case("osmo")] -// #[case("neutron")] -// fn test_outbound_transfer(#[case] hrp: &str) -> anyhow::Result<()> { -// let deployer = Addr::unchecked("deployer"); -// let mailbox = Addr::unchecked("mailbox"); -// let router = Addr::unchecked("router"); -// let owner = Addr::unchecked("owner"); - -// let token = Addr::unchecked("token-cw20"); -// let amount: u64 = 100_000; - -// let user_remote = Addr::unchecked("user-remote____________________1"); - -// let dest_domain = 1; - -// let env = mock_env(); - -// let burn_msg: CosmosMsg = WasmMsg::Execute { -// contract_addr: token.to_string(), -// msg: to_binary(&cw20::Cw20ExecuteMsg::Burn { -// amount: amount.into(), -// })?, -// funds: vec![], -// } -// .into(); - -// let dispatch_msg: CosmosMsg = WasmMsg::Execute { -// contract_addr: mailbox.to_string(), -// msg: to_binary(&mailbox::ExecuteMsg::Dispatch { -// dest_domain, -// recipient_addr: Binary(router.as_bytes().to_vec()).into(), -// msg_body: token::Message { -// recipient: Binary(user_remote.as_bytes().to_vec()), -// amount: Uint256::from_str(&amount.to_string())?, -// metadata: Binary::default(), -// } -// .into(), -// })?, -// funds: vec![], -// } -// .into(); - -// for (mode, routers, expected_resp) in [ -// ( -// TokenMode::Bridged, -// vec![(dest_domain, Binary(router.as_bytes().to_vec()))], -// Ok(vec![burn_msg, dispatch_msg.clone()]), -// ), -// ( -// TokenMode::Bridged, -// vec![], -// Err(ContractError::NoRouter { -// domain: dest_domain, -// }), -// ), -// ( -// TokenMode::Collateral, -// vec![(dest_domain, Binary(router.as_bytes().to_vec()))], -// Ok(vec![dispatch_msg]), -// ), -// ( -// TokenMode::Collateral, -// vec![], -// Err(ContractError::NoRouter { -// domain: dest_domain, -// }), -// ), -// ] { -// let mut warp = TokenCW20 { -// env: env.clone(), -// ..Default::default() -// }; - -// warp.init( -// &deployer, -// &owner, -// &mailbox, -// Some(TokenOption::Reuse { -// contract: token.to_string(), -// }), -// mode.clone(), -// hrp, -// )?; -// if mode == TokenMode::Collateral { -// TOKEN.save(&mut warp.deps.storage, &token)?; -// } - -// for (domain, router) in routers { -// warp.router_enroll(&owner, domain, router)?; -// } - -// let resp = warp.transfer_remote( -// &deployer, -// &token, -// amount.into(), -// dest_domain, -// user_remote.as_bytes().into(), -// ); - -// assert_eq!( -// resp.map(|v| v.messages.into_iter().map(|v| v.msg).collect::>()), -// expected_resp -// ); -// } - -// Ok(()) -// } - -// #[rstest] -// #[case("osmo")] -// #[case("neutron")] -// fn test_inbound_transfer(#[case] hrp: &str) -> anyhow::Result<()> { -// let deployer = Addr::unchecked("deployer"); -// let mailbox = Addr::unchecked("mailbox"); -// let router = Addr::unchecked("router"); -// let owner = Addr::unchecked("owner"); -// let errortic = Addr::unchecked("errortic"); - -// let token = Addr::unchecked("token-cw20"); -// let amount = 100_000; - -// let user_remote = Addr::unchecked("user-remote____________________1"); - -// let env = mock_env(); - -// let origin_domain = 1; - -// let mint_msg: CosmosMsg = WasmMsg::Execute { -// contract_addr: token.to_string(), -// msg: to_binary(&cw20::Cw20ExecuteMsg::Mint { -// recipient: bech32_encode(hrp, user_remote.as_bytes())?.to_string(), -// amount: amount.into(), -// })?, -// funds: vec![], -// } -// .into(); - -// let send_msg: CosmosMsg = WasmMsg::Execute { -// contract_addr: token.to_string(), -// msg: to_binary(&cw20::Cw20ExecuteMsg::Transfer { -// recipient: bech32_encode(hrp, user_remote.as_bytes())?.to_string(), -// amount: amount.into(), -// })?, -// funds: vec![], -// } -// .into(); - -// let default_msg = token::Message { -// recipient: user_remote.as_bytes().to_vec().into(), -// amount: Uint256::from_u128(amount), -// metadata: Binary::default(), -// }; - -// for (mode, sender, origin, origin_sender, token_msg, expected_resp) in [ -// // happy -// ( -// TokenMode::Bridged, -// &mailbox, -// origin_domain, -// &router, -// default_msg.clone(), -// Ok(vec![mint_msg]), -// ), -// ( -// TokenMode::Collateral, -// &mailbox, -// origin_domain, -// &router, -// default_msg.clone(), -// Ok(vec![send_msg]), -// ), -// // errors -// ( -// TokenMode::Bridged, -// &errortic, -// origin_domain, -// &router, -// default_msg.clone(), -// Err(ContractError::Unauthorized), -// ), -// ( -// TokenMode::Bridged, -// &mailbox, -// origin_domain, -// &errortic, -// default_msg.clone(), -// Err(ContractError::Unauthorized), -// ), -// ( -// TokenMode::Collateral, -// &errortic, -// origin_domain, -// &router, -// default_msg.clone(), -// Err(ContractError::Unauthorized), -// ), -// ( -// TokenMode::Collateral, -// &mailbox, -// origin_domain, -// &errortic, -// default_msg, -// Err(ContractError::Unauthorized), -// ), -// ] { -// let mut warp = TokenCW20 { -// env: env.clone(), -// ..Default::default() -// }; - -// warp.init( -// &deployer, -// &owner, -// &mailbox, -// Some(TokenOption::Reuse { -// contract: token.to_string(), -// }), -// mode.clone(), -// hrp, -// )?; -// if mode == TokenMode::Collateral { -// TOKEN.save(&mut warp.deps.storage, &token)?; -// } - -// warp.router_enroll(&owner, origin_domain, router.as_bytes().into())?; - -// let resp = warp.mailbox_handle( -// sender, -// mailbox::HandleMsg { -// origin, -// sender: origin_sender.as_bytes().to_vec().into(), -// body: token_msg.into(), -// }, -// ); - -// assert_eq!( -// resp.map(|v| v.messages.into_iter().map(|v| v.msg).collect::>()), -// expected_resp -// ); -// } - -// Ok(()) -// } diff --git a/contracts/warp/cw20/src/tests/mod.rs b/contracts/warp/cw20/src/tests/mod.rs deleted file mode 100644 index e26b05af..00000000 --- a/contracts/warp/cw20/src/tests/mod.rs +++ /dev/null @@ -1,115 +0,0 @@ -// use cosmwasm_std::{ -// from_binary, -// testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, -// to_binary, Addr, Binary, Empty, Env, MessageInfo, OwnedDeps, Response, Uint128, -// }; -// use hpl_interface::{ -// mailbox, router, -// token::TokenMode, -// token_cw20::{ExecuteMsg, QueryMsg, ReceiveMsg}, -// }; -// use serde::de::DeserializeOwned; - -// use crate::{ -// contract::{execute, instantiate, query}, -// error::ContractError, -// msg::{InstantiateMsg, TokenOption}, -// }; - -// mod contracts; - -// pub struct TokenCW20 { -// pub deps: OwnedDeps, -// pub env: Env, -// } - -// impl Default for TokenCW20 { -// fn default() -> Self { -// Self { -// deps: mock_dependencies(), -// env: mock_env(), -// } -// } -// } - -// impl TokenCW20 { -// pub fn init( -// &mut self, -// sender: &Addr, -// owner: &Addr, -// mailbox: &Addr, -// token: Option, -// mode: TokenMode, -// hrp: &str, -// ) -> Result { -// instantiate( -// self.deps.as_mut(), -// self.env.clone(), -// mock_info(sender.as_str(), &[]), -// InstantiateMsg { -// token, -// mode, -// hrp: hrp.to_string(), -// owner: owner.to_string(), -// mailbox: mailbox.to_string(), -// }, -// ) -// } - -// fn execute(&mut self, info: MessageInfo, msg: ExecuteMsg) -> Result { -// execute(self.deps.as_mut(), self.env.clone(), info, msg) -// } - -// #[allow(dead_code)] -// fn query(&self, msg: QueryMsg) -> Result { -// query(self.deps.as_ref(), self.env.clone(), msg) -// .map(|v| from_binary::(&v))? -// .map_err(|e| e.into()) -// } - -// pub fn router_enroll( -// &mut self, -// sender: &Addr, -// domain: u32, -// router: Binary, -// ) -> Result { -// self.execute( -// mock_info(sender.as_str(), &[]), -// ExecuteMsg::Router(router::RouterMsg::EnrollRemoteRouter { -// set: router::RouterSet { domain, router }, -// }), -// ) -// } - -// pub fn mailbox_handle( -// &mut self, -// sender: &Addr, -// handle_msg: mailbox::HandleMsg, -// ) -> Result { -// self.execute( -// mock_info(sender.as_str(), &[]), -// ExecuteMsg::Handle(handle_msg), -// ) -// } - -// pub fn transfer_remote( -// &mut self, -// sender: &Addr, -// token: &Addr, -// amount: Uint128, -// dest_domain: u32, -// recipient: Binary, -// ) -> Result { -// self.execute( -// mock_info(token.as_str(), &[]), -// ExecuteMsg::Receive(cw20::Cw20ReceiveMsg { -// sender: sender.to_string(), -// amount, -// msg: to_binary(&ReceiveMsg::TransferRemote { -// dest_domain, -// recipient, -// })?, -// }), -// ) -// } -// } diff --git a/contracts/warp/native/Cargo.toml b/contracts/warp/native/Cargo.toml index c0b218e1..9b0e3789 100644 --- a/contracts/warp/native/Cargo.toml +++ b/contracts/warp/native/Cargo.toml @@ -44,6 +44,7 @@ hpl-router.workspace = true hpl-interface.workspace = true [dev-dependencies] +ibcx-test-utils.workspace = true rstest.workspace = true anyhow.workspace = true k256.workspace = true diff --git a/contracts/warp/native/src/contract.rs b/contracts/warp/native/src/contract.rs index 2c71d8c4..c778693c 100644 --- a/contracts/warp/native/src/contract.rs +++ b/contracts/warp/native/src/contract.rs @@ -42,9 +42,7 @@ pub fn instantiate( hpl_ownable::initialize(deps.storage, &owner)?; - let mut denom = "".into(); - - let msgs = match msg.token { + let (msgs, denom) = match msg.token { // create native denom if token is bridged TokenModeMsg::Bridged(token) => { let mut msgs = vec![]; @@ -64,13 +62,12 @@ pub fn instantiate( ))); } - msgs + (msgs, token.denom) } // use denom directly if token is native TokenModeMsg::Collateral(token) => { TOKEN.save(deps.storage, &token.denom)?; - denom = token.denom; - vec![] + (vec![], token.denom) } }; @@ -90,10 +87,13 @@ pub fn execute( info: MessageInfo, msg: ExecuteMsg, ) -> Result { + use ExecuteMsg::*; + match msg { - ExecuteMsg::Router(msg) => Ok(hpl_router::handle(deps, env, info, msg)?), - ExecuteMsg::Handle(msg) => mailbox_handle(deps, env, info, msg), - ExecuteMsg::TransferRemote { + Ownable(msg) => Ok(hpl_ownable::handle(deps, env, info, msg)?), + Router(msg) => Ok(hpl_router::handle(deps, env, info, msg)?), + Handle(msg) => mailbox_handle(deps, env, info, msg), + TransferRemote { dest_domain, recipient, } => transfer_remote(deps, env, info, dest_domain, recipient), @@ -110,9 +110,8 @@ pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result Result { Ok(TokenModeResponse { mode }) } + +#[cfg(test)] +mod test { + use cosmwasm_std::{ + coin, + testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, + Coin, OwnedDeps, + }; + use hpl_interface::{ + build_test_executor, build_test_querier, + core::HandleMsg, + router::DomainRouteSet, + warp::native::{Metadata, NativeModeBriged, NativeModeCollateral}, + }; + use hpl_router::set_route; + use ibcx_test_utils::{addr, gen_bz}; + use rstest::{fixture, rstest}; + + use super::*; + + build_test_executor!(super::execute); + build_test_querier!(super::query); + + type NativeTokenMode = TokenModeMsg; + type TestDeps = OwnedDeps; + + const DEPLOYER: &str = "deployer"; + const OWNER: &str = "owner"; + const MAILBOX: &str = "mailbox"; + const DENOM: &str = "utest"; + + #[fixture] + fn metadata(#[default(true)] empty: bool) -> Option { + if empty { + None + } else { + Some(Metadata { + description: "testtesttest".into(), + denom_units: vec![], + base: "basebasebase".into(), + display: "displaydisplaydisplay".into(), + name: DENOM.into(), + symbol: DENOM.into(), + }) + } + } + + #[fixture] + fn token_mode_bridged(metadata: Option) -> NativeTokenMode { + TokenModeMsg::Bridged(NativeModeBriged { + denom: DENOM.into(), + metadata, + }) + } + + #[fixture] + fn token_mode_collateral() -> NativeTokenMode { + TokenModeMsg::Collateral(NativeModeCollateral { + denom: DENOM.into(), + }) + } + + #[fixture] + fn deps( + #[default(token_mode_collateral())] token_mode: NativeTokenMode, + #[default("osmo")] hrp: &str, + ) -> TestDeps { + let mut deps = mock_dependencies(); + + super::instantiate( + deps.as_mut(), + mock_env(), + mock_info(DEPLOYER, &[]), + super::InstantiateMsg { + token: token_mode, + hrp: hrp.into(), + owner: OWNER.into(), + mailbox: MAILBOX.into(), + }, + ) + .unwrap(); + + deps + } + + #[rstest] + #[case(token_mode_bridged(metadata(true)))] + #[case(token_mode_bridged(metadata(false)))] + #[case(token_mode_collateral())] + fn test_queries(#[values("osmo", "neutron")] hrp: &str, #[case] token_mode: NativeTokenMode) { + let mut deps = deps(token_mode.clone(), hrp); + + if TokenMode::from(token_mode.clone()) == TokenMode::Bridged { + super::TOKEN + .save(deps.as_mut().storage, &DENOM.into()) + .unwrap(); + } + + let res: warp::TokenTypeResponse = test_query( + deps.as_ref(), + QueryMsg::TokenDefault(warp::TokenWarpDefaultQueryMsg::TokenType {}), + ); + assert_eq!( + res.typ, + warp::TokenType::Native(warp::TokenTypeNative::Fungible { + denom: DENOM.into() + }) + ); + + let res: warp::TokenModeResponse = test_query( + deps.as_ref(), + QueryMsg::TokenDefault(warp::TokenWarpDefaultQueryMsg::TokenMode {}), + ); + assert_eq!(res.mode, token_mode.into()); + } + + #[rstest] + #[case(token_mode_bridged(metadata(true)))] + #[case(token_mode_bridged(metadata(false)))] + #[case(token_mode_collateral())] + fn test_init(#[values("osmo", "neutron")] hrp: &str, #[case] token_mode: NativeTokenMode) { + let mut deps = mock_dependencies(); + + let res = super::instantiate( + deps.as_mut(), + mock_env(), + mock_info(DEPLOYER, &[]), + super::InstantiateMsg { + token: token_mode.clone(), + hrp: hrp.into(), + owner: OWNER.into(), + mailbox: MAILBOX.into(), + }, + ) + .unwrap(); + + let storage = deps.as_ref().storage; + assert_eq!(super::HRP.load(storage).unwrap(), hrp); + assert_eq!( + super::MODE.load(storage).unwrap(), + token_mode.clone().into() + ); + assert_eq!(super::MAILBOX.load(storage).unwrap(), MAILBOX); + + match token_mode { + TokenModeMsg::Bridged(v) => { + if v.metadata.is_some() { + assert_eq!(res.messages.len(), 2); + } else { + assert_eq!(res.messages.len(), 1); + } + } + TokenModeMsg::Collateral(_) => { + assert_eq!(res.messages.len(), 0); + assert_eq!(super::TOKEN.load(storage).unwrap(), DENOM); + } + } + } + + #[rstest] + #[case(MAILBOX, 1, gen_bz(32))] + #[should_panic(expected = "unauthorized")] + #[case(OWNER, 1, gen_bz(32))] + #[should_panic(expected = "route not found")] + #[case(MAILBOX, 2, gen_bz(32))] + fn test_mailbox_handle( + mut deps: TestDeps, + #[case] sender: &str, + #[case] origin_domain: u32, + #[case] origin_sender: HexBinary, + ) { + let recipient = gen_bz(32); + + let handle_msg = HandleMsg { + origin: origin_domain, + sender: origin_sender.clone(), + body: warp::Message { + recipient: recipient.clone(), + amount: Uint256::from_u128(100), + metadata: HexBinary::default(), + } + .into(), + }; + + set_route( + deps.as_mut().storage, + &addr(OWNER), + DomainRouteSet { + domain: 1, + route: Some(origin_sender), + }, + ) + .unwrap(); + + let res = test_execute( + deps.as_mut(), + &addr(sender), + ExecuteMsg::Handle(handle_msg), + vec![], + ); + let mut msgs: Vec<_> = res.messages.into_iter().map(|v| v.msg).collect(); + + let mode = MODE.load(deps.as_ref().storage).unwrap(); + + assert_eq!( + msgs.pop().unwrap(), + conv::to_send_msg( + &bech32_encode("osmo", recipient.as_slice()).unwrap(), + vec![coin(100, DENOM)] + ) + .into() + ); + + if mode == TokenMode::Bridged { + assert_eq!( + msgs.pop().unwrap(), + conv::to_mint_msg(&mock_env().contract.address, DENOM, "100").into() + ); + } else { + assert!(msgs.is_empty()); + } + } + + #[rstest] + #[case(1, gen_bz(32), gen_bz(32), vec![coin(100, DENOM)])] + #[should_panic(expected = "route not found")] + #[case(2, gen_bz(32), gen_bz(32), vec![coin(100, DENOM)])] + #[should_panic(expected = "No funds sent")] + #[case(1, gen_bz(32), gen_bz(32), vec![])] + #[should_panic(expected = "Must send reserve token 'utest'")] + #[case(1, gen_bz(32), gen_bz(32), vec![coin(100, "uatom")])] + #[should_panic(expected = "Sent more than one denomination")] + #[case(1, gen_bz(32), gen_bz(32), vec![coin(100, DENOM), coin(100, "uatom")])] + fn test_transfer_remote( + mut deps: TestDeps, + #[case] dest_domain: u32, + #[case] dest_router: HexBinary, + #[case] dest_recipient: HexBinary, + #[case] funds: Vec, + ) { + set_route( + deps.as_mut().storage, + &addr(OWNER), + DomainRouteSet { + domain: 1, + route: Some(dest_router.clone()), + }, + ) + .unwrap(); + + let res = test_execute( + deps.as_mut(), + &addr("sender"), + ExecuteMsg::TransferRemote { + dest_domain, + recipient: dest_recipient.clone(), + }, + funds, + ); + let mut msgs: Vec<_> = res.messages.into_iter().map(|v| v.msg).collect(); + + let mode = MODE.load(deps.as_ref().storage).unwrap(); + + assert_eq!( + msgs.last().unwrap(), + &mailbox::dispatch( + MAILBOX, + dest_domain, + dest_router, + warp::Message { + recipient: dest_recipient, + amount: Uint256::from_u128(100), + metadata: HexBinary::default(), + } + .into(), + None, + None + ) + .unwrap() + ); + msgs.remove(msgs.len() - 1); // remove last (dispatch) msg + + if mode == TokenMode::Bridged { + assert_eq!( + msgs.pop().unwrap(), + conv::to_burn_msg(&mock_env().contract.address, DENOM, "100").into() + ); + } else { + assert!(msgs.is_empty()); + } + } +} diff --git a/contracts/warp/native/src/error.rs b/contracts/warp/native/src/error.rs index e0715bce..25a421cc 100644 --- a/contracts/warp/native/src/error.rs +++ b/contracts/warp/native/src/error.rs @@ -12,15 +12,15 @@ pub enum ContractError { #[error("{0}")] RecoverPubkeyError(#[from] RecoverPubkeyError), - #[error("Unauthorized")] + #[error("unauthorized")] Unauthorized, - #[error("WrongLength")] + #[error("wrong length")] WrongLength, - #[error("InvalidReplyId")] + #[error("invalid reply id")] InvalidReplyId, - #[error("NoRouter domain:{domain:?}")] + #[error("no route for domain {domain:?}")] NoRouter { domain: u32 }, } diff --git a/contracts/warp/native/src/lib.rs b/contracts/warp/native/src/lib.rs index 426030f8..3b625710 100644 --- a/contracts/warp/native/src/lib.rs +++ b/contracts/warp/native/src/lib.rs @@ -7,9 +7,6 @@ mod conv; pub mod error; mod proto; -#[cfg(test)] -mod tests; - // reply message pub const REPLY_ID_CREATE_DENOM: u64 = 0; diff --git a/contracts/warp/native/src/tests/contracts.rs b/contracts/warp/native/src/tests/contracts.rs deleted file mode 100644 index c7d92baa..00000000 --- a/contracts/warp/native/src/tests/contracts.rs +++ /dev/null @@ -1,279 +0,0 @@ -// use std::str::FromStr; - -// use cosmwasm_std::{ -// coin, testing::mock_env, to_binary, Addr, BankMsg, CosmosMsg, HexBinary, Uint256, WasmMsg, -// }; -// use hpl_interface::{ -// core::mailbox, -// types::bech32_encode, -// warp::{self, TokenMode}, -// }; -// use rstest::rstest; - -// use crate::{ -// error::ContractError, -// proto::{self, MsgBurn, MsgMint}, -// }; - -// use super::TokenNative; - -// #[rstest] -// #[case("osmo")] -// #[case("neutron")] -// fn test_init(#[case] hrp: &str) -> anyhow::Result<()> { -// let deployer = Addr::unchecked("deployer"); -// let mailbox = Addr::unchecked("mailbox"); -// let owner = Addr::unchecked("owner"); - -// let mut warp = TokenNative::default(); - -// warp.init( -// &deployer, -// hrp, -// &owner, -// &mailbox, -// "token-warp", -// None, -// TokenMode::Bridged, -// )?; - -// Ok(()) -// } - -// #[rstest] -// #[case("osmo")] -// #[case("neutron")] -// fn test_router_role(#[case] hrp: &str) -> anyhow::Result<()> { -// let deployer = Addr::unchecked("deployer"); -// let mailbox = Addr::unchecked("mailbox"); -// let owner = Addr::unchecked("owner"); - -// let denom = "token-native"; -// let domain = 999; -// let router = b"hello".to_vec(); - -// let mut warp = TokenNative::default(); - -// warp.init_hack(&deployer, &owner, &mailbox, hrp, denom, TokenMode::Bridged)?; - -// // err -// let err = warp -// .router_enroll(&mailbox, domain, router.clone()) -// .unwrap_err(); -// assert_eq!(err, ContractError::Unauthorized); - -// // ok -// warp.router_enroll(&owner, domain, router)?; - -// Ok(()) -// } - -// #[rstest] -// fn test_outbound_transfer(#[values("osmo", "neutron")] hrp: &str) -> anyhow::Result<()> { -// let deployer = Addr::unchecked("deployer"); -// let mailbox = Addr::unchecked("mailbox"); -// let router = Addr::unchecked("router"); -// let owner = Addr::unchecked("owner"); - -// let denom = "token-native"; -// let amount = 100_000; - -// let user_remote = Addr::unchecked("user-remote"); - -// let dest_domain = 1; - -// let env = mock_env(); - -// let burn_msg: CosmosMsg = MsgBurn { -// sender: env.contract.address.to_string(), -// amount: Some(proto::Coin { -// amount: amount.to_string(), -// denom: denom.to_string(), -// }), -// } -// .into(); - -// let dispatch_msg = mailbox::dispatch( -// mailbox, -// dest_domain, -// router.as_bytes().to_vec().into(), -// warp::Message { -// recipient: user_remote.as_bytes().to_vec().into(), -// amount: Uint256::from_str(&amount.to_string())?, -// metadata: HexBinary::default(), -// } -// .into(), -// None, -// None, -// ); - -// for (mode, routers, expected_resp) in [ -// ( -// TokenMode::Bridged, -// vec![(dest_domain, router.as_bytes().into())], -// Ok(vec![burn_msg, dispatch_msg.clone()]), -// ), -// ( -// TokenMode::Bridged, -// vec![], -// Err(ContractError::NoRouter { -// domain: dest_domain, -// }), -// ), -// ( -// TokenMode::Collateral, -// vec![(dest_domain, router.as_bytes().into())], -// Ok(vec![dispatch_msg]), -// ), -// ( -// TokenMode::Collateral, -// vec![], -// Err(ContractError::NoRouter { -// domain: dest_domain, -// }), -// ), -// ] { -// let mut warp = TokenNative { -// env: env.clone(), -// ..Default::default() -// }; - -// warp.init_hack(&deployer, &owner, &mailbox, hrp, denom, mode)?; - -// for (domain, router) in routers { -// warp.router_enroll(&owner, domain, router)?; -// } - -// let resp = warp.transfer_remote( -// &owner, -// coin(amount, denom), -// dest_domain, -// user_remote.as_bytes().into(), -// ); - -// assert_eq!( -// resp.map(|v| v.messages.into_iter().map(|v| v.msg).collect::>()), -// expected_resp -// ); -// } - -// Ok(()) -// } - -// #[rstest] -// #[case("osmo")] -// #[case("neutron")] -// fn test_inbound_transfer(#[case] hrp: &str) -> anyhow::Result<()> { -// let deployer = Addr::unchecked("deployer"); -// let mailbox = Addr::unchecked("mailbox"); -// let router = Addr::unchecked("router"); -// let owner = Addr::unchecked("owner"); -// let errortic = Addr::unchecked("errortic"); - -// let denom = "token-native"; -// let amount = 100_000; - -// let user_remote = Addr::unchecked("user-remote____________________1"); - -// let env = mock_env(); - -// let origin_domain = 1; - -// let mint_msg: CosmosMsg = MsgMint { -// sender: env.contract.address.to_string(), -// amount: Some(proto::Coin { -// amount: amount.to_string(), -// denom: denom.to_string(), -// }), -// } -// .into(); - -// let send_msg: CosmosMsg = BankMsg::Send { -// to_address: bech32_encode(hrp, user_remote.as_bytes())?.to_string(), -// amount: vec![coin(amount, denom)], -// } -// .into(); - -// let default_msg = token::Message { -// recipient: user_remote.as_bytes().to_vec().into(), -// amount: Uint256::from_u128(amount), -// metadata: Binary::default(), -// }; - -// for (mode, sender, origin, origin_sender, token_msg, expected_resp) in [ -// // happy -// ( -// TokenMode::Bridged, -// &mailbox, -// origin_domain, -// &router, -// default_msg.clone(), -// Ok(vec![mint_msg, send_msg.clone()]), -// ), -// ( -// TokenMode::Collateral, -// &mailbox, -// origin_domain, -// &router, -// default_msg.clone(), -// Ok(vec![send_msg]), -// ), -// // errors -// ( -// TokenMode::Bridged, -// &errortic, -// origin_domain, -// &router, -// default_msg.clone(), -// Err(ContractError::Unauthorized), -// ), -// ( -// TokenMode::Bridged, -// &mailbox, -// origin_domain, -// &errortic, -// default_msg.clone(), -// Err(ContractError::Unauthorized), -// ), -// ( -// TokenMode::Collateral, -// &errortic, -// origin_domain, -// &router, -// default_msg.clone(), -// Err(ContractError::Unauthorized), -// ), -// ( -// TokenMode::Collateral, -// &mailbox, -// origin_domain, -// &errortic, -// default_msg, -// Err(ContractError::Unauthorized), -// ), -// ] { -// let mut warp = TokenNative { -// env: env.clone(), -// ..Default::default() -// }; - -// warp.init_hack(&deployer, &owner, &mailbox, hrp, denom, mode)?; -// warp.router_enroll(&owner, origin_domain, router.as_bytes().into())?; - -// let resp = warp.mailbox_handle( -// sender, -// mailbox::HandleMsg { -// origin, -// sender: origin_sender.as_bytes().to_vec().into(), -// body: token_msg.into(), -// }, -// ); - -// assert_eq!( -// resp.map(|v| v.messages.into_iter().map(|v| v.msg).collect::>()), -// expected_resp -// ); -// } - -// Ok(()) -// } diff --git a/contracts/warp/native/src/tests/mod.rs b/contracts/warp/native/src/tests/mod.rs deleted file mode 100644 index e3c7904a..00000000 --- a/contracts/warp/native/src/tests/mod.rs +++ /dev/null @@ -1,132 +0,0 @@ -// use cosmwasm_std::{ -// from_binary, -// testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, -// Addr, Binary, Coin, Empty, Env, MessageInfo, OwnedDeps, Response, -// }; -// use hpl_interface::{ -// mailbox, router, -// token::TokenMode, -// token_native::{ExecuteMsg, QueryMsg}, -// }; -// use serde::de::DeserializeOwned; - -// use crate::{ -// contract::{execute, instantiate, query}, -// error::ContractError, -// msg::{InstantiateMsg, Metadata}, -// state::{HRP, MAILBOX, MODE, OWNER, TOKEN}, -// }; - -// mod contracts; - -// pub struct TokenNative { -// pub deps: OwnedDeps, -// pub env: Env, -// } - -// impl Default for TokenNative { -// fn default() -> Self { -// Self { -// deps: mock_dependencies(), -// env: mock_env(), -// } -// } -// } - -// impl TokenNative { -// #[allow(clippy::too_many_arguments)] -// pub fn init( -// &mut self, -// sender: &Addr, -// hrp: &str, -// owner: &Addr, -// mailbox: &Addr, -// denom: &str, -// metadata: Option, -// mode: TokenMode, -// ) -> Result { -// instantiate( -// self.deps.as_mut(), -// self.env.clone(), -// mock_info(sender.as_str(), &[]), -// InstantiateMsg { -// denom: denom.to_string(), -// metadata, -// mode, -// hrp: hrp.to_string(), -// owner: owner.to_string(), -// mailbox: mailbox.to_string(), -// }, -// ) -// } - -// pub fn init_hack( -// &mut self, -// _sender: &Addr, -// owner: &Addr, -// mailbox: &Addr, -// hrp: &str, -// denom: &str, -// mode: TokenMode, -// ) -> anyhow::Result<()> { -// MODE.save(&mut self.deps.storage, &mode)?; -// HRP.save(&mut self.deps.storage, &hrp.to_string())?; -// TOKEN.save(&mut self.deps.storage, &denom.to_string())?; -// OWNER.save(&mut self.deps.storage, owner)?; -// MAILBOX.save(&mut self.deps.storage, mailbox)?; - -// Ok(()) -// } - -// fn execute(&mut self, info: MessageInfo, msg: ExecuteMsg) -> Result { -// execute(self.deps.as_mut(), self.env.clone(), info, msg) -// } - -// #[allow(dead_code)] -// fn query(&self, msg: QueryMsg) -> Result { -// query(self.deps.as_ref(), self.env.clone(), msg) -// .map(|v| from_binary::(&v))? -// .map_err(|e| e.into()) -// } - -// pub fn router_enroll( -// &mut self, -// sender: &Addr, -// domain: u32, -// router: Binary, -// ) -> Result { -// self.execute( -// mock_info(sender.as_str(), &[]), -// ExecuteMsg::Router(router::RouterMsg::EnrollRemoteRouter { -// set: router::RouterSet { domain, router }, -// }), -// ) -// } - -// pub fn mailbox_handle( -// &mut self, -// sender: &Addr, -// handle_msg: mailbox::HandleMsg, -// ) -> Result { -// self.execute( -// mock_info(sender.as_str(), &[]), -// ExecuteMsg::Handle(handle_msg), -// ) -// } - -// pub fn transfer_remote( -// &mut self, -// sender: &Addr, -// fund: Coin, -// dest_domain: u32, -// recipient: Binary, -// ) -> Result { -// self.execute( -// mock_info(sender.as_str(), &[fund]), -// ExecuteMsg::TransferRemote { -// dest_domain, -// recipient, -// }, -// ) -// } -// } diff --git a/integration-test/Cargo.toml b/integration-test/Cargo.toml index 9e9ed859..63f76ff3 100644 --- a/integration-test/Cargo.toml +++ b/integration-test/Cargo.toml @@ -39,6 +39,9 @@ ripemd.workspace = true hex-literal.workspace = true ibcx-test-utils.workspace = true +rstest.workspace = true +cw20.workspace = true + hpl-ownable.workspace = true hpl-ism-multisig.workspace = true hpl-interface.workspace = true diff --git a/integration-test/tests/contracts/cw/deploy.rs b/integration-test/tests/contracts/cw/deploy.rs index b9a03c0e..5bb262de 100644 --- a/integration-test/tests/contracts/cw/deploy.rs +++ b/integration-test/tests/contracts/cw/deploy.rs @@ -5,33 +5,32 @@ use cosmwasm_std::HexBinary; use hpl_interface::{ core::mailbox, router::{DomainRouteSet, RouterMsg}, - warp::{self, cw20::Cw20ModeBridged}, + warp::{self, cw20::Cw20ModeBridged, native::NativeModeBriged}, }; -use test_tube::{Account, Runner, SigningAccount, Wasm}; +use osmosis_test_tube::osmosis_std::types::cosmwasm::wasm::v1::MsgInstantiateContractResponse; +use test_tube::{Account, ExecuteResponse, Runner, SigningAccount, Wasm}; use super::{ types::{Codes, CoreDeployments}, Hook, Ism, }; -fn instantiate<'a, M: Serialize, R: Runner<'a>>( +pub fn instantiate<'a, M: Serialize, R: Runner<'a>>( wasm: &Wasm<'a, R>, code: u64, deployer: &SigningAccount, name: &str, msg: &M, -) -> eyre::Result { - Ok(wasm - .instantiate( - code, - msg, - Some(&deployer.address()), - Some(name), - &[], - deployer, - )? - .data - .address) +) -> ExecuteResponse { + wasm.instantiate( + code, + msg, + Some(&deployer.address()), + Some(name), + &[], + deployer, + ) + .unwrap() } #[allow(clippy::too_many_arguments)] @@ -57,11 +56,13 @@ pub fn deploy_core<'a, R: Runner<'a>>( owner: deployer.address(), domain: origin_domain, }, - )?; + ) + .data + .address; // set default ism, hook, igp - let default_ism = default_ism.deploy(wasm, codes, deployer)?; + let default_ism = default_ism.deploy(wasm, codes, owner, deployer)?; let default_hook = default_hook.deploy(wasm, codes, mailbox.clone(), owner, deployer)?; let required_hook = required_hook.deploy(wasm, codes, mailbox.clone(), owner, deployer)?; @@ -106,7 +107,9 @@ pub fn deploy_core<'a, R: Runner<'a>>( &ReceiverInitMsg { hrp: hrp.to_string(), }, - )?; + ) + .data + .address; Ok(CoreDeployments { mailbox, @@ -126,29 +129,48 @@ pub fn deploy_warp_route_bridged<'a, R: Runner<'a>>( hrp: &str, codes: &Codes, denom: String, -) -> eyre::Result { - instantiate( - wasm, - codes.warp_cw20, - deployer, - "warp-cw20", - &warp::cw20::InstantiateMsg { - token: warp::TokenModeMsg::Bridged(Cw20ModeBridged { - code_id: codes.cw20_base, - init_msg: Box::new(warp::cw20::Cw20InitMsg { - name: denom.clone(), - symbol: denom, - decimals: 6, - initial_balances: vec![], - mint: None, - marketing: None, + token_type: warp::TokenType, +) -> ExecuteResponse { + match token_type { + warp::TokenType::Native(_) => instantiate( + wasm, + codes.warp_native, + deployer, + "warp-native", + &warp::native::InstantiateMsg { + token: warp::TokenModeMsg::Bridged(NativeModeBriged { + denom, + metadata: None, }), - }), - hrp: hrp.to_string(), - owner: owner.address(), - mailbox: mailbox.to_string(), - }, - ) + hrp: hrp.to_string(), + owner: owner.address(), + mailbox: mailbox.to_string(), + }, + ), + warp::TokenType::CW20 { .. } => instantiate( + wasm, + codes.warp_cw20, + deployer, + "warp-cw20", + &warp::cw20::InstantiateMsg { + token: warp::TokenModeMsg::Bridged(Cw20ModeBridged { + code_id: codes.cw20_base, + init_msg: Box::new(warp::cw20::Cw20InitMsg { + name: denom.clone(), + symbol: denom, + decimals: 6, + initial_balances: vec![], + mint: None, + marketing: None, + }), + }), + hrp: hrp.to_string(), + owner: owner.address(), + mailbox: mailbox.to_string(), + }, + ), + warp::TokenType::CW721 { .. } => todo!(), + } } #[allow(dead_code)] @@ -160,10 +182,10 @@ pub fn deploy_warp_route_collateral<'a, R: Runner<'a>>( hrp: &str, codes: &Codes, denom: String, -) -> eyre::Result { +) -> ExecuteResponse { if denom.starts_with(format!("{hrp}1").as_str()) { // cw20 - let route = instantiate( + instantiate( wasm, codes.warp_cw20, deployer, @@ -176,12 +198,10 @@ pub fn deploy_warp_route_collateral<'a, R: Runner<'a>>( owner: owner.address(), mailbox: mailbox.to_string(), }, - )?; - - Ok(route) + ) } else { // native - let route = instantiate( + instantiate( wasm, codes.warp_native, deployer, @@ -192,9 +212,7 @@ pub fn deploy_warp_route_collateral<'a, R: Runner<'a>>( owner: owner.address(), mailbox: mailbox.to_string(), }, - )?; - - Ok(route) + ) } } diff --git a/integration-test/tests/contracts/cw/hook.rs b/integration-test/tests/contracts/cw/hook.rs index 863dfda7..c7d4cfb3 100644 --- a/integration-test/tests/contracts/cw/hook.rs +++ b/integration-test/tests/contracts/cw/hook.rs @@ -10,7 +10,7 @@ use ibcx_test_utils::addr; use osmosis_test_tube::Wasm; use test_tube::{Account, Runner, SigningAccount}; -use super::{igp::Igp, types::Codes}; +use super::{igp::Igp, instantiate, types::Codes}; #[allow(dead_code)] pub enum Hook { @@ -43,8 +43,8 @@ pub enum Hook { }, } +#[allow(dead_code)] impl Hook { - #[allow(dead_code)] pub fn mock(gas: Uint256) -> Self { Self::Mock { gas } } @@ -183,6 +183,36 @@ impl Hook { Ok(hook) } + fn deploy_aggregate<'a, R: Runner<'a>>( + wasm: &Wasm<'a, R>, + code: u64, + codes: &Codes, + mailbox: String, + hooks: Vec, + owner: &SigningAccount, + deployer: &SigningAccount, + ) -> eyre::Result { + use hpl_interface::hook::aggregate::*; + + let hook_addrs = hooks + .into_iter() + .map(|hook| hook.deploy(wasm, codes, mailbox.clone(), owner, deployer)) + .collect::>>()?; + + let hook = instantiate( + wasm, + code, + deployer, + "cw-hpl-hook-aggregate", + &InstantiateMsg { + owner: owner.address(), + hooks: hook_addrs, + }, + ); + + Ok(hook.data.address) + } + pub fn deploy<'a, R: Runner<'a>>( self, wasm: &Wasm<'a, R>, @@ -193,7 +223,7 @@ impl Hook { ) -> eyre::Result { match self { Hook::Mock { gas } => Self::deploy_mock(wasm, codes, gas, deployer), - Hook::Igp(igp) => Ok(igp.deploy(wasm, codes, mailbox, owner, deployer)?.core), + Hook::Igp(igp) => Ok(igp.deploy(wasm, codes, owner, deployer)?.core), Hook::Merkle {} => Self::deploy_merkle(wasm, codes, mailbox, owner, deployer), Hook::Pausable {} => Self::deploy_pausable(wasm, codes, owner, deployer), Hook::Routing { routes } => Self::deploy_routing( @@ -266,16 +296,15 @@ impl Hook { Ok(hook_addr) } - Hook::Aggregate { .. } => todo!(), + Hook::Aggregate { hooks } => Self::deploy_aggregate( + wasm, + codes.hook_aggregate, + codes, + mailbox, + hooks, + owner, + deployer, + ), } } } - -pub fn prepare_routing_hook(routes: Vec<(u32, u128)>) -> Hook { - let routes = routes - .into_iter() - .map(|(domain, _)| (domain, Hook::Merkle {})) - .collect(); - - Hook::routing(routes) -} diff --git a/integration-test/tests/contracts/cw/igp.rs b/integration-test/tests/contracts/cw/igp.rs index 7479922c..a1ccaadb 100644 --- a/integration-test/tests/contracts/cw/igp.rs +++ b/integration-test/tests/contracts/cw/igp.rs @@ -25,7 +25,6 @@ impl Igp { self, wasm: &Wasm<'a, R>, codes: &Codes, - mailbox: String, owner: &SigningAccount, deployer: &SigningAccount, ) -> eyre::Result { @@ -35,7 +34,6 @@ impl Igp { &igp::core::InstantiateMsg { hrp: self.hrp, owner: owner.address(), - mailbox, gas_token: self.gas_token, beneficiary: self.beneficiary, }, diff --git a/integration-test/tests/contracts/cw/ism.rs b/integration-test/tests/contracts/cw/ism.rs index 302dd18a..6cc17a44 100644 --- a/integration-test/tests/contracts/cw/ism.rs +++ b/integration-test/tests/contracts/cw/ism.rs @@ -16,6 +16,11 @@ pub enum Ism { validators: validator::TestValidators, }, + Aggregate { + isms: Vec, + threshold: u8, + }, + #[allow(dead_code)] Mock, } @@ -50,6 +55,7 @@ impl Ism { codes: &Codes, hrp: String, set: validator::TestValidators, + owner: &SigningAccount, deployer: &SigningAccount, ) -> eyre::Result { let multisig_ism = wasm @@ -57,7 +63,7 @@ impl Ism { codes.ism_multisig, &hpl_interface::ism::multisig::InstantiateMsg { hrp: hrp.to_string(), - owner: deployer.address(), + owner: owner.address(), }, None, None, @@ -73,7 +79,7 @@ impl Ism { set: set.to_set(&hrp), }, &[], - deployer, + owner, )?; wasm.execute( @@ -85,7 +91,7 @@ impl Ism { }, }, &[], - deployer, + owner, )?; Ok(multisig_ism) @@ -95,19 +101,20 @@ impl Ism { wasm: &Wasm<'a, R>, codes: &Codes, isms: Vec<(u32, Self)>, + owner: &SigningAccount, deployer: &SigningAccount, ) -> eyre::Result { let routing_ism = wasm .instantiate( codes.ism_routing, &hpl_interface::ism::routing::InstantiateMsg { - owner: deployer.address(), + owner: owner.address(), isms: isms .into_iter() .map(|(domain, ism)| { Ok(hpl_interface::ism::routing::IsmSet { domain, - address: ism.deploy(wasm, codes, deployer)?, + address: ism.deploy(wasm, codes, owner, deployer)?, }) }) .collect::>>()?, @@ -123,10 +130,45 @@ impl Ism { Ok(routing_ism) } + fn deploy_aggregate<'a, R: Runner<'a>>( + wasm: &Wasm<'a, R>, + codes: &Codes, + isms: Vec, + threshold: u8, + owner: &SigningAccount, + deployer: &SigningAccount, + ) -> eyre::Result { + use hpl_interface::ism::aggregate::*; + + let ism_addrs = isms + .into_iter() + .map(|v| v.deploy(wasm, codes, owner, deployer)) + .collect::>>()?; + + let aggregate_ism = wasm + .instantiate( + codes.ism_aggregate, + &InstantiateMsg { + owner: owner.address(), + isms: ism_addrs, + threshold, + }, + None, + None, + &[], + deployer, + )? + .data + .address; + + Ok(aggregate_ism) + } + pub fn deploy<'a, R: Runner<'a>>( self, wasm: &Wasm<'a, R>, codes: &Codes, + owner: &SigningAccount, deployer: &SigningAccount, ) -> eyre::Result { match self { @@ -134,8 +176,11 @@ impl Ism { Self::Multisig { hrp, validators: set, - } => Self::deploy_multisig(wasm, codes, hrp, set, deployer), - Self::Routing(isms) => Self::deploy_routing(wasm, codes, isms, deployer), + } => Self::deploy_multisig(wasm, codes, hrp, set, owner, deployer), + Self::Aggregate { isms, threshold } => { + Self::deploy_aggregate(wasm, codes, isms, threshold, owner, deployer) + } + Self::Routing(isms) => Self::deploy_routing(wasm, codes, isms, owner, deployer), } } } @@ -144,7 +189,13 @@ pub fn prepare_routing_ism(info: Vec<(u32, &str, TestValidators)>) -> Ism { let mut isms = vec![]; for (domain, hrp, set) in info { - isms.push((domain, Ism::multisig(hrp, set))); + isms.push(( + domain, + Ism::Aggregate { + isms: vec![Ism::multisig(hrp, set)], + threshold: 1, + }, + )); } Ism::routing(isms) diff --git a/integration-test/tests/contracts/cw/mod.rs b/integration-test/tests/contracts/cw/mod.rs index 28a25936..c0762aee 100644 --- a/integration-test/tests/contracts/cw/mod.rs +++ b/integration-test/tests/contracts/cw/mod.rs @@ -6,8 +6,8 @@ mod setup; mod store; mod types; -pub use deploy::deploy_core; -pub use hook::{prepare_routing_hook, Hook}; +pub use deploy::*; +pub use hook::Hook; pub use ism::{prepare_routing_ism, Ism}; pub use setup::{setup_env, Env}; pub use store::store_code; diff --git a/integration-test/tests/contracts/cw/setup.rs b/integration-test/tests/contracts/cw/setup.rs index fafa83af..28ee591a 100644 --- a/integration-test/tests/contracts/cw/setup.rs +++ b/integration-test/tests/contracts/cw/setup.rs @@ -1,14 +1,17 @@ use std::{collections::BTreeMap, path::PathBuf}; -use cosmwasm_std::{coin, Coin}; +use cosmwasm_std::{coin, Coin, Uint256}; use hpl_interface::igp::oracle::RemoteGasDataConfig; use test_tube::{Account, Module, Runner, SigningAccount, Wasm}; use crate::validator::TestValidators; use super::{ - deploy_core, igp::Igp, prepare_routing_hook, prepare_routing_ism, store_code, - types::CoreDeployments, Hook, + deploy_core, + igp::Igp, + prepare_routing_ism, store_code, + types::{Codes, CoreDeployments}, + Hook, }; const DEFAULT_GAS: u128 = 300_000; @@ -18,6 +21,7 @@ pub struct Env<'a, R: Runner<'a>> { pub app: &'a R, pub core: CoreDeployments, + pub codes: Codes, pub domain: u32, acc_gen: Box SigningAccount>, @@ -58,14 +62,20 @@ pub fn setup_env<'a, R: Runner<'a>>( .map(|v| (v.domain, hrp, v.clone())) .collect(), ); - let default_hook = - prepare_routing_hook(validators.iter().map(|v| (v.domain, DEFAULT_GAS)).collect()); - let required_hook = Hook::Igp(Igp { - hrp: hrp.to_string(), - gas_token: "uosmo".to_string(), - beneficiary: deployer.address(), - oracle_configs: oracle_config.to_vec(), - }); + + let default_hook = Hook::mock(Uint256::from_u128(DEFAULT_GAS)); + + let required_hook = Hook::Aggregate { + hooks: vec![ + Hook::Merkle {}, + Hook::Igp(Igp { + hrp: hrp.to_string(), + gas_token: "uosmo".to_string(), + beneficiary: deployer.address(), + oracle_configs: oracle_config.to_vec(), + }), + ], + }; let wasm = Wasm::new(app); let codes = store_code(&wasm, &deployer, artifacts)?; @@ -86,6 +96,7 @@ pub fn setup_env<'a, R: Runner<'a>>( app, core, + codes, domain, acc_gen: Box::new(acc_gen), diff --git a/integration-test/tests/contracts/cw/store.rs b/integration-test/tests/contracts/cw/store.rs index 0e3a69ea..9db25a4d 100644 --- a/integration-test/tests/contracts/cw/store.rs +++ b/integration-test/tests/contracts/cw/store.rs @@ -7,9 +7,10 @@ use super::types::{Codes, CodesMap}; const DEFAULT_ARTIFACTS_PATH: &str = "../target/wasm32-unknown-unknown/release/"; -const CONTRACTS: [&str; 16] = [ +const CONTRACTS: [&str; 18] = [ "mailbox", "validator_announce", + "hook_aggregate", "hook_merkle", "hook_pausable", "hook_routing", @@ -17,6 +18,7 @@ const CONTRACTS: [&str; 16] = [ "hook_routing_fallback", "igp", "igp_oracle", + "ism_aggregate", "ism_multisig", "ism_routing", "test_mock_hook", diff --git a/integration-test/tests/contracts/cw/types.rs b/integration-test/tests/contracts/cw/types.rs index 41ec7468..d1f58024 100644 --- a/integration-test/tests/contracts/cw/types.rs +++ b/integration-test/tests/contracts/cw/types.rs @@ -17,6 +17,7 @@ pub struct Codes { #[serde(rename = "validator_announce")] pub va: u64, + pub hook_aggregate: u64, pub hook_merkle: u64, pub hook_pausable: u64, pub hook_routing: u64, @@ -26,6 +27,7 @@ pub struct Codes { pub igp: u64, pub igp_oracle: u64, + pub ism_aggregate: u64, pub ism_multisig: u64, pub ism_routing: u64, diff --git a/integration-test/tests/mailbox.rs b/integration-test/tests/mailbox.rs index 69ca8807..26aeef0a 100644 --- a/integration-test/tests/mailbox.rs +++ b/integration-test/tests/mailbox.rs @@ -8,14 +8,18 @@ use cosmwasm_std::{attr, coin, Attribute, Binary, Uint128}; use ethers::{ prelude::parse_log, providers::Middleware, signers::Signer, types::TransactionReceipt, }; -use osmosis_test_tube::{Account, Module, OsmosisTestApp, Wasm}; +use ibcx_test_utils::addr; +use osmosis_test_tube::{ + osmosis_std::types::cosmwasm::wasm::v1::MsgExecuteContractResponse, Account, Module, + OsmosisTestApp, Wasm, +}; use hpl_interface::{ core::mailbox::{self, DispatchMsg}, igp::oracle::RemoteGasDataConfig, - types::{bech32_decode, bech32_encode, bech32_to_h256}, + types::{bech32_decode, bech32_encode, bech32_to_h256, AggregateMetadata}, }; -use test_tube::Runner; +use test_tube::{ExecuteResponse, Runner}; use crate::{ constants::*, @@ -35,9 +39,9 @@ fn sorted(mut attrs: Vec) -> Vec { attrs } -async fn send_msg<'a, M, S, R>( - anvil: ð::Env, - cosmos: &cw::Env<'a, R>, +async fn send_msg_cw_to_evm<'a, M, S, R>( + from: &cw::Env<'a, R>, + to: ð::Env, ) -> eyre::Result where M: Middleware + 'static, @@ -45,13 +49,13 @@ where R: Runner<'a>, { let mut receiver = [0u8; 32]; - receiver[12..].copy_from_slice(&anvil.core.msg_receiver.address().0); - let _sender = bech32_decode(cosmos.acc_tester.address().as_str())?; + receiver[12..].copy_from_slice(&to.core.msg_receiver.address().0); + let _sender = bech32_decode(from.acc_tester.address().as_str())?; let msg_body = b"hello world"; // dispatch - let dispatch_res = Wasm::new(cosmos.app).execute( - &cosmos.core.mailbox, + let dispatch_res = Wasm::new(from.app).execute( + &from.core.mailbox, &mailbox::ExecuteMsg::Dispatch(DispatchMsg { dest_domain: DOMAIN_EVM, recipient_addr: receiver.into(), @@ -60,13 +64,13 @@ where metadata: None, }), &[coin(56_000_000, "uosmo")], - &cosmos.acc_tester, + &from.acc_tester, )?; let dispatch = parse_dispatch_from_res(&dispatch_res.events); let _dispatch_id = parse_dispatch_id_from_res(&dispatch_res.events); - let process_tx = anvil + let process_tx = to .core .mailbox .process(vec![].into(), Binary::from(dispatch.message).0.into()); @@ -75,6 +79,79 @@ where Ok(process_tx_res) } +async fn send_msg_evm_to_cw<'a, M, S, R>( + from: ð::Env, + to: &cw::Env<'a, R>, +) -> eyre::Result> +where + M: Middleware + 'static, + S: Signer + 'static, + R: Runner<'a>, +{ + // prepare message arguments + let sender = bech32_encode("osmo", from.acc_owner.address().as_bytes())?; + let receiver = bech32_to_h256(&to.core.msg_receiver)?; + let msg_body = b"hello world"; + + // dispatch + let dispatch_tx_call = from + .core + .mailbox + .dispatch(DOMAIN_OSMO, receiver, msg_body.into()); + let dispatch_res = dispatch_tx_call.send().await?.await?.unwrap(); + + let dispatch: DispatchFilter = parse_log(dispatch_res.logs[0].clone())?; + let dispatch_id: DispatchIdFilter = parse_log(dispatch_res.logs[1].clone())?; + + // generate ism metadata + let multisig_ism_metadata = to.get_validator_set(DOMAIN_EVM)?.make_metadata( + from.core.mailbox.address(), + from.core.mailbox.root().await?, + from.core.mailbox.count().await? - 1, + dispatch_id.message_id, + true, + )?; + + let aggregate_ism_metadata = AggregateMetadata::new(vec![( + addr(&to.core.default_ism), + multisig_ism_metadata.into(), + )]); + + // process + let process_res = Wasm::new(to.app).execute( + &to.core.mailbox, + &mailbox::ExecuteMsg::Process { + metadata: aggregate_ism_metadata.into(), + message: dispatch.message.to_vec().into(), + }, + &[], + &to.acc_owner, + )?; + let process_recv_evt = process_res + .events + .iter() + .find(|v| v.ty == "wasm-mailbox_msg_received") + .unwrap(); + + assert_eq!( + process_recv_evt.attributes, + sorted(vec![ + Attribute { + key: "_contract_address".to_string(), + value: to.core.msg_receiver.clone(), + }, + attr("sender", sender), + attr( + "origin", + from.core.mailbox.local_domain().await?.to_string() + ), + attr("body", std::str::from_utf8(msg_body)?), + ]), + ); + + Ok(process_res) +} + #[tokio::test] async fn test_mailbox_cw_to_evm() -> eyre::Result<()> { // init Osmosis env @@ -96,7 +173,7 @@ async fn test_mailbox_cw_to_evm() -> eyre::Result<()> { // init Anvil env let anvil1 = eth::setup_env(DOMAIN_EVM).await?; - let _ = send_msg(&anvil1, &osmo).await?; + let _ = send_msg_cw_to_evm(&osmo, &anvil1).await?; Ok(()) } @@ -122,61 +199,7 @@ async fn test_mailbox_evm_to_cw() -> eyre::Result<()> { // init Anvil env let anvil1 = eth::setup_env(DOMAIN_EVM).await?; - // prepare message arguments - let sender = bech32_encode("osmo", anvil1.acc_owner.address().as_bytes())?; - let receiver = bech32_to_h256(&osmo.core.msg_receiver)?; - let msg_body = b"hello world"; - - // dispatch - let dispatch_tx_call = anvil1 - .core - .mailbox - .dispatch(DOMAIN_OSMO, receiver, msg_body.into()); - let dispatch_res = dispatch_tx_call.send().await?.await?.unwrap(); - - let dispatch: DispatchFilter = parse_log(dispatch_res.logs[0].clone())?; - let dispatch_id: DispatchIdFilter = parse_log(dispatch_res.logs[1].clone())?; - - // generate ism metadata - let ism_metadata = osmo.get_validator_set(DOMAIN_EVM)?.make_metadata( - anvil1.core.mailbox.address(), - anvil1.core.mailbox.root().await?, - anvil1.core.mailbox.count().await?, - dispatch_id.message_id, - true, - )?; - - // process - let process_res = Wasm::new(osmo.app).execute( - &osmo.core.mailbox, - &mailbox::ExecuteMsg::Process { - metadata: ism_metadata.into(), - message: dispatch.message.to_vec().into(), - }, - &[], - &osmo.acc_owner, - )?; - let process_recv_evt = process_res - .events - .iter() - .find(|v| v.ty == "wasm-mailbox_msg_received") - .unwrap(); - - assert_eq!( - process_recv_evt.attributes, - sorted(vec![ - Attribute { - key: "_contract_address".to_string(), - value: osmo.core.msg_receiver, - }, - attr("sender", sender), - attr( - "origin", - anvil1.core.mailbox.local_domain().await?.to_string() - ), - attr("body", std::str::from_utf8(msg_body)?), - ]), - ); + let _ = send_msg_evm_to_cw(&anvil1, &osmo).await?; Ok(()) } diff --git a/integration-test/tests/validator.rs b/integration-test/tests/validator.rs index 959f0fbd..23e360a7 100644 --- a/integration-test/tests/validator.rs +++ b/integration-test/tests/validator.rs @@ -145,7 +145,7 @@ impl TestValidators { let multisig_hash = hpl_ism_multisig::multisig_hash( hpl_ism_multisig::domain_hash(self.domain, addr.to_vec().into())?.to_vec(), merkle_root.to_vec(), - merkle_index.to_be_bytes().to_vec(), + merkle_index, message_id.to_vec(), )?; @@ -206,7 +206,7 @@ fn test_validator() { .make_metadata( H160::from_slice(gen_bz(20).as_slice()), gen_bz(32).as_slice().try_into().unwrap(), - 1, + 0, message_id.as_slice().try_into().unwrap(), true, ) diff --git a/integration-test/tests/warp.rs b/integration-test/tests/warp.rs new file mode 100644 index 00000000..5821b530 --- /dev/null +++ b/integration-test/tests/warp.rs @@ -0,0 +1,288 @@ +#[allow(dead_code)] +mod constants; +mod contracts; +mod event; +mod validator; + +use std::collections::BTreeMap; + +use cosmwasm_std::{Event, Uint128}; +use hpl_interface::{igp::oracle::RemoteGasDataConfig, warp}; +use osmosis_test_tube::{ + osmosis_std::types::osmosis::tokenfactory::{ + self, + v1beta1::{MsgChangeAdmin, MsgCreateDenom}, + }, + OsmosisTestApp, TokenFactory, +}; +use rstest::rstest; +use test_tube::{Account, Module, Wasm}; + +use crate::{constants::*, contracts::cw, validator::TestValidators}; + +fn wasm_events(events: Vec) -> BTreeMap> { + events + .into_iter() + .filter(|v| v.ty.starts_with("wasm-")) + .map(|v| { + ( + v.ty, + v.attributes + .into_iter() + .map(|x| (x.key, x.value)) + .collect::>(), + ) + }) + .collect::>() +} + +#[tokio::test] +async fn test_cw20_colleteral() -> eyre::Result<()> { + let osmo_app = OsmosisTestApp::new(); + let osmo = cw::setup_env( + &osmo_app, + |app, coins| app.init_account(coins).unwrap(), + None::<&str>, + "osmo", + DOMAIN_OSMO, + &[TestValidators::new(DOMAIN_EVM, 5, 3)], + &[RemoteGasDataConfig { + remote_domain: DOMAIN_EVM, + token_exchange_rate: Uint128::from(10u128.pow(4)), + gas_price: Uint128::from(10u128.pow(9)), + }], + )?; + + let wasm = Wasm::new(&osmo_app); + + // deploy new cw20 token + let mock_token = cw::instantiate( + &wasm, + osmo.codes.cw20_base, + &osmo.acc_deployer, + "cw20-base", + &hpl_interface::warp::cw20::Cw20InitMsg { + name: "denomdenom".into(), + symbol: "denomdenom".into(), + decimals: 6, + initial_balances: vec![], + mint: Some(cw20::MinterResponse { + minter: osmo.acc_owner.address(), + cap: None, + }), + marketing: None, + }, + ) + .data + .address; + + // deploy warp route with deployed cw20 token + let warp_resp = cw::deploy_warp_route_collateral( + &wasm, + &osmo.acc_owner, + &osmo.acc_deployer, + &osmo.core.mailbox, + "osmo", + &osmo.codes, + mock_token.clone(), + ); + + let events = wasm_events(warp_resp.events); + assert_eq!( + events["wasm-hpl_warp_cw20::instantiate"]["denom"], + mock_token + ); + + // move cw20 token's minter to warp route contract + wasm.execute( + &mock_token, + &cw20::Cw20ExecuteMsg::UpdateMinter { + new_minter: Some(warp_resp.data.address), + }, + &[], + &osmo.acc_owner, + )?; + + // ready to mint / burn! + + Ok(()) +} + +#[rstest] +#[tokio::test] +async fn test_cw20_bridged() -> eyre::Result<()> { + let osmo_app = OsmosisTestApp::new(); + let osmo = cw::setup_env( + &osmo_app, + |app, coins| app.init_account(coins).unwrap(), + None::<&str>, + "osmo", + DOMAIN_OSMO, + &[TestValidators::new(DOMAIN_EVM, 5, 3)], + &[RemoteGasDataConfig { + remote_domain: DOMAIN_EVM, + token_exchange_rate: Uint128::from(10u128.pow(4)), + gas_price: Uint128::from(10u128.pow(9)), + }], + )?; + + let wasm = Wasm::new(&osmo_app); + + let warp_resp = cw::deploy_warp_route_bridged( + &wasm, + &osmo.acc_owner, + &osmo.acc_deployer, + &osmo.core.mailbox, + "osmo", + &osmo.codes, + "denomdenom".into(), + warp::TokenType::CW20 { + contract: "".into(), + }, + ); + + let events = wasm_events(warp_resp.events); + assert_eq!( + events["wasm-hpl_warp_cw20::instantiate"]["denom"], + "denomdenom" + ); + + let token_addr_from_evt = &events["wasm-hpl_warp_cw20::reply-init"]["new_token"]; + let token_info: cw20::TokenInfoResponse = + wasm.query(token_addr_from_evt, &cw20::Cw20QueryMsg::TokenInfo {})?; + assert_eq!(token_info.name, "denomdenom"); + + let minter_info: Option = + wasm.query(token_addr_from_evt, &cw20::Cw20QueryMsg::Minter {})?; + assert_eq!(minter_info.unwrap().minter, warp_resp.data.address); + + Ok(()) +} + +#[rstest] +#[case("utest")] +#[tokio::test] +async fn test_native_collateral(#[case] denom: &str) -> eyre::Result<()> { + let osmo_app = OsmosisTestApp::new(); + let osmo = cw::setup_env( + &osmo_app, + |app, coins| app.init_account(coins).unwrap(), + None::<&str>, + "osmo", + DOMAIN_OSMO, + &[TestValidators::new(DOMAIN_EVM, 5, 3)], + &[RemoteGasDataConfig { + remote_domain: DOMAIN_EVM, + token_exchange_rate: Uint128::from(10u128.pow(4)), + gas_price: Uint128::from(10u128.pow(9)), + }], + )?; + + let wasm = Wasm::new(&osmo_app); + let tf = TokenFactory::new(&osmo_app); + + let mock_token = tf + .create_denom( + MsgCreateDenom { + sender: osmo.acc_deployer.address(), + subdenom: denom.to_string(), + }, + &osmo.acc_deployer, + )? + .data + .new_token_denom; + + let warp_resp = cw::deploy_warp_route_collateral( + &wasm, + &osmo.acc_owner, + &osmo.acc_deployer, + &osmo.core.mailbox, + "osmo", + &osmo.codes, + mock_token.clone(), + ); + + let events = wasm_events(warp_resp.events); + assert_eq!( + events["wasm-hpl_warp_native::instantiate"]["denom"], + mock_token + ); + + let resp = tf.query_denom_authority_metadata( + &tokenfactory::v1beta1::QueryDenomAuthorityMetadataRequest { + denom: mock_token.clone(), + }, + )?; + assert_eq!( + resp.authority_metadata.unwrap().admin, + osmo.acc_deployer.address() + ); + + tf.change_admin( + MsgChangeAdmin { + sender: osmo.acc_deployer.address(), + denom: mock_token, + new_admin: warp_resp.data.address, + }, + &osmo.acc_deployer, + )?; + + // ready to test! + + Ok(()) +} + +#[rstest] +#[case("utest")] +#[tokio::test] +async fn test_native_bridged(#[case] denom: &str) -> eyre::Result<()> { + let osmo_app = OsmosisTestApp::new(); + let osmo = cw::setup_env( + &osmo_app, + |app, coins| app.init_account(coins).unwrap(), + None::<&str>, + "osmo", + DOMAIN_OSMO, + &[TestValidators::new(DOMAIN_EVM, 5, 3)], + &[RemoteGasDataConfig { + remote_domain: DOMAIN_EVM, + token_exchange_rate: Uint128::from(10u128.pow(4)), + gas_price: Uint128::from(10u128.pow(9)), + }], + )?; + + let wasm = Wasm::new(&osmo_app); + let tf = TokenFactory::new(&osmo_app); + + let warp_resp = cw::deploy_warp_route_bridged( + &wasm, + &osmo.acc_owner, + &osmo.acc_deployer, + &osmo.core.mailbox, + "osmo", + &osmo.codes, + denom.into(), + warp::TokenType::Native(warp::TokenTypeNative::Fungible { denom: "".into() }), + ); + + let events = wasm_events(warp_resp.events); + assert_eq!(events["wasm-hpl_warp_native::instantiate"]["denom"], denom); + + let new_denom = &events["wasm-hpl_warp_native::reply-init"]["denom"]; + assert_eq!( + new_denom.clone(), + format!("factory/{}/{denom}", warp_resp.data.address) + ); + + let resp = tf.query_denom_authority_metadata( + &tokenfactory::v1beta1::QueryDenomAuthorityMetadataRequest { + denom: new_denom.clone(), + }, + )?; + assert_eq!( + resp.authority_metadata.unwrap().admin, + warp_resp.data.address + ); + + Ok(()) +} diff --git a/packages/interface/src/core/mailbox.rs b/packages/interface/src/core/mailbox.rs index da5c1b77..9732ac4c 100644 --- a/packages/interface/src/core/mailbox.rs +++ b/packages/interface/src/core/mailbox.rs @@ -1,6 +1,7 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::{wasm_execute, Addr, Api, CosmosMsg, HexBinary, StdResult}; +#[allow(unused_imports)] use crate::{ hook::QuoteDispatchResponse, ownable::{OwnableMsg, OwnableQueryMsg}, diff --git a/packages/interface/src/core/mod.rs b/packages/interface/src/core/mod.rs index 2aaf00f4..7dee669b 100644 --- a/packages/interface/src/core/mod.rs +++ b/packages/interface/src/core/mod.rs @@ -5,6 +5,7 @@ pub mod mailbox; pub mod va; #[cw_serde] +#[derive(Default)] pub struct HandleMsg { pub origin: u32, pub sender: HexBinary, diff --git a/packages/interface/src/core/va.rs b/packages/interface/src/core/va.rs index 7bf018ae..8e8c6463 100644 --- a/packages/interface/src/core/va.rs +++ b/packages/interface/src/core/va.rs @@ -35,3 +35,5 @@ pub struct GetAnnounceStorageLocationsResponse { pub struct GetAnnouncedValidatorsResponse { pub validators: Vec, } + + diff --git a/packages/interface/src/hook/aggregate.rs b/packages/interface/src/hook/aggregate.rs new file mode 100644 index 00000000..0036cb46 --- /dev/null +++ b/packages/interface/src/hook/aggregate.rs @@ -0,0 +1,72 @@ +use cosmwasm_schema::{cw_serde, QueryResponses}; + +use crate::ownable::{OwnableMsg, OwnableQueryMsg}; + +use super::{HookQueryMsg, PostDispatchMsg}; + +pub const TREE_DEPTH: usize = 32; + +#[cw_serde] +pub struct InstantiateMsg { + pub owner: String, + pub hooks: Vec, +} + +#[cw_serde] +pub enum ExecuteMsg { + Ownable(OwnableMsg), + PostDispatch(PostDispatchMsg), + SetHooks { hooks: Vec }, +} + +#[cw_serde] +#[derive(QueryResponses)] +#[query_responses(nested)] +pub enum QueryMsg { + Ownable(OwnableQueryMsg), + Hook(HookQueryMsg), + AggregateHook(AggregateHookQueryMsg), +} + +#[cw_serde] +#[derive(QueryResponses)] +pub enum AggregateHookQueryMsg { + #[returns(HooksResponse)] + Hooks {}, +} + +#[cw_serde] +pub struct HooksResponse { + pub hooks: Vec, +} + +#[cfg(test)] +mod test { + use cosmwasm_std::HexBinary; + + use super::*; + use crate::{ + hook::{ExpectedHookQueryMsg, PostDispatchMsg, QuoteDispatchMsg}, + msg_checker, + }; + + #[test] + fn test_hook_interface() { + let _checked: ExecuteMsg = msg_checker( + PostDispatchMsg { + metadata: HexBinary::default(), + message: HexBinary::default(), + } + .wrap(), + ); + + let _checked: QueryMsg = msg_checker(ExpectedHookQueryMsg::Hook(HookQueryMsg::Mailbox {})); + let _checked: QueryMsg = msg_checker( + QuoteDispatchMsg { + metadata: HexBinary::default(), + message: HexBinary::default(), + } + .request(), + ); + } +} diff --git a/packages/interface/src/hook/mod.rs b/packages/interface/src/hook/mod.rs index ec1580d4..9d2a036d 100644 --- a/packages/interface/src/hook/mod.rs +++ b/packages/interface/src/hook/mod.rs @@ -1,3 +1,4 @@ +pub mod aggregate; pub mod merkle; pub mod pausable; pub mod routing; diff --git a/packages/interface/src/igp/core.rs b/packages/interface/src/igp/core.rs index 66cdd48b..01e488f4 100644 --- a/packages/interface/src/igp/core.rs +++ b/packages/interface/src/igp/core.rs @@ -13,7 +13,6 @@ use super::oracle::IgpGasOracleQueryMsg; pub struct InstantiateMsg { pub hrp: String, pub owner: String, - pub mailbox: String, pub gas_token: String, pub beneficiary: String, } diff --git a/packages/interface/src/ism/aggregate.rs b/packages/interface/src/ism/aggregate.rs new file mode 100644 index 00000000..a0262a22 --- /dev/null +++ b/packages/interface/src/ism/aggregate.rs @@ -0,0 +1,54 @@ +use cosmwasm_schema::{cw_serde, QueryResponses}; + +use crate::ownable::{OwnableMsg, OwnableQueryMsg}; + +use super::IsmQueryMsg; + +#[cw_serde] +pub struct InstantiateMsg { + pub owner: String, + pub isms: Vec, + pub threshold: u8, +} + +#[cw_serde] +pub enum ExecuteMsg { + Ownable(OwnableMsg), + + SetIsms { isms: Vec }, +} + +#[cw_serde] +#[derive(QueryResponses)] +#[query_responses(nested)] +pub enum QueryMsg { + Ownable(OwnableQueryMsg), + + Ism(IsmQueryMsg), +} + +#[cfg(test)] +mod test { + use cosmwasm_std::HexBinary; + + use super::*; + use crate::{ism::IsmQueryMsg, msg_checker}; + + #[test] + fn test_ism_interface() { + let _checked: QueryMsg = msg_checker(IsmQueryMsg::ModuleType {}.wrap()); + let _checked: QueryMsg = msg_checker( + IsmQueryMsg::Verify { + metadata: HexBinary::default(), + message: HexBinary::default(), + } + .wrap(), + ); + let _checked: QueryMsg = msg_checker( + IsmQueryMsg::VerifyInfo { + message: HexBinary::default(), + } + .wrap(), + ); + } +} diff --git a/packages/interface/src/ism/mod.rs b/packages/interface/src/ism/mod.rs index 75fa3a8a..18d10299 100644 --- a/packages/interface/src/ism/mod.rs +++ b/packages/interface/src/ism/mod.rs @@ -1,3 +1,4 @@ +pub mod aggregate; pub mod multisig; pub mod routing; diff --git a/packages/interface/src/types/metadata.rs b/packages/interface/src/types/metadata.rs index eb93c7ad..15b56ab1 100644 --- a/packages/interface/src/types/metadata.rs +++ b/packages/interface/src/types/metadata.rs @@ -115,23 +115,61 @@ impl From for MessageIdMultisigIsmMetadata { } } +impl MessageIdMultisigIsmMetadata { + pub fn merkle_index(&self) -> u32 { + u32::from_be_bytes(self.merkle_index.to_vec().try_into().unwrap()) + } +} + +use std::convert::AsMut; + +fn clone_into_array(slice: &[T]) -> A +where + A: Sized + Default + AsMut<[T]>, + T: Clone, +{ + let mut a = Default::default(); + >::as_mut(&mut a).clone_from_slice(slice); + a +} + #[cw_serde] -pub struct AggregateIsmMetadata(pub BTreeMap); +pub struct AggregateMetadata(BTreeMap); + +impl AggregateMetadata { + pub const RANGE_SIZE: usize = 4; -impl AggregateIsmMetadata { - const RANGE_SIZE: usize = 4; + pub fn new(set: Vec<(Addr, HexBinary)>) -> Self { + Self(set.into_iter().collect()) + } +} - pub fn from_hex(v: HexBinary, isms: Vec) -> Self { +impl Iterator for AggregateMetadata { + type Item = (Addr, HexBinary); + + fn next(&mut self) -> Option { + self.0.pop_first() + } +} + +impl AggregateMetadata { + pub fn from_hex(v: HexBinary, addrs: Vec) -> Self { Self( - isms.into_iter() + addrs + .into_iter() .enumerate() .map(|(i, ism)| { let start = i * Self::RANGE_SIZE * 2; let mid = start + Self::RANGE_SIZE; let end = mid + Self::RANGE_SIZE; - let meta_start = usize::from_be_bytes(v[start..mid].try_into().unwrap()); - let meta_end = usize::from_be_bytes(v[mid..end].try_into().unwrap()); + let mut meta_start = [0u8; 4]; + meta_start.copy_from_slice(&v[start..mid]); + let mut meta_end = [0u8; 4]; + meta_end.copy_from_slice(&v[mid..end]); + + let meta_start = u32::from_be_bytes(meta_start) as usize; + let meta_end = u32::from_be_bytes(meta_end) as usize; (ism, v[meta_start..meta_end].to_vec().into()) }) @@ -140,13 +178,44 @@ impl AggregateIsmMetadata { } } -// impl From for HexBinary { -// fn from(v: AggregateIsmMetadata) -> Self { -// v.0.into_iter().fold(vec![], |acc, (ism, metaedata)| { +impl From for HexBinary { + fn from(v: AggregateMetadata) -> Self { + let pos_start = v.0.len() * AggregateMetadata::RANGE_SIZE * 2; + + let ls: Vec<( + [u8; AggregateMetadata::RANGE_SIZE], + [u8; AggregateMetadata::RANGE_SIZE], + HexBinary, + )> = + v.0.values() + .fold(vec![] as Vec<(usize, usize, HexBinary)>, |mut acc, m| { + let l = acc.last().map(|v| v.1).unwrap_or(pos_start); + + acc.push((l, l + m.len(), m.clone())); + acc + }) + .into_iter() + .map(|(start, end, metadata)| { + ( + clone_into_array(&start.to_be_bytes()[AggregateMetadata::RANGE_SIZE..]), + clone_into_array(&end.to_be_bytes()[AggregateMetadata::RANGE_SIZE..]), + metadata, + ) + }) + .collect(); -// }) -// } -// } + let mut pos = vec![]; + let mut metadata = vec![]; + + for (start, end, meta) in ls { + pos.extend_from_slice(&start); + pos.extend_from_slice(&end); + metadata.extend_from_slice(meta.as_slice()); + } + + [pos, metadata].concat().into() + } +} #[cw_serde] pub struct IGPMetadata { @@ -198,10 +267,27 @@ impl IGPMetadata { #[cfg(test)] mod test { - use ibcx_test_utils::hex; + use ibcx_test_utils::{addr, gen_bz, hex}; use super::*; + #[test] + fn test_aggregate() { + let set = vec![ + (addr("test1"), gen_bz(12)), + (addr("test2"), gen_bz(12)), + (addr("test3"), gen_bz(12)), + ]; + + let metadata = AggregateMetadata::new(set); + let isms = metadata.0.clone().into_keys().collect(); + + let metadata_bz: HexBinary = metadata.clone().into(); + + let new_metadata = AggregateMetadata::from_hex(metadata_bz, isms); + assert_eq!(metadata, new_metadata); + } + #[test] fn test_message_id_multisig_metadata() { let testdata = hex("fadafdf4db5e6264d450bafa5951b2180b8fe8aac2e012f280784ae841e9a7f732a2601709a27a5e370a59f98a67b5da6baa522b6421edf2ea240d94d84511a800000000df4eaf1947af0858139b90054561d5ab2a423b4ad8d75a5ec7f9e860fd3de1bb3924e2593e29b595aae2717538c0af6d6ae9fc20477da49d223a0d928a1efb311bdf4eaf1947af0858139b90054561d5ab2a423b4ad8d75a5ec7f9e860fd3de1bb3924e2593e29b595aae2717538c0af6d6ae9fc20477da49d223a0d928a1efb311b"); diff --git a/packages/interface/src/warp/cw20.rs b/packages/interface/src/warp/cw20.rs index 6b20cf87..fbdc4794 100644 --- a/packages/interface/src/warp/cw20.rs +++ b/packages/interface/src/warp/cw20.rs @@ -3,7 +3,7 @@ use cosmwasm_std::HexBinary; use crate::{ core, - ownable::OwnableQueryMsg, + ownable::{OwnableMsg, OwnableQueryMsg}, router::{self, RouterQuery}, }; @@ -53,6 +53,7 @@ pub enum ReceiveMsg { #[cw_serde] pub enum ExecuteMsg { + Ownable(OwnableMsg), Router(router::RouterMsg), /// handle transfer remote diff --git a/packages/interface/src/warp/native.rs b/packages/interface/src/warp/native.rs index a880be38..35a91079 100644 --- a/packages/interface/src/warp/native.rs +++ b/packages/interface/src/warp/native.rs @@ -3,7 +3,7 @@ use cosmwasm_std::HexBinary; use crate::{ core, - ownable::OwnableQueryMsg, + ownable::{OwnableMsg, OwnableQueryMsg}, router::{RouterMsg, RouterQuery}, }; @@ -52,6 +52,7 @@ pub struct InstantiateMsg { #[cw_serde] pub enum ExecuteMsg { + Ownable(OwnableMsg), Router(RouterMsg), // handle transfer remote diff --git a/scripts/action/deploy.ts b/scripts/action/deploy.ts index 1d845432..1de1b708 100644 --- a/scripts/action/deploy.ts +++ b/scripts/action/deploy.ts @@ -1,10 +1,5 @@ -import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate"; -import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"; -import { GasPrice } from "@cosmjs/stargate"; - import { loadContext } from "../src/load_context"; import HplMailbox from "../src/contracts/hpl_mailbox"; -import { Context } from "../src/types"; import HplHookMerkle from "../src/contracts/hpl_hook_merkle"; import HplTestMockHook from "../src/contracts/hpl_test_mock_hook"; import HplIgpGasOracle from "../src/contracts/hpl_igp_oracle"; @@ -13,65 +8,15 @@ import HplIsmMultisig from "../src/contracts/hpl_ism_multisig"; import { writeFileSync } from "fs"; import HplValidatorAnnounce from "../src/contracts/hpl_validator_announce"; import HplTestMockMsgReceiver from "../src/contracts/hpl_test_mock_msg_receiver"; - -const NETWORK_ID = process.env.NETWORK_ID || "osmo-test-5"; -const NETWORK_HRP = process.env.NETWORK_HRP || "osmo"; -const NETWORK_URL = - process.env.NETWORK_URL || "https://rpc.osmotest5.osmosis.zone"; -const NETWORK_GAS = process.env.NETWORK_GAS || "0.025uosmo"; - -async function getSigningClient(): Promise<{ - client: SigningCosmWasmClient; - address: string; -}> { - const mnemonic = process.env["SIGNING_MNEMONIC"] as string; - const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, { - prefix: NETWORK_HRP, - }); - const [{ address }] = await wallet.getAccounts(); - - const client = await SigningCosmWasmClient.connectWithSigner( - NETWORK_URL, - wallet, - { - gasPrice: GasPrice.fromString(NETWORK_GAS), - } - ); - return { client, address }; -} - -type Const = new ( - address: string | undefined, - codeId: number | undefined, - digest: string, - signer: string, - client: SigningCosmWasmClient -) => T; - -class ContractFetcher { - constructor( - private ctx: Context, - private owner: string, - private client: SigningCosmWasmClient - ) {} - - public get(f: Const, name: string): T { - return new f( - this.ctx.contracts[name].address, - this.ctx.contracts[name].codeId, - this.ctx.contracts[name].digest, - this.owner, - this.client - ); - } -} +import { config, getSigningClient } from "../src/config"; +import { ContractFetcher } from "./fetch"; async function main() { - const { client, address: owner } = await getSigningClient(); + const client = await getSigningClient(config); - const ctx = loadContext(NETWORK_ID); + const ctx = loadContext(config.network.id); - const fetcher = new ContractFetcher(ctx, owner, client); + const fetcher = new ContractFetcher(ctx, client); const mailbox = fetcher.get(HplMailbox, "hpl_mailbox"); const va = fetcher.get(HplValidatorAnnounce, "hpl_validator_announce"); @@ -90,7 +35,7 @@ async function main() { // init mailbox ctx.contracts[mailbox.contractName] = await mailbox.instantiate({ hrp: "dual", - owner, + owner: client.signer, domain: 33333, }); @@ -136,8 +81,8 @@ async function main() { await test_mock_receiver.instantiate({ hrp: "dual" }); // pre-setup - await client.executeMultiple( - owner, + await client.wasm.executeMultiple( + client.signer, [ { contractAddress: ctx.contracts[mailbox.contractName].address!, diff --git a/scripts/action/migrate.ts b/scripts/action/migrate.ts new file mode 100644 index 00000000..5deee01e --- /dev/null +++ b/scripts/action/migrate.ts @@ -0,0 +1,70 @@ +import { ExecuteResult } from "@cosmjs/cosmwasm-stargate"; +import { config, getSigningClient } from "../src/config"; +import HplHookAggregate from "../src/contracts/hpl_hook_aggregate"; +import HplIgp from "../src/contracts/hpl_igp"; +import HplMailbox from "../src/contracts/hpl_mailbox"; +import HplTestMockHook from "../src/contracts/hpl_test_mock_hook"; +import { loadContext, saveContext } from "../src/load_context"; +import { ContractFetcher } from "./fetch"; + +const parseWasmEventLog = (res: ExecuteResult) => { + return res.events + .filter((v) => v.type.startsWith("wasm")) + .map((v) => ({ + "@type": v.type.slice(5), + ...Object.fromEntries(v.attributes.map((x) => [x.key, x.value])), + })); +}; + +async function main() { + const client = await getSigningClient(config); + + const ctx = loadContext(config.network.id); + + const fetcher = new ContractFetcher(ctx, client); + + const igp = fetcher.get(HplIgp, "hpl_igp"); + const mailbox = fetcher.get(HplMailbox, "hpl_mailbox"); + const hook_test = fetcher.get(HplTestMockHook, "hpl_test_mock_hook"); + const hook_merkle = fetcher.get(HplHookAggregate, "hpl_hook_merkle"); + const hook_aggregate = fetcher.get(HplHookAggregate, "hpl_hook_aggregate"); + + ctx.contracts["hpl_hook_merkle"] = await hook_merkle.instantiate({ + owner: client.signer, + mailbox: mailbox.address!, + }); + console.log(ctx.contracts["hpl_hook_merkle"].address!); + + ctx.contracts["hpl_hook_aggregate"] = await hook_aggregate.instantiate({ + owner: client.signer, + hooks: [igp.address!, ctx.contracts["hpl_hook_merkle"].address!], + }); + + const res = await client.wasm.executeMultiple( + client.signer, + [ + { + contractAddress: mailbox.address!, + msg: { + set_default_hook: { + hook: hook_test.address!, + }, + }, + }, + { + contractAddress: mailbox.address!, + msg: { + set_required_hook: { + hook: hook_aggregate.address!, + }, + }, + }, + ], + "auto" + ); + console.log(parseWasmEventLog(res)); + + saveContext(config.network.id, ctx); +} + +main().catch(console.error); diff --git a/scripts/src/contracts/hpl_hook_aggregate.ts b/scripts/src/contracts/hpl_hook_aggregate.ts new file mode 100644 index 00000000..d14a5819 --- /dev/null +++ b/scripts/src/contracts/hpl_hook_aggregate.ts @@ -0,0 +1,5 @@ +import { BaseContract } from "../types"; + +export default class HplHookAggregate extends BaseContract { + contractName: string = "hpl_hook_aggregate"; +} diff --git a/scripts/src/contracts/hpl_ism_aggregate.ts b/scripts/src/contracts/hpl_ism_aggregate.ts new file mode 100644 index 00000000..17e4ae50 --- /dev/null +++ b/scripts/src/contracts/hpl_ism_aggregate.ts @@ -0,0 +1,5 @@ +import { BaseContract } from "../types"; + +export default class HplIsmAggregate extends BaseContract { + contractName: string = "hpl_ism_aggregate"; +} diff --git a/scripts/src/load_wasm.ts b/scripts/src/load_wasm.ts index a1f9cdeb..e269aab5 100644 --- a/scripts/src/load_wasm.ts +++ b/scripts/src/load_wasm.ts @@ -53,5 +53,5 @@ export async function loadWasmFileDigest() { } export function getWasmPath(contractName: string): string { - return path.join(directoryPath, `${contractName}.wasm`); + return path.join(directoryPath, `${contractName}-aarch64.wasm`); } From d9bad73f55df79d16d00c36911d184d1df788438 Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Wed, 25 Oct 2023 16:09:37 +0900 Subject: [PATCH 04/53] with proper igp and aggregation --- scripts/action/deploy.ts | 402 ++++++++++++++++++++++++++++++-------- scripts/action/fetch.ts | 91 +++++++++ scripts/action/mailbox.ts | 45 +++-- scripts/save.json | 102 ++++++++++ scripts/src/config.ts | 72 ++++++- scripts/src/types.ts | 13 ++ 6 files changed, 632 insertions(+), 93 deletions(-) create mode 100644 scripts/save.json diff --git a/scripts/action/deploy.ts b/scripts/action/deploy.ts index 1de1b708..79680838 100644 --- a/scripts/action/deploy.ts +++ b/scripts/action/deploy.ts @@ -1,110 +1,65 @@ -import { loadContext } from "../src/load_context"; -import HplMailbox from "../src/contracts/hpl_mailbox"; -import HplHookMerkle from "../src/contracts/hpl_hook_merkle"; -import HplTestMockHook from "../src/contracts/hpl_test_mock_hook"; -import HplIgpGasOracle from "../src/contracts/hpl_igp_oracle"; -import HplIgp from "../src/contracts/hpl_igp"; -import HplIsmMultisig from "../src/contracts/hpl_ism_multisig"; import { writeFileSync } from "fs"; -import HplValidatorAnnounce from "../src/contracts/hpl_validator_announce"; -import HplTestMockMsgReceiver from "../src/contracts/hpl_test_mock_msg_receiver"; -import { config, getSigningClient } from "../src/config"; -import { ContractFetcher } from "./fetch"; - -async function main() { - const client = await getSigningClient(config); - - const ctx = loadContext(config.network.id); - - const fetcher = new ContractFetcher(ctx, client); - - const mailbox = fetcher.get(HplMailbox, "hpl_mailbox"); - const va = fetcher.get(HplValidatorAnnounce, "hpl_validator_announce"); - const hook_merkle = fetcher.get(HplHookMerkle, "hpl_hook_merkle"); - const igp_oracle = fetcher.get(HplIgpGasOracle, "hpl_igp_oracle"); - const igp = fetcher.get(HplIgp, "hpl_igp"); - const ism_multisig = fetcher.get(HplIsmMultisig, "hpl_ism_multisig"); +import { loadContext } from "../src/load_context"; +import { + Client, + HookType, + IsmType, + config, + getSigningClient, +} from "../src/config"; - const test_mock_hook = fetcher.get(HplTestMockHook, "hpl_test_mock_hook"); - const test_mock_receiver = fetcher.get( - HplTestMockMsgReceiver, - "hpl_test_mock_msg_receiver" - ); +import { ContractFetcher, Contracts } from "./fetch"; +import { Context } from "../src/types"; - // init mailbox - ctx.contracts[mailbox.contractName] = await mailbox.instantiate({ - hrp: "dual", - owner: client.signer, - domain: 33333, - }); +const name = (c: any) => c.contractName; +const addr = (ctx: Context, c: any) => ctx.contracts[name(c)].address!; - // init validator announce - ctx.contracts[va.contractName] = await va.instantiate({ - hrp: "dual", - mailbox: ctx.contracts[mailbox.contractName].address, - }); +async function main() { + const client = await getSigningClient(config); - // init merkle hook - (required hook) - ctx.contracts[hook_merkle.contractName] = await hook_merkle.instantiate({ - owner: ctx.address!, - mailbox: ctx.contracts[mailbox.contractName].address, - }); + let ctx = loadContext(config.network.id); - // init mock hook - (default hook) - ctx.contracts[test_mock_hook.contractName] = await test_mock_hook.instantiate( - {} - ); + const contracts = new ContractFetcher(ctx, client).getContracts(); + const { + core: { mailbox }, + mocks, + } = contracts; - // init igp oracle - ctx.contracts[igp_oracle.contractName] = await igp_oracle.instantiate({ - owner: ctx.address!, - }); - - // init igp - ctx.contracts[igp.contractName] = await igp.instantiate({ - hrp: "dual", - owner: ctx.address!, - mailbox: ctx.contracts[mailbox.contractName].address, - gas_token: "token", - beneficiary: ctx.address!, - }); - - // init ism multisig - ctx.contracts[ism_multisig.contractName] = await ism_multisig.instantiate({ - hrp: "dual", - owner: ctx.address!, - }); + ctx = await deploy_core(ctx, client, contracts); + ctx = await deploy_igp(ctx, client, contracts); + ctx = await deploy_ism_hook(ctx, client, contracts); // init test mock msg receiver - ctx.contracts[test_mock_receiver.contractName] = - await test_mock_receiver.instantiate({ hrp: "dual" }); + ctx.contracts[name(mocks.receiver)] = await mocks.receiver.instantiate({ + hrp: config.network.hrp, + }); // pre-setup await client.wasm.executeMultiple( client.signer, [ { - contractAddress: ctx.contracts[mailbox.contractName].address!, + contractAddress: addr(ctx, mailbox), msg: { set_default_ism: { - ism: ctx.contracts[ism_multisig.contractName].address!, + ism: ctx.contracts["hpl_default_ism"].address!, }, }, }, { - contractAddress: ctx.contracts[mailbox.contractName].address!, + contractAddress: addr(ctx, mailbox), msg: { set_default_hook: { - hook: ctx.contracts[test_mock_hook.contractName].address!, + hook: ctx.contracts["hpl_default_hook"].address!, }, }, }, { - contractAddress: ctx.contracts[mailbox.contractName].address!, + contractAddress: addr(ctx, mailbox), msg: { set_required_hook: { - hook: ctx.contracts[hook_merkle.contractName].address!, + hook: ctx.contracts["hpl_required_hook"].address!, }, }, }, @@ -115,4 +70,297 @@ async function main() { writeFileSync("./save.json", JSON.stringify(ctx, null, 2)); } +const deploy_core = async ( + ctx: Context, + client: Client, + { core: { mailbox, va } }: Contracts +): Promise => { + // init mailbox + ctx.contracts[name(mailbox)] = await mailbox.instantiate({ + hrp: config.network.hrp, + owner: client.signer, + domain: config.network.domain, + }); + + // init validator announce + ctx.contracts[name(va)] = await va.instantiate({ + hrp: config.network.hrp, + mailbox: addr(ctx, mailbox), + }); + + return ctx; +}; + +const deploy_igp = async ( + ctx: Context, + client: Client, + { igp }: Contracts +): Promise => { + // init igp + ctx.contracts[name(igp.core)] = await igp.core.instantiate({ + hrp: config.network.hrp, + owner: ctx.address!, + gas_token: config.deploy.igp.token || config.network.gas.denom, + beneficiary: ctx.address!, + }); + + // init igp oracle + ctx.contracts[name(igp.oracle)] = await igp.oracle.instantiate({ + owner: ctx.address!, + }); + + await client.wasm.execute( + client.signer, + addr(ctx, igp.oracle), + { + set_remote_gas_data_configs: { + configs: Object.entries(config.deploy.igp.configs).map( + ([domain, v]) => ({ + remote_domain: Number(domain), + token_exchange_rate: v.exchange_rate.toString(), + gas_price: v.gas_price.toString(), + }) + ), + }, + }, + "auto" + ); + + await client.wasm.execute( + client.signer, + addr(ctx, igp.core), + { + router: { + set_routes: { + set: Object.keys(config.deploy.igp.configs).map((domain) => ({ + domain: Number(domain), + route: addr(ctx, igp.oracle), + })), + }, + }, + }, + "auto" + ); + + return ctx; +}; + +const deploy_ism_hook = async ( + ctx: Context, + client: Client, + contracts: Contracts +) => { + ctx.contracts["hpl_default_ism"] = { + ...ctx.contracts[`hpl_ism_${config.deploy.ism?.type || "multisig"}`], + + address: await deploy_ism( + client, + config.deploy.ism || { + type: "multisig", + owner: "", + validators: [ + { + addr: client.signer, + pubkey: client.signer_pubkey, + }, + ], + threshold: 1, + }, + contracts + ), + }; + + ctx.contracts["hpl_default_hook"] = { + ...ctx.contracts[ + config.deploy.hooks?.default?.type && + config.deploy.hooks?.default?.type !== "mock" + ? `hpl_hook_${config.deploy.hooks.default.type}` + : "hpl_test_mock_hook" + ], + + address: await deploy_hook( + ctx, + client, + config.deploy.hooks?.default || { type: "mock" }, + contracts + ), + }; + + ctx.contracts["hpl_required_hook"] = { + ...ctx.contracts[ + config.deploy.hooks?.required?.type && + config.deploy.hooks?.required?.type !== "mock" + ? `hpl_hook_${config.deploy.hooks.required.type}` + : "hpl_test_mock_hook" + ], + + address: await deploy_hook( + ctx, + client, + config.deploy.hooks?.required || { type: "mock" }, + contracts + ), + }; + + return ctx; +}; + +const deploy_ism = async ( + client: Client, + ism: IsmType, + contracts: Contracts +): Promise => { + const { isms } = contracts; + + switch (ism.type) { + case "multisig": + const multisig_ism_res = await isms.multisig.instantiate({ + hrp: config.network.hrp, + owner: ism.owner === "" ? client.signer : ism.owner, + }); + + await client.wasm.execute( + client.signer, + multisig_ism_res.address!, + { + enroll_validators: { + set: Object.entries(ism.validators).map(([domain, v]) => ({ + domain: Number(domain), + validator: v.addr, + validator_pubkey: v.pubkey, + })), + }, + }, + "auto" + ); + + await client.wasm.execute( + client.signer, + multisig_ism_res.address!, + { + set_thresholds: { + set: Object.keys(ism.validators).map((domain) => ({ + domain: Number(domain), + threshold: ism.threshold, + })), + }, + }, + "auto" + ); + + return multisig_ism_res.address!; + + case "aggregate": + const aggregate_ism_res = await isms.aggregate.instantiate({ + owner: ism.owner === "" ? client.signer : ism.owner, + isms: await Promise.all( + ism.isms.map((v) => deploy_ism(client, v, contracts)) + ), + }); + + return aggregate_ism_res.address!; + case "routing": + const routing_ism_res = await isms.routing.instantiate({ + owner: ism.owner === "" ? client.signer : ism.owner, + }); + + await client.wasm.execute( + client.signer, + routing_ism_res.address!, + { + router: { + set_routes: { + set: await Promise.all( + Object.entries(ism.isms).map(async ([domain, v]) => { + const route = await deploy_ism(client, v, contracts); + return { domain, route }; + }) + ), + }, + }, + }, + "auto" + ); + + return routing_ism_res.address!; + + default: + throw new Error("invalid ism type"); + } +}; + +const deploy_hook = async ( + ctx: Context, + client: Client, + hook: HookType, + contracts: Contracts +): Promise => { + const { + core: { mailbox }, + hooks, + igp, + mocks, + } = contracts; + + switch (hook.type) { + case "aggregate": + const aggregate_hook_res = await hooks.aggregate.instantiate({ + owner: hook.owner === "" ? client.signer : hook.owner, + hooks: await Promise.all( + hook.hooks.map((v) => deploy_hook(ctx, client, v, contracts)) + ), + }); + + return aggregate_hook_res.address!; + + case "merkle": + const merkle_hook_res = await hooks.merkle.instantiate({ + owner: hook.owner === "" ? client.signer : hook.owner, + mailbox: addr(ctx, mailbox), + }); + + return merkle_hook_res.address!; + + case "mock": + const mock_hook_res = await mocks.hook.instantiate({}); + + return mock_hook_res.address!; + + case "pausable": + const pausable_hook_res = await hooks.pausable.instantiate({ + owner: hook.owner === "" ? client.signer : hook.owner, + }); + + return pausable_hook_res.address!; + + case "igp": + return ctx.contracts[name(igp.core)].address!; + + case "routing": + const routing_hook_res = await hooks.routing.instantiate({ + owner: hook.owner === "" ? client.signer : hook.owner, + }); + + await client.wasm.execute( + client.signer, + routing_hook_res.address!, + { + router: { + set_routes: { + set: await Promise.all( + Object.entries(hook.hooks).map(async ([domain, v]) => { + const route = await deploy_hook(ctx, client, v, contracts); + return { domain, route }; + }) + ), + }, + }, + }, + "auto" + ); + default: + throw new Error("invalid hook type"); + } +}; + main().catch(console.error); diff --git a/scripts/action/fetch.ts b/scripts/action/fetch.ts index f6d4ef72..7f2807d4 100644 --- a/scripts/action/fetch.ts +++ b/scripts/action/fetch.ts @@ -1,6 +1,22 @@ import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate"; import { Context } from "../src/types"; import { Client } from "../src/config"; +import HplMailbox from "../src/contracts/hpl_mailbox"; +import HplValidatorAnnounce from "../src/contracts/hpl_validator_announce"; +import HplHookAggregate from "../src/contracts/hpl_hook_aggregate"; +import HplHookMerkle from "../src/contracts/hpl_hook_merkle"; +import HplHookPausable from "../src/contracts/hpl_hook_pausable"; +import HplHookRouting from "../src/contracts/hpl_hook_routing"; +import HplHookRoutingCustom from "../src/contracts/hpl_hook_routing_custom"; +import HplIgp from "../src/contracts/hpl_igp"; +import HplIgpGasOracle from "../src/contracts/hpl_igp_oracle"; +import HplIsmAggregate from "../src/contracts/hpl_ism_aggregate"; +import HplIsmMultisig from "../src/contracts/hpl_ism_multisig"; +import HplIsmRouting from "../src/contracts/hpl_ism_routing"; +import HplTestMockHook from "../src/contracts/hpl_test_mock_hook"; +import HplTestMockMsgReceiver from "../src/contracts/hpl_test_mock_msg_receiver"; +import HplWarpCw20 from "../src/contracts/hpl_warp_cw20"; +import HplWarpNative from "../src/contracts/hpl_warp_native"; type Const = new ( address: string | undefined, @@ -10,6 +26,38 @@ type Const = new ( client: SigningCosmWasmClient ) => T; +export type Contracts = { + core: { + mailbox: HplMailbox; + va: HplValidatorAnnounce; + }; + hooks: { + aggregate: HplHookAggregate; + merkle: HplHookMerkle; + pausable: HplHookPausable; + routing: HplHookRouting; + routing_custom: HplHookRoutingCustom; + routing_fallback: HplHookRoutingCustom; + }; + igp: { + core: HplIgp; + oracle: HplIgpGasOracle; + }; + isms: { + aggregate: HplIsmAggregate; + multisig: HplIsmMultisig; + routing: HplIsmRouting; + }; + mocks: { + hook: HplTestMockHook; + receiver: HplTestMockMsgReceiver; + }; + warp: { + cw20: HplWarpCw20; + native: HplWarpNative; + }; +}; + export class ContractFetcher { constructor(private ctx: Context, private client: Client) {} @@ -22,4 +70,47 @@ export class ContractFetcher { this.client.wasm ); } + + public getContracts(): Contracts { + return { + core: { + mailbox: this.get(HplMailbox, "hpl_mailbox"), + va: this.get(HplValidatorAnnounce, "hpl_validator_announce"), + }, + hooks: { + aggregate: this.get(HplHookAggregate, "hpl_hook_aggregate"), + merkle: this.get(HplHookMerkle, "hpl_hook_merkle"), + pausable: this.get(HplHookPausable, "hpl_hook_pausable"), + routing: this.get(HplHookRouting, "hpl_hook_routing"), + routing_custom: this.get( + HplHookRoutingCustom, + "hpl_hook_routing_custom" + ), + routing_fallback: this.get( + HplHookRoutingCustom, + "hpl_hook_routing_fallback" + ), + }, + igp: { + core: this.get(HplIgp, "hpl_igp"), + oracle: this.get(HplIgpGasOracle, "hpl_igp_oracle"), + }, + isms: { + aggregate: this.get(HplIsmAggregate, "hpl_ism_aggregate"), + multisig: this.get(HplIsmMultisig, "hpl_ism_multisig"), + routing: this.get(HplIsmRouting, "hpl_ism_routing"), + }, + mocks: { + hook: this.get(HplTestMockHook, "hpl_test_mock_hook"), + receiver: this.get( + HplTestMockMsgReceiver, + "hpl_test_mock_msg_receiver" + ), + }, + warp: { + cw20: this.get(HplWarpCw20, "hpl_warp_cw20"), + native: this.get(HplWarpNative, "hpl_warp_native"), + }, + }; + } } diff --git a/scripts/action/mailbox.ts b/scripts/action/mailbox.ts index afdbc005..5aba751f 100644 --- a/scripts/action/mailbox.ts +++ b/scripts/action/mailbox.ts @@ -7,6 +7,10 @@ import { addPad } from "../src/conv"; import { loadContext } from "../src/load_context"; import { ContractFetcher } from "./fetch"; import { ExecuteResult } from "@cosmjs/cosmwasm-stargate"; +import HplIsmAggregate from "../src/contracts/hpl_ism_aggregate"; +import HplIgp from "../src/contracts/hpl_igp"; +import HplIgpGasOracle from "../src/contracts/hpl_igp_oracle"; +import HplHookMerkle from "../src/contracts/hpl_hook_merkle"; const program = new Command(); @@ -28,12 +32,14 @@ program program.parseAsync(process.argv).catch(console.error); const parseWasmEventLog = (res: ExecuteResult) => { - return res.events - .filter((v) => v.type.startsWith("wasm")) - .map((v) => ({ - "@type": v.type.slice(5), - ...Object.fromEntries(v.attributes.map((x) => [x.key, x.value])), - })); + return ( + res.events + // .filter((v) => v.type.startsWith("wasm")) + .map((v) => ({ + "@type": v.type.slice(5), + ...Object.fromEntries(v.attributes.map((x) => [x.key, x.value])), + })) + ); }; function makeHandler( @@ -45,7 +51,17 @@ function makeHandler( const client = await getSigningClient(config); const fetcher = new ContractFetcher(ctx, client); const mailbox = fetcher.get(HplMailbox, "hpl_mailbox"); - return { mailbox }; + const igp = fetcher.get(HplIgp, "hpl_igp"); + const igp_oracle = fetcher.get(HplIgpGasOracle, "hpl_igp_oracle"); + const hook_merkle = fetcher.get(HplHookMerkle, "hpl_hook_merkle"); + const hook_aggregate = fetcher.get(HplIsmAggregate, "hpl_hook_aggregate"); + + return { + client, + mailbox, + igp: { core: igp, oracle: igp_oracle }, + hook: { merkle: hook_merkle, aggregate: hook_aggregate }, + }; }; switch (action) { @@ -57,13 +73,16 @@ function makeHandler( ) => { const { mailbox } = await loadDeps(); - const res = await mailbox.execute({ - dispatch: { - dest_domain: Number(dest_domain), - recipient_addr: addPad(recipient_addr), - msg_body: Buffer.from(msg_body, "utf-8").toString("hex"), + const res = await mailbox.execute( + { + dispatch: { + dest_domain: Number(dest_domain), + recipient_addr: addPad(recipient_addr), + msg_body: Buffer.from(msg_body, "utf-8").toString("hex"), + }, }, - }); + [{ denom: "token", amount: "26000000" }] + ); console.log(parseWasmEventLog(res)); }; case "process": diff --git a/scripts/save.json b/scripts/save.json new file mode 100644 index 00000000..dc862e33 --- /dev/null +++ b/scripts/save.json @@ -0,0 +1,102 @@ +{ + "contracts": { + "hpl_hook_merkle": { + "address": "dual1pk99xge6q94qtu3568x3qhp68zzv0mx7za4ct008ks36qhx5tvss3qawfh", + "codeId": 34, + "digest": "1e8cabd844f11ba4cdeaaeffb483a0057d8a36f945ad9166dd3276ef8dc89aa2" + }, + "hpl_igp": { + "address": "dual1q25d0y0nyyp8mjnqc8y4s6t3wmq5as73plk3hyfa3dy82w4jsl7qry0wqf", + "codeId": 50, + "digest": "38305f60838fda89236490ba0e8d0a07cfb224e93bc805b0de4fe9f4217194fd" + }, + "hpl_igp_oracle": { + "address": "dual148g378f05algmm5n9jftqef3jv232n5aynfxud0tu8vlnhw7vepqjyyd20", + "codeId": 40, + "digest": "9489e237e542da054becabe952d28959d541d1207d3000d49f5c1dfc57e94536" + }, + "hpl_ism_multisig": { + "address": "dual1jwedcsjxv8e3umnnqm5cvtylg0han2wfqwlw9n27n2kns0vjgneq8d6m2a", + "codeId": 42, + "digest": "394c84042f2b8064575360c39d0c0ffbec9a57e753cadc508f8f62c72c22beee" + }, + "hpl_mailbox": { + "address": "dual1yp0lgxq460ked0egtzyj2nck3mdhr8smjrshsfl4zeefct033jts0xfa86", + "codeId": 44, + "digest": "46c5d2d39ae74fd7e79a35a208fc77c6f9155f266bff8e4b1b69ad87ee3864e3" + }, + "hpl_test_mock_hook": { + "address": "dual1q5wmfzl6gfe629cc5yacluerphf8d5gq8j2d52pn7ua70279mvlq39cetg", + "codeId": 45, + "digest": "1aa8037a0adc680b92b0b9dd1d682289384ae525a1e1138cf10b10bada3d4acc" + }, + "hpl_test_mock_msg_receiver": { + "address": "dual1dettnvwzcec0yk7sfhxed8phl62k36fg5nezdasfherfgjhhur8s0fg77l", + "codeId": 46, + "digest": "09c21975d627436c705ee93f037cfbbde8f6f0dd192603f6c0b032c672dea959" + }, + "hpl_validator_announce": { + "address": "dual1rs04xd0dq3c9mra7jwjy9q7twyzmgc93w2we5h5nj2km3ccqxhaq2js68f", + "codeId": 47, + "digest": "92ee2775bf7b9604f6ab8cf77e11b47ca858ab7b17da12a659f43392e0f071d3" + }, + "hpl_warp_cw20": { + "codeId": 48, + "digest": "b78f5931b71da233bb9e90f3b686e06ed0e62430219399b6d844bdea4b1bf61d" + }, + "hpl_warp_native": { + "codeId": 49, + "digest": "bcb8191f002b5b5fc8bda10b6c7ee25b48ea4591ff51bc805967e394b623795e" + }, + "hpl_warp_native_ibc": { + "address": "dual1572xwqajsht372ra2cy65zk8ec75eq7k7ayqqcw9r3y03lxvn6aqjnvhys", + "codeId": 32, + "digest": "eed9d7dfa18163de64893cfe88e038cfc82e487561c94b142978efdacc1f9dff" + }, + "hpl_hook_aggregate": { + "address": "dual1r2v20edjksh8842dkse6qzcdgntt0dtym693tpyftvmpc3grzczs0wrl47", + "codeId": 33, + "digest": "eefbcb3c2a0f6a62d8a88902e2caa371dd3add9eee42c16e377b6f6eeb37c972" + }, + "hpl_hook_pausable": { + "codeId": 35, + "digest": "b06c21fe3f4e2f2c41756e45a410a3a5bf0c21322196b24488f327b9db399198" + }, + "hpl_hook_routing": { + "codeId": 36, + "digest": "516e462877f1f2ae9e6dfdf1b4def9817f46ee9719413a9d2ef44196782e4ee1" + }, + "hpl_hook_routing_custom": { + "codeId": 37, + "digest": "e8b933536e97db737d28d101d29d14a7644c2e10efdb324a98bae1fe46c912b9" + }, + "hpl_hook_routing_fallback": { + "codeId": 38, + "digest": "af03a19000421a8652ac7e709162ce55f194fe3ceb3e13bdccfced6dc2f2010c" + }, + "hpl_ism_aggregate": { + "codeId": 41, + "digest": "4a262c7221450e0cf988c8d6c05b724e1faeed4dd1c715e85527d901acffdf42" + }, + "hpl_ism_routing": { + "codeId": 43, + "digest": "6f4bc63847fe9581da19eabd0f869bc6c4339d03db5dcff683a4ec8780a41dfd" + }, + "hpl_default_ism": { + "address": "dual1ql2d2glpgnzw0ufegafqtggm2r4zhql0x8z46pmufr98sdtx0g9q464p7j", + "codeId": 42, + "digest": "394c84042f2b8064575360c39d0c0ffbec9a57e753cadc508f8f62c72c22beee" + }, + "hpl_default_hook": { + "address": "dual14ljshs3j9vlqv9lg3x8equf7450jjanx45cz343uaepk57ppe6cs74cuzf", + "codeId": 45, + "digest": "1aa8037a0adc680b92b0b9dd1d682289384ae525a1e1138cf10b10bada3d4acc" + }, + "hpl_required_hook": { + "address": "dual1pthrq98hpym0k3tmnwjp9shj77ecaymk5e5xaf6y5hstwgphq8tq5avlrr", + "codeId": 33, + "digest": "eefbcb3c2a0f6a62d8a88902e2caa371dd3add9eee42c16e377b6f6eeb37c972" + } + }, + "address": "dual1dwnrgwsf5c9vqjxsax04pdm0mx007yrraj2dgn" +} \ No newline at end of file diff --git a/scripts/src/config.ts b/scripts/src/config.ts index f79d74f6..f78211d9 100644 --- a/scripts/src/config.ts +++ b/scripts/src/config.ts @@ -9,17 +9,83 @@ import { import { DirectSecp256k1Wallet } from "@cosmjs/proto-signing"; import { GasPrice, SigningStargateClient } from "@cosmjs/stargate"; +export type IsmType = + | { + type: "multisig"; + owner: string; + validators: { + [domain: number]: { addr: string; pubkey: string }; + }; + threshold: number; + } + | { + type: "aggregate"; + owner: string; + isms: IsmType[]; + } + | { + type: "routing"; + owner: string; + isms: { [domain: number]: IsmType }; + }; + +export type HookType = + | { + type: "merkle"; + owner: string; + } + | { + type: "mock"; + } + | { + type: "pausable"; + owner: string; + } + | { + type: "igp"; + } + | { type: "aggregate"; owner: string; hooks: HookType[] } + | { + type: "routing"; + owner: string; + hooks: { [domain: number]: HookType }; + custom_hooks?: { + [domain: number]: { recipient: string; hook: string }; + }; + fallback_hook?: string; + }; + export type Config = { network: { id: string; hrp: string; url: string; - gas: string; + gas: { + price: string; + denom: string; + }; domain: number; tm_version?: "34" | "37"; }; signer: string; + + deploy: { + igp: { + token?: string; + configs: { + [domain: number]: { + exchange_rate: number; + gas_price: number; + }; + }; + }; + ism?: IsmType; + hooks?: { + default?: HookType; + required?: HookType; + }; + }; }; export type Client = { @@ -59,14 +125,14 @@ export async function getSigningClient({ clientBase, wallet, { - gasPrice: GasPrice.fromString(network.gas), + gasPrice: GasPrice.fromString(`${network.gas.price}${network.gas.denom}`), } ); const stargate = await SigningStargateClient.createWithSigner( clientBase, wallet, { - gasPrice: GasPrice.fromString(network.gas), + gasPrice: GasPrice.fromString(`${network.gas.price}${network.gas.denom}`), } ); diff --git a/scripts/src/types.ts b/scripts/src/types.ts index d3467ca8..1f439aea 100644 --- a/scripts/src/types.ts +++ b/scripts/src/types.ts @@ -94,6 +94,13 @@ export abstract class BaseContract implements Contract { "auto", { admin: this.signer } ); + console.log( + [ + this.contractName.padEnd(30), + contract.contractAddress.padEnd(65), + contract.transactionHash.padEnd(65), + ].join("| ") + ); this.address = contract.contractAddress; return this.getContractContext(); @@ -119,6 +126,12 @@ export abstract class BaseContract implements Contract { undefined, funds ); + console.log( + [ + `${this.contractName}:${Object.keys(msg)[0]}`.padEnd(30), + res.transactionHash.padEnd(65), + ].join("| ") + ); return res; } From f75fb9499fb402bb1ef2d08e35cdd8ffca8252ae Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Thu, 26 Oct 2023 02:54:42 +0900 Subject: [PATCH 05/53] rc4 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index fed4bf5f..95448195 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ panic = "abort" rpath = false [workspace.package] -version = "0.0.6-rc1" +version = "0.0.6-rc4" authors = [ "byeongsu-hong ", "Eric ", From aa50f617ef72c1c8a3e24c04ef25021c55a94567 Mon Sep 17 00:00:00 2001 From: ByeongSu Hong Date: Thu, 26 Oct 2023 19:25:41 +0900 Subject: [PATCH 06/53] fix: use ethereum-style address scheme (#55) * set mailbox version to 3 * feat(ism): use ethereum-style address to verify * feat: add enroll / unenroll test * refactor: merge into one file * make test runnable * test: add case * fix: with all (#54) * fix: with all * broken test --------- Co-authored-by: byeongsu-hong * feat: mailbox as binary * rc5 * feat: use recovery param from signature * fix: va test --------- Co-authored-by: Eric <125242347+hashableric@users.noreply.github.com> Co-authored-by: hashableric --- Cargo.toml | 2 +- contracts/core/mailbox/src/lib.rs | 2 +- contracts/core/va/src/contract.rs | 188 +++----- contracts/core/va/src/state.rs | 11 +- contracts/isms/aggregate/src/lib.rs | 6 +- contracts/isms/multisig/Cargo.toml | 2 + contracts/isms/multisig/src/contract.rs | 10 +- contracts/isms/multisig/src/error.rs | 24 +- contracts/isms/multisig/src/execute.rs | 307 ++++++++++++ contracts/isms/multisig/src/execute/mod.rs | 5 - .../isms/multisig/src/execute/threshold.rs | 136 ------ .../isms/multisig/src/execute/validator.rs | 456 ------------------ contracts/isms/multisig/src/lib.rs | 11 - contracts/isms/multisig/src/query.rs | 171 +++---- contracts/isms/multisig/src/state.rs | 16 +- contracts/isms/multisig/src/verify.rs | 39 -- contracts/mocks/mock-ism/src/contract.rs | 2 +- integration-test/tests/contracts/cw/ism.rs | 27 +- integration-test/tests/contracts/cw/setup.rs | 8 +- integration-test/tests/validator.rs | 22 +- packages/interface/src/core/va.rs | 4 +- packages/interface/src/ism/mod.rs | 2 +- packages/interface/src/ism/multisig.rs | 8 +- packages/interface/src/types/crypto.rs | 10 + 24 files changed, 501 insertions(+), 968 deletions(-) create mode 100644 contracts/isms/multisig/src/execute.rs delete mode 100644 contracts/isms/multisig/src/execute/mod.rs delete mode 100644 contracts/isms/multisig/src/execute/threshold.rs delete mode 100644 contracts/isms/multisig/src/execute/validator.rs delete mode 100644 contracts/isms/multisig/src/verify.rs diff --git a/Cargo.toml b/Cargo.toml index 95448195..99fb693c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ panic = "abort" rpath = false [workspace.package] -version = "0.0.6-rc4" +version = "0.0.6-rc5" authors = [ "byeongsu-hong ", "Eric ", diff --git a/contracts/core/mailbox/src/lib.rs b/contracts/core/mailbox/src/lib.rs index 87a985c6..81ac1eba 100644 --- a/contracts/core/mailbox/src/lib.rs +++ b/contracts/core/mailbox/src/lib.rs @@ -7,7 +7,7 @@ mod state; pub use crate::error::ContractError; -pub const MAILBOX_VERSION: u8 = 0; +pub const MAILBOX_VERSION: u8 = 3; // version info for migration info const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); diff --git a/contracts/core/va/src/contract.rs b/contracts/core/va/src/contract.rs index cfb4a29f..fe52ad4b 100644 --- a/contracts/core/va/src/contract.rs +++ b/contracts/core/va/src/contract.rs @@ -1,8 +1,8 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - ensure, Addr, Deps, DepsMut, Empty, Env, Event, HexBinary, MessageInfo, Order, QueryResponse, - Response, StdResult, + ensure, ensure_eq, Deps, DepsMut, Empty, Env, Event, HexBinary, MessageInfo, Order, + QueryResponse, Response, StdResult, }; use hpl_interface::{ @@ -14,13 +14,12 @@ use hpl_interface::{ }, }, to_binary, - types::{bech32_decode, bech32_encode, eth_hash, keccak256_hash, pub_to_addr}, + types::{bech32_decode, eth_addr, eth_hash, keccak256_hash}, }; -use k256::{ecdsa::VerifyingKey, EncodedPoint}; use crate::{ error::ContractError, - state::{HRP, LOCAL_DOMAIN, MAILBOX, REPLAY_PROTECITONS, STORAGE_LOCATIONS, VALIDATORS}, + state::{LOCAL_DOMAIN, MAILBOX, REPLAY_PROTECITONS, STORAGE_LOCATIONS, VALIDATORS}, CONTRACT_NAME, CONTRACT_VERSION, }; @@ -34,6 +33,7 @@ pub fn instantiate( cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; let mailbox = deps.api.addr_validate(&msg.mailbox)?; + let mailbox_addr = bech32_decode(mailbox.as_str())?; let local_domain = deps .querier @@ -43,8 +43,7 @@ pub fn instantiate( )? .local_domain; - HRP.save(deps.storage, &msg.hrp)?; - MAILBOX.save(deps.storage, &mailbox)?; + MAILBOX.save(deps.storage, &mailbox_addr)?; LOCAL_DOMAIN.save(deps.storage, &local_domain)?; Ok(Response::new().add_event( @@ -85,18 +84,13 @@ fn get_announce( deps: Deps, validators: Vec, ) -> Result { - let hrp = HRP.load(deps.storage)?; - let storage_locations = validators .into_iter() .map(|v| { - let raw_validator = bech32_encode(&hrp, &v)?; - let validator = deps.api.addr_validate(raw_validator.as_str())?; - let storage_locations = STORAGE_LOCATIONS - .may_load(deps.storage, validator.clone())? + .may_load(deps.storage, v.to_vec())? .unwrap_or_default(); - Ok((validator.to_string(), storage_locations)) + Ok((v.to_hex(), storage_locations)) }) .collect::>>()?; @@ -106,27 +100,24 @@ fn get_announce( fn get_validators(deps: Deps) -> Result { let validators = VALIDATORS .keys(deps.storage, None, None, Order::Ascending) - .map(|v| v.map(String::from)) + .map(|v| v.map(HexBinary::from).map(|v| v.to_hex())) .collect::>>()?; Ok(GetAnnouncedValidatorsResponse { validators }) } -fn replay_hash(validator: &Addr, storage_location: &str) -> StdResult { +fn replay_hash(validator: &HexBinary, storage_location: &str) -> StdResult { Ok(keccak256_hash( - [ - bech32_decode(validator.as_str())?, - storage_location.as_bytes().to_vec(), - ] - .concat() - .as_slice(), + [validator.to_vec(), storage_location.as_bytes().to_vec()] + .concat() + .as_slice(), )) } -fn domain_hash(local_domain: u32, mailbox: &str) -> StdResult { +fn domain_hash(local_domain: u32, mailbox: HexBinary) -> StdResult { let mut bz = vec![]; bz.append(&mut local_domain.to_be_bytes().to_vec()); - bz.append(&mut bech32_decode(mailbox)?); + bz.append(&mut mailbox.to_vec()); bz.append(&mut "HYPERLANE_ANNOUNCEMENT".as_bytes().to_vec()); let hash = keccak256_hash(&bz); @@ -149,10 +140,6 @@ fn announce( storage_location: String, signature: HexBinary, ) -> Result { - let hrp = HRP.load(deps.storage)?; - let raw_validator = bech32_encode(hrp.as_str(), &validator)?; - let validator = deps.api.addr_validate(raw_validator.as_str())?; - // check replay protection let replay_id = replay_hash(&validator, &storage_location)?; ensure!( @@ -163,45 +150,44 @@ fn announce( // make announcement digest let local_domain = LOCAL_DOMAIN.load(deps.storage)?; - let mailbox = MAILBOX.load(deps.storage)?; + let mailbox_addr = MAILBOX.load(deps.storage)?; // make digest let message_hash = eth_hash(announcement_hash( - domain_hash(local_domain, mailbox.as_str())?.to_vec(), + domain_hash(local_domain, mailbox_addr.into())?.to_vec(), &storage_location, ))?; // recover pubkey from signature & verify - let recovered = deps.api.secp256k1_recover_pubkey( + let pubkey = deps.api.secp256k1_recover_pubkey( &message_hash, &signature.as_slice()[..64], // We subs 27 according to this - https://eips.ethereum.org/EIPS/eip-155 signature[64] - 27, )?; - let pubkey = EncodedPoint::from_bytes(recovered).expect("failed to parse recovered pubkey"); - let pubkey = VerifyingKey::from_encoded_point(&pubkey).expect("invalid recovered public key"); - let pubkey_bin = pubkey.to_encoded_point(true).as_bytes().to_vec(); - - let recovered_addr = bech32_encode(&hrp, &pub_to_addr(pubkey_bin.into())?)?; - ensure!(recovered_addr == validator, ContractError::VerifyFailed {}); + ensure_eq!( + eth_addr(pubkey.into())?, + validator, + ContractError::VerifyFailed {} + ); // save validator if not saved yet - if !VALIDATORS.has(deps.storage, validator.clone()) { - VALIDATORS.save(deps.storage, validator.clone(), &Empty {})?; + if !VALIDATORS.has(deps.storage, validator.to_vec()) { + VALIDATORS.save(deps.storage, validator.to_vec(), &Empty {})?; } // append storage_locations let mut storage_locations = STORAGE_LOCATIONS - .may_load(deps.storage, validator.clone())? + .may_load(deps.storage, validator.to_vec())? .unwrap_or_default(); storage_locations.push(storage_location.clone()); - STORAGE_LOCATIONS.save(deps.storage, validator.clone(), &storage_locations)?; + STORAGE_LOCATIONS.save(deps.storage, validator.to_vec(), &storage_locations)?; Ok(Response::new().add_event( Event::new("validator-announcement") .add_attribute("sender", info.sender) - .add_attribute("validator", validator) + .add_attribute("validator", validator.to_string()) .add_attribute("storage-location", storage_location), )) } @@ -212,23 +198,26 @@ mod test { testing::{mock_dependencies, mock_env, mock_info}, ContractResult, QuerierResult, SystemResult, WasmQuery, }; - use ibcx_test_utils::{gen_addr, gen_bz}; + + use hpl_interface::build_test_querier; + use ibcx_test_utils::{gen_addr, gen_bz, hex}; use k256::{ ecdsa::{RecoveryId, Signature, SigningKey}, elliptic_curve::{rand_core::OsRng, sec1::ToEncodedPoint}, SecretKey, }; use rstest::rstest; - use serde::de::DeserializeOwned; use super::*; + build_test_querier!(crate::contract::query); + struct Announcement { - validator: String, + validator: HexBinary, mailbox: String, domain: u32, location: String, - signature: String, + signature: HexBinary, } impl Announcement { @@ -240,37 +229,27 @@ mod test { signature: &str, ) -> Self { Self { - validator: validator.into(), + validator: hex(validator), mailbox: mailbox.into(), domain, location: location.into(), - signature: signature.into(), + signature: hex(signature), } } - fn preset1() -> Self { + fn preset() -> Self { Self::new( - "f9e25a6be80f6d48727e42381fc3c3b7834c0cb4", - "62634b0c56b57fef1c27f25039cfb872875a9eeeb42d80a034f8d6b55ed20d09", - 26658, - "file:///var/folders/3v/g38z040x54x8l6b160vv66b40000gn/T/.tmp7XoxND/checkpoint", - "6c30e1072f0e23694d3a3a96dc41fc4d17636ce145e83adef3224a6f4732c2db715407b42478c581b6ac1b79e64807a7748935d398a33bf4b73d37924c293c941b", - ) - } - - fn preset2() -> Self { - Self::new( - "f9e25a6be80f6d48727e42381fc3c3b7834c0cb4", - "62634b0c56b57fef1c27f25039cfb872875a9eeeb42d80a034f8d6b55ed20d09", - 26657, - "file:///var/folders/3v/g38z040x54x8l6b160vv66b40000gn/T/.tmpBJPK8C/checkpoint", - "76c637d605f683734c672c0437f14ae48520e85fb68b0c0b9c28069f183e3bfc46f0de0655f06937c74b5a0a15f5b8fe37f1d1ad4dd8b64dc55307a2103fedad1c", + "05a9b5efe9f61f9142453d8e9f61565f333c6768", + "00000000000000000000000049cfd6ef774acab14814d699e3f7ee36fdfba932", + 5, + "s3://hyperlane-testnet4-goerli-validator-0/us-east-1", + "dc47d48744fdb42b983f0244ed397feac08ee556eb48416582b5b638ada7b5322c8822e56a9020de7fe663ad43070f04b341514faf430ebf880bb1932434027d1c", ) } fn rand() -> Self { // prepare test data - let mailbox = gen_bz(20); + let mailbox = gen_bz(32); let local_domain = 26657; let storage_location = "file://foo/bar"; @@ -279,18 +258,12 @@ mod test { let pubkey = secret_key.public_key(); let signing_key = SigningKey::from(secret_key); - let pubkey_bin = pubkey.to_encoded_point(true).as_bytes().to_vec(); - - let addr_bin = pub_to_addr(pubkey_bin.into()).unwrap(); + let pubkey_bin = pubkey.to_encoded_point(false).as_bytes().to_vec(); + let addr_bin = eth_addr(pubkey_bin.into()).unwrap(); // make announcement data let verify_digest = eth_hash(announcement_hash( - domain_hash( - local_domain, - bech32_encode("asdf", mailbox.as_slice()).unwrap().as_str(), - ) - .unwrap() - .to_vec(), + domain_hash(local_domain, mailbox.clone()).unwrap().to_vec(), storage_location, )) .unwrap(); @@ -301,11 +274,11 @@ mod test { ); Self { - validator: addr_bin.to_hex(), + validator: addr_bin, mailbox: mailbox.to_hex(), domain: local_domain, location: storage_location.to_string(), - signature: signature.to_hex(), + signature, } } @@ -324,11 +297,6 @@ mod test { bz.into() } - fn query(deps: Deps, msg: QueryMsg) -> T { - let res = super::query(deps, mock_env(), msg).unwrap(); - cosmwasm_std::from_binary(&res).unwrap() - } - #[rstest] fn test_init(#[values("osmo", "neutron")] hrp: &str) { let sender = gen_addr(hrp); @@ -355,26 +323,16 @@ mod test { }, ) .unwrap(); - - assert_eq!(HRP.load(deps.as_ref().storage).unwrap(), hrp); } #[rstest] - fn test_query( - #[values("osmo", "neutron")] hrp: &str, - #[values(0, 4)] validators_len: usize, - #[values(0, 4)] locations_len: usize, - ) { + fn test_queries(#[values(0, 4)] validators_len: usize, #[values(0, 4)] locations_len: usize) { let mut deps = mock_dependencies(); - HRP.save(deps.as_mut().storage, &hrp.to_string()).unwrap(); - - let validators = (0..validators_len) - .map(|_| gen_addr(hrp)) - .collect::>(); + let validators = (0..validators_len).map(|_| gen_bz(20)).collect::>(); for validator in validators { VALIDATORS - .save(deps.as_mut().storage, validator.clone(), &Empty {}) + .save(deps.as_mut().storage, validator.to_vec(), &Empty {}) .unwrap(); let locations = (0..locations_len) @@ -382,21 +340,18 @@ mod test { .collect::>(); STORAGE_LOCATIONS - .save(deps.as_mut().storage, validator, &locations) + .save(deps.as_mut().storage, validator.to_vec(), &locations) .unwrap(); } let GetAnnouncedValidatorsResponse { validators } = - query(deps.as_ref(), QueryMsg::GetAnnouncedValidators {}); + test_query(deps.as_ref(), QueryMsg::GetAnnouncedValidators {}); assert_eq!(validators.len(), validators_len); - let GetAnnounceStorageLocationsResponse { storage_locations } = query( + let GetAnnounceStorageLocationsResponse { storage_locations } = test_query( deps.as_ref(), QueryMsg::GetAnnounceStorageLocations { - validators: validators - .iter() - .map(|v| HexBinary::from(bech32_decode(v).unwrap())) - .collect::>(), + validators: validators.iter().map(|v| hex(v)).collect(), }, ); for (validator, locations) in storage_locations { @@ -407,32 +362,25 @@ mod test { #[rstest] #[case::rand(Announcement::rand(), false)] - #[case::actual_data_1(Announcement::preset1(), false)] - #[case::actual_data_2(Announcement::preset2(), false)] + #[case::actual_data_1(Announcement::preset(), false)] #[should_panic(expected = "unauthorized")] #[case::replay(Announcement::rand(), true)] #[should_panic(expected = "verify failed")] #[case::verify(Announcement::fail(), false)] - fn test_announce( - #[values("osmo", "neutron")] hrp: &str, - #[case] announcement: Announcement, - #[case] enable_duplication: bool, - ) { - let validator = HexBinary::from_hex(&announcement.validator).unwrap(); - let validator_addr = bech32_encode(hrp, validator.as_slice()).unwrap(); - + fn test_announce(#[case] announcement: Announcement, #[case] enable_duplication: bool) { + let validator = announcement.validator; let mailbox = HexBinary::from_hex(&announcement.mailbox).unwrap(); - let mailbox_addr = bech32_encode(hrp, mailbox.as_slice()).unwrap(); let mut deps = mock_dependencies(); - HRP.save(deps.as_mut().storage, &hrp.to_string()).unwrap(); LOCAL_DOMAIN .save(deps.as_mut().storage, &announcement.domain) .unwrap(); - MAILBOX.save(deps.as_mut().storage, &mailbox_addr).unwrap(); + MAILBOX + .save(deps.as_mut().storage, &mailbox.to_vec()) + .unwrap(); - let replay_id = replay_hash(&validator_addr, &announcement.location).unwrap(); + let replay_id = replay_hash(&validator, &announcement.location).unwrap(); if enable_duplication { REPLAY_PROTECITONS .save(deps.as_mut().storage, replay_id.to_vec(), &Empty {}) @@ -441,20 +389,20 @@ mod test { announce( deps.as_mut(), - mock_info(validator_addr.as_str(), &[]), - validator, + mock_info("someone", &[]), + validator.clone(), announcement.location.clone(), - HexBinary::from_hex(&announcement.signature).unwrap(), + announcement.signature, ) .map_err(|e| e.to_string()) .unwrap(); // check state assert!(REPLAY_PROTECITONS.has(deps.as_ref().storage, replay_id.to_vec())); - assert!(VALIDATORS.has(deps.as_ref().storage, validator_addr.clone())); + assert!(VALIDATORS.has(deps.as_ref().storage, validator.to_vec())); assert_eq!( STORAGE_LOCATIONS - .load(deps.as_ref().storage, validator_addr) + .load(deps.as_ref().storage, validator.to_vec()) .unwrap(), vec![announcement.location] ); diff --git a/contracts/core/va/src/state.rs b/contracts/core/va/src/state.rs index a4d58a46..103fc8c7 100644 --- a/contracts/core/va/src/state.rs +++ b/contracts/core/va/src/state.rs @@ -1,20 +1,17 @@ -use cosmwasm_std::{Addr, Empty}; +use cosmwasm_std::Empty; use cw_storage_plus::{Item, Map}; -pub const HRP_KEY: &str = "hrp"; -pub const HRP: Item = Item::new(HRP_KEY); - pub const MAILBOX_KEY: &str = "mailbox"; -pub const MAILBOX: Item = Item::new(MAILBOX_KEY); +pub const MAILBOX: Item> = Item::new(MAILBOX_KEY); pub const LOCAL_DOMAIN_KEY: &str = "local_domain"; pub const LOCAL_DOMAIN: Item = Item::new(LOCAL_DOMAIN_KEY); pub const VALIDATORS_PREFIX: &str = "validators"; -pub const VALIDATORS: Map = Map::new(VALIDATORS_PREFIX); +pub const VALIDATORS: Map, Empty> = Map::new(VALIDATORS_PREFIX); pub const STORAGE_LOCATIONS_PREFIX: &str = "storage_locations"; -pub const STORAGE_LOCATIONS: Map> = Map::new(STORAGE_LOCATIONS_PREFIX); +pub const STORAGE_LOCATIONS: Map, Vec> = Map::new(STORAGE_LOCATIONS_PREFIX); pub const REPLAY_PROTECTIONS_PREFIX: &str = "replay_protections"; pub const REPLAY_PROTECITONS: Map, Empty> = Map::new(REPLAY_PROTECTIONS_PREFIX); diff --git a/contracts/isms/aggregate/src/lib.rs b/contracts/isms/aggregate/src/lib.rs index 3d8b32d6..d77a841c 100644 --- a/contracts/isms/aggregate/src/lib.rs +++ b/contracts/isms/aggregate/src/lib.rs @@ -15,7 +15,7 @@ use hpl_interface::{ IsmQueryMsg, IsmType, ModuleTypeResponse, VerifyInfoResponse, VerifyResponse, }, to_binary, - types::AggregateMetadata, + types::{bech32_decode, AggregateMetadata}, }; use hpl_ownable::get_owner; @@ -143,7 +143,7 @@ fn verify_info(deps: Deps, _message: HexBinary) -> Result>()?, }) } diff --git a/contracts/isms/multisig/Cargo.toml b/contracts/isms/multisig/Cargo.toml index c9880308..2c68489d 100644 --- a/contracts/isms/multisig/Cargo.toml +++ b/contracts/isms/multisig/Cargo.toml @@ -38,5 +38,7 @@ hpl-ownable.workspace = true hpl-interface.workspace = true [dev-dependencies] +rstest.workspace = true ibcx-test-utils.workspace = true cw-multi-test.workspace = true +k256.workspace = true diff --git a/contracts/isms/multisig/src/contract.rs b/contracts/isms/multisig/src/contract.rs index c56f600d..4b70daed 100644 --- a/contracts/isms/multisig/src/contract.rs +++ b/contracts/isms/multisig/src/contract.rs @@ -15,7 +15,7 @@ use hpl_interface::{ use crate::{ error::ContractError, execute, - state::{HRP, THRESHOLD, VALIDATORS}, + state::{THRESHOLD, VALIDATORS}, CONTRACT_NAME, CONTRACT_VERSION, }; @@ -32,8 +32,6 @@ pub fn instantiate( hpl_ownable::initialize(deps.storage, &owner)?; - HRP.save(deps.storage, &msg.hrp)?; - Ok(Response::new().add_attribute("method", "instantiate")) } @@ -84,11 +82,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result(EnrolledValidatorsResponse { - validators: validators - .0 - .into_iter() - .map(|v| v.signer.to_string()) - .collect::>(), + validators, threshold, }) }), diff --git a/contracts/isms/multisig/src/error.rs b/contracts/isms/multisig/src/error.rs index 83b12765..dd9bac18 100644 --- a/contracts/isms/multisig/src/error.rs +++ b/contracts/isms/multisig/src/error.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{StdError, VerificationError}; +use cosmwasm_std::{RecoverPubkeyError, StdError, VerificationError}; use thiserror::Error; #[derive(Error, Debug)] @@ -9,27 +9,21 @@ pub enum ContractError { #[error("{0}")] VerificationError(#[from] VerificationError), - #[error("Unauthorized")] + #[error("{0}")] + RecoverPubkeyError(#[from] RecoverPubkeyError), + + #[error("unauthorized")] Unauthorized, - #[error("Wrong length")] + #[error("wrong length")] WrongLength, - #[error("Invalid pubkey")] + #[error("invalid pubkey")] InvalidPubKey, - #[error("Ownership transfer not started")] - OwnershipTransferNotStarted, - - #[error("Ownership transfer already started")] - OwnershipTransferAlreadyStarted, - - #[error("Validator pubkey mismatched")] - ValidatorPubKeyMismatched, - - #[error("Duplicate Validator")] + #[error("duplicate validator")] ValidatorDuplicate, - #[error("Validator not exists")] + #[error("validator not exists")] ValidatorNotExist, } diff --git a/contracts/isms/multisig/src/execute.rs b/contracts/isms/multisig/src/execute.rs new file mode 100644 index 00000000..15918dfa --- /dev/null +++ b/contracts/isms/multisig/src/execute.rs @@ -0,0 +1,307 @@ +use cosmwasm_std::{ensure_eq, DepsMut, Event, HexBinary, MessageInfo, Response, StdResult}; +use hpl_interface::ism::multisig::{ThresholdSet, ValidatorSet as MsgValidatorSet}; +use hpl_ownable::get_owner; + +use crate::{ + event::{emit_enroll_validator, emit_set_threshold, emit_unenroll_validator}, + state::{THRESHOLD, VALIDATORS}, + ContractError, +}; + +pub fn set_threshold( + deps: DepsMut, + info: MessageInfo, + threshold: ThresholdSet, +) -> Result { + ensure_eq!( + get_owner(deps.storage)?, + info.sender, + ContractError::Unauthorized + ); + THRESHOLD.save(deps.storage, threshold.domain, &threshold.threshold)?; + + Ok(Response::new().add_event(emit_set_threshold(threshold.domain, threshold.threshold))) +} + +pub fn set_thresholds( + deps: DepsMut, + info: MessageInfo, + thresholds: Vec, +) -> Result { + ensure_eq!( + get_owner(deps.storage)?, + info.sender, + ContractError::Unauthorized + ); + + let events: Vec = thresholds + .into_iter() + .map(|v| { + THRESHOLD.save(deps.storage, v.domain, &v.threshold)?; + Ok(emit_set_threshold(v.domain, v.threshold)) + }) + .collect::>()?; + + Ok(Response::new().add_events(events)) +} + +pub fn enroll_validator( + deps: DepsMut, + info: MessageInfo, + msg: MsgValidatorSet, +) -> Result { + ensure_eq!( + info.sender, + get_owner(deps.storage)?, + ContractError::Unauthorized {} + ); + + let validator_state = VALIDATORS.may_load(deps.storage, msg.domain)?; + + if let Some(mut validators) = validator_state { + if validators.contains(&msg.validator) { + return Err(ContractError::ValidatorDuplicate {}); + } + + validators.push(msg.validator.clone()); + validators.sort(); + + VALIDATORS.save(deps.storage, msg.domain, &validators)?; + } else { + VALIDATORS.save(deps.storage, msg.domain, &vec![msg.validator.clone()])?; + } + + Ok(Response::new().add_event(emit_enroll_validator(msg.domain, msg.validator.to_hex()))) +} + +pub fn enroll_validators( + deps: DepsMut, + info: MessageInfo, + validators: Vec, +) -> Result { + ensure_eq!( + info.sender, + get_owner(deps.storage)?, + ContractError::Unauthorized {} + ); + + let mut events: Vec = Vec::new(); + + for msg in validators.into_iter() { + let validators_state = VALIDATORS.may_load(deps.storage, msg.domain)?; + + if let Some(mut validators) = validators_state { + if validators.contains(&msg.validator) { + return Err(ContractError::ValidatorDuplicate {}); + } + + validators.push(msg.validator.clone()); + validators.sort(); + + VALIDATORS.save(deps.storage, msg.domain, &validators)?; + events.push(emit_enroll_validator(msg.domain, msg.validator.to_hex())); + } else { + VALIDATORS.save(deps.storage, msg.domain, &vec![msg.validator.clone()])?; + events.push(emit_enroll_validator(msg.domain, msg.validator.to_hex())); + } + } + + Ok(Response::new().add_events(events)) +} + +pub fn unenroll_validator( + deps: DepsMut, + info: MessageInfo, + domain: u32, + validator: HexBinary, +) -> Result { + ensure_eq!( + info.sender, + get_owner(deps.storage)?, + ContractError::Unauthorized {} + ); + + let validators = VALIDATORS + .load(deps.storage, domain) + .map_err(|_| ContractError::ValidatorNotExist {})?; + + if !validators.contains(&validator) { + return Err(ContractError::ValidatorNotExist {}); + } + + let mut validator_list: Vec = + validators.into_iter().filter(|v| v != &validator).collect(); + + validator_list.sort(); + + VALIDATORS.save(deps.storage, domain, &validator_list)?; + + Ok(Response::new().add_event(emit_unenroll_validator(domain, validator.to_hex()))) +} + +#[cfg(test)] +mod test { + use cosmwasm_std::{ + testing::{mock_dependencies, mock_info}, + Addr, HexBinary, Storage, + }; + use hpl_interface::{ + build_test_executor, build_test_querier, + ism::multisig::{ExecuteMsg, ValidatorSet}, + }; + use ibcx_test_utils::{addr, hex}; + use rstest::rstest; + + use crate::state::VALIDATORS; + + build_test_executor!(crate::contract::execute); + build_test_querier!(crate::contract::query); + + use super::*; + const ADDR1_VAULE: &str = "addr1"; + const ADDR2_VAULE: &str = "addr2"; + + fn mock_owner(storage: &mut dyn Storage, owner: Addr) { + hpl_ownable::initialize(storage, &owner).unwrap(); + } + + #[test] + fn test_set_threshold() { + let mut deps = mock_dependencies(); + let owner = Addr::unchecked(ADDR1_VAULE); + mock_owner(deps.as_mut().storage, owner.clone()); + + let threshold = ThresholdSet { + domain: 1u32, + threshold: 8u8, + }; + + // set_threshold failure test + let info = mock_info(ADDR2_VAULE, &[]); + let fail_result = set_threshold(deps.as_mut(), info, threshold.clone()).unwrap_err(); + + assert!(matches!(fail_result, ContractError::Unauthorized {})); + + // set_threshold success test + let info = mock_info(owner.as_str(), &[]); + let result = set_threshold(deps.as_mut(), info, threshold.clone()).unwrap(); + + assert_eq!( + result.events, + vec![emit_set_threshold(threshold.domain, threshold.threshold)] + ); + + // check it actually saved + let saved_threshold = THRESHOLD.load(&deps.storage, threshold.domain).unwrap(); + assert_eq!(saved_threshold, threshold.threshold); + } + + #[test] + fn test_set_thresholds() { + let mut deps = mock_dependencies(); + let owner = Addr::unchecked(ADDR1_VAULE); + mock_owner(deps.as_mut().storage, owner.clone()); + + let thresholds: Vec = vec![ + ThresholdSet { + domain: 1u32, + threshold: 8u8, + }, + ThresholdSet { + domain: 2u32, + threshold: 7u8, + }, + ThresholdSet { + domain: 3u32, + threshold: 6u8, + }, + ]; + + // set_threshold failure test + let info = mock_info(ADDR2_VAULE, &[]); + let fail_result = set_thresholds(deps.as_mut(), info, thresholds.clone()).unwrap_err(); + + assert!(matches!(fail_result, ContractError::Unauthorized {})); + + // set_threshold success test + let info = mock_info(owner.as_str(), &[]); + let result = set_thresholds(deps.as_mut(), info, thresholds.clone()).unwrap(); + + assert_eq!( + result.events, + vec![ + emit_set_threshold(1u32, 8u8), + emit_set_threshold(2u32, 7u8), + emit_set_threshold(3u32, 6u8), + ] + ); + + // check it actually saved + for threshold in thresholds { + let saved_threshold = THRESHOLD.load(&deps.storage, threshold.domain).unwrap(); + assert_eq!(saved_threshold, threshold.threshold); + } + } + + #[rstest] + #[case("owner", vec![hex("deadbeef")])] + #[should_panic(expected = "unauthorized")] + #[case("someone", vec![hex("deadbeef")])] + #[should_panic(expected = "duplicate validator")] + #[case("owner", vec![hex("deadbeef"),hex("deadbeef")])] + fn test_enroll(#[case] sender: &str, #[case] validators: Vec) { + let mut deps = mock_dependencies(); + + hpl_ownable::initialize(deps.as_mut().storage, &addr("owner")).unwrap(); + + for validator in validators.clone() { + test_execute( + deps.as_mut(), + &addr(sender), + ExecuteMsg::EnrollValidator { + set: ValidatorSet { + domain: 1, + validator, + }, + }, + vec![], + ); + } + + assert_eq!( + VALIDATORS.load(deps.as_ref().storage, 1).unwrap(), + validators + ); + } + + #[rstest] + #[case("owner", hex("deadbeef"))] + #[should_panic(expected = "unauthorized")] + #[case("someone", hex("deadbeef"))] + #[should_panic(expected = "validator not exist")] + #[case("owner", hex("debeefed"))] + fn test_unenroll(#[case] sender: &str, #[case] target: HexBinary) { + let mut deps = mock_dependencies(); + + hpl_ownable::initialize(deps.as_mut().storage, &addr("owner")).unwrap(); + + VALIDATORS + .save(deps.as_mut().storage, 1, &vec![hex("deadbeef")]) + .unwrap(); + + test_execute( + deps.as_mut(), + &addr(sender), + ExecuteMsg::UnenrollValidator { + domain: 1, + validator: target, + }, + vec![], + ); + + assert!(VALIDATORS + .load(deps.as_ref().storage, 1) + .unwrap() + .is_empty()); + } +} diff --git a/contracts/isms/multisig/src/execute/mod.rs b/contracts/isms/multisig/src/execute/mod.rs deleted file mode 100644 index 0f99ddcc..00000000 --- a/contracts/isms/multisig/src/execute/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod threshold; -mod validator; - -pub use threshold::{set_threshold, set_thresholds}; -pub use validator::{enroll_validator, enroll_validators, unenroll_validator}; diff --git a/contracts/isms/multisig/src/execute/threshold.rs b/contracts/isms/multisig/src/execute/threshold.rs deleted file mode 100644 index 654d07c6..00000000 --- a/contracts/isms/multisig/src/execute/threshold.rs +++ /dev/null @@ -1,136 +0,0 @@ -use cosmwasm_std::{ensure_eq, DepsMut, Event, MessageInfo, Response, StdResult}; -use hpl_interface::ism::multisig::ThresholdSet; -use hpl_ownable::get_owner; - -use crate::{event::emit_set_threshold, state::THRESHOLD, ContractError}; - -pub fn set_threshold( - deps: DepsMut, - info: MessageInfo, - threshold: ThresholdSet, -) -> Result { - ensure_eq!( - get_owner(deps.storage)?, - info.sender, - ContractError::Unauthorized - ); - THRESHOLD.save(deps.storage, threshold.domain, &threshold.threshold)?; - - Ok(Response::new().add_event(emit_set_threshold(threshold.domain, threshold.threshold))) -} - -pub fn set_thresholds( - deps: DepsMut, - info: MessageInfo, - thresholds: Vec, -) -> Result { - ensure_eq!( - get_owner(deps.storage)?, - info.sender, - ContractError::Unauthorized - ); - - let events: Vec = thresholds - .into_iter() - .map(|v| { - THRESHOLD.save(deps.storage, v.domain, &v.threshold)?; - Ok(emit_set_threshold(v.domain, v.threshold)) - }) - .collect::>()?; - - Ok(Response::new().add_events(events)) -} - -#[cfg(test)] -mod test { - use cosmwasm_std::{ - testing::{mock_dependencies, mock_info}, - Addr, Storage, - }; - - use super::*; - const ADDR1_VAULE: &str = "addr1"; - const ADDR2_VAULE: &str = "addr2"; - - fn mock_owner(storage: &mut dyn Storage, owner: Addr) { - hpl_ownable::initialize(storage, &owner).unwrap(); - } - - #[test] - fn test_set_threshold() { - let mut deps = mock_dependencies(); - let owner = Addr::unchecked(ADDR1_VAULE); - mock_owner(deps.as_mut().storage, owner.clone()); - - let threshold = ThresholdSet { - domain: 1u32, - threshold: 8u8, - }; - - // set_threshold failure test - let info = mock_info(ADDR2_VAULE, &[]); - let fail_result = set_threshold(deps.as_mut(), info, threshold.clone()).unwrap_err(); - - assert!(matches!(fail_result, ContractError::Unauthorized {})); - - // set_threshold success test - let info = mock_info(owner.as_str(), &[]); - let result = set_threshold(deps.as_mut(), info, threshold.clone()).unwrap(); - - assert_eq!( - result.events, - vec![emit_set_threshold(threshold.domain, threshold.threshold)] - ); - - // check it actually saved - let saved_threshold = THRESHOLD.load(&deps.storage, threshold.domain).unwrap(); - assert_eq!(saved_threshold, threshold.threshold); - } - - #[test] - fn test_set_thresholds() { - let mut deps = mock_dependencies(); - let owner = Addr::unchecked(ADDR1_VAULE); - mock_owner(deps.as_mut().storage, owner.clone()); - - let thresholds: Vec = vec![ - ThresholdSet { - domain: 1u32, - threshold: 8u8, - }, - ThresholdSet { - domain: 2u32, - threshold: 7u8, - }, - ThresholdSet { - domain: 3u32, - threshold: 6u8, - }, - ]; - - // set_threshold failure test - let info = mock_info(ADDR2_VAULE, &[]); - let fail_result = set_thresholds(deps.as_mut(), info, thresholds.clone()).unwrap_err(); - - assert!(matches!(fail_result, ContractError::Unauthorized {})); - - // set_threshold success test - let info = mock_info(owner.as_str(), &[]); - let result = set_thresholds(deps.as_mut(), info, thresholds.clone()).unwrap(); - - assert_eq!( - result.events, - vec![ - emit_set_threshold(1u32, 8u8), - emit_set_threshold(2u32, 7u8), - emit_set_threshold(3u32, 6u8), - ] - ); - - // check it actually saved - for threshold in thresholds { - let saved_threshold = THRESHOLD.load(&deps.storage, threshold.domain).unwrap(); - assert_eq!(saved_threshold, threshold.threshold); - } - } -} diff --git a/contracts/isms/multisig/src/execute/validator.rs b/contracts/isms/multisig/src/execute/validator.rs deleted file mode 100644 index 10876df0..00000000 --- a/contracts/isms/multisig/src/execute/validator.rs +++ /dev/null @@ -1,456 +0,0 @@ -use cosmwasm_std::{ensure_eq, DepsMut, Event, HexBinary, MessageInfo, Response}; -use hpl_interface::ism::multisig::ValidatorSet as MsgValidatorSet; -use hpl_ownable::get_owner; - -use crate::{ - event::{emit_enroll_validator, emit_unenroll_validator}, - state::{ValidatorSet, Validators, HRP, VALIDATORS}, - verify::{self}, - ContractError, -}; - -fn assert_pubkey_validate( - validator: String, - pubkey: HexBinary, - hrp: &str, -) -> Result<(), ContractError> { - let pub_to_addr = verify::pub_to_addr(pubkey, hrp)?; - - if validator != pub_to_addr { - return Err(ContractError::ValidatorPubKeyMismatched {}); - } - - Ok(()) -} - -pub fn enroll_validator( - deps: DepsMut, - info: MessageInfo, - msg: MsgValidatorSet, -) -> Result { - ensure_eq!( - info.sender, - get_owner(deps.storage)?, - ContractError::Unauthorized {} - ); - - assert_pubkey_validate( - msg.validator.clone(), - msg.validator_pubkey.clone(), - HRP.load(deps.storage)?.as_str(), - )?; - - let candidate = deps.api.addr_validate(&msg.validator)?; - let validator_state = VALIDATORS.may_load(deps.storage, msg.domain)?; - - if let Some(mut validators) = validator_state { - if validators.0.iter().any(|v| v.signer == candidate) { - return Err(ContractError::ValidatorDuplicate {}); - } - - validators.0.push(ValidatorSet { - signer: candidate, - signer_pubkey: msg.validator_pubkey, - }); - validators.0.sort_by(|a, b| a.signer.cmp(&b.signer)); - - VALIDATORS.save(deps.storage, msg.domain, &validators)?; - } else { - let validators = Validators(vec![ValidatorSet { - signer: candidate, - signer_pubkey: msg.validator_pubkey, - }]); - - VALIDATORS.save(deps.storage, msg.domain, &validators)?; - } - - Ok(Response::new().add_event(emit_enroll_validator(msg.domain, msg.validator))) -} - -pub fn enroll_validators( - deps: DepsMut, - info: MessageInfo, - validators: Vec, -) -> Result { - ensure_eq!( - info.sender, - get_owner(deps.storage)?, - ContractError::Unauthorized {} - ); - - let hrp = HRP.load(deps.storage)?; - let mut events: Vec = Vec::new(); - - for msg in validators.into_iter() { - assert_pubkey_validate(msg.validator.clone(), msg.validator_pubkey.clone(), &hrp)?; - - let candidate = deps.api.addr_validate(&msg.validator)?; - let validators_state = VALIDATORS.may_load(deps.storage, msg.domain)?; - - if let Some(mut validators) = validators_state { - if validators.0.iter().any(|v| v.signer == candidate) { - return Err(ContractError::ValidatorDuplicate {}); - } - - validators.0.push(ValidatorSet { - signer: candidate, - signer_pubkey: msg.validator_pubkey, - }); - validators.0.sort_by(|a, b| a.signer.cmp(&b.signer)); - - VALIDATORS.save(deps.storage, msg.domain, &validators)?; - events.push(emit_enroll_validator(msg.domain, msg.validator)); - } else { - let validators = Validators(vec![ValidatorSet { - signer: candidate, - signer_pubkey: msg.validator_pubkey, - }]); - - VALIDATORS.save(deps.storage, msg.domain, &validators)?; - events.push(emit_enroll_validator(msg.domain, msg.validator)); - } - } - - Ok(Response::new().add_events(events)) -} - -pub fn unenroll_validator( - deps: DepsMut, - info: MessageInfo, - domain: u32, - validator: String, -) -> Result { - ensure_eq!( - info.sender, - get_owner(deps.storage)?, - ContractError::Unauthorized {} - ); - - let unenroll_target = deps.api.addr_validate(&validator)?; - let validators = VALIDATORS - .load(deps.storage, domain) - .map_err(|_| ContractError::ValidatorNotExist {})?; - - if !validators.0.iter().any(|v| v.signer == validator) { - return Err(ContractError::ValidatorNotExist {}); - } - - let mut validator_list: Vec = validators - .0 - .into_iter() - .filter(|v| v.signer != unenroll_target) - .collect(); - - validator_list.sort_by(|a, b| a.signer.cmp(&b.signer)); - - VALIDATORS.save(deps.storage, domain, &Validators(validator_list))?; - Ok(Response::new().add_event(emit_unenroll_validator(domain, validator))) -} - -#[cfg(test)] -mod test { - use cosmwasm_std::{ - testing::{mock_dependencies, mock_info}, - Addr, Storage, - }; - - use super::*; - const ADDR1_VAULE: &str = "addr1"; - const ADDR2_VAULE: &str = "addr2"; - - const VAL_HRP: &str = "osmo"; - const VALIDATOR_ADDR: &str = "osmo1q28uzwtvvvlkz6k84gd7flu576x2l2ry9506p5"; - const VALIDATOR_PUBKEY: &str = - "033a59bbc4cb7f1e7110541e54be1ff8de6abb75fe16adaea242c52d0d7a384baf"; - - fn mock_owner(storage: &mut dyn Storage, owner: Addr) { - hpl_ownable::initialize(storage, &owner).unwrap(); - } - - #[test] - fn test_assert_pubkey_validate() { - let validator = String::from(VALIDATOR_ADDR); - let validator_pubkey = HexBinary::from_hex(VALIDATOR_PUBKEY).unwrap(); - let hrp = String::from("osmo"); - - // fail - let invalid_validator = - assert_pubkey_validate("test".to_string(), validator_pubkey.clone(), &hrp).unwrap_err(); - - assert!(matches!( - invalid_validator, - ContractError::ValidatorPubKeyMismatched {} - )); - - // success - assert_pubkey_validate(validator, validator_pubkey, &hrp).unwrap(); - } - - #[test] - fn test_enroll_validator_failure() { - let mut deps = mock_dependencies(); - let owner = Addr::unchecked(ADDR1_VAULE); - - mock_owner(deps.as_mut().storage, owner.clone()); - - HRP.save(deps.as_mut().storage, &VAL_HRP.into()).unwrap(); - - let msg = MsgValidatorSet { - domain: 1u32, - validator: "test".to_string(), - validator_pubkey: HexBinary::from_hex(VALIDATOR_PUBKEY).unwrap(), - }; - - // unauthorized - let info = mock_info(ADDR2_VAULE, &[]); - let unauthorize_resp = enroll_validator(deps.as_mut(), info, msg).unwrap_err(); - assert!(matches!(unauthorize_resp, ContractError::Unauthorized {})); - - // already exist pubkey - let valid_message = MsgValidatorSet { - domain: 1u32, - validator: VALIDATOR_ADDR.to_string(), - validator_pubkey: HexBinary::from_hex(VALIDATOR_PUBKEY).unwrap(), - }; - VALIDATORS - .save( - deps.as_mut().storage, - 1u32, - &Validators(vec![ValidatorSet { - signer: Addr::unchecked(valid_message.validator.clone()), - signer_pubkey: valid_message.validator_pubkey.clone(), - }]), - ) - .unwrap(); - - let info = mock_info(owner.as_str(), &[]); - let duplicate_pubkey = enroll_validator(deps.as_mut(), info, valid_message).unwrap_err(); - assert!(matches!( - duplicate_pubkey, - ContractError::ValidatorDuplicate {} - )) - } - - #[test] - fn test_enroll_validator_success() { - let mut deps = mock_dependencies(); - let owner = Addr::unchecked(ADDR1_VAULE); - let validator: String = VALIDATOR_ADDR.to_string(); - let domain: u32 = 1; - - HRP.save(deps.as_mut().storage, &VAL_HRP.into()).unwrap(); - - mock_owner(deps.as_mut().storage, owner.clone()); - let msg = MsgValidatorSet { - domain, - validator: validator.clone(), - validator_pubkey: HexBinary::from_hex(VALIDATOR_PUBKEY).unwrap(), - }; - - // validators not exist - let info = mock_info(ADDR1_VAULE, &[]); - let result = enroll_validator(deps.as_mut(), info, msg.clone()).unwrap(); - - assert_eq!( - result.events, - vec![emit_enroll_validator(1u32, validator.clone())] - ); - - // check it actually save - let saved_validators = VALIDATORS.load(&deps.storage, domain).unwrap(); - assert_eq!(validator, saved_validators.0[0].signer); - - // validator is exist already - VALIDATORS - .save( - deps.as_mut().storage, - 1u32, - &Validators(vec![ValidatorSet { - signer: Addr::unchecked(ADDR2_VAULE), - signer_pubkey: msg.validator_pubkey.clone(), - }]), - ) - .unwrap(); - - let info = mock_info(owner.as_str(), &[]); - let result = enroll_validator(deps.as_mut(), info, msg).unwrap(); - - assert_eq!( - result.events, - vec![emit_enroll_validator(1u32, validator.clone())] - ); - let saved_validators = VALIDATORS.load(&deps.storage, domain).unwrap(); - assert_eq!(validator, saved_validators.0.last().unwrap().signer); - } - - #[test] - fn test_enroll_validators_failure() { - let mut deps = mock_dependencies(); - let owner = Addr::unchecked(ADDR1_VAULE); - - mock_owner(deps.as_mut().storage, owner); - - HRP.save(deps.as_mut().storage, &VAL_HRP.into()).unwrap(); - - let msg = vec![ - MsgValidatorSet { - domain: 1u32, - validator: String::from(VALIDATOR_ADDR), - validator_pubkey: HexBinary::from_hex(VALIDATOR_PUBKEY).unwrap(), - }, - MsgValidatorSet { - domain: 1u32, - validator: String::from(VALIDATOR_ADDR), - validator_pubkey: HexBinary::from_hex(VALIDATOR_PUBKEY).unwrap(), - }, - ]; - - let info = mock_info(ADDR2_VAULE, &[]); - let unauthorized = enroll_validators(deps.as_mut(), info, msg.clone()).unwrap_err(); - assert!(matches!(unauthorized, ContractError::Unauthorized {})); - - let info = mock_info(ADDR1_VAULE, &[]); - let duplicated = enroll_validators(deps.as_mut(), info, msg).unwrap_err(); - assert!(matches!(duplicated, ContractError::ValidatorDuplicate {})); - } - - #[test] - fn test_enroll_validators_success() { - let mut deps = mock_dependencies(); - let owner = Addr::unchecked(ADDR1_VAULE); - let validator = String::from(VALIDATOR_ADDR); - let validator_pubkey = HexBinary::from_hex(VALIDATOR_PUBKEY).unwrap(); - mock_owner(deps.as_mut().storage, owner.clone()); - - HRP.save(deps.as_mut().storage, &VAL_HRP.into()).unwrap(); - - let msg = vec![ - MsgValidatorSet { - domain: 1u32, - validator: validator.clone(), - validator_pubkey: validator_pubkey.clone(), - }, - MsgValidatorSet { - domain: 2u32, - validator: validator.clone(), - validator_pubkey: validator_pubkey.clone(), - }, - ]; - - VALIDATORS - .save( - deps.as_mut().storage, - 2u32, - &Validators(vec![ValidatorSet { - signer: Addr::unchecked(ADDR2_VAULE), - signer_pubkey: validator_pubkey, - }]), - ) - .unwrap(); - - let info = mock_info(owner.as_str(), &[]); - let result = enroll_validators(deps.as_mut(), info, msg).unwrap(); - - assert_eq!( - result.events, - vec![ - emit_enroll_validator(1u32, validator.clone()), - emit_enroll_validator(2u32, validator.clone()) - ] - ); - - // check it actually saved - assert_eq!( - validator, - VALIDATORS - .load(&deps.storage, 1u32) - .unwrap() - .0 - .last() - .unwrap() - .signer - ); - assert_eq!( - validator, - VALIDATORS - .load(&deps.storage, 2u32) - .unwrap() - .0 - .last() - .unwrap() - .signer - ); - } - - #[test] - fn test_unenroll_validator_failure() { - let mut deps = mock_dependencies(); - let owner = Addr::unchecked(ADDR1_VAULE); - let validator = String::from(VALIDATOR_ADDR); - let domain: u32 = 1; - - mock_owner(deps.as_mut().storage, owner.clone()); - - // unauthorization - let info = mock_info(ADDR2_VAULE, &[]); - let unauthorized = - unenroll_validator(deps.as_mut(), info, domain, validator.clone()).unwrap_err(); - assert!(matches!(unauthorized, ContractError::Unauthorized {})); - - // not exists - let info = mock_info(owner.as_str(), &[]); - let not_exist_state = - unenroll_validator(deps.as_mut(), info.clone(), domain, validator.clone()).unwrap_err(); - assert!(matches!( - not_exist_state, - ContractError::ValidatorNotExist {} - )); - - // not exists in exist state - VALIDATORS - .save( - deps.as_mut().storage, - 1u32, - &Validators(vec![ValidatorSet { - signer: Addr::unchecked(ADDR2_VAULE), - signer_pubkey: HexBinary::from_hex(VALIDATOR_PUBKEY).unwrap(), - }]), - ) - .unwrap(); - let not_exist_state = - unenroll_validator(deps.as_mut(), info, domain, validator).unwrap_err(); - assert!(matches!( - not_exist_state, - ContractError::ValidatorNotExist {} - )); - } - - #[test] - fn test_unenroll_validator_success() { - let mut deps = mock_dependencies(); - let owner = Addr::unchecked(ADDR1_VAULE); - let validator = String::from(VALIDATOR_ADDR); - let domain: u32 = 1; - - mock_owner(deps.as_mut().storage, owner.clone()); - - let info = mock_info(owner.as_str(), &[]); - VALIDATORS - .save( - deps.as_mut().storage, - domain, - &Validators(vec![ValidatorSet { - signer: Addr::unchecked(validator.clone()), - signer_pubkey: HexBinary::from_hex(VALIDATOR_PUBKEY).unwrap(), - }]), - ) - .unwrap(); - let result = unenroll_validator(deps.as_mut(), info, domain, validator.clone()).unwrap(); - - assert_eq!( - result.events, - vec![emit_unenroll_validator(domain, validator)] - ); - assert_eq!(VALIDATORS.load(&deps.storage, domain).unwrap().0.len(), 0) - } -} diff --git a/contracts/isms/multisig/src/lib.rs b/contracts/isms/multisig/src/lib.rs index 3330d8f6..aba3b52f 100644 --- a/contracts/isms/multisig/src/lib.rs +++ b/contracts/isms/multisig/src/lib.rs @@ -4,7 +4,6 @@ pub mod event; pub mod execute; pub mod query; pub mod state; -mod verify; use cosmwasm_std::{HexBinary, StdResult}; use hpl_interface::types::keccak256_hash; @@ -15,8 +14,6 @@ pub use crate::error::ContractError; pub const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); -const PREFIX: &str = "\x19Ethereum Signed Message:\n"; - pub fn domain_hash(local_domain: u32, address: HexBinary) -> StdResult { let mut bz = vec![]; bz.append(&mut local_domain.to_be_bytes().to_vec()); @@ -28,14 +25,6 @@ pub fn domain_hash(local_domain: u32, address: HexBinary) -> StdResult Result { - let mut eth_message = format!("{PREFIX}{}", message.len()).into_bytes(); - eth_message.extend_from_slice(&message); - let message_hash = keccak256_hash(ð_message); - - Ok(message_hash) -} - pub fn multisig_hash( mut domain_hash: Vec, mut root: Vec, diff --git a/contracts/isms/multisig/src/query.rs b/contracts/isms/multisig/src/query.rs index 2f1529d8..ac848b91 100644 --- a/contracts/isms/multisig/src/query.rs +++ b/contracts/isms/multisig/src/query.rs @@ -1,21 +1,15 @@ use cosmwasm_std::{Deps, HexBinary}; use hpl_interface::{ ism::{IsmType, ModuleTypeResponse, VerifyInfoResponse, VerifyResponse}, - types::{Message, MessageIdMultisigIsmMetadata}, + types::{eth_addr, eth_hash, Message, MessageIdMultisigIsmMetadata}, }; use crate::{ - domain_hash, eth_hash, multisig_hash, + domain_hash, multisig_hash, state::{THRESHOLD, VALIDATORS}, ContractError, }; -fn product(x: Vec, y: Vec) -> Vec<(T, U)> { - x.iter() - .flat_map(|item_x| y.iter().map(move |item_y| (item_x.clone(), item_y.clone()))) - .collect() -} - pub fn get_module_type() -> Result { Ok(ModuleTypeResponse { typ: IsmType::MessageIdMultisig, @@ -35,16 +29,8 @@ pub fn verify_message( let metadata: MessageIdMultisigIsmMetadata = raw_metadata.into(); let message: Message = raw_message.into(); - let threshold = THRESHOLD.load(deps.storage, message.origin_domain)?; - let validators = VALIDATORS.load(deps.storage, message.origin_domain)?; - let merkle_index = metadata.merkle_index(); - let verifiable_cases = product( - validators.0.into_iter().map(|v| v.signer_pubkey).collect(), - metadata.signatures, - ); - let multisig_hash = multisig_hash( domain_hash(message.origin_domain, metadata.origin_merkle_tree)?.to_vec(), metadata.merkle_root.to_vec(), @@ -54,17 +40,28 @@ pub fn verify_message( let hashed_message = eth_hash(multisig_hash)?; - let success: u8 = verifiable_cases - .into_iter() - .map(|v| { - deps.api - .secp256k1_verify(&hashed_message, &v.1[0..64], &v.0) - .unwrap() as u8 - }) - .sum(); + // pizza :) + let validators = VALIDATORS.load(deps.storage, message.origin_domain)?; + let mut threshold = THRESHOLD.load(deps.storage, message.origin_domain)?; + + for signature in metadata.signatures { + let signature = signature.as_slice(); + let pubkey = deps.api.secp256k1_recover_pubkey( + &hashed_message, + &signature[..64], + signature[64] - 27, + )?; + + if validators.contains(ð_addr(pubkey.into())?) { + threshold -= 1; + if threshold == 0 { + break; + } + } + } Ok(VerifyResponse { - verified: success >= threshold, + verified: threshold == 0, }) } @@ -79,28 +76,23 @@ pub fn get_verify_info( Ok(VerifyInfoResponse { threshold, - validators: validators - .0 - .into_iter() - .map(|v| v.signer.to_string()) - .collect(), + validators, }) } #[cfg(test)] mod test { - use crate::{ - query::get_verify_info, - state::{ValidatorSet, Validators, THRESHOLD, VALIDATORS}, - }; - use cosmwasm_std::testing::mock_dependencies; + use crate::state::{THRESHOLD, VALIDATORS}; + use cosmwasm_std::{testing::mock_dependencies, HexBinary}; use hpl_interface::{ - ism::{IsmType, ModuleTypeResponse, VerifyInfoResponse, VerifyResponse}, - types::{bech32_encode, Message}, + ism::{IsmType, ModuleTypeResponse, VerifyResponse}, + types::{eth_addr, Message}, }; - use ibcx_test_utils::{addr, hex}; + use ibcx_test_utils::hex; + use k256::{ecdsa::SigningKey, elliptic_curve::rand_core::OsRng}; + use rstest::rstest; - use super::{get_module_type, verify_message}; + use super::{get_module_type, get_verify_info, verify_message}; #[test] fn test_get_module_type() { @@ -114,29 +106,35 @@ mod test { ); } - #[test] - fn test_verify_with_e2e_data() { - let raw_message = hex("0000000000000068220000000000000000000000000d1255b09d94659bb0888e0aa9fca60245ce402a0000682155208cd518cffaac1b5d8df216a9bd050c9a03f0d4f3ba88e5268ac4cd12ee2d68656c6c6f"); - let raw_metadata = hex("986a1625d44e4b3969b08a5876171b2b4fcdf61b3e5c70a86ad17b304f17740a9f45d99ea6bec61392a47684f4e5d1416ddbcb5fdef0f132c27d7034e9bbff1c00000000ba9911d78ec6d561413e3589f920388cbd7554fbddd8ce50739337250853ec3577a51fa40e727c05b50f15db13f5aad5857c89d432644be48d70325ea83fdb6c1c"); - - let signer = hex("f9e25a6be80f6d48727e42381fc3c3b7834c0cb4"); - let signer = bech32_encode("osmo", signer.as_slice()).unwrap(); - let signer_pubkey = - hex("039cdc58e622e25767cfa565802534b2f777107a3382f46a88323471bbd3d84c22"); - + #[rstest] + #[case( + hex("0000000000000068220000000000000000000000000d1255b09d94659bb0888e0aa9fca60245ce402a0000682155208cd518cffaac1b5d8df216a9bd050c9a03f0d4f3ba88e5268ac4cd12ee2d68656c6c6f"), + hex("986a1625d44e4b3969b08a5876171b2b4fcdf61b3e5c70a86ad17b304f17740a9f45d99ea6bec61392a47684f4e5d1416ddbcb5fdef0f132c27d7034e9bbff1c00000000ba9911d78ec6d561413e3589f920388cbd7554fbddd8ce50739337250853ec3577a51fa40e727c05b50f15db13f5aad5857c89d432644be48d70325ea83fdb6c1c"), + vec![ + hex("122e0663ccc190266427e7fc0ed6589b5d7d36db"), + hex("01d7525e91dfc3f594fd366aad70f956b398de9e"), + ] + )] + #[case( + hex("03000000240001388100000000000000000000000004980c17e2ce26578c82f81207e706e4505fae3b0000a8690000000000000000000000000b1c1b54f45e02552331d3106e71f5e0b573d5d448656c6c6f21"), + hex("0000000000000000000000009af85731edd41e2e50f81ef8a0a69d2fb836edf9a84430f822e0e9b5942faace72bd5b97f0b59a58a9b8281231d9e5c393b5859c00000024539feceace17782697e29e74151006dc7b47227cf48aba02926336cb5f7fa38b3d05e8293045f7b5811eda3ae8aa070116bb5fbf57c79e143a69e909df90cefa1b6e6ead7180e0415c36642ee4bc5454bc4f5ca250ca77a1a83562035544e0e898734d6541a20404e05fd53eb1c75b0bd21851c3bd8122cfa3550d7b6fb94d7cee1b"), + vec![ + hex("ebc301013b6cd2548e347c28d2dc43ec20c068f2"), + hex("315db9868fc8813b221b1694f8760ece39f45447"), + hex("17517c98358c5937c5d9ee47ce1f5b4c2b7fc9f5"), + ] + )] + fn test_verify_with_e2e_data( + #[case] raw_message: HexBinary, + #[case] raw_metadata: HexBinary, + #[case] validators: Vec, + ) { let mut deps = mock_dependencies(); let message: Message = raw_message.clone().into(); VALIDATORS - .save( - deps.as_mut().storage, - message.origin_domain, - &Validators(vec![ValidatorSet { - signer, - signer_pubkey, - }]), - ) + .save(deps.as_mut().storage, message.origin_domain, &validators) .unwrap(); THRESHOLD .save(deps.as_mut().storage, message.origin_domain, &1u8) @@ -148,60 +146,23 @@ mod test { #[test] fn test_get_verify_info() { + let raw_message = hex("0000000000000068220000000000000000000000000d1255b09d94659bb0888e0aa9fca60245ce402a0000682155208cd518cffaac1b5d8df216a9bd050c9a03f0d4f3ba88e5268ac4cd12ee2d68656c6c6f"); + let mut deps = mock_dependencies(); - let message = Message { - version: 0, - nonce: 8528, - origin_domain: 44787, - sender: hex("000000000000000000000000477d860f8f41bc69ddd32821f2bf2c2af0243f16"), - dest_domain: 11155111, - recipient: hex("0000000000000000000000005d56b8a669f50193b54319442c6eee5edd662381"), - body: hex("48656c6c6f21"), - }; + let signing_key = SigningKey::random(&mut OsRng); + let verifying_key = signing_key.verifying_key(); + + let addr = eth_addr(verifying_key.to_encoded_point(false).as_bytes().into()).unwrap(); VALIDATORS - .save( - deps.as_mut().storage, - message.origin_domain, - &Validators(vec![ - ValidatorSet { - signer: addr("osmo1pql3lj3kftaf5pn507y74xfxlew0tufs8tey2k"), - signer_pubkey: hex( - "02b539cc3dbc1a266ee87648a2764ff6d85ab95384459ef2e5d4787c88724f581c", - ), - }, - ValidatorSet { - signer: addr("osmo13t2lcawapgppddj9hf0qk5yrrcvrre5gkslkat"), - signer_pubkey: hex( - "02fee848be8b8ea38d0ebae9fb11a8cdbea93f20c4accf40fde4f87ff213d09821", - ), - }, - ValidatorSet { - signer: addr("osmo1wjfete3kxrhyzcuhdp3lc6g3a8r275dp80w9xd"), - signer_pubkey: hex( - "0278360f516ef0f90c175fce5de37cc721f122780efa956f17ee08bbfcbe5cb101", - ), - }, - ]), - ) + .save(deps.as_mut().storage, 26658, &vec![addr.clone()]) .unwrap(); + THRESHOLD.save(deps.as_mut().storage, 26658, &1u8).unwrap(); - THRESHOLD - .save(deps.as_mut().storage, message.origin_domain, &2u8) - .unwrap(); + let info = get_verify_info(deps.as_ref(), raw_message).unwrap(); - let success_result = get_verify_info(deps.as_ref(), message.into()).unwrap(); - assert_eq!( - success_result, - VerifyInfoResponse { - threshold: 2, - validators: vec![ - "osmo1pql3lj3kftaf5pn507y74xfxlew0tufs8tey2k".to_string(), - "osmo13t2lcawapgppddj9hf0qk5yrrcvrre5gkslkat".to_string(), - "osmo1wjfete3kxrhyzcuhdp3lc6g3a8r275dp80w9xd".to_string(), - ] - } - ); + assert_eq!(info.validators, vec![addr]); + assert_eq!(info.threshold, 1); } } diff --git a/contracts/isms/multisig/src/state.rs b/contracts/isms/multisig/src/state.rs index 41fc124d..25c8564b 100644 --- a/contracts/isms/multisig/src/state.rs +++ b/contracts/isms/multisig/src/state.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::cw_serde; use cosmwasm_std::{Addr, HexBinary}; -use cw_storage_plus::{Item, Map}; +use cw_storage_plus::Map; #[cw_serde] pub struct Config { @@ -8,20 +8,8 @@ pub struct Config { pub addr_prefix: String, } -#[cw_serde] -pub struct ValidatorSet { - pub signer: Addr, - pub signer_pubkey: HexBinary, -} - -#[cw_serde] -pub struct Validators(pub Vec); - -pub const HRP_KEY: &str = "hrp"; -pub const HRP: Item = Item::new(HRP_KEY); - pub const VALIDATORS_PREFIX: &str = "validators"; -pub const VALIDATORS: Map = Map::new(VALIDATORS_PREFIX); +pub const VALIDATORS: Map> = Map::new(VALIDATORS_PREFIX); pub const THRESHOLD_PREFIX: &str = "threshold"; pub const THRESHOLD: Map = Map::new(THRESHOLD_PREFIX); diff --git a/contracts/isms/multisig/src/verify.rs b/contracts/isms/multisig/src/verify.rs deleted file mode 100644 index 4ca11cbc..00000000 --- a/contracts/isms/multisig/src/verify.rs +++ /dev/null @@ -1,39 +0,0 @@ -use crate::error::ContractError; -use bech32::ToBase32; -use cosmwasm_std::HexBinary; -use ripemd::{Digest, Ripemd160}; -use sha2::Sha256; - -pub fn sha256_digest(bz: impl AsRef<[u8]>) -> Result<[u8; 32], ContractError> { - let mut hasher = Sha256::new(); - - hasher.update(bz); - - hasher - .finalize() - .as_slice() - .try_into() - .map_err(|_| ContractError::WrongLength {}) -} - -pub fn ripemd160_digest(bz: impl AsRef<[u8]>) -> Result<[u8; 20], ContractError> { - let mut hasher = Ripemd160::new(); - - hasher.update(bz); - - hasher - .finalize() - .as_slice() - .try_into() - .map_err(|_| ContractError::WrongLength {}) -} - -pub fn pub_to_addr(pub_key: HexBinary, prefix: &str) -> Result { - let sha_hash = sha256_digest(pub_key)?; - let rip_hash = ripemd160_digest(sha_hash)?; - - let addr = bech32::encode(prefix, rip_hash.to_base32(), bech32::Variant::Bech32) - .map_err(|_| ContractError::InvalidPubKey {})?; - - Ok(addr) -} diff --git a/contracts/mocks/mock-ism/src/contract.rs b/contracts/mocks/mock-ism/src/contract.rs index 824e6901..cf673bae 100644 --- a/contracts/mocks/mock-ism/src/contract.rs +++ b/contracts/mocks/mock-ism/src/contract.rs @@ -59,7 +59,7 @@ pub fn query(_deps: Deps, _env: Env, msg: ExpectedIsmQueryMsg) -> StdResult Ok(to_binary(&VerifyResponse { verified: true })?), VerifyInfo { .. } => Ok(to_binary(&VerifyInfoResponse { threshold: 1u8, - validators: vec!["".to_string()], + validators: vec![], })?), }, } diff --git a/integration-test/tests/contracts/cw/ism.rs b/integration-test/tests/contracts/cw/ism.rs index 6cc17a44..14d4a5ca 100644 --- a/integration-test/tests/contracts/cw/ism.rs +++ b/integration-test/tests/contracts/cw/ism.rs @@ -12,7 +12,6 @@ pub enum Ism { Routing(Vec<(u32, Self)>), Multisig { - hrp: String, validators: validator::TestValidators, }, @@ -30,11 +29,8 @@ impl Ism { Self::Routing(isms) } - pub fn multisig(hrp: &str, validators: validator::TestValidators) -> Self { - Self::Multisig { - hrp: hrp.to_string(), - validators, - } + pub fn multisig(validators: validator::TestValidators) -> Self { + Self::Multisig { validators } } } @@ -53,7 +49,6 @@ impl Ism { fn deploy_multisig<'a, R: Runner<'a>>( wasm: &Wasm<'a, R>, codes: &Codes, - hrp: String, set: validator::TestValidators, owner: &SigningAccount, deployer: &SigningAccount, @@ -62,7 +57,6 @@ impl Ism { .instantiate( codes.ism_multisig, &hpl_interface::ism::multisig::InstantiateMsg { - hrp: hrp.to_string(), owner: owner.address(), }, None, @@ -75,9 +69,7 @@ impl Ism { wasm.execute( &multisig_ism, - &hpl_interface::ism::multisig::ExecuteMsg::EnrollValidators { - set: set.to_set(&hrp), - }, + &hpl_interface::ism::multisig::ExecuteMsg::EnrollValidators { set: set.to_set() }, &[], owner, )?; @@ -173,10 +165,9 @@ impl Ism { ) -> eyre::Result { match self { Self::Mock => Self::deploy_mock(wasm, codes, deployer), - Self::Multisig { - hrp, - validators: set, - } => Self::deploy_multisig(wasm, codes, hrp, set, owner, deployer), + Self::Multisig { validators: set } => { + Self::deploy_multisig(wasm, codes, set, owner, deployer) + } Self::Aggregate { isms, threshold } => { Self::deploy_aggregate(wasm, codes, isms, threshold, owner, deployer) } @@ -185,14 +176,14 @@ impl Ism { } } -pub fn prepare_routing_ism(info: Vec<(u32, &str, TestValidators)>) -> Ism { +pub fn prepare_routing_ism(info: Vec<(u32, TestValidators)>) -> Ism { let mut isms = vec![]; - for (domain, hrp, set) in info { + for (domain, set) in info { isms.push(( domain, Ism::Aggregate { - isms: vec![Ism::multisig(hrp, set)], + isms: vec![Ism::multisig(set)], threshold: 1, }, )); diff --git a/integration-test/tests/contracts/cw/setup.rs b/integration-test/tests/contracts/cw/setup.rs index 28ee591a..aa40b9dc 100644 --- a/integration-test/tests/contracts/cw/setup.rs +++ b/integration-test/tests/contracts/cw/setup.rs @@ -56,12 +56,8 @@ pub fn setup_env<'a, R: Runner<'a>>( let deployer = acc_gen(app, &[coin(1_000_000u128.pow(3), "uosmo")]); let tester = acc_gen(app, &[coin(1_000_000u128.pow(3), "uosmo")]); - let default_ism = prepare_routing_ism( - validators - .iter() - .map(|v| (v.domain, hrp, v.clone())) - .collect(), - ); + let default_ism = + prepare_routing_ism(validators.iter().map(|v| (v.domain, v.clone())).collect()); let default_hook = Hook::mock(Uint256::from_u128(DEFAULT_GAS)); diff --git a/integration-test/tests/validator.rs b/integration-test/tests/validator.rs index 23e360a7..1f7b4c60 100644 --- a/integration-test/tests/validator.rs +++ b/integration-test/tests/validator.rs @@ -6,7 +6,9 @@ use ethers::types::{Address, H160}; use ethers::utils::hex::FromHex; use hpl_interface::{ ism::multisig::{ThresholdSet, ValidatorSet}, - types::{bech32_encode, pub_to_addr, Message, MessageIdMultisigIsmMetadata}, + types::{ + bech32_encode, eth_addr, eth_hash, pub_to_addr, Message, MessageIdMultisigIsmMetadata, + }, }; use ibcx_test_utils::{addr, gen_bz}; use k256::{ @@ -54,11 +56,10 @@ impl TestValidator { .into() } - pub fn to_val(&self, domain: u32, hrp: &str) -> ValidatorSet { + pub fn to_val(&self, domain: u32) -> ValidatorSet { ValidatorSet { domain, - validator: self.addr(hrp), - validator_pubkey: self.pub_key_to_binary(), + validator: eth_addr(self.pub_key.to_encoded_point(false).as_bytes().into()).unwrap(), } } @@ -108,10 +109,10 @@ impl TestValidators { } } - pub fn to_set(&self, hrp: &str) -> Vec { + pub fn to_set(&self) -> Vec { self.validators .iter() - .map(|v| v.to_val(self.domain, hrp)) + .map(|v| v.to_val(self.domain)) .collect::>() } @@ -149,7 +150,7 @@ impl TestValidators { message_id.to_vec(), )?; - let hashed_message = hpl_ism_multisig::eth_hash(multisig_hash)?; + let hashed_message = eth_hash(multisig_hash)?; let signatures = if is_passed { self.sign(self.threshold, hashed_message.as_slice().try_into()?) @@ -168,7 +169,6 @@ impl TestValidators { #[test] fn test_validator() { - let hrp = "osmo"; let owner = addr("owner"); let validators = TestValidators::new(2, 5, 3); @@ -176,14 +176,10 @@ fn test_validator() { hpl_ownable::initialize(deps.as_mut().storage, &owner).unwrap(); - hpl_ism_multisig::state::HRP - .save(deps.as_mut().storage, &hrp.to_string()) - .unwrap(); - hpl_ism_multisig::execute::enroll_validators( deps.as_mut(), mock_info(owner.as_str(), &[]), - validators.to_set(hrp), + validators.to_set(), ) .unwrap(); diff --git a/packages/interface/src/core/va.rs b/packages/interface/src/core/va.rs index 8e8c6463..7a9ad92a 100644 --- a/packages/interface/src/core/va.rs +++ b/packages/interface/src/core/va.rs @@ -11,8 +11,8 @@ pub struct InstantiateMsg { pub enum ExecuteMsg { Announce { validator: HexBinary, - storage_location: String, signature: HexBinary, + storage_location: String, }, } @@ -35,5 +35,3 @@ pub struct GetAnnounceStorageLocationsResponse { pub struct GetAnnouncedValidatorsResponse { pub validators: Vec, } - - diff --git a/packages/interface/src/ism/mod.rs b/packages/interface/src/ism/mod.rs index 18d10299..373ac4f9 100644 --- a/packages/interface/src/ism/mod.rs +++ b/packages/interface/src/ism/mod.rs @@ -68,7 +68,7 @@ pub struct VerifyResponse { #[cw_serde] pub struct VerifyInfoResponse { pub threshold: u8, - pub validators: Vec, + pub validators: Vec, } #[cw_serde] diff --git a/packages/interface/src/ism/multisig.rs b/packages/interface/src/ism/multisig.rs index 0727ea78..4f91ac67 100644 --- a/packages/interface/src/ism/multisig.rs +++ b/packages/interface/src/ism/multisig.rs @@ -10,14 +10,12 @@ use super::{ModuleTypeResponse, VerifyInfoResponse, VerifyResponse}; #[cw_serde] pub struct InstantiateMsg { pub owner: String, - pub hrp: String, } #[cw_serde] pub struct ValidatorSet { pub domain: u32, - pub validator: String, - pub validator_pubkey: HexBinary, + pub validator: HexBinary, } #[cw_serde] @@ -32,7 +30,7 @@ pub enum ExecuteMsg { EnrollValidator { set: ValidatorSet }, EnrollValidators { set: Vec }, - UnenrollValidator { domain: u32, validator: String }, + UnenrollValidator { domain: u32, validator: HexBinary }, SetThreshold { set: ThresholdSet }, SetThresholds { set: Vec }, @@ -56,7 +54,7 @@ pub enum MultisigIsmQueryMsg { #[cw_serde] pub struct EnrolledValidatorsResponse { - pub validators: Vec, + pub validators: Vec, pub threshold: u8, } diff --git a/packages/interface/src/types/crypto.rs b/packages/interface/src/types/crypto.rs index 28f322a3..2bf1c759 100644 --- a/packages/interface/src/types/crypto.rs +++ b/packages/interface/src/types/crypto.rs @@ -20,6 +20,16 @@ pub fn eth_hash(message: HexBinary) -> StdResult { Ok(message_hash) } +pub fn eth_addr(pubkey: HexBinary) -> StdResult { + let hash = keccak256_hash(&pubkey.as_slice()[1..]); + + let mut bytes = [0u8; 20]; + bytes.copy_from_slice(&hash.as_slice()[12..]); + let addr = HexBinary::from(bytes.to_vec()); + + Ok(addr.to_vec().into()) +} + pub fn sha256_digest(bz: impl AsRef<[u8]>) -> StdResult<[u8; 32]> { use sha2::{Digest, Sha256}; From 3e4898a8b83531d2d06d390d5e28a30a11cc2971 Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Thu, 26 Oct 2023 20:49:50 +0900 Subject: [PATCH 07/53] feat: use eth_addr --- scripts/action/deploy.ts | 39 ++++++++++++++++++++------------------- scripts/src/config.ts | 9 +++++++-- scripts/src/types.ts | 5 +++++ 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/scripts/action/deploy.ts b/scripts/action/deploy.ts index 79680838..1e9b625e 100644 --- a/scripts/action/deploy.ts +++ b/scripts/action/deploy.ts @@ -99,14 +99,14 @@ const deploy_igp = async ( // init igp ctx.contracts[name(igp.core)] = await igp.core.instantiate({ hrp: config.network.hrp, - owner: ctx.address!, + owner: client.signer, gas_token: config.deploy.igp.token || config.network.gas.denom, - beneficiary: ctx.address!, + beneficiary: client.signer, }); // init igp oracle ctx.contracts[name(igp.oracle)] = await igp.oracle.instantiate({ - owner: ctx.address!, + owner: client.signer, }); await client.wasm.execute( @@ -158,13 +158,12 @@ const deploy_ism_hook = async ( config.deploy.ism || { type: "multisig", owner: "", - validators: [ - { - addr: client.signer, - pubkey: client.signer_pubkey, + validators: { + 5: { + addrs: [client.signer_addr], + threshold: 1, }, - ], - threshold: 1, + }, }, contracts ), @@ -215,7 +214,6 @@ const deploy_ism = async ( switch (ism.type) { case "multisig": const multisig_ism_res = await isms.multisig.instantiate({ - hrp: config.network.hrp, owner: ism.owner === "" ? client.signer : ism.owner, }); @@ -224,11 +222,12 @@ const deploy_ism = async ( multisig_ism_res.address!, { enroll_validators: { - set: Object.entries(ism.validators).map(([domain, v]) => ({ - domain: Number(domain), - validator: v.addr, - validator_pubkey: v.pubkey, - })), + set: Object.entries(ism.validators).flatMap(([domain, validator]) => + validator.addrs.map((v) => ({ + domain: Number(domain), + validator: v, + })) + ), }, }, "auto" @@ -239,10 +238,12 @@ const deploy_ism = async ( multisig_ism_res.address!, { set_thresholds: { - set: Object.keys(ism.validators).map((domain) => ({ - domain: Number(domain), - threshold: ism.threshold, - })), + set: Object.entries(ism.validators).map( + ([domain, { threshold }]) => ({ + domain: Number(domain), + threshold, + }) + ), }, }, "auto" diff --git a/scripts/src/config.ts b/scripts/src/config.ts index f78211d9..86f7337c 100644 --- a/scripts/src/config.ts +++ b/scripts/src/config.ts @@ -8,15 +8,15 @@ import { } from "@cosmjs/tendermint-rpc"; import { DirectSecp256k1Wallet } from "@cosmjs/proto-signing"; import { GasPrice, SigningStargateClient } from "@cosmjs/stargate"; +import { Secp256k1, keccak256 } from "@cosmjs/crypto"; export type IsmType = | { type: "multisig"; owner: string; validators: { - [domain: number]: { addr: string; pubkey: string }; + [domain: number]: { addrs: string[]; threshold: number }; }; - threshold: number; } | { type: "aggregate"; @@ -92,6 +92,7 @@ export type Client = { wasm: SigningCosmWasmClient; stargate: SigningStargateClient; signer: string; + signer_addr: string; signer_pubkey: string; }; @@ -136,10 +137,14 @@ export async function getSigningClient({ } ); + const pubkey = Secp256k1.uncompressPubkey(account.pubkey); + const ethaddr = keccak256(pubkey.slice(1)).slice(-20); + return { wasm, stargate, signer: account.address, + signer_addr: Buffer.from(ethaddr).toString("hex"), signer_pubkey: Buffer.from(account.pubkey).toString("hex"), }; } diff --git a/scripts/src/types.ts b/scripts/src/types.ts index 1f439aea..75f01aca 100644 --- a/scripts/src/types.ts +++ b/scripts/src/types.ts @@ -4,6 +4,7 @@ import { } from "@cosmjs/cosmwasm-stargate"; import { getWasmPath } from "./load_wasm"; import fs from "fs"; +import { fromBech32 } from "@cosmjs/encoding"; export interface ContractContext { codeId: number | undefined; @@ -94,10 +95,14 @@ export abstract class BaseContract implements Contract { "auto", { admin: this.signer } ); + console.log( [ this.contractName.padEnd(30), contract.contractAddress.padEnd(65), + Buffer.from(fromBech32(contract.contractAddress).data) + .toString("hex") + .padEnd(65), contract.transactionHash.padEnd(65), ].join("| ") ); From 67d0c7fc68f63829ed1f76354ab63f099003b823 Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Fri, 27 Oct 2023 00:39:13 +0900 Subject: [PATCH 08/53] feat: open testnet deployment --- scripts/.gitignore | 1 - scripts/context/duality-devnet.json | 96 ++++++++++++++++++++++++++ scripts/save.json | 102 ---------------------------- 3 files changed, 96 insertions(+), 103 deletions(-) create mode 100644 scripts/context/duality-devnet.json delete mode 100644 scripts/save.json diff --git a/scripts/.gitignore b/scripts/.gitignore index 70264f89..08786d6c 100644 --- a/scripts/.gitignore +++ b/scripts/.gitignore @@ -1,5 +1,4 @@ /dist -/context .env config.yaml /node_modules diff --git a/scripts/context/duality-devnet.json b/scripts/context/duality-devnet.json new file mode 100644 index 00000000..4ce9c082 --- /dev/null +++ b/scripts/context/duality-devnet.json @@ -0,0 +1,96 @@ +{ + "contracts": { + "hpl_hook_merkle": { + "codeId": 69, + "digest": "986cfcb90f4491d5ca87c57cdb1623f46daac16a8c2dfc5b0afa22f7f5876fab" + }, + "hpl_igp": { + "address": "dual14cu2z6xw62cumt7hmfw7977jyaymr26jazxdpvfp7ag2dss29q2q700yum", + "codeId": 74, + "digest": "8bec7d6dd094fcb1ff4d89ec3631b08242215d003e8cb1e46454ef5713c399e8" + }, + "hpl_igp_oracle": { + "address": "dual1alp24grk7e5f3msv0apk68hehspglpr7y7upfrvkpdfu7c84sqtq8ze5p2", + "codeId": 75, + "digest": "e076ecc28e12972d66b9a7a87aa1ed210752f64ed682e14d45e12236a41f364c" + }, + "hpl_ism_multisig": { + "codeId": 77, + "digest": "435f13deae9c7b7cb0d244bdbe9ae095c27c69bf81f4b9857be68e8b9174f4de" + }, + "hpl_mailbox": { + "address": "dual1mveu0r9rj4qa6aqxt8almpha6cqluu397y8jd6r4jhzm3hmtmndq8lvk47", + "codeId": 79, + "digest": "2ba401ab148c755c935b7f5aaed0da373aef165fa712390161d1b9e9e3b2a3d0" + }, + "hpl_test_mock_hook": { + "codeId": 80, + "digest": "7a4413b88f334a0ac0009a68887cee1cd160fbb83e3b8dd87bd4489b44d28bdb" + }, + "hpl_test_mock_msg_receiver": { + "address": "dual1rvtgvc38sfd9zehtgsp3eh8k269naq949u5qdcqm3x35mjg2uctqfdn3yq", + "codeId": 81, + "digest": "3718a207a02ba1d9ee4c57ee33a2ae73962b5eb27e542836c069840914e2be89" + }, + "hpl_validator_announce": { + "address": "dual1982lwq4rt4qntkv2hafvvtwupn7hgqqkv0kpf55yahlh6pqeldvqd24lld", + "codeId": 82, + "digest": "3e3172f31c46d51b4e25d151eeee286919af129eb66714d88610838b2c1405ca" + }, + "hpl_warp_cw20": { + "codeId": 83, + "digest": "6a3a1a6a3a85109842ced3c50c8a30c2bd50e7f3648271f634190e58aa62ff19" + }, + "hpl_warp_native": { + "codeId": 84, + "digest": "5d3fa9232d3873cef2918677203d785286add4f48d95353a70be646c27d30198" + }, + "hpl_warp_native_ibc": { + "codeId": 32, + "digest": "eed9d7dfa18163de64893cfe88e038cfc82e487561c94b142978efdacc1f9dff" + }, + "hpl_hook_aggregate": { + "codeId": 68, + "digest": "1d0d021c3200bdae970d4a17b28aa3d35136953e3a931c77acb422c466d3a2c1" + }, + "hpl_hook_pausable": { + "codeId": 70, + "digest": "9df275eabf0d4683314935b6cb8dbcd4f68f08bc308d2d979afccbbe34ea3f18" + }, + "hpl_hook_routing": { + "codeId": 71, + "digest": "4f96a3a784102a172955b0b1f33bab1cf50380f13fe2aefa00880368c401bfba" + }, + "hpl_hook_routing_custom": { + "codeId": 72, + "digest": "3bd3d02c02feb98f7e1e2009571b43ef36c0f4ec702e0669cec6ecddf3e3c3dd" + }, + "hpl_hook_routing_fallback": { + "codeId": 73, + "digest": "49f5ce6e6b175b2c15046d2f0e628b6d4dea4ba7f97decfce2de48b4a24c444b" + }, + "hpl_ism_aggregate": { + "codeId": 76, + "digest": "4d5d558efc0ea10d2e139b2aae9c356f5c5007f15cb8ab4ed77c202213268f0b" + }, + "hpl_ism_routing": { + "codeId": 78, + "digest": "f8ec1fd4f6638c4d8d82606406d4fd4f6e938ef711c7f1c529fd010953ce55d3" + }, + "hpl_default_ism": { + "codeId": 77, + "digest": "435f13deae9c7b7cb0d244bdbe9ae095c27c69bf81f4b9857be68e8b9174f4de", + "address": "dual192t0g0gu7xg4czamvs2qwr0w3arpfcsa6nsj9tp0g0fjqe6x5wrs35rzmq" + }, + "hpl_default_hook": { + "codeId": 80, + "digest": "7a4413b88f334a0ac0009a68887cee1cd160fbb83e3b8dd87bd4489b44d28bdb", + "address": "dual1uu64dmvdt6s2a0w9xa066cr42q35um8arsfh4qw8wspcfsmy3jasnjjzkt" + }, + "hpl_required_hook": { + "codeId": 68, + "digest": "1d0d021c3200bdae970d4a17b28aa3d35136953e3a931c77acb422c466d3a2c1", + "address": "dual1nz2n6vfk5fjlex2qquupyaqhaucr4xn30lm0teqwh0r0zv0v9xhsky7h3a" + } + } +} diff --git a/scripts/save.json b/scripts/save.json deleted file mode 100644 index dc862e33..00000000 --- a/scripts/save.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "contracts": { - "hpl_hook_merkle": { - "address": "dual1pk99xge6q94qtu3568x3qhp68zzv0mx7za4ct008ks36qhx5tvss3qawfh", - "codeId": 34, - "digest": "1e8cabd844f11ba4cdeaaeffb483a0057d8a36f945ad9166dd3276ef8dc89aa2" - }, - "hpl_igp": { - "address": "dual1q25d0y0nyyp8mjnqc8y4s6t3wmq5as73plk3hyfa3dy82w4jsl7qry0wqf", - "codeId": 50, - "digest": "38305f60838fda89236490ba0e8d0a07cfb224e93bc805b0de4fe9f4217194fd" - }, - "hpl_igp_oracle": { - "address": "dual148g378f05algmm5n9jftqef3jv232n5aynfxud0tu8vlnhw7vepqjyyd20", - "codeId": 40, - "digest": "9489e237e542da054becabe952d28959d541d1207d3000d49f5c1dfc57e94536" - }, - "hpl_ism_multisig": { - "address": "dual1jwedcsjxv8e3umnnqm5cvtylg0han2wfqwlw9n27n2kns0vjgneq8d6m2a", - "codeId": 42, - "digest": "394c84042f2b8064575360c39d0c0ffbec9a57e753cadc508f8f62c72c22beee" - }, - "hpl_mailbox": { - "address": "dual1yp0lgxq460ked0egtzyj2nck3mdhr8smjrshsfl4zeefct033jts0xfa86", - "codeId": 44, - "digest": "46c5d2d39ae74fd7e79a35a208fc77c6f9155f266bff8e4b1b69ad87ee3864e3" - }, - "hpl_test_mock_hook": { - "address": "dual1q5wmfzl6gfe629cc5yacluerphf8d5gq8j2d52pn7ua70279mvlq39cetg", - "codeId": 45, - "digest": "1aa8037a0adc680b92b0b9dd1d682289384ae525a1e1138cf10b10bada3d4acc" - }, - "hpl_test_mock_msg_receiver": { - "address": "dual1dettnvwzcec0yk7sfhxed8phl62k36fg5nezdasfherfgjhhur8s0fg77l", - "codeId": 46, - "digest": "09c21975d627436c705ee93f037cfbbde8f6f0dd192603f6c0b032c672dea959" - }, - "hpl_validator_announce": { - "address": "dual1rs04xd0dq3c9mra7jwjy9q7twyzmgc93w2we5h5nj2km3ccqxhaq2js68f", - "codeId": 47, - "digest": "92ee2775bf7b9604f6ab8cf77e11b47ca858ab7b17da12a659f43392e0f071d3" - }, - "hpl_warp_cw20": { - "codeId": 48, - "digest": "b78f5931b71da233bb9e90f3b686e06ed0e62430219399b6d844bdea4b1bf61d" - }, - "hpl_warp_native": { - "codeId": 49, - "digest": "bcb8191f002b5b5fc8bda10b6c7ee25b48ea4591ff51bc805967e394b623795e" - }, - "hpl_warp_native_ibc": { - "address": "dual1572xwqajsht372ra2cy65zk8ec75eq7k7ayqqcw9r3y03lxvn6aqjnvhys", - "codeId": 32, - "digest": "eed9d7dfa18163de64893cfe88e038cfc82e487561c94b142978efdacc1f9dff" - }, - "hpl_hook_aggregate": { - "address": "dual1r2v20edjksh8842dkse6qzcdgntt0dtym693tpyftvmpc3grzczs0wrl47", - "codeId": 33, - "digest": "eefbcb3c2a0f6a62d8a88902e2caa371dd3add9eee42c16e377b6f6eeb37c972" - }, - "hpl_hook_pausable": { - "codeId": 35, - "digest": "b06c21fe3f4e2f2c41756e45a410a3a5bf0c21322196b24488f327b9db399198" - }, - "hpl_hook_routing": { - "codeId": 36, - "digest": "516e462877f1f2ae9e6dfdf1b4def9817f46ee9719413a9d2ef44196782e4ee1" - }, - "hpl_hook_routing_custom": { - "codeId": 37, - "digest": "e8b933536e97db737d28d101d29d14a7644c2e10efdb324a98bae1fe46c912b9" - }, - "hpl_hook_routing_fallback": { - "codeId": 38, - "digest": "af03a19000421a8652ac7e709162ce55f194fe3ceb3e13bdccfced6dc2f2010c" - }, - "hpl_ism_aggregate": { - "codeId": 41, - "digest": "4a262c7221450e0cf988c8d6c05b724e1faeed4dd1c715e85527d901acffdf42" - }, - "hpl_ism_routing": { - "codeId": 43, - "digest": "6f4bc63847fe9581da19eabd0f869bc6c4339d03db5dcff683a4ec8780a41dfd" - }, - "hpl_default_ism": { - "address": "dual1ql2d2glpgnzw0ufegafqtggm2r4zhql0x8z46pmufr98sdtx0g9q464p7j", - "codeId": 42, - "digest": "394c84042f2b8064575360c39d0c0ffbec9a57e753cadc508f8f62c72c22beee" - }, - "hpl_default_hook": { - "address": "dual14ljshs3j9vlqv9lg3x8equf7450jjanx45cz343uaepk57ppe6cs74cuzf", - "codeId": 45, - "digest": "1aa8037a0adc680b92b0b9dd1d682289384ae525a1e1138cf10b10bada3d4acc" - }, - "hpl_required_hook": { - "address": "dual1pthrq98hpym0k3tmnwjp9shj77ecaymk5e5xaf6y5hstwgphq8tq5avlrr", - "codeId": 33, - "digest": "eefbcb3c2a0f6a62d8a88902e2caa371dd3add9eee42c16e377b6f6eeb37c972" - } - }, - "address": "dual1dwnrgwsf5c9vqjxsax04pdm0mx007yrraj2dgn" -} \ No newline at end of file From ac8cd58c6bc5a831bf4b36d65f8a8d81f4edaf1a Mon Sep 17 00:00:00 2001 From: ByeongSu Hong Date: Fri, 27 Oct 2023 18:03:10 +0900 Subject: [PATCH 09/53] fix(warp): consideration of igp (#56) * fix: merkle tree * fix(warp): gas issue * fix(warp): use transfer from * feat(warp): enable migration * clippy * feat: scripts * feat(hook): enable migrate * build: new code * chore: addr converter * fix: inherit ism * build: migrate * feat: inheritable ism receiver interface * fix: hide important files * rc6 * feat: migratable * migrate * fix: remove feature & deploy codes --- Cargo.toml | 4 +- contracts/core/mailbox/src/contract.rs | 7 +- contracts/core/mailbox/src/execute.rs | 7 +- contracts/hooks/merkle/src/lib.rs | 14 +- .../mocks/mock-msg-receiver/src/contract.rs | 32 ++-- contracts/warp/cw20/src/contract.rs | 120 ++++++-------- contracts/warp/native/src/contract.rs | 54 ++++-- contracts/warp/native/src/error.rs | 3 + integration-test/tests/validator.rs | 2 +- packages/interface/src/core/mailbox.rs | 5 +- packages/interface/src/ism/mod.rs | 15 +- packages/interface/src/types/bech32.rs | 10 ++ packages/interface/src/warp/cw20.rs | 26 +-- packages/interface/src/warp/native.rs | 6 +- scripts/.gitignore | 2 + scripts/action/migrate.ts | 66 ++------ scripts/action/warp.ts | 154 ++++++++++++++++-- scripts/context/duality-devnet.json | 73 +++++---- scripts/context/neutron-1.json | 73 +++++++++ 19 files changed, 441 insertions(+), 232 deletions(-) create mode 100644 scripts/context/neutron-1.json diff --git a/Cargo.toml b/Cargo.toml index 99fb693c..44247c11 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ panic = "abort" rpath = false [workspace.package] -version = "0.0.6-rc5" +version = "0.0.6-rc6" authors = [ "byeongsu-hong ", "Eric ", @@ -42,7 +42,7 @@ keywords = ["hyperlane", "cosmos", "cosmwasm"] [workspace.dependencies] # cosmwasm -cosmwasm-std = { version = "1.2.7", features = ["stargate", "cosmwasm_1_2"] } +cosmwasm-std = { version = "1.2.7", features = ["stargate"] } cosmwasm-storage = "1.2.7" cosmwasm-schema = "1.2.7" cosmwasm-crypto = "1.2.7" diff --git a/contracts/core/mailbox/src/contract.rs b/contracts/core/mailbox/src/contract.rs index 306cafe3..e74ca14f 100644 --- a/contracts/core/mailbox/src/contract.rs +++ b/contracts/core/mailbox/src/contract.rs @@ -1,6 +1,6 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{Deps, DepsMut, Env, MessageInfo, QueryResponse, Response}; +use cosmwasm_std::{Deps, DepsMut, Empty, Env, MessageInfo, QueryResponse, Response}; use hpl_interface::{ core::mailbox::{ExecuteMsg, InstantiateMsg, MailboxHookQueryMsg, MailboxQueryMsg, QueryMsg}, @@ -87,6 +87,11 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result Result { + Ok(Response::default()) +} + #[cfg(test)] mod test { use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; diff --git a/contracts/core/mailbox/src/execute.rs b/contracts/core/mailbox/src/execute.rs index 1c0bde16..2170c952 100644 --- a/contracts/core/mailbox/src/execute.rs +++ b/contracts/core/mailbox/src/execute.rs @@ -495,9 +495,12 @@ mod tests { fn test_process_query_handler(query: &WasmQuery) -> QuerierResult { match query { WasmQuery::Smart { contract_addr, msg } => { - if let Ok(req) = cosmwasm_std::from_binary::(msg) { + if let Ok(req) = cosmwasm_std::from_binary::(msg) + { match req { - ism::IsmSpecifierQueryMsg::InterchainSecurityModule() => { + hpl_interface::ism::ExpectedIsmSpecifierQueryMsg::IsmSpecifier( + ism::IsmSpecifierQueryMsg::InterchainSecurityModule(), + ) => { return SystemResult::Ok( cosmwasm_std::to_binary(&ism::InterchainSecurityModuleResponse { ism: Some(addr("default_ism")), diff --git a/contracts/hooks/merkle/src/lib.rs b/contracts/hooks/merkle/src/lib.rs index 86a94cb5..765ed396 100644 --- a/contracts/hooks/merkle/src/lib.rs +++ b/contracts/hooks/merkle/src/lib.rs @@ -1,7 +1,8 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - ensure_eq, Addr, Deps, DepsMut, Env, Event, MessageInfo, QueryResponse, Response, StdError, + ensure_eq, Addr, Deps, DepsMut, Empty, Env, Event, MessageInfo, QueryResponse, Response, + StdError, }; use cw_storage_plus::Item; use hpl_interface::{ @@ -181,10 +182,19 @@ fn get_tree_checkpoint(deps: Deps) -> Result Result { + Ok(Response::new()) +} + #[cfg(test)] mod test { use super::*; diff --git a/contracts/mocks/mock-msg-receiver/src/contract.rs b/contracts/mocks/mock-msg-receiver/src/contract.rs index ad9bb9a8..b127e108 100644 --- a/contracts/mocks/mock-msg-receiver/src/contract.rs +++ b/contracts/mocks/mock-msg-receiver/src/contract.rs @@ -2,7 +2,8 @@ use cosmwasm_schema::cw_serde; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - attr, to_binary, Deps, DepsMut, Env, Event, MessageInfo, QueryResponse, Response, StdResult, + attr, to_binary, Deps, DepsMut, Empty, Env, Event, MessageInfo, QueryResponse, Response, + StdResult, }; use cw2::set_contract_version; use cw_storage_plus::Item; @@ -15,9 +16,6 @@ pub struct InstantiateMsg { pub hrp: String, } -#[cw_serde] -pub struct MigrateMsg {} - #[cw_serde] pub struct ExecuteMsg {} @@ -38,11 +36,6 @@ pub fn instantiate( Ok(Response::new().add_attribute("method", "instantiate")) } -#[cfg_attr(not(feature = "library"), entry_point)] -pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult { - Ok(Response::default()) -} - /// Handling contract execution #[cfg_attr(not(feature = "library"), entry_point)] pub fn execute( @@ -67,12 +60,21 @@ pub fn execute( /// Handling contract query #[cfg_attr(not(feature = "library"), entry_point)] -pub fn query(_deps: Deps, _env: Env, msg: ism::IsmSpecifierQueryMsg) -> StdResult { +pub fn query( + _deps: Deps, + _env: Env, + msg: ism::ExpectedIsmSpecifierQueryMsg, +) -> StdResult { match msg { - ism::IsmSpecifierQueryMsg::InterchainSecurityModule() => { - Ok(to_binary(&ism::InterchainSecurityModuleResponse { - ism: None, - })?) - } + ism::ExpectedIsmSpecifierQueryMsg::IsmSpecifier( + ism::IsmSpecifierQueryMsg::InterchainSecurityModule(), + ) => Ok(to_binary(&ism::InterchainSecurityModuleResponse { + ism: None, + })?), } } + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate(_deps: DepsMut, _env: Env, _msg: Empty) -> StdResult { + Ok(Response::default()) +} diff --git a/contracts/warp/cw20/src/contract.rs b/contracts/warp/cw20/src/contract.rs index 2b06b440..70a67cb4 100644 --- a/contracts/warp/cw20/src/contract.rs +++ b/contracts/warp/cw20/src/contract.rs @@ -1,18 +1,19 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - ensure_eq, from_binary, CosmosMsg, Deps, DepsMut, Env, HexBinary, MessageInfo, QueryResponse, - Reply, Response, SubMsg, Uint256, WasmMsg, + ensure_eq, wasm_execute, CosmosMsg, Deps, DepsMut, Env, HexBinary, MessageInfo, QueryResponse, + Reply, Response, SubMsg, Uint128, Uint256, WasmMsg, }; -use cw20::Cw20ReceiveMsg; +use cw20::Cw20ExecuteMsg; use hpl_interface::{ core::mailbox, + ism::{InterchainSecurityModuleResponse, IsmSpecifierQueryMsg}, to_binary, types::bech32_encode, warp::{ self, - cw20::{ExecuteMsg, InstantiateMsg, QueryMsg, ReceiveMsg}, + cw20::{ExecuteMsg, InstantiateMsg, QueryMsg}, TokenMode, TokenModeMsg, TokenModeResponse, TokenTypeResponse, }, }; @@ -92,20 +93,11 @@ pub fn execute( Ownable(msg) => Ok(hpl_ownable::handle(deps, env, info, msg)?), Router(msg) => Ok(hpl_router::handle(deps, env, info, msg)?), Handle(msg) => mailbox_handle(deps, info, msg), - Receive(msg) => { - ensure_eq!( - info.sender, - TOKEN.load(deps.storage)?, - ContractError::Unauthorized - ); - - match from_binary::(&msg.msg)? { - ReceiveMsg::TransferRemote { - dest_domain, - recipient, - } => transfer_remote(deps, msg, dest_domain, recipient), - } - } + TransferRemote { + dest_domain, + recipient, + amount, + } => transfer_remote(deps, env, info, dest_domain, recipient, amount), } } @@ -173,9 +165,11 @@ fn mailbox_handle( fn transfer_remote( deps: DepsMut, - receive_msg: Cw20ReceiveMsg, + env: Env, + info: MessageInfo, dest_domain: u32, recipient: HexBinary, + transfer_amount: Uint128, ) -> Result { let token = TOKEN.load(deps.storage)?; let mode = MODE.load(deps.storage)?; @@ -187,9 +181,23 @@ fn transfer_remote( let mut msgs: Vec = vec![]; + // push token transfer msg + msgs.push( + wasm_execute( + &token, + &Cw20ExecuteMsg::TransferFrom { + owner: info.sender.to_string(), + recipient: env.contract.address.to_string(), + amount: transfer_amount, + }, + vec![], + )? + .into(), + ); + if mode == TokenMode::Bridged { // push token burn msg if token is bridged - msgs.push(conv::to_burn_msg(&token, receive_msg.amount)?.into()); + msgs.push(conv::to_burn_msg(&token, transfer_amount)?.into()); } // push mailbox dispatch msg @@ -199,21 +207,22 @@ fn transfer_remote( dest_router, warp::Message { recipient: recipient.clone(), - amount: Uint256::from_uint128(receive_msg.amount), + amount: Uint256::from_uint128(transfer_amount), metadata: HexBinary::default(), } .into(), None, None, + info.funds, )?); Ok(Response::new().add_messages(msgs).add_event( new_event("transfer-remote") - .add_attribute("sender", receive_msg.sender) + .add_attribute("sender", info.sender) .add_attribute("dest_domain", dest_domain.to_string()) .add_attribute("recipient", recipient.to_hex()) .add_attribute("token", token) - .add_attribute("amount", receive_msg.amount), + .add_attribute("amount", transfer_amount), )) } @@ -228,6 +237,9 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result to_binary(get_token_type(deps)), TokenMode {} => to_binary(get_token_mode(deps)), }, + QueryMsg::IsmSpecifier(IsmSpecifierQueryMsg::InterchainSecurityModule()) => Ok( + cosmwasm_std::to_binary(&InterchainSecurityModuleResponse { ism: None })?, + ), } } @@ -413,46 +425,16 @@ mod test { } } - enum Method { - Handle, - Receive, - } - - fn default_cw20_receive_msg() -> Cw20ReceiveMsg { - Cw20ReceiveMsg { - sender: Default::default(), - amount: Default::default(), - msg: Default::default(), - } - } - #[rstest] + #[case(MAILBOX, 1, gen_bz(32), token_mode_bridged())] + #[case(MAILBOX, 1, gen_bz(32), token_mode_collateral())] #[should_panic(expected = "unauthorized")] - #[case(MAILBOX, Method::Receive)] - #[should_panic(expected = "unauthorized")] - #[case(TOKEN, Method::Handle)] - fn test_execute_authority( - deps: (TestDeps, Response), - #[case] sender: &str, - #[case] method: Method, - ) { - let (mut deps, _) = deps; - - let msg = match method { - Method::Handle => ExecuteMsg::Handle(HandleMsg::default()), - Method::Receive => ExecuteMsg::Receive(default_cw20_receive_msg()), - }; - - test_execute(deps.as_mut(), &addr(sender), msg, vec![]); - } - - #[rstest] - #[case(1, gen_bz(32), token_mode_bridged())] - #[case(1, gen_bz(32), token_mode_collateral())] + #[case(TOKEN, 1, gen_bz(32), token_mode_collateral())] #[should_panic(expected = "route not found")] - #[case(2, gen_bz(32), token_mode_collateral())] + #[case(MAILBOX, 2, gen_bz(32), token_mode_collateral())] fn test_mailbox_handle( #[values("osmo", "neutron")] hrp: &str, + #[case] sender: &str, #[case] domain: u32, #[case] route: HexBinary, #[case] token_mode: Cw20TokenMode, @@ -464,8 +446,6 @@ mod test { token_mode.clone(), ); - let sender = MAILBOX; - let warp_msg = warp::Message { recipient: gen_bz(32), amount: Uint256::from_u128(100), @@ -539,20 +519,14 @@ mod test { let sender = addr("sender"); let recipient = gen_bz(32); - let receive_msg = Cw20ReceiveMsg { - sender: sender.to_string(), - amount: Uint128::new(100), - msg: cosmwasm_std::to_binary(&ReceiveMsg::TransferRemote { - dest_domain: domain, - recipient: recipient.clone(), - }) - .unwrap(), - }; - let res = test_execute( deps.as_mut(), - &addr(TOKEN), - ExecuteMsg::Receive(receive_msg), + &sender, + ExecuteMsg::TransferRemote { + dest_domain: domain, + recipient: route.clone(), + amount: Uint128::new(100), + }, vec![], ); let msgs = res.messages.into_iter().map(|v| v.msg).collect::>(); @@ -564,7 +538,7 @@ mod test { }; let dispatch_msg = - mailbox::dispatch(MAILBOX, domain, route, warp_msg.into(), None, None).unwrap(); + mailbox::dispatch(MAILBOX, domain, route, warp_msg.into(), None, None, vec![]).unwrap(); match token_mode { TokenModeMsg::Bridged(_) => { diff --git a/contracts/warp/native/src/contract.rs b/contracts/warp/native/src/contract.rs index c778693c..1db173ff 100644 --- a/contracts/warp/native/src/contract.rs +++ b/contracts/warp/native/src/contract.rs @@ -1,11 +1,12 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - ensure_eq, CosmosMsg, Deps, DepsMut, Env, HexBinary, MessageInfo, QueryResponse, Reply, - Response, SubMsg, Uint256, + ensure, ensure_eq, CosmosMsg, Deps, DepsMut, Empty, Env, HexBinary, MessageInfo, QueryResponse, + Reply, Response, SubMsg, Uint128, Uint256, }; use hpl_interface::{ core::mailbox, + ism::{InterchainSecurityModuleResponse, IsmSpecifierQueryMsg}, to_binary, types::bech32_encode, warp::{ @@ -96,7 +97,8 @@ pub fn execute( TransferRemote { dest_domain, recipient, - } => transfer_remote(deps, env, info, dest_domain, recipient), + amount, + } => transfer_remote(deps, env, info, dest_domain, recipient, amount), } } @@ -177,11 +179,25 @@ fn transfer_remote( info: MessageInfo, dest_domain: u32, recipient: HexBinary, + transfer_amount: Uint128, ) -> Result { let token = TOKEN.load(deps.storage)?; let mode = MODE.load(deps.storage)?; let mailbox = MAILBOX.load(deps.storage)?; - let transfer_amount = cw_utils::must_pay(&info, &token)?; + + let mut funds = info.funds.clone(); + + let (token_index, token_received) = funds + .iter() + .enumerate() + .find(|(_, v)| v.denom == token) + .expect("no funds sent"); + ensure!( + token_received.amount >= transfer_amount, + ContractError::InsufficientFunds + ); + + funds[token_index].amount -= transfer_amount; let dest_router = get_route::(deps.storage, dest_domain)? .route @@ -208,6 +224,7 @@ fn transfer_remote( dispatch_payload.into(), None, None, + funds, )?); Ok(Response::new().add_messages(msgs).add_event( @@ -230,6 +247,9 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result to_binary(get_token_type(deps)), TokenMode {} => to_binary(get_token_mode(deps)), }, + QueryMsg::IsmSpecifier(IsmSpecifierQueryMsg::InterchainSecurityModule()) => Ok( + cosmwasm_std::to_binary(&InterchainSecurityModuleResponse { ism: None })?, + ), } } @@ -247,12 +267,17 @@ fn get_token_mode(deps: Deps) -> Result { Ok(TokenModeResponse { mode }) } +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate(_deps: DepsMut, _env: Env, _msg: Empty) -> Result { + Ok(Response::new()) +} + #[cfg(test)] mod test { use cosmwasm_std::{ coin, testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, - Coin, OwnedDeps, + Coin, OwnedDeps, Uint128, }; use hpl_interface::{ build_test_executor, build_test_querier, @@ -471,14 +496,13 @@ mod test { #[rstest] #[case(1, gen_bz(32), gen_bz(32), vec![coin(100, DENOM)])] + #[case(1, gen_bz(32), gen_bz(32), vec![coin(100, DENOM), coin(100, "uatom")])] #[should_panic(expected = "route not found")] #[case(2, gen_bz(32), gen_bz(32), vec![coin(100, DENOM)])] - #[should_panic(expected = "No funds sent")] + #[should_panic(expected = "no funds sent")] #[case(1, gen_bz(32), gen_bz(32), vec![])] - #[should_panic(expected = "Must send reserve token 'utest'")] + #[should_panic(expected = "no funds sent")] #[case(1, gen_bz(32), gen_bz(32), vec![coin(100, "uatom")])] - #[should_panic(expected = "Sent more than one denomination")] - #[case(1, gen_bz(32), gen_bz(32), vec![coin(100, DENOM), coin(100, "uatom")])] fn test_transfer_remote( mut deps: TestDeps, #[case] dest_domain: u32, @@ -502,8 +526,9 @@ mod test { ExecuteMsg::TransferRemote { dest_domain, recipient: dest_recipient.clone(), + amount: Uint128::new(50), }, - funds, + funds.clone(), ); let mut msgs: Vec<_> = res.messages.into_iter().map(|v| v.msg).collect(); @@ -517,12 +542,17 @@ mod test { dest_router, warp::Message { recipient: dest_recipient, - amount: Uint256::from_u128(100), + amount: Uint256::from_u128(50), metadata: HexBinary::default(), } .into(), None, - None + None, + vec![ + vec![coin(50, DENOM)], + funds.into_iter().filter(|v| v.denom != DENOM).collect() + ] + .concat() ) .unwrap() ); diff --git a/contracts/warp/native/src/error.rs b/contracts/warp/native/src/error.rs index 25a421cc..18dd1b41 100644 --- a/contracts/warp/native/src/error.rs +++ b/contracts/warp/native/src/error.rs @@ -21,6 +21,9 @@ pub enum ContractError { #[error("invalid reply id")] InvalidReplyId, + #[error("insufficient funds")] + InsufficientFunds, + #[error("no route for domain {domain:?}")] NoRouter { domain: u32 }, } diff --git a/integration-test/tests/validator.rs b/integration-test/tests/validator.rs index 1f7b4c60..05230e41 100644 --- a/integration-test/tests/validator.rs +++ b/integration-test/tests/validator.rs @@ -124,7 +124,7 @@ impl TestValidators { .iter() .map(|v| { let (mut signature, recov_id) = v.sign(digest); - signature.0.extend(vec![recov_id.to_byte()]); + signature.0.extend(vec![recov_id.to_byte() + 27]); signature.into() }) .collect::>(); diff --git a/packages/interface/src/core/mailbox.rs b/packages/interface/src/core/mailbox.rs index 9732ac4c..b5aa8c7e 100644 --- a/packages/interface/src/core/mailbox.rs +++ b/packages/interface/src/core/mailbox.rs @@ -1,5 +1,5 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::{wasm_execute, Addr, Api, CosmosMsg, HexBinary, StdResult}; +use cosmwasm_std::{wasm_execute, Addr, Api, Coin, CosmosMsg, HexBinary, StdResult}; #[allow(unused_imports)] use crate::{ @@ -110,6 +110,7 @@ pub fn dispatch( msg_body: HexBinary, hook: Option, metadata: Option, + funds: Vec, ) -> StdResult { Ok(wasm_execute( mailbox, @@ -120,7 +121,7 @@ pub fn dispatch( hook, metadata, }), - vec![], + funds, )? .into()) } diff --git a/packages/interface/src/ism/mod.rs b/packages/interface/src/ism/mod.rs index 373ac4f9..13cccbe4 100644 --- a/packages/interface/src/ism/mod.rs +++ b/packages/interface/src/ism/mod.rs @@ -47,6 +47,13 @@ pub enum ExpectedIsmQueryMsg { Ism(IsmQueryMsg), } +#[cw_serde] +#[derive(QueryResponses)] +#[query_responses(nested)] +pub enum ExpectedIsmSpecifierQueryMsg { + IsmSpecifier(IsmSpecifierQueryMsg), +} + #[cw_serde] #[derive(QueryResponses)] pub enum IsmSpecifierQueryMsg { @@ -54,6 +61,12 @@ pub enum IsmSpecifierQueryMsg { InterchainSecurityModule(), } +impl IsmSpecifierQueryMsg { + pub fn wrap(self) -> ExpectedIsmSpecifierQueryMsg { + ExpectedIsmSpecifierQueryMsg::IsmSpecifier(self) + } +} + #[cw_serde] pub struct ModuleTypeResponse { #[serde(rename = "type")] @@ -82,7 +95,7 @@ pub fn recipient( ) -> StdResult> { let res = querier.query_wasm_smart::( recipient, - &IsmSpecifierQueryMsg::InterchainSecurityModule(), + &IsmSpecifierQueryMsg::InterchainSecurityModule().wrap(), )?; Ok(res.ism) diff --git a/packages/interface/src/types/bech32.rs b/packages/interface/src/types/bech32.rs index 4470eaee..8ad751d7 100644 --- a/packages/interface/src/types/bech32.rs +++ b/packages/interface/src/types/bech32.rs @@ -46,6 +46,7 @@ pub fn bech32_encode(hrp: &str, raw_addr: &[u8]) -> StdResult { #[cfg(test)] mod tests { + use cosmwasm_std::HexBinary; use rstest::rstest; use crate::types::bech32_to_h256; @@ -64,6 +65,15 @@ mod tests { ); } + #[test] + fn conv() { + let addr = "dual1dwnrgwsf5c9vqjxsax04pdm0mx007yrraj2dgn"; + let addr_bin = bech32_decode(addr).unwrap(); + let addr_hex = HexBinary::from(addr_bin).to_hex(); + + println!("{}", addr_hex); + } + #[rstest] #[case( "osmo", diff --git a/packages/interface/src/warp/cw20.rs b/packages/interface/src/warp/cw20.rs index fbdc4794..b820f9a1 100644 --- a/packages/interface/src/warp/cw20.rs +++ b/packages/interface/src/warp/cw20.rs @@ -1,8 +1,9 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::HexBinary; +use cosmwasm_std::{HexBinary, Uint128}; use crate::{ core, + ism::IsmSpecifierQueryMsg, ownable::{OwnableMsg, OwnableQueryMsg}, router::{self, RouterQuery}, }; @@ -42,25 +43,20 @@ pub struct InstantiateMsg { pub mailbox: String, } -#[cw_serde] -pub enum ReceiveMsg { - // transfer to remote - TransferRemote { - dest_domain: u32, - recipient: HexBinary, - }, -} - #[cw_serde] pub enum ExecuteMsg { Ownable(OwnableMsg), Router(router::RouterMsg), - /// handle transfer remote + // handle transfer remote Handle(core::HandleMsg), - // cw20 receiver - Receive(cw20::Cw20ReceiveMsg), + // transfer to remote + TransferRemote { + dest_domain: u32, + recipient: HexBinary, + amount: Uint128, + }, } #[cw_serde] @@ -68,6 +64,10 @@ pub enum ExecuteMsg { #[query_responses(nested)] pub enum QueryMsg { Ownable(OwnableQueryMsg), + Router(RouterQuery), + TokenDefault(TokenWarpDefaultQueryMsg), + + IsmSpecifier(IsmSpecifierQueryMsg), } diff --git a/packages/interface/src/warp/native.rs b/packages/interface/src/warp/native.rs index 35a91079..46deb51a 100644 --- a/packages/interface/src/warp/native.rs +++ b/packages/interface/src/warp/native.rs @@ -1,8 +1,9 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::HexBinary; +use cosmwasm_std::{HexBinary, Uint128}; use crate::{ core, + ism::IsmSpecifierQueryMsg, ownable::{OwnableMsg, OwnableQueryMsg}, router::{RouterMsg, RouterQuery}, }; @@ -62,6 +63,7 @@ pub enum ExecuteMsg { TransferRemote { dest_domain: u32, recipient: HexBinary, + amount: Uint128, }, } @@ -74,6 +76,8 @@ pub enum QueryMsg { Router(RouterQuery), TokenDefault(TokenWarpDefaultQueryMsg), + + IsmSpecifier(IsmSpecifierQueryMsg), } mod as_str { diff --git a/scripts/.gitignore b/scripts/.gitignore index 08786d6c..ffbc4e4d 100644 --- a/scripts/.gitignore +++ b/scripts/.gitignore @@ -1,4 +1,6 @@ /dist .env config.yaml +config.*.yaml +!config.example.yaml /node_modules diff --git a/scripts/action/migrate.ts b/scripts/action/migrate.ts index 5deee01e..d26d99d9 100644 --- a/scripts/action/migrate.ts +++ b/scripts/action/migrate.ts @@ -1,19 +1,13 @@ -import { ExecuteResult } from "@cosmjs/cosmwasm-stargate"; +import { Event } from "@cosmjs/cosmwasm-stargate"; import { config, getSigningClient } from "../src/config"; -import HplHookAggregate from "../src/contracts/hpl_hook_aggregate"; -import HplIgp from "../src/contracts/hpl_igp"; -import HplMailbox from "../src/contracts/hpl_mailbox"; -import HplTestMockHook from "../src/contracts/hpl_test_mock_hook"; -import { loadContext, saveContext } from "../src/load_context"; +import { loadContext } from "../src/load_context"; import { ContractFetcher } from "./fetch"; -const parseWasmEventLog = (res: ExecuteResult) => { - return res.events - .filter((v) => v.type.startsWith("wasm")) - .map((v) => ({ - "@type": v.type.slice(5), - ...Object.fromEntries(v.attributes.map((x) => [x.key, x.value])), - })); +const parseEventLog = (events: readonly Event[]) => { + return events.map((v) => ({ + "@type": v.type.slice(5), + ...Object.fromEntries(v.attributes.map((x) => [x.key, x.value])), + })); }; async function main() { @@ -21,50 +15,16 @@ async function main() { const ctx = loadContext(config.network.id); - const fetcher = new ContractFetcher(ctx, client); + const contracts = new ContractFetcher(ctx, client).getContracts(); - const igp = fetcher.get(HplIgp, "hpl_igp"); - const mailbox = fetcher.get(HplMailbox, "hpl_mailbox"); - const hook_test = fetcher.get(HplTestMockHook, "hpl_test_mock_hook"); - const hook_merkle = fetcher.get(HplHookAggregate, "hpl_hook_merkle"); - const hook_aggregate = fetcher.get(HplHookAggregate, "hpl_hook_aggregate"); - - ctx.contracts["hpl_hook_merkle"] = await hook_merkle.instantiate({ - owner: client.signer, - mailbox: mailbox.address!, - }); - console.log(ctx.contracts["hpl_hook_merkle"].address!); - - ctx.contracts["hpl_hook_aggregate"] = await hook_aggregate.instantiate({ - owner: client.signer, - hooks: [igp.address!, ctx.contracts["hpl_hook_merkle"].address!], - }); - - const res = await client.wasm.executeMultiple( + const migrate_resp = await client.wasm.migrate( client.signer, - [ - { - contractAddress: mailbox.address!, - msg: { - set_default_hook: { - hook: hook_test.address!, - }, - }, - }, - { - contractAddress: mailbox.address!, - msg: { - set_required_hook: { - hook: hook_aggregate.address!, - }, - }, - }, - ], + "dual1rvtgvc38sfd9zehtgsp3eh8k269naq949u5qdcqm3x35mjg2uctqfdn3yq", + contracts.mocks.receiver.codeId!, + {}, "auto" ); - console.log(parseWasmEventLog(res)); - - saveContext(config.network.id, ctx); + console.log(parseEventLog(migrate_resp.events)); } main().catch(console.error); diff --git a/scripts/action/warp.ts b/scripts/action/warp.ts index 4ed62aff..42d5d9f7 100644 --- a/scripts/action/warp.ts +++ b/scripts/action/warp.ts @@ -1,35 +1,153 @@ +import { version } from "../package.json"; import { loadContext } from "../src/load_context"; -import HplMailbox from "../src/contracts/hpl_mailbox"; -import HplWarpNative from "../src/contracts/hpl_warp_native"; import { config, getSigningClient } from "../src/config"; import { ContractFetcher } from "./fetch"; +import { addPad } from "../src/conv"; +import { ExecuteResult } from "@cosmjs/cosmwasm-stargate"; +import { Command } from "commander"; -async function main() { - const client = await getSigningClient(config); +const program = new Command(); + +program.name("Warp CLI").version(version); + +program + .command("new") + .argument("", 'token denom, e.g. "untrn"') + .option( + "--token-mode ", + 'token mode, e.g. "collateral" or "bridged"', + "collateral" + ) + .action(create); + +program + .command("link") + .argument("
", "address of internal warp route") + .argument("", "domain of external chain, e.g. 5 (goerli)") + .argument("", "address of external route") + .action(link); + +program + .command("transfer") + .argument("
", "address of internal warp route") + .argument("", "domain of external chain, e.g. 5 (goerli)") + .argument("", "recipient address") + .argument("") + .action(transfer); +program.parseAsync(process.argv).catch(console.error); + +const parseWasmEventLog = (res: ExecuteResult) => { + return ( + res.events + // .filter((v) => v.type.startsWith("wasm")) + .map((v) => ({ + "@type": v.type.slice(5), + ...Object.fromEntries(v.attributes.map((x) => [x.key, x.value])), + })) + ); +}; + +async function create( + denom: string, + { tokenMode }: { tokenMode: "collateral" | "bridged" } +) { + const client = await getSigningClient(config); const ctx = loadContext(config.network.id); const fetcher = new ContractFetcher(ctx, client); + const { + core: { mailbox }, + warp, + } = fetcher.getContracts(); + + switch (tokenMode) { + case "collateral": + const ibc_route = await warp.native.instantiate({ + token: { + collateral: { + denom, + }, + }, + hrp: config.network.hrp, + owner: client.signer, + mailbox: mailbox.address!, + }); + + console.log("ibc_route", ibc_route); + return; + case "bridged": + throw Error("not implemented"); + } +} - const mailbox = fetcher.get(HplMailbox, "hpl_mailbox"); +async function link(address: string, domain: string, external_route: string) { + const client = await getSigningClient(config); - const warp_native = fetcher.get(HplWarpNative, "hpl_warp_native"); + const resp = await client.wasm.execute( + client.signer, + address, + { + router: { + set_route: { + set: { + domain: Number(domain), + route: addPad(external_route), + }, + }, + }, + }, + "auto" + ); + console.log(parseWasmEventLog(resp)); + console.log(resp.transactionHash); +} - const target_denom = - "ibc/B5CB286F69D48B2C4F6F8D8CF59011C40590DCF8A91617A5FBA9FF0A7B21307F"; +async function transfer( + address: string, + domain: string, + recipient: string, + amount: string +) { + const client = await getSigningClient(config); - const ibc_route = await warp_native.instantiate({ - token: { - collateral: { - denom: target_denom, + const { + type: { + native: { + fungible: { denom }, }, }, - hrp: "dual", - owner: client.signer, - mailbox: mailbox.address!, + }: { + type: { + native: { + fungible: { + denom: string; + }; + }; + }; + } = await client.wasm.queryContractSmart(address, { + token_default: { + token_type: {}, + }, }); - console.log("ibc_route", ibc_route); + const resp = await client.wasm.execute( + client.signer, + address, + { + transfer_remote: { + dest_domain: Number(domain), + recipient: addPad(recipient), + amount, + }, + }, + "auto", + undefined, + [ + { amount, denom }, + { amount: "2500000", denom: "token" }, + ] + ); + console.log(parseWasmEventLog(resp)); + console.log(resp.transactionHash); } - -main().catch(console.error); diff --git a/scripts/context/duality-devnet.json b/scripts/context/duality-devnet.json index 4ce9c082..ad22e7d3 100644 --- a/scripts/context/duality-devnet.json +++ b/scripts/context/duality-devnet.json @@ -1,81 +1,81 @@ { "contracts": { "hpl_hook_merkle": { - "codeId": 69, - "digest": "986cfcb90f4491d5ca87c57cdb1623f46daac16a8c2dfc5b0afa22f7f5876fab" + "codeId": 94, + "digest": "1a13b59eac4f6262bff526ce16e8d1c82639d30cde6d599183410a8ce7a5abb4" }, "hpl_igp": { "address": "dual14cu2z6xw62cumt7hmfw7977jyaymr26jazxdpvfp7ag2dss29q2q700yum", - "codeId": 74, - "digest": "8bec7d6dd094fcb1ff4d89ec3631b08242215d003e8cb1e46454ef5713c399e8" + "codeId": 99, + "digest": "8a68a04c72717598b57dba73a16aa3ccad25498ecc1735d8f49b8e650028709a" }, "hpl_igp_oracle": { "address": "dual1alp24grk7e5f3msv0apk68hehspglpr7y7upfrvkpdfu7c84sqtq8ze5p2", - "codeId": 75, - "digest": "e076ecc28e12972d66b9a7a87aa1ed210752f64ed682e14d45e12236a41f364c" + "codeId": 100, + "digest": "8533c3893d4aa47c845cfa4dcb119f2958c35438538ebba413c2677d47c1306b" }, "hpl_ism_multisig": { - "codeId": 77, - "digest": "435f13deae9c7b7cb0d244bdbe9ae095c27c69bf81f4b9857be68e8b9174f4de" + "codeId": 102, + "digest": "98dc12a30bc151941a89ecc81656470a8bdba299f2c56f26a1a04a7567222714" }, "hpl_mailbox": { "address": "dual1mveu0r9rj4qa6aqxt8almpha6cqluu397y8jd6r4jhzm3hmtmndq8lvk47", - "codeId": 79, - "digest": "2ba401ab148c755c935b7f5aaed0da373aef165fa712390161d1b9e9e3b2a3d0" + "codeId": 104, + "digest": "355c5c5ef5168a099d03d6cd89529211dec206b6ed14ca778a98620ef280f652" }, "hpl_test_mock_hook": { - "codeId": 80, - "digest": "7a4413b88f334a0ac0009a68887cee1cd160fbb83e3b8dd87bd4489b44d28bdb" + "codeId": 105, + "digest": "d17655f65e364f259c4f48c2b67288d852f4e5ed38fbaef5aa4946f3b641b138" }, "hpl_test_mock_msg_receiver": { "address": "dual1rvtgvc38sfd9zehtgsp3eh8k269naq949u5qdcqm3x35mjg2uctqfdn3yq", - "codeId": 81, - "digest": "3718a207a02ba1d9ee4c57ee33a2ae73962b5eb27e542836c069840914e2be89" + "codeId": 106, + "digest": "41a067c8d55912c6fefe559e508ae0bbab81ed0caae4db8c8d2f2f732f4f6f18" }, "hpl_validator_announce": { "address": "dual1982lwq4rt4qntkv2hafvvtwupn7hgqqkv0kpf55yahlh6pqeldvqd24lld", - "codeId": 82, - "digest": "3e3172f31c46d51b4e25d151eeee286919af129eb66714d88610838b2c1405ca" + "codeId": 107, + "digest": "78ac1d373a9f21b7bd4698d74f762882672db2af56fffe8501ece9b2cfadb964" }, "hpl_warp_cw20": { - "codeId": 83, - "digest": "6a3a1a6a3a85109842ced3c50c8a30c2bd50e7f3648271f634190e58aa62ff19" + "codeId": 108, + "digest": "bf517d714856673acf628602284eca5f1a3cb27e4277b64c70b502cf603312c6" }, "hpl_warp_native": { - "codeId": 84, - "digest": "5d3fa9232d3873cef2918677203d785286add4f48d95353a70be646c27d30198" + "codeId": 109, + "digest": "858366c03a932325bd34b080474e8ed1f2e313161cb41c3e68402c837c2b2b88" }, "hpl_warp_native_ibc": { "codeId": 32, "digest": "eed9d7dfa18163de64893cfe88e038cfc82e487561c94b142978efdacc1f9dff" }, "hpl_hook_aggregate": { - "codeId": 68, - "digest": "1d0d021c3200bdae970d4a17b28aa3d35136953e3a931c77acb422c466d3a2c1" + "codeId": 93, + "digest": "fb05c417ced388eabb743fefaa3f89dd2ae8e720c63947143e4027d1a34101a9" }, "hpl_hook_pausable": { - "codeId": 70, - "digest": "9df275eabf0d4683314935b6cb8dbcd4f68f08bc308d2d979afccbbe34ea3f18" + "codeId": 95, + "digest": "c81b4829045035372f1cdd190d6f99be912165f0624d763f9eae4fdaf49db1d9" }, "hpl_hook_routing": { - "codeId": 71, - "digest": "4f96a3a784102a172955b0b1f33bab1cf50380f13fe2aefa00880368c401bfba" + "codeId": 96, + "digest": "eeb2548015d1cd0d887e1b0387303d840a7cf744bc5834d6af369785bef23aa9" }, "hpl_hook_routing_custom": { - "codeId": 72, - "digest": "3bd3d02c02feb98f7e1e2009571b43ef36c0f4ec702e0669cec6ecddf3e3c3dd" + "codeId": 97, + "digest": "f5fa5986cc177096d56ec40e14c71d4c4830b59feafe585d249469420bdb5cc7" }, "hpl_hook_routing_fallback": { - "codeId": 73, - "digest": "49f5ce6e6b175b2c15046d2f0e628b6d4dea4ba7f97decfce2de48b4a24c444b" + "codeId": 98, + "digest": "7363e92061dfdea646729f3cfbc1e4bd718a276b1e130600b11f3fd1541575cb" }, "hpl_ism_aggregate": { - "codeId": 76, - "digest": "4d5d558efc0ea10d2e139b2aae9c356f5c5007f15cb8ab4ed77c202213268f0b" + "codeId": 101, + "digest": "16c799ac7e57a8cb51eaf702b14948950dc72ab7e266f1615cbb727547b171c7" }, "hpl_ism_routing": { - "codeId": 78, - "digest": "f8ec1fd4f6638c4d8d82606406d4fd4f6e938ef711c7f1c529fd010953ce55d3" + "codeId": 103, + "digest": "228a9c770b32701980f05a928c617c4c1811939454a1f5230550600635e98aaa" }, "hpl_default_ism": { "codeId": 77, @@ -92,5 +92,6 @@ "digest": "1d0d021c3200bdae970d4a17b28aa3d35136953e3a931c77acb422c466d3a2c1", "address": "dual1nz2n6vfk5fjlex2qquupyaqhaucr4xn30lm0teqwh0r0zv0v9xhsky7h3a" } - } -} + }, + "address": "dual1dwnrgwsf5c9vqjxsax04pdm0mx007yrraj2dgn" +} \ No newline at end of file diff --git a/scripts/context/neutron-1.json b/scripts/context/neutron-1.json new file mode 100644 index 00000000..3320eb8b --- /dev/null +++ b/scripts/context/neutron-1.json @@ -0,0 +1,73 @@ +{ + "contracts": { + "hpl_hook_aggregate": { + "codeId": 384, + "digest": "03fedc6af9ad56aa247993d451cbf0669ce91035e87cd43cbeecb14114ce4ff5" + }, + "hpl_hook_merkle": { + "codeId": 385, + "digest": "b68a6715634a0a567f699c98539e87ac779649e850102bab98610116e2e10259" + }, + "hpl_hook_pausable": { + "codeId": 386, + "digest": "aeeb15ed555fbcedd8f1535498888b45ecf21a521871c4f683c1503c6cbb39e9" + }, + "hpl_hook_routing": { + "codeId": 387, + "digest": "601f0a02aa236376c50fa4f4f23cd90f3ce4535cb2fe2e8756afcd31ecefc859" + }, + "hpl_hook_routing_custom": { + "codeId": 388, + "digest": "24bcc5053094cd9280f1cc3d1bd9b76312bffa8d0b8bdf8743d4d1aa27f3efae" + }, + "hpl_hook_routing_fallback": { + "codeId": 389, + "digest": "27d6754aeaa78242e99445938ab73e6b2837d5ada8eb7c708ea7f3bf7b3861ba" + }, + "hpl_igp": { + "codeId": 390, + "digest": "377fd5ddc2f9866c69aa6858f8a75683c5d3ee461fdb33658237f0e5951b4e7d" + }, + "hpl_igp_oracle": { + "codeId": 391, + "digest": "0d0ffc64d0ec318d99f11a68f69b6fa0d4004b7fd387a677c621d4efdb8ea7d1" + }, + "hpl_ism_aggregate": { + "codeId": 392, + "digest": "a1ed31b7b55bbee4b293ac78da9cc54ad1464e058122b5feda6cfba3e82ae3a8" + }, + "hpl_ism_multisig": { + "codeId": 393, + "digest": "d38bb495b36d4ae7a8055e12ea9e6e295ca26009094028c401d89688c4612d9e" + }, + "hpl_ism_routing": { + "codeId": 394, + "digest": "aa851324bb1d402ba1445223c7b7fa50641b7fb8372030983daca9082166fa95" + }, + "hpl_mailbox": { + "codeId": 395, + "digest": "c28afc65e96bf730f7654e15cbcb962d603065581664907655fcaabf8745fb2a" + }, + "hpl_test_mock_hook": { + "codeId": 396, + "digest": "bbbdd167b7acebed3283a7482cb032666ee13201442bafd9814067765303e797" + }, + "hpl_test_mock_msg_receiver": { + "codeId": 397, + "digest": "54068ecbb73db316e756c6d1343896e3920175985430d24c477f6abe0db77757" + }, + "hpl_validator_announce": { + "codeId": 398, + "digest": "70042fe83ab067f7f1a4420dded5a78ce1b9e7832f4e9639c641f8268aed5b75" + }, + "hpl_warp_cw20": { + "codeId": 399, + "digest": "533b8f38e853a832b4c69f65e8c87cb75604e5e03b3c353c210f4828140f8881" + }, + "hpl_warp_native": { + "codeId": 400, + "digest": "b245ed32563f6bb4a9f9046d28b36e757ea0ee168f8a3998b0b9ac1dcfc4f0f8" + } + }, + "address": "neutron1ugu6ktzqwrmr0pgw603kakuhvj46a7mmrl6pw3" +} \ No newline at end of file From e4a30eb3bb1301d418df4d4808c9f1d81458bb4d Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Sat, 28 Oct 2023 00:57:30 +0900 Subject: [PATCH 10/53] feat(warp): make ism configurable --- contracts/warp/cw20/src/contract.rs | 22 +++++++++++++++++++--- contracts/warp/cw20/src/lib.rs | 3 +++ contracts/warp/native/src/contract.rs | 19 +++++++++++++++++-- contracts/warp/native/src/lib.rs | 3 +++ packages/interface/src/warp/cw20.rs | 4 ++++ packages/interface/src/warp/native.rs | 4 ++++ 6 files changed, 50 insertions(+), 5 deletions(-) diff --git a/contracts/warp/cw20/src/contract.rs b/contracts/warp/cw20/src/contract.rs index 70a67cb4..ce822191 100644 --- a/contracts/warp/cw20/src/contract.rs +++ b/contracts/warp/cw20/src/contract.rs @@ -17,11 +17,12 @@ use hpl_interface::{ TokenMode, TokenModeMsg, TokenModeResponse, TokenTypeResponse, }, }; +use hpl_ownable::get_owner; use hpl_router::get_route; use crate::{ - conv, error::ContractError, new_event, CONTRACT_NAME, CONTRACT_VERSION, HRP, MAILBOX, MODE, - REPLY_ID_CREATE_DENOM, TOKEN, + conv, error::ContractError, new_event, CONTRACT_NAME, CONTRACT_VERSION, HRP, ISM, MAILBOX, + MODE, REPLY_ID_CREATE_DENOM, TOKEN, }; #[cfg_attr(not(feature = "library"), entry_point)] @@ -98,6 +99,19 @@ pub fn execute( recipient, amount, } => transfer_remote(deps, env, info, dest_domain, recipient, amount), + SetIsm { ism } => { + ensure_eq!( + get_owner(deps.storage)?, + info.sender, + ContractError::Unauthorized + ); + + let ism_addr = deps.api.addr_validate(&ism)?; + + ISM.save(deps.storage, &ism_addr)?; + + Ok(Response::new().add_event(new_event("set-ism").add_attribute("ism", ism_addr))) + } } } @@ -238,7 +252,9 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result to_binary(get_token_mode(deps)), }, QueryMsg::IsmSpecifier(IsmSpecifierQueryMsg::InterchainSecurityModule()) => Ok( - cosmwasm_std::to_binary(&InterchainSecurityModuleResponse { ism: None })?, + cosmwasm_std::to_binary(&InterchainSecurityModuleResponse { + ism: ISM.may_load(deps.storage)?, + })?, ), } } diff --git a/contracts/warp/cw20/src/lib.rs b/contracts/warp/cw20/src/lib.rs index 0924bda9..cd4d263b 100644 --- a/contracts/warp/cw20/src/lib.rs +++ b/contracts/warp/cw20/src/lib.rs @@ -29,6 +29,9 @@ const HRP: Item = Item::new(HRP_KEY); const MAILBOX_KEY: &str = "mailbox"; const MAILBOX: Item = Item::new(MAILBOX_KEY); +const ISM_KEY: &str = "ism"; +const ISM: Item = Item::new(ISM_KEY); + fn new_event(name: &str) -> Event { Event::new(format!("hpl_warp_cw20::{name}")) } diff --git a/contracts/warp/native/src/contract.rs b/contracts/warp/native/src/contract.rs index 1db173ff..f8ce70b1 100644 --- a/contracts/warp/native/src/contract.rs +++ b/contracts/warp/native/src/contract.rs @@ -15,6 +15,7 @@ use hpl_interface::{ }, warp::{TokenMode, TokenModeMsg, TokenModeResponse, TokenTypeResponse}, }; +use hpl_ownable::get_owner; use hpl_router::get_route; use crate::{ @@ -22,7 +23,7 @@ use crate::{ error::ContractError, new_event, proto::{MsgCreateDenom, MsgCreateDenomResponse}, - CONTRACT_NAME, CONTRACT_VERSION, HRP, MAILBOX, MODE, REPLY_ID_CREATE_DENOM, TOKEN, + CONTRACT_NAME, CONTRACT_VERSION, HRP, ISM, MAILBOX, MODE, REPLY_ID_CREATE_DENOM, TOKEN, }; #[cfg_attr(not(feature = "library"), entry_point)] @@ -99,6 +100,18 @@ pub fn execute( recipient, amount, } => transfer_remote(deps, env, info, dest_domain, recipient, amount), + SetIsm { ism } => { + ensure_eq!( + get_owner(deps.storage)?, + info.sender, + ContractError::Unauthorized + ); + + let ism_addr = deps.api.addr_validate(&ism)?; + ISM.save(deps.storage, &ism_addr)?; + + Ok(Response::new().add_event(new_event("set-ism").add_attribute("ism", ism_addr))) + } } } @@ -248,7 +261,9 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result to_binary(get_token_mode(deps)), }, QueryMsg::IsmSpecifier(IsmSpecifierQueryMsg::InterchainSecurityModule()) => Ok( - cosmwasm_std::to_binary(&InterchainSecurityModuleResponse { ism: None })?, + cosmwasm_std::to_binary(&InterchainSecurityModuleResponse { + ism: ISM.may_load(deps.storage)?, + })?, ), } } diff --git a/contracts/warp/native/src/lib.rs b/contracts/warp/native/src/lib.rs index 3b625710..3051e7a3 100644 --- a/contracts/warp/native/src/lib.rs +++ b/contracts/warp/native/src/lib.rs @@ -30,6 +30,9 @@ const HRP: Item = Item::new(HRP_KEY); const MAILBOX_KEY: &str = "mailbox"; const MAILBOX: Item = Item::new(MAILBOX_KEY); +const ISM_KEY: &str = "ism"; +const ISM: Item = Item::new(ISM_KEY); + fn new_event(name: &str) -> Event { Event::new(format!("hpl_warp_native::{name}")) } diff --git a/packages/interface/src/warp/cw20.rs b/packages/interface/src/warp/cw20.rs index b820f9a1..30d49092 100644 --- a/packages/interface/src/warp/cw20.rs +++ b/packages/interface/src/warp/cw20.rs @@ -57,6 +57,10 @@ pub enum ExecuteMsg { recipient: HexBinary, amount: Uint128, }, + + SetIsm { + ism: String, + }, } #[cw_serde] diff --git a/packages/interface/src/warp/native.rs b/packages/interface/src/warp/native.rs index 46deb51a..2103078c 100644 --- a/packages/interface/src/warp/native.rs +++ b/packages/interface/src/warp/native.rs @@ -65,6 +65,10 @@ pub enum ExecuteMsg { recipient: HexBinary, amount: Uint128, }, + + SetIsm { + ism: String, + }, } #[cw_serde] From f1a828e541a9b93a116750907668153848872c8d Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Sat, 28 Oct 2023 00:59:15 +0900 Subject: [PATCH 11/53] feat(warp): make hook configurable --- contracts/warp/cw20/src/contract.rs | 17 +++++++++++++++-- contracts/warp/cw20/src/lib.rs | 3 +++ contracts/warp/native/src/contract.rs | 15 ++++++++++++++- contracts/warp/native/src/lib.rs | 3 +++ packages/interface/src/warp/cw20.rs | 3 +++ packages/interface/src/warp/native.rs | 3 +++ 6 files changed, 41 insertions(+), 3 deletions(-) diff --git a/contracts/warp/cw20/src/contract.rs b/contracts/warp/cw20/src/contract.rs index ce822191..08e38900 100644 --- a/contracts/warp/cw20/src/contract.rs +++ b/contracts/warp/cw20/src/contract.rs @@ -21,8 +21,8 @@ use hpl_ownable::get_owner; use hpl_router::get_route; use crate::{ - conv, error::ContractError, new_event, CONTRACT_NAME, CONTRACT_VERSION, HRP, ISM, MAILBOX, - MODE, REPLY_ID_CREATE_DENOM, TOKEN, + conv, error::ContractError, new_event, CONTRACT_NAME, CONTRACT_VERSION, HOOK, HRP, ISM, + MAILBOX, MODE, REPLY_ID_CREATE_DENOM, TOKEN, }; #[cfg_attr(not(feature = "library"), entry_point)] @@ -112,6 +112,19 @@ pub fn execute( Ok(Response::new().add_event(new_event("set-ism").add_attribute("ism", ism_addr))) } + SetHook { hook } => { + ensure_eq!( + get_owner(deps.storage)?, + info.sender, + ContractError::Unauthorized + ); + + let hook_addr = deps.api.addr_validate(&hook)?; + + HOOK.save(deps.storage, &hook_addr)?; + + Ok(Response::new().add_event(new_event("set-hook").add_attribute("hook", hook_addr))) + } } } diff --git a/contracts/warp/cw20/src/lib.rs b/contracts/warp/cw20/src/lib.rs index cd4d263b..8130666e 100644 --- a/contracts/warp/cw20/src/lib.rs +++ b/contracts/warp/cw20/src/lib.rs @@ -32,6 +32,9 @@ const MAILBOX: Item = Item::new(MAILBOX_KEY); const ISM_KEY: &str = "ism"; const ISM: Item = Item::new(ISM_KEY); +const HOOK_KEY: &str = "hook"; +const HOOK: Item = Item::new(HOOK_KEY); + fn new_event(name: &str) -> Event { Event::new(format!("hpl_warp_cw20::{name}")) } diff --git a/contracts/warp/native/src/contract.rs b/contracts/warp/native/src/contract.rs index f8ce70b1..0a4fd8fd 100644 --- a/contracts/warp/native/src/contract.rs +++ b/contracts/warp/native/src/contract.rs @@ -23,7 +23,7 @@ use crate::{ error::ContractError, new_event, proto::{MsgCreateDenom, MsgCreateDenomResponse}, - CONTRACT_NAME, CONTRACT_VERSION, HRP, ISM, MAILBOX, MODE, REPLY_ID_CREATE_DENOM, TOKEN, + CONTRACT_NAME, CONTRACT_VERSION, HOOK, HRP, ISM, MAILBOX, MODE, REPLY_ID_CREATE_DENOM, TOKEN, }; #[cfg_attr(not(feature = "library"), entry_point)] @@ -112,6 +112,19 @@ pub fn execute( Ok(Response::new().add_event(new_event("set-ism").add_attribute("ism", ism_addr))) } + SetHook { hook } => { + ensure_eq!( + get_owner(deps.storage)?, + info.sender, + ContractError::Unauthorized + ); + + let hook_addr = deps.api.addr_validate(&hook)?; + + HOOK.save(deps.storage, &hook_addr)?; + + Ok(Response::new().add_event(new_event("set-hook").add_attribute("hook", hook_addr))) + } } } diff --git a/contracts/warp/native/src/lib.rs b/contracts/warp/native/src/lib.rs index 3051e7a3..5f69b92a 100644 --- a/contracts/warp/native/src/lib.rs +++ b/contracts/warp/native/src/lib.rs @@ -33,6 +33,9 @@ const MAILBOX: Item = Item::new(MAILBOX_KEY); const ISM_KEY: &str = "ism"; const ISM: Item = Item::new(ISM_KEY); +const HOOK_KEY: &str = "hook"; +const HOOK: Item = Item::new(HOOK_KEY); + fn new_event(name: &str) -> Event { Event::new(format!("hpl_warp_native::{name}")) } diff --git a/packages/interface/src/warp/cw20.rs b/packages/interface/src/warp/cw20.rs index 30d49092..2474ebec 100644 --- a/packages/interface/src/warp/cw20.rs +++ b/packages/interface/src/warp/cw20.rs @@ -61,6 +61,9 @@ pub enum ExecuteMsg { SetIsm { ism: String, }, + SetHook { + hook: String, + }, } #[cw_serde] diff --git a/packages/interface/src/warp/native.rs b/packages/interface/src/warp/native.rs index 2103078c..c2d9420a 100644 --- a/packages/interface/src/warp/native.rs +++ b/packages/interface/src/warp/native.rs @@ -69,6 +69,9 @@ pub enum ExecuteMsg { SetIsm { ism: String, }, + SetHook { + hook: String, + }, } #[cw_serde] From 428abeeb2baeba1d6c81c83d8d34b9cb2853dfe3 Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Sat, 28 Oct 2023 01:02:04 +0900 Subject: [PATCH 12/53] feat(warp): apply hook --- contracts/warp/cw20/src/contract.rs | 2 +- contracts/warp/native/src/contract.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/warp/cw20/src/contract.rs b/contracts/warp/cw20/src/contract.rs index 08e38900..6e38a135 100644 --- a/contracts/warp/cw20/src/contract.rs +++ b/contracts/warp/cw20/src/contract.rs @@ -238,7 +238,7 @@ fn transfer_remote( metadata: HexBinary::default(), } .into(), - None, + HOOK.may_load(deps.storage)?.map(|v| v.into()), None, info.funds, )?); diff --git a/contracts/warp/native/src/contract.rs b/contracts/warp/native/src/contract.rs index 0a4fd8fd..0f151a7d 100644 --- a/contracts/warp/native/src/contract.rs +++ b/contracts/warp/native/src/contract.rs @@ -248,7 +248,7 @@ fn transfer_remote( dest_domain, dest_router, dispatch_payload.into(), - None, + HOOK.may_load(deps.storage)?.map(|v| v.into()), None, funds, )?); From 955a7e67815fa3d5202b8bd7d2714b8e388620fb Mon Sep 17 00:00:00 2001 From: ByeongSu Hong Date: Sat, 28 Oct 2023 09:00:30 +0900 Subject: [PATCH 13/53] feat: connection client (#58) --- Cargo.toml | 1 + contracts/isms/aggregate/src/lib.rs | 21 ++++- contracts/warp/cw20/Cargo.toml | 1 + contracts/warp/cw20/src/contract.rs | 38 ++------- contracts/warp/cw20/src/lib.rs | 6 -- contracts/warp/native/Cargo.toml | 1 + contracts/warp/native/src/contract.rs | 35 ++------- contracts/warp/native/src/lib.rs | 6 -- packages/connection/Cargo.toml | 38 +++++++++ packages/connection/src/lib.rs | 100 ++++++++++++++++++++++++ packages/interface/src/connection.rs | 14 ++-- packages/interface/src/ism/aggregate.rs | 14 ++++ packages/interface/src/warp/cw20.rs | 11 +-- packages/interface/src/warp/native.rs | 11 +-- 14 files changed, 201 insertions(+), 96 deletions(-) create mode 100644 packages/connection/Cargo.toml create mode 100644 packages/connection/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 44247c11..283425bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -109,6 +109,7 @@ hpl-warp-cw20 = { path = "./contracts/warp/cw20" } hpl-warp-native = { path = "./contracts/warp/native" } # workspace aliases (./packages) +hpl-connection = { path = "./packages/connection" } hpl-ownable = { path = "./packages/ownable" } hpl-pausable = { path = "./packages/pausable" } hpl-router = { path = "./packages/router" } diff --git a/contracts/isms/aggregate/src/lib.rs b/contracts/isms/aggregate/src/lib.rs index d77a841c..45d4e34e 100644 --- a/contracts/isms/aggregate/src/lib.rs +++ b/contracts/isms/aggregate/src/lib.rs @@ -4,14 +4,14 @@ pub use crate::error::ContractError; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - ensure_eq, Addr, Deps, DepsMut, Env, Event, HexBinary, MessageInfo, QueryResponse, Response, - StdResult, + ensure_eq, Addr, Deps, DepsMut, Empty, Env, Event, HexBinary, MessageInfo, QueryResponse, + Response, StdResult, }; use cw2::set_contract_version; use cw_storage_plus::Item; use hpl_interface::{ ism::{ - aggregate::{ExecuteMsg, InstantiateMsg, QueryMsg}, + aggregate::{AggregateIsmQueryMsg, ExecuteMsg, InstantiateMsg, IsmsResponse, QueryMsg}, IsmQueryMsg, IsmType, ModuleTypeResponse, VerifyInfoResponse, VerifyResponse, }, to_binary, @@ -108,6 +108,16 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result to_binary(verify(deps, metadata, message)), VerifyInfo { message } => to_binary(verify_info(deps, message)), }, + + QueryMsg::AggregateIsm(msg) => match msg { + AggregateIsmQueryMsg::Isms {} => Ok(cosmwasm_std::to_binary(&IsmsResponse { + isms: ISMS + .load(deps.storage)? + .into_iter() + .map(|v| v.into()) + .collect(), + })?), + }, } } @@ -147,3 +157,8 @@ fn verify_info(deps: Deps, _message: HexBinary) -> Result>()?, }) } + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate(_deps: DepsMut, _env: Env, _msg: Empty) -> Result { + Ok(Response::default()) +} diff --git a/contracts/warp/cw20/Cargo.toml b/contracts/warp/cw20/Cargo.toml index 4ed1c449..0c2de70e 100644 --- a/contracts/warp/cw20/Cargo.toml +++ b/contracts/warp/cw20/Cargo.toml @@ -38,6 +38,7 @@ schemars.workspace = true thiserror.workspace = true +hpl-connection.workspace = true hpl-ownable.workspace = true hpl-router.workspace = true hpl-interface.workspace = true diff --git a/contracts/warp/cw20/src/contract.rs b/contracts/warp/cw20/src/contract.rs index 6e38a135..cd5a0e8d 100644 --- a/contracts/warp/cw20/src/contract.rs +++ b/contracts/warp/cw20/src/contract.rs @@ -6,6 +6,7 @@ use cosmwasm_std::{ }; use cw20::Cw20ExecuteMsg; +use hpl_connection::{get_hook, get_ism}; use hpl_interface::{ core::mailbox, ism::{InterchainSecurityModuleResponse, IsmSpecifierQueryMsg}, @@ -17,12 +18,11 @@ use hpl_interface::{ TokenMode, TokenModeMsg, TokenModeResponse, TokenTypeResponse, }, }; -use hpl_ownable::get_owner; use hpl_router::get_route; use crate::{ - conv, error::ContractError, new_event, CONTRACT_NAME, CONTRACT_VERSION, HOOK, HRP, ISM, - MAILBOX, MODE, REPLY_ID_CREATE_DENOM, TOKEN, + conv, error::ContractError, new_event, CONTRACT_NAME, CONTRACT_VERSION, HRP, MAILBOX, MODE, + REPLY_ID_CREATE_DENOM, TOKEN, }; #[cfg_attr(not(feature = "library"), entry_point)] @@ -93,38 +93,13 @@ pub fn execute( match msg { Ownable(msg) => Ok(hpl_ownable::handle(deps, env, info, msg)?), Router(msg) => Ok(hpl_router::handle(deps, env, info, msg)?), + Connection(msg) => Ok(hpl_connection::handle(deps, env, info, msg)?), Handle(msg) => mailbox_handle(deps, info, msg), TransferRemote { dest_domain, recipient, amount, } => transfer_remote(deps, env, info, dest_domain, recipient, amount), - SetIsm { ism } => { - ensure_eq!( - get_owner(deps.storage)?, - info.sender, - ContractError::Unauthorized - ); - - let ism_addr = deps.api.addr_validate(&ism)?; - - ISM.save(deps.storage, &ism_addr)?; - - Ok(Response::new().add_event(new_event("set-ism").add_attribute("ism", ism_addr))) - } - SetHook { hook } => { - ensure_eq!( - get_owner(deps.storage)?, - info.sender, - ContractError::Unauthorized - ); - - let hook_addr = deps.api.addr_validate(&hook)?; - - HOOK.save(deps.storage, &hook_addr)?; - - Ok(Response::new().add_event(new_event("set-hook").add_attribute("hook", hook_addr))) - } } } @@ -238,7 +213,7 @@ fn transfer_remote( metadata: HexBinary::default(), } .into(), - HOOK.may_load(deps.storage)?.map(|v| v.into()), + get_hook(deps.storage)?.map(|v| v.into()), None, info.funds, )?); @@ -260,13 +235,14 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result Ok(hpl_ownable::handle_query(deps, env, msg)?), QueryMsg::Router(msg) => Ok(hpl_router::handle_query(deps, env, msg)?), + QueryMsg::Connection(msg) => Ok(hpl_connection::handle_query(deps, env, msg)?), QueryMsg::TokenDefault(msg) => match msg { TokenType {} => to_binary(get_token_type(deps)), TokenMode {} => to_binary(get_token_mode(deps)), }, QueryMsg::IsmSpecifier(IsmSpecifierQueryMsg::InterchainSecurityModule()) => Ok( cosmwasm_std::to_binary(&InterchainSecurityModuleResponse { - ism: ISM.may_load(deps.storage)?, + ism: get_ism(deps.storage)?, })?, ), } diff --git a/contracts/warp/cw20/src/lib.rs b/contracts/warp/cw20/src/lib.rs index 8130666e..0924bda9 100644 --- a/contracts/warp/cw20/src/lib.rs +++ b/contracts/warp/cw20/src/lib.rs @@ -29,12 +29,6 @@ const HRP: Item = Item::new(HRP_KEY); const MAILBOX_KEY: &str = "mailbox"; const MAILBOX: Item = Item::new(MAILBOX_KEY); -const ISM_KEY: &str = "ism"; -const ISM: Item = Item::new(ISM_KEY); - -const HOOK_KEY: &str = "hook"; -const HOOK: Item = Item::new(HOOK_KEY); - fn new_event(name: &str) -> Event { Event::new(format!("hpl_warp_cw20::{name}")) } diff --git a/contracts/warp/native/Cargo.toml b/contracts/warp/native/Cargo.toml index 9b0e3789..c17dce97 100644 --- a/contracts/warp/native/Cargo.toml +++ b/contracts/warp/native/Cargo.toml @@ -39,6 +39,7 @@ serde-json-wasm.workspace = true thiserror.workspace = true +hpl-connection.workspace = true hpl-ownable.workspace = true hpl-router.workspace = true hpl-interface.workspace = true diff --git a/contracts/warp/native/src/contract.rs b/contracts/warp/native/src/contract.rs index 0f151a7d..da89fa0d 100644 --- a/contracts/warp/native/src/contract.rs +++ b/contracts/warp/native/src/contract.rs @@ -4,6 +4,7 @@ use cosmwasm_std::{ ensure, ensure_eq, CosmosMsg, Deps, DepsMut, Empty, Env, HexBinary, MessageInfo, QueryResponse, Reply, Response, SubMsg, Uint128, Uint256, }; +use hpl_connection::{get_hook, get_ism}; use hpl_interface::{ core::mailbox, ism::{InterchainSecurityModuleResponse, IsmSpecifierQueryMsg}, @@ -15,7 +16,6 @@ use hpl_interface::{ }, warp::{TokenMode, TokenModeMsg, TokenModeResponse, TokenTypeResponse}, }; -use hpl_ownable::get_owner; use hpl_router::get_route; use crate::{ @@ -23,7 +23,7 @@ use crate::{ error::ContractError, new_event, proto::{MsgCreateDenom, MsgCreateDenomResponse}, - CONTRACT_NAME, CONTRACT_VERSION, HOOK, HRP, ISM, MAILBOX, MODE, REPLY_ID_CREATE_DENOM, TOKEN, + CONTRACT_NAME, CONTRACT_VERSION, HRP, MAILBOX, MODE, REPLY_ID_CREATE_DENOM, TOKEN, }; #[cfg_attr(not(feature = "library"), entry_point)] @@ -94,37 +94,13 @@ pub fn execute( match msg { Ownable(msg) => Ok(hpl_ownable::handle(deps, env, info, msg)?), Router(msg) => Ok(hpl_router::handle(deps, env, info, msg)?), + Connection(msg) => Ok(hpl_connection::handle(deps, env, info, msg)?), Handle(msg) => mailbox_handle(deps, env, info, msg), TransferRemote { dest_domain, recipient, amount, } => transfer_remote(deps, env, info, dest_domain, recipient, amount), - SetIsm { ism } => { - ensure_eq!( - get_owner(deps.storage)?, - info.sender, - ContractError::Unauthorized - ); - - let ism_addr = deps.api.addr_validate(&ism)?; - ISM.save(deps.storage, &ism_addr)?; - - Ok(Response::new().add_event(new_event("set-ism").add_attribute("ism", ism_addr))) - } - SetHook { hook } => { - ensure_eq!( - get_owner(deps.storage)?, - info.sender, - ContractError::Unauthorized - ); - - let hook_addr = deps.api.addr_validate(&hook)?; - - HOOK.save(deps.storage, &hook_addr)?; - - Ok(Response::new().add_event(new_event("set-hook").add_attribute("hook", hook_addr))) - } } } @@ -248,7 +224,7 @@ fn transfer_remote( dest_domain, dest_router, dispatch_payload.into(), - HOOK.may_load(deps.storage)?.map(|v| v.into()), + get_hook(deps.storage)?.map(|v| v.into()), None, funds, )?); @@ -269,13 +245,14 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result Ok(hpl_ownable::handle_query(deps, env, msg)?), QueryMsg::Router(msg) => Ok(hpl_router::handle_query(deps, env, msg)?), + QueryMsg::Connection(msg) => Ok(hpl_connection::handle_query(deps, env, msg)?), QueryMsg::TokenDefault(msg) => match msg { TokenType {} => to_binary(get_token_type(deps)), TokenMode {} => to_binary(get_token_mode(deps)), }, QueryMsg::IsmSpecifier(IsmSpecifierQueryMsg::InterchainSecurityModule()) => Ok( cosmwasm_std::to_binary(&InterchainSecurityModuleResponse { - ism: ISM.may_load(deps.storage)?, + ism: get_ism(deps.storage)?, })?, ), } diff --git a/contracts/warp/native/src/lib.rs b/contracts/warp/native/src/lib.rs index 5f69b92a..3b625710 100644 --- a/contracts/warp/native/src/lib.rs +++ b/contracts/warp/native/src/lib.rs @@ -30,12 +30,6 @@ const HRP: Item = Item::new(HRP_KEY); const MAILBOX_KEY: &str = "mailbox"; const MAILBOX: Item = Item::new(MAILBOX_KEY); -const ISM_KEY: &str = "ism"; -const ISM: Item = Item::new(ISM_KEY); - -const HOOK_KEY: &str = "hook"; -const HOOK: Item = Item::new(HOOK_KEY); - fn new_event(name: &str) -> Event { Event::new(format!("hpl_warp_native::{name}")) } diff --git a/packages/connection/Cargo.toml b/packages/connection/Cargo.toml new file mode 100644 index 00000000..4eaf5e0b --- /dev/null +++ b/packages/connection/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "hpl-connection" +version.workspace = true +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true +documentation.workspace = true +keywords.workspace = true + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +# for more explicit tests, cargo test --features=backtraces +backtraces = ["cosmwasm-std/backtraces"] +# use library feature to disable all instantiate/execute/query exports +library = [] + +[dependencies] +cosmwasm-std.workspace = true +cosmwasm-storage.workspace = true +cw-storage-plus.workspace = true +cw2.workspace = true +bech32.workspace = true +sha3.workspace = true +schemars.workspace = true +serde.workspace = true +serde-json-wasm.workspace = true +thiserror.workspace = true +cosmwasm-schema.workspace = true + +hpl-ownable.workspace = true +hpl-interface.workspace = true + +[dev-dependencies] +anyhow.workspace = true diff --git a/packages/connection/src/lib.rs b/packages/connection/src/lib.rs new file mode 100644 index 00000000..5506c9d8 --- /dev/null +++ b/packages/connection/src/lib.rs @@ -0,0 +1,100 @@ +use cosmwasm_std::{ + ensure_eq, Addr, CustomQuery, Deps, DepsMut, Env, Event, MessageInfo, QueryResponse, Response, + StdError, StdResult, Storage, +}; +use cw_storage_plus::Item; +use hpl_interface::connection::{ + ConnectionMsg, ConnectionQueryMsg, HookResponse, IsmResponse, MailboxResponse, +}; + +const MAILBOX_KEY: &str = "conn::mailbox"; +const MAILBOX: Item = Item::new(MAILBOX_KEY); + +const ISM_KEY: &str = "conn::ism"; +const ISM: Item = Item::new(ISM_KEY); + +const HOOK_KEY: &str = "conn::hook"; +const HOOK: Item = Item::new(HOOK_KEY); + +fn event_to_resp(event: Event) -> Response { + Response::new().add_event(event) +} + +fn new_event(name: &str) -> Event { + Event::new(format!("hpl_connection::{}", name)) +} + +pub fn handle( + deps: DepsMut<'_, C>, + _env: Env, + info: MessageInfo, + msg: ConnectionMsg, +) -> StdResult { + use ConnectionMsg::*; + + ensure_eq!( + hpl_ownable::get_owner(deps.storage)?, + info.sender, + StdError::generic_err("unauthorized") + ); + + match msg { + SetMailbox { mailbox } => { + let mailbox_addr = deps.api.addr_validate(&mailbox)?; + + MAILBOX.save(deps.storage, &mailbox_addr)?; + + Ok(event_to_resp( + new_event("set_mailbox").add_attribute("mailbox", mailbox), + )) + } + SetIsm { ism } => { + let ism_addr = deps.api.addr_validate(&ism)?; + + ISM.save(deps.storage, &ism_addr)?; + + Ok(event_to_resp( + new_event("set_ism").add_attribute("ism", ism), + )) + } + SetHook { hook } => { + let hook_addr = deps.api.addr_validate(&hook)?; + + HOOK.save(deps.storage, &hook_addr)?; + + Ok(event_to_resp( + new_event("set_hook").add_attribute("hook", hook), + )) + } + } +} + +pub fn handle_query( + deps: Deps<'_, C>, + _env: Env, + msg: ConnectionQueryMsg, +) -> StdResult { + match msg { + ConnectionQueryMsg::GetMailbox {} => Ok(cosmwasm_std::to_binary(&MailboxResponse { + mailbox: get_mailbox(deps.storage)?.map(|v| v.into()), + })?), + ConnectionQueryMsg::GetHook {} => Ok(cosmwasm_std::to_binary(&HookResponse { + hook: get_hook(deps.storage)?.map(|v| v.into()), + })?), + ConnectionQueryMsg::GetIsm {} => Ok(cosmwasm_std::to_binary(&IsmResponse { + ism: get_ism(deps.storage)?.map(|v| v.into()), + })?), + } +} + +pub fn get_mailbox(storage: &dyn Storage) -> StdResult> { + MAILBOX.may_load(storage) +} + +pub fn get_ism(storage: &dyn Storage) -> StdResult> { + ISM.may_load(storage) +} + +pub fn get_hook(storage: &dyn Storage) -> StdResult> { + HOOK.may_load(storage) +} diff --git a/packages/interface/src/connection.rs b/packages/interface/src/connection.rs index db5fff04..7e655037 100644 --- a/packages/interface/src/connection.rs +++ b/packages/interface/src/connection.rs @@ -4,7 +4,7 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; pub enum ConnectionMsg { SetMailbox { mailbox: String }, - SetIgp { igp: String }, + SetHook { hook: String }, SetIsm { ism: String }, } @@ -15,8 +15,8 @@ pub enum ConnectionQueryMsg { #[returns(MailboxResponse)] GetMailbox {}, - #[returns(IgpResponse)] - GetIgp {}, + #[returns(HookResponse)] + GetHook {}, #[returns(IsmResponse)] GetIsm {}, @@ -24,15 +24,15 @@ pub enum ConnectionQueryMsg { #[cw_serde] pub struct MailboxResponse { - pub mailbox: String, + pub mailbox: Option, } #[cw_serde] -pub struct IgpResponse { - pub igp: String, +pub struct HookResponse { + pub hook: Option, } #[cw_serde] pub struct IsmResponse { - pub ism: String, + pub ism: Option, } diff --git a/packages/interface/src/ism/aggregate.rs b/packages/interface/src/ism/aggregate.rs index a0262a22..fcb48d5c 100644 --- a/packages/interface/src/ism/aggregate.rs +++ b/packages/interface/src/ism/aggregate.rs @@ -25,6 +25,20 @@ pub enum QueryMsg { Ownable(OwnableQueryMsg), Ism(IsmQueryMsg), + + AggregateIsm(AggregateIsmQueryMsg), +} + +#[cw_serde] +#[derive(QueryResponses)] +pub enum AggregateIsmQueryMsg { + #[returns(IsmsResponse)] + Isms {}, +} + +#[cw_serde] +pub struct IsmsResponse { + pub isms: Vec, } #[cfg(test)] diff --git a/packages/interface/src/warp/cw20.rs b/packages/interface/src/warp/cw20.rs index 2474ebec..914cc1fc 100644 --- a/packages/interface/src/warp/cw20.rs +++ b/packages/interface/src/warp/cw20.rs @@ -2,6 +2,7 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::{HexBinary, Uint128}; use crate::{ + connection::{ConnectionMsg, ConnectionQueryMsg}, core, ism::IsmSpecifierQueryMsg, ownable::{OwnableMsg, OwnableQueryMsg}, @@ -47,6 +48,7 @@ pub struct InstantiateMsg { pub enum ExecuteMsg { Ownable(OwnableMsg), Router(router::RouterMsg), + Connection(ConnectionMsg), // handle transfer remote Handle(core::HandleMsg), @@ -57,13 +59,6 @@ pub enum ExecuteMsg { recipient: HexBinary, amount: Uint128, }, - - SetIsm { - ism: String, - }, - SetHook { - hook: String, - }, } #[cw_serde] @@ -74,6 +69,8 @@ pub enum QueryMsg { Router(RouterQuery), + Connection(ConnectionQueryMsg), + TokenDefault(TokenWarpDefaultQueryMsg), IsmSpecifier(IsmSpecifierQueryMsg), diff --git a/packages/interface/src/warp/native.rs b/packages/interface/src/warp/native.rs index c2d9420a..254516e3 100644 --- a/packages/interface/src/warp/native.rs +++ b/packages/interface/src/warp/native.rs @@ -2,6 +2,7 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::{HexBinary, Uint128}; use crate::{ + connection::{ConnectionMsg, ConnectionQueryMsg}, core, ism::IsmSpecifierQueryMsg, ownable::{OwnableMsg, OwnableQueryMsg}, @@ -55,6 +56,7 @@ pub struct InstantiateMsg { pub enum ExecuteMsg { Ownable(OwnableMsg), Router(RouterMsg), + Connection(ConnectionMsg), // handle transfer remote Handle(core::HandleMsg), @@ -65,13 +67,6 @@ pub enum ExecuteMsg { recipient: HexBinary, amount: Uint128, }, - - SetIsm { - ism: String, - }, - SetHook { - hook: String, - }, } #[cw_serde] @@ -82,6 +77,8 @@ pub enum QueryMsg { Router(RouterQuery), + Connection(ConnectionQueryMsg), + TokenDefault(TokenWarpDefaultQueryMsg), IsmSpecifier(IsmSpecifierQueryMsg), From bac3aa297facb6647e4f31d3c569282f810fe47b Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Sun, 29 Oct 2023 00:15:12 +0900 Subject: [PATCH 14/53] feat: make gas amount none if is zero --- contracts/igps/core/src/query.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/contracts/igps/core/src/query.rs b/contracts/igps/core/src/query.rs index db736d56..0f8a1adb 100644 --- a/contracts/igps/core/src/query.rs +++ b/contracts/igps/core/src/query.rs @@ -68,13 +68,17 @@ pub fn quote_dispatch( let igp_message: Message = req.message.into(); - let quote_res = quote_gas_payment(deps, igp_message.dest_domain, gas_limit); + let gas_needed = quote_gas_payment(deps, igp_message.dest_domain, gas_limit)?.gas_needed; Ok(QuoteDispatchResponse { - gas_amount: Some(coin( - quote_res?.gas_needed.to_string().parse::()?, - GAS_TOKEN.load(deps.storage)?, - )), + gas_amount: if gas_needed.is_zero() { + None + } else { + Some(coin( + gas_needed.to_string().parse::()?, + GAS_TOKEN.load(deps.storage)?, + )) + }, }) } From 4ddc1b7202648c44e44fb072627334778c70d391 Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Sat, 28 Oct 2023 15:29:30 +0000 Subject: [PATCH 15/53] Revert "feat: make gas amount none if is zero" This reverts commit bac3aa297facb6647e4f31d3c569282f810fe47b. --- contracts/igps/core/src/query.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/contracts/igps/core/src/query.rs b/contracts/igps/core/src/query.rs index 0f8a1adb..db736d56 100644 --- a/contracts/igps/core/src/query.rs +++ b/contracts/igps/core/src/query.rs @@ -68,17 +68,13 @@ pub fn quote_dispatch( let igp_message: Message = req.message.into(); - let gas_needed = quote_gas_payment(deps, igp_message.dest_domain, gas_limit)?.gas_needed; + let quote_res = quote_gas_payment(deps, igp_message.dest_domain, gas_limit); Ok(QuoteDispatchResponse { - gas_amount: if gas_needed.is_zero() { - None - } else { - Some(coin( - gas_needed.to_string().parse::()?, - GAS_TOKEN.load(deps.storage)?, - )) - }, + gas_amount: Some(coin( + quote_res?.gas_needed.to_string().parse::()?, + GAS_TOKEN.load(deps.storage)?, + )), }) } From 6ab3891762f0192107175251620e42cf35cd0cc0 Mon Sep 17 00:00:00 2001 From: ByeongSu Hong Date: Sun, 29 Oct 2023 23:59:54 +0900 Subject: [PATCH 16/53] feat(igp): handle zero gas (#59) * feat: make gas amount none if is zero * clean code --- contracts/igps/core/src/query.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/contracts/igps/core/src/query.rs b/contracts/igps/core/src/query.rs index db736d56..51db48b7 100644 --- a/contracts/igps/core/src/query.rs +++ b/contracts/igps/core/src/query.rs @@ -68,14 +68,17 @@ pub fn quote_dispatch( let igp_message: Message = req.message.into(); - let quote_res = quote_gas_payment(deps, igp_message.dest_domain, gas_limit); - - Ok(QuoteDispatchResponse { - gas_amount: Some(coin( - quote_res?.gas_needed.to_string().parse::()?, + let gas_amount = quote_gas_payment(deps, igp_message.dest_domain, gas_limit)?.gas_needed; + let gas_amount = if !gas_amount.is_zero() { + Some(coin( + gas_amount.to_string().parse::()?, GAS_TOKEN.load(deps.storage)?, - )), - }) + )) + } else { + None + }; + + Ok(QuoteDispatchResponse { gas_amount }) } pub fn get_exchange_rate_and_gas_price( From 1818448d32e6e04477323c27f60188c6c5a3626f Mon Sep 17 00:00:00 2001 From: ByeongSu Hong Date: Mon, 30 Oct 2023 13:51:00 +0900 Subject: [PATCH 17/53] feat: uploading codes to each networks (#60) * new codes for devnet - dev * new codes for mainnet - dev * new codes for devnet / mainnet - prod --- scripts/context/duality-devnet.json | 70 ++++++++++++++--------------- scripts/context/neutron-1.json | 70 ++++++++++++++--------------- 2 files changed, 70 insertions(+), 70 deletions(-) diff --git a/scripts/context/duality-devnet.json b/scripts/context/duality-devnet.json index ad22e7d3..495030cd 100644 --- a/scripts/context/duality-devnet.json +++ b/scripts/context/duality-devnet.json @@ -1,81 +1,81 @@ { "contracts": { "hpl_hook_merkle": { - "codeId": 94, - "digest": "1a13b59eac4f6262bff526ce16e8d1c82639d30cde6d599183410a8ce7a5abb4" + "codeId": 131, + "digest": "0fa54766b2e8cb3e87ef076fbe0d50e09a21c23b0ab5671b1d2c548571bd2728" }, "hpl_igp": { "address": "dual14cu2z6xw62cumt7hmfw7977jyaymr26jazxdpvfp7ag2dss29q2q700yum", - "codeId": 99, - "digest": "8a68a04c72717598b57dba73a16aa3ccad25498ecc1735d8f49b8e650028709a" + "codeId": 136, + "digest": "92e97402efe3aedcc84ef33608d5199f7a6506a61d13afc472e4b1f6740393cd" }, "hpl_igp_oracle": { "address": "dual1alp24grk7e5f3msv0apk68hehspglpr7y7upfrvkpdfu7c84sqtq8ze5p2", - "codeId": 100, - "digest": "8533c3893d4aa47c845cfa4dcb119f2958c35438538ebba413c2677d47c1306b" + "codeId": 137, + "digest": "be0546baf9b1c2eaf28a8111f784521ee011f69a474098162bbda73a2db68e63" }, "hpl_ism_multisig": { - "codeId": 102, - "digest": "98dc12a30bc151941a89ecc81656470a8bdba299f2c56f26a1a04a7567222714" + "codeId": 139, + "digest": "168a875843bd6b13420e289e6a089e1c566d62f95e9b9fb4bcec0c61966940f4" }, "hpl_mailbox": { "address": "dual1mveu0r9rj4qa6aqxt8almpha6cqluu397y8jd6r4jhzm3hmtmndq8lvk47", - "codeId": 104, - "digest": "355c5c5ef5168a099d03d6cd89529211dec206b6ed14ca778a98620ef280f652" + "codeId": 141, + "digest": "66dec947cc99c61c9edf56576e0b5c0e675b0d86e39495acf6f05db1657fabd1" }, "hpl_test_mock_hook": { - "codeId": 105, - "digest": "d17655f65e364f259c4f48c2b67288d852f4e5ed38fbaef5aa4946f3b641b138" + "codeId": 142, + "digest": "df1490919845c9a12b0e63488cbf83b242117493ad4b2b0d2473f6460bb3c074" }, "hpl_test_mock_msg_receiver": { "address": "dual1rvtgvc38sfd9zehtgsp3eh8k269naq949u5qdcqm3x35mjg2uctqfdn3yq", - "codeId": 106, - "digest": "41a067c8d55912c6fefe559e508ae0bbab81ed0caae4db8c8d2f2f732f4f6f18" + "codeId": 143, + "digest": "d0f8bc913e07df303911222970e09dd3abeef97399cb1c554d76cf98caa7f37a" }, "hpl_validator_announce": { "address": "dual1982lwq4rt4qntkv2hafvvtwupn7hgqqkv0kpf55yahlh6pqeldvqd24lld", - "codeId": 107, - "digest": "78ac1d373a9f21b7bd4698d74f762882672db2af56fffe8501ece9b2cfadb964" + "codeId": 144, + "digest": "db7afc67aa15c4f287af98c7836b3acf2016b96c6aa1eee01458bc11e6732178" }, "hpl_warp_cw20": { - "codeId": 108, - "digest": "bf517d714856673acf628602284eca5f1a3cb27e4277b64c70b502cf603312c6" + "codeId": 145, + "digest": "5fdafe6f066e2f646b88a6ec29ae401012672fb8f674a18808771b5051beb7fc" }, "hpl_warp_native": { - "codeId": 109, - "digest": "858366c03a932325bd34b080474e8ed1f2e313161cb41c3e68402c837c2b2b88" + "codeId": 146, + "digest": "983943571c74276e36a36f62ef9c6c54dc6de2c57fb76af4a0de37a0b5fef3ec" }, "hpl_warp_native_ibc": { "codeId": 32, "digest": "eed9d7dfa18163de64893cfe88e038cfc82e487561c94b142978efdacc1f9dff" }, "hpl_hook_aggregate": { - "codeId": 93, - "digest": "fb05c417ced388eabb743fefaa3f89dd2ae8e720c63947143e4027d1a34101a9" + "codeId": 130, + "digest": "910a823bb524dbbaed9952f484cb242ea837e50b5f7c458c1bf52d16c7de21fb" }, "hpl_hook_pausable": { - "codeId": 95, - "digest": "c81b4829045035372f1cdd190d6f99be912165f0624d763f9eae4fdaf49db1d9" + "codeId": 132, + "digest": "226c4125998bf88666fb5ef7caeb86de28cde42ff679ec4029dedaf02c3a83c7" }, "hpl_hook_routing": { - "codeId": 96, - "digest": "eeb2548015d1cd0d887e1b0387303d840a7cf744bc5834d6af369785bef23aa9" + "codeId": 133, + "digest": "55482f9f69d4d6d4784d1a378321ebf72936f0f13c5539ce1598bc9e8ed087f3" }, "hpl_hook_routing_custom": { - "codeId": 97, - "digest": "f5fa5986cc177096d56ec40e14c71d4c4830b59feafe585d249469420bdb5cc7" + "codeId": 134, + "digest": "48b7dc080418493f4f6f2737340ffa34bdcc6fd3197a2585bc4eea8eb72ee829" }, "hpl_hook_routing_fallback": { - "codeId": 98, - "digest": "7363e92061dfdea646729f3cfbc1e4bd718a276b1e130600b11f3fd1541575cb" + "codeId": 135, + "digest": "6fcf09c7d50fd5bfddbdeb0020625bdab2094237dca4f29e93ae12371a550f6f" }, "hpl_ism_aggregate": { - "codeId": 101, - "digest": "16c799ac7e57a8cb51eaf702b14948950dc72ab7e266f1615cbb727547b171c7" + "codeId": 138, + "digest": "3a4adfaed525c40845768e05da436f1ea39f6c9d7e9d47ec2de8457034a229c1" }, "hpl_ism_routing": { - "codeId": 103, - "digest": "228a9c770b32701980f05a928c617c4c1811939454a1f5230550600635e98aaa" + "codeId": 140, + "digest": "80bfb6964f9a1e4bd64aa69421b7b27327b00ab503931f0a22e7169d774eaf69" }, "hpl_default_ism": { "codeId": 77, @@ -94,4 +94,4 @@ } }, "address": "dual1dwnrgwsf5c9vqjxsax04pdm0mx007yrraj2dgn" -} \ No newline at end of file +} diff --git a/scripts/context/neutron-1.json b/scripts/context/neutron-1.json index 3320eb8b..96906131 100644 --- a/scripts/context/neutron-1.json +++ b/scripts/context/neutron-1.json @@ -1,73 +1,73 @@ { "contracts": { "hpl_hook_aggregate": { - "codeId": 384, - "digest": "03fedc6af9ad56aa247993d451cbf0669ce91035e87cd43cbeecb14114ce4ff5" + "codeId": 405, + "digest": "910a823bb524dbbaed9952f484cb242ea837e50b5f7c458c1bf52d16c7de21fb" }, "hpl_hook_merkle": { - "codeId": 385, - "digest": "b68a6715634a0a567f699c98539e87ac779649e850102bab98610116e2e10259" + "codeId": 406, + "digest": "0fa54766b2e8cb3e87ef076fbe0d50e09a21c23b0ab5671b1d2c548571bd2728" }, "hpl_hook_pausable": { - "codeId": 386, - "digest": "aeeb15ed555fbcedd8f1535498888b45ecf21a521871c4f683c1503c6cbb39e9" + "codeId": 407, + "digest": "226c4125998bf88666fb5ef7caeb86de28cde42ff679ec4029dedaf02c3a83c7" }, "hpl_hook_routing": { - "codeId": 387, - "digest": "601f0a02aa236376c50fa4f4f23cd90f3ce4535cb2fe2e8756afcd31ecefc859" + "codeId": 408, + "digest": "55482f9f69d4d6d4784d1a378321ebf72936f0f13c5539ce1598bc9e8ed087f3" }, "hpl_hook_routing_custom": { - "codeId": 388, - "digest": "24bcc5053094cd9280f1cc3d1bd9b76312bffa8d0b8bdf8743d4d1aa27f3efae" + "codeId": 409, + "digest": "48b7dc080418493f4f6f2737340ffa34bdcc6fd3197a2585bc4eea8eb72ee829" }, "hpl_hook_routing_fallback": { - "codeId": 389, - "digest": "27d6754aeaa78242e99445938ab73e6b2837d5ada8eb7c708ea7f3bf7b3861ba" + "codeId": 410, + "digest": "6fcf09c7d50fd5bfddbdeb0020625bdab2094237dca4f29e93ae12371a550f6f" }, "hpl_igp": { - "codeId": 390, - "digest": "377fd5ddc2f9866c69aa6858f8a75683c5d3ee461fdb33658237f0e5951b4e7d" + "codeId": 411, + "digest": "92e97402efe3aedcc84ef33608d5199f7a6506a61d13afc472e4b1f6740393cd" }, "hpl_igp_oracle": { - "codeId": 391, - "digest": "0d0ffc64d0ec318d99f11a68f69b6fa0d4004b7fd387a677c621d4efdb8ea7d1" + "codeId": 412, + "digest": "be0546baf9b1c2eaf28a8111f784521ee011f69a474098162bbda73a2db68e63" }, "hpl_ism_aggregate": { - "codeId": 392, - "digest": "a1ed31b7b55bbee4b293ac78da9cc54ad1464e058122b5feda6cfba3e82ae3a8" + "codeId": 413, + "digest": "3a4adfaed525c40845768e05da436f1ea39f6c9d7e9d47ec2de8457034a229c1" }, "hpl_ism_multisig": { - "codeId": 393, - "digest": "d38bb495b36d4ae7a8055e12ea9e6e295ca26009094028c401d89688c4612d9e" + "codeId": 414, + "digest": "168a875843bd6b13420e289e6a089e1c566d62f95e9b9fb4bcec0c61966940f4" }, "hpl_ism_routing": { - "codeId": 394, - "digest": "aa851324bb1d402ba1445223c7b7fa50641b7fb8372030983daca9082166fa95" + "codeId": 415, + "digest": "80bfb6964f9a1e4bd64aa69421b7b27327b00ab503931f0a22e7169d774eaf69" }, "hpl_mailbox": { - "codeId": 395, - "digest": "c28afc65e96bf730f7654e15cbcb962d603065581664907655fcaabf8745fb2a" + "codeId": 416, + "digest": "66dec947cc99c61c9edf56576e0b5c0e675b0d86e39495acf6f05db1657fabd1" }, "hpl_test_mock_hook": { - "codeId": 396, - "digest": "bbbdd167b7acebed3283a7482cb032666ee13201442bafd9814067765303e797" + "codeId": 417, + "digest": "df1490919845c9a12b0e63488cbf83b242117493ad4b2b0d2473f6460bb3c074" }, "hpl_test_mock_msg_receiver": { - "codeId": 397, - "digest": "54068ecbb73db316e756c6d1343896e3920175985430d24c477f6abe0db77757" + "codeId": 418, + "digest": "d0f8bc913e07df303911222970e09dd3abeef97399cb1c554d76cf98caa7f37a" }, "hpl_validator_announce": { - "codeId": 398, - "digest": "70042fe83ab067f7f1a4420dded5a78ce1b9e7832f4e9639c641f8268aed5b75" + "codeId": 419, + "digest": "db7afc67aa15c4f287af98c7836b3acf2016b96c6aa1eee01458bc11e6732178" }, "hpl_warp_cw20": { - "codeId": 399, - "digest": "533b8f38e853a832b4c69f65e8c87cb75604e5e03b3c353c210f4828140f8881" + "codeId": 420, + "digest": "5fdafe6f066e2f646b88a6ec29ae401012672fb8f674a18808771b5051beb7fc" }, "hpl_warp_native": { - "codeId": 400, - "digest": "b245ed32563f6bb4a9f9046d28b36e757ea0ee168f8a3998b0b9ac1dcfc4f0f8" + "codeId": 421, + "digest": "983943571c74276e36a36f62ef9c6c54dc6de2c57fb76af4a0de37a0b5fef3ec" } }, "address": "neutron1ugu6ktzqwrmr0pgw603kakuhvj46a7mmrl6pw3" -} \ No newline at end of file +} From 5bd1578202a67303802ca4b352fbf1f4aef8ddf3 Mon Sep 17 00:00:00 2001 From: ByeongSu Hong Date: Mon, 30 Oct 2023 13:51:08 +0900 Subject: [PATCH 18/53] fix: assert addr len (#61) * fix: assert addr len * new codes for devnet / mainnet - prod --- contracts/core/va/src/contract.rs | 6 ++++++ contracts/core/va/src/error.rs | 9 +++++++++ contracts/isms/multisig/src/error.rs | 9 +++++++++ contracts/isms/multisig/src/execute.rs | 12 ++++++++++++ 4 files changed, 36 insertions(+) diff --git a/contracts/core/va/src/contract.rs b/contracts/core/va/src/contract.rs index fe52ad4b..7948c0df 100644 --- a/contracts/core/va/src/contract.rs +++ b/contracts/core/va/src/contract.rs @@ -140,6 +140,12 @@ fn announce( storage_location: String, signature: HexBinary, ) -> Result { + ensure_eq!( + validator.len(), + 20, + ContractError::invalid_addr("length should be 20") + ); + // check replay protection let replay_id = replay_hash(&validator, &storage_location)?; ensure!( diff --git a/contracts/core/va/src/error.rs b/contracts/core/va/src/error.rs index cd37059d..1cd84e7f 100644 --- a/contracts/core/va/src/error.rs +++ b/contracts/core/va/src/error.rs @@ -12,6 +12,15 @@ pub enum ContractError { #[error("unauthorized")] Unauthorized {}, + #[error("invalid address. reason: {0}")] + InvalidAddress(String), + #[error("verify failed")] VerifyFailed {}, } + +impl ContractError { + pub fn invalid_addr(reason: &str) -> Self { + ContractError::InvalidAddress(reason.into()) + } +} diff --git a/contracts/isms/multisig/src/error.rs b/contracts/isms/multisig/src/error.rs index dd9bac18..70db89bc 100644 --- a/contracts/isms/multisig/src/error.rs +++ b/contracts/isms/multisig/src/error.rs @@ -21,9 +21,18 @@ pub enum ContractError { #[error("invalid pubkey")] InvalidPubKey, + #[error("invalid address. reason: {0}")] + InvalidAddress(String), + #[error("duplicate validator")] ValidatorDuplicate, #[error("validator not exists")] ValidatorNotExist, } + +impl ContractError { + pub fn invalid_addr(reason: &str) -> Self { + ContractError::InvalidAddress(reason.into()) + } +} diff --git a/contracts/isms/multisig/src/execute.rs b/contracts/isms/multisig/src/execute.rs index 15918dfa..d8ce34a4 100644 --- a/contracts/isms/multisig/src/execute.rs +++ b/contracts/isms/multisig/src/execute.rs @@ -56,6 +56,12 @@ pub fn enroll_validator( ContractError::Unauthorized {} ); + ensure_eq!( + msg.validator.len(), + 20, + ContractError::invalid_addr("length should be 20") + ); + let validator_state = VALIDATORS.may_load(deps.storage, msg.domain)?; if let Some(mut validators) = validator_state { @@ -88,6 +94,12 @@ pub fn enroll_validators( let mut events: Vec = Vec::new(); for msg in validators.into_iter() { + ensure_eq!( + msg.validator.len(), + 20, + ContractError::invalid_addr("length should be 20") + ); + let validators_state = VALIDATORS.may_load(deps.storage, msg.domain)?; if let Some(mut validators) = validators_state { From 586080666720f47df9f250d1097ad01c7afd6587 Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Mon, 30 Oct 2023 17:21:49 +0900 Subject: [PATCH 19/53] fix test --- contracts/isms/multisig/src/execute.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/isms/multisig/src/execute.rs b/contracts/isms/multisig/src/execute.rs index d8ce34a4..d4661fbb 100644 --- a/contracts/isms/multisig/src/execute.rs +++ b/contracts/isms/multisig/src/execute.rs @@ -256,11 +256,11 @@ mod test { } #[rstest] - #[case("owner", vec![hex("deadbeef")])] + #[case("owner", vec![hex(&"deadbeef".repeat(5))])] #[should_panic(expected = "unauthorized")] - #[case("someone", vec![hex("deadbeef")])] + #[case("someone", vec![hex(&"deadbeef".repeat(5))])] #[should_panic(expected = "duplicate validator")] - #[case("owner", vec![hex("deadbeef"),hex("deadbeef")])] + #[case("owner", vec![hex(&"deadbeef".repeat(5)),hex(&"deadbeef".repeat(5))])] fn test_enroll(#[case] sender: &str, #[case] validators: Vec) { let mut deps = mock_dependencies(); From e1ddc26447a699a56c2e717e54c54d870524613c Mon Sep 17 00:00:00 2001 From: Nam Chu Hoai Date: Tue, 31 Oct 2023 04:26:39 -0400 Subject: [PATCH 20/53] Deployment scripts (#62) * Deployment scripts * chore: use dash case (#63) * chore: use dash case * fix: make inject works --------- Co-authored-by: ByeongSu Hong --- scripts/action/deploy.ts | 99 +----------- scripts/action/fetch.ts | 69 +++----- scripts/action/ism.ts | 109 +++++++++++++ scripts/action/link.ts | 46 ------ scripts/action/mailbox.ts | 14 +- scripts/action/warp.ts | 26 ++- scripts/src/contracts/hpl_hook_aggregate.ts | 4 +- scripts/src/contracts/hpl_hook_merkle.ts | 4 +- scripts/src/contracts/hpl_hook_pausable.ts | 4 +- scripts/src/contracts/hpl_hook_routing.ts | 4 +- .../src/contracts/hpl_hook_routing_custom.ts | 4 +- .../contracts/hpl_hook_routing_fallback.ts | 4 +- scripts/src/contracts/hpl_igp.ts | 4 +- scripts/src/contracts/hpl_igp_oracle.ts | 4 +- scripts/src/contracts/hpl_ism_aggregate.ts | 4 +- scripts/src/contracts/hpl_ism_multisig.ts | 4 +- scripts/src/contracts/hpl_ism_routing.ts | 4 +- scripts/src/contracts/hpl_mailbox.ts | 4 +- scripts/src/contracts/hpl_test_mock_hook.ts | 4 +- .../contracts/hpl_test_mock_msg_receiver.ts | 4 +- .../src/contracts/hpl_validator_announce.ts | 4 +- scripts/src/contracts/hpl_warp_cw20.ts | 4 +- scripts/src/contracts/hpl_warp_native.ts | 4 +- scripts/src/contracts/index.ts | 53 +++++- scripts/src/deploy.ts | 153 ++++++++++++++++++ scripts/src/index.ts | 2 + scripts/src/load_wasm.ts | 2 +- .../src/migrations/InitializeStandalone.ts | 74 +++++++++ scripts/src/migrations/index.ts | 48 +++--- scripts/src/migrations/initialize.ts | 18 ++- scripts/src/migrations/mailbox.ts | 17 +- scripts/src/migrations/mailbox_related.ts | 12 +- scripts/src/types.ts | 24 +-- 33 files changed, 543 insertions(+), 291 deletions(-) create mode 100644 scripts/action/ism.ts delete mode 100644 scripts/action/link.ts create mode 100644 scripts/src/deploy.ts create mode 100644 scripts/src/migrations/InitializeStandalone.ts diff --git a/scripts/action/deploy.ts b/scripts/action/deploy.ts index 1e9b625e..3df56508 100644 --- a/scripts/action/deploy.ts +++ b/scripts/action/deploy.ts @@ -1,16 +1,11 @@ import { writeFileSync } from "fs"; import { loadContext } from "../src/load_context"; -import { - Client, - HookType, - IsmType, - config, - getSigningClient, -} from "../src/config"; - -import { ContractFetcher, Contracts } from "./fetch"; +import { Client, HookType, config, getSigningClient } from "../src/config"; + +import { ContractFetcher } from "./fetch"; import { Context } from "../src/types"; +import { Contracts, deploy_ism } from "../src/deploy"; const name = (c: any) => c.contractName; const addr = (ctx: Context, c: any) => ctx.contracts[name(c)].address!; @@ -204,92 +199,6 @@ const deploy_ism_hook = async ( return ctx; }; -const deploy_ism = async ( - client: Client, - ism: IsmType, - contracts: Contracts -): Promise => { - const { isms } = contracts; - - switch (ism.type) { - case "multisig": - const multisig_ism_res = await isms.multisig.instantiate({ - owner: ism.owner === "" ? client.signer : ism.owner, - }); - - await client.wasm.execute( - client.signer, - multisig_ism_res.address!, - { - enroll_validators: { - set: Object.entries(ism.validators).flatMap(([domain, validator]) => - validator.addrs.map((v) => ({ - domain: Number(domain), - validator: v, - })) - ), - }, - }, - "auto" - ); - - await client.wasm.execute( - client.signer, - multisig_ism_res.address!, - { - set_thresholds: { - set: Object.entries(ism.validators).map( - ([domain, { threshold }]) => ({ - domain: Number(domain), - threshold, - }) - ), - }, - }, - "auto" - ); - - return multisig_ism_res.address!; - - case "aggregate": - const aggregate_ism_res = await isms.aggregate.instantiate({ - owner: ism.owner === "" ? client.signer : ism.owner, - isms: await Promise.all( - ism.isms.map((v) => deploy_ism(client, v, contracts)) - ), - }); - - return aggregate_ism_res.address!; - case "routing": - const routing_ism_res = await isms.routing.instantiate({ - owner: ism.owner === "" ? client.signer : ism.owner, - }); - - await client.wasm.execute( - client.signer, - routing_ism_res.address!, - { - router: { - set_routes: { - set: await Promise.all( - Object.entries(ism.isms).map(async ([domain, v]) => { - const route = await deploy_ism(client, v, contracts); - return { domain, route }; - }) - ), - }, - }, - }, - "auto" - ); - - return routing_ism_res.address!; - - default: - throw new Error("invalid ism type"); - } -}; - const deploy_hook = async ( ctx: Context, client: Client, diff --git a/scripts/action/fetch.ts b/scripts/action/fetch.ts index 7f2807d4..270968d4 100644 --- a/scripts/action/fetch.ts +++ b/scripts/action/fetch.ts @@ -1,22 +1,25 @@ import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate"; import { Context } from "../src/types"; import { Client } from "../src/config"; -import HplMailbox from "../src/contracts/hpl_mailbox"; -import HplValidatorAnnounce from "../src/contracts/hpl_validator_announce"; -import HplHookAggregate from "../src/contracts/hpl_hook_aggregate"; -import HplHookMerkle from "../src/contracts/hpl_hook_merkle"; -import HplHookPausable from "../src/contracts/hpl_hook_pausable"; -import HplHookRouting from "../src/contracts/hpl_hook_routing"; -import HplHookRoutingCustom from "../src/contracts/hpl_hook_routing_custom"; -import HplIgp from "../src/contracts/hpl_igp"; -import HplIgpGasOracle from "../src/contracts/hpl_igp_oracle"; -import HplIsmAggregate from "../src/contracts/hpl_ism_aggregate"; -import HplIsmMultisig from "../src/contracts/hpl_ism_multisig"; -import HplIsmRouting from "../src/contracts/hpl_ism_routing"; -import HplTestMockHook from "../src/contracts/hpl_test_mock_hook"; -import HplTestMockMsgReceiver from "../src/contracts/hpl_test_mock_msg_receiver"; -import HplWarpCw20 from "../src/contracts/hpl_warp_cw20"; -import HplWarpNative from "../src/contracts/hpl_warp_native"; +import { Contracts } from "../src/deploy"; +import { + HplMailbox, + HplValidatorAnnounce, + HplHookAggregate, + HplHookMerkle, + HplHookPausable, + HplHookRouting, + HplHookRoutingCustom, + HplIgp, + HplIgpOracle, + HplIsmAggregate, + HplIsmMultisig, + HplIsmRouting, + HplTestMockHook, + HplTestMockMsgReceiver, + HplWarpCw20, + HplWarpNative, +} from "../src/contracts"; type Const = new ( address: string | undefined, @@ -26,38 +29,6 @@ type Const = new ( client: SigningCosmWasmClient ) => T; -export type Contracts = { - core: { - mailbox: HplMailbox; - va: HplValidatorAnnounce; - }; - hooks: { - aggregate: HplHookAggregate; - merkle: HplHookMerkle; - pausable: HplHookPausable; - routing: HplHookRouting; - routing_custom: HplHookRoutingCustom; - routing_fallback: HplHookRoutingCustom; - }; - igp: { - core: HplIgp; - oracle: HplIgpGasOracle; - }; - isms: { - aggregate: HplIsmAggregate; - multisig: HplIsmMultisig; - routing: HplIsmRouting; - }; - mocks: { - hook: HplTestMockHook; - receiver: HplTestMockMsgReceiver; - }; - warp: { - cw20: HplWarpCw20; - native: HplWarpNative; - }; -}; - export class ContractFetcher { constructor(private ctx: Context, private client: Client) {} @@ -93,7 +64,7 @@ export class ContractFetcher { }, igp: { core: this.get(HplIgp, "hpl_igp"), - oracle: this.get(HplIgpGasOracle, "hpl_igp_oracle"), + oracle: this.get(HplIgpOracle, "hpl_igp_oracle"), }, isms: { aggregate: this.get(HplIsmAggregate, "hpl_ism_aggregate"), diff --git a/scripts/action/ism.ts b/scripts/action/ism.ts new file mode 100644 index 00000000..897a8d3f --- /dev/null +++ b/scripts/action/ism.ts @@ -0,0 +1,109 @@ +import { Command } from "commander"; +import { ExecuteResult } from "@cosmjs/cosmwasm-stargate"; + +import { version } from "../package.json"; +import { config, getSigningClient } from "../src/config"; +import { loadContext } from "../src/load_context"; +import { ContractFetcher } from "./fetch"; +import { + HplMailbox, + HplIgp, + HplIgpGasOracle, + HplHookMerkle, + HplIsmAggregate, +} from "../src/contracts"; + +const program = new Command(); + +program.name("Mailbox CLI").version(version); + +program + .command("get-ism") + .argument("", "recipient address in bech32") + .action(makeHandler("get-ism")); + +program + .command("show") + .argument("", "ism address in bech32") + .argument("", "origin domain to be used when multisig") + .action(makeHandler("show-ism")); + +program.parseAsync(process.argv).catch(console.error); + +const parseWasmEventLog = (res: ExecuteResult) => { + return ( + res.events + // .filter((v) => v.type.startsWith("wasm")) + .map((v) => ({ + "@type": v.type.slice(5), + ...Object.fromEntries(v.attributes.map((x) => [x.key, x.value])), + })) + ); +}; + +function makeHandler( + action: "get-ism" | "show-ism" +): (...args: any[]) => void | Promise { + const ctx = loadContext(config.network.id); + + const loadDeps = async () => { + const client = await getSigningClient(config); + const fetcher = new ContractFetcher(ctx, client); + const mailbox = fetcher.get(HplMailbox, "hpl_mailbox"); + const igp = fetcher.get(HplIgp, "hpl_igp"); + const igp_oracle = fetcher.get(HplIgpGasOracle, "hpl_igp_oracle"); + const hook_merkle = fetcher.get(HplHookMerkle, "hpl_hook_merkle"); + const hook_aggregate = fetcher.get(HplIsmAggregate, "hpl_hook_aggregate"); + + return { + client, + mailbox, + igp: { core: igp, oracle: igp_oracle }, + hook: { merkle: hook_merkle, aggregate: hook_aggregate }, + }; + }; + + switch (action) { + case "get-ism": + return async (recipient_addr: string) => { + const { mailbox } = await loadDeps(); + + const ism = await mailbox.query({ mailbox: { default_ism: {} } }); + console.log("Default ISM on mailbox is", ism); + + const recipientIsm = await mailbox.query({ + mailbox: { recipient_ism: { recipient_addr } }, + }); + + console.log("Recipient ISM is ", recipientIsm); + }; + case "show-ism": + return async (ism_addr: string, originDomain?: string) => { + // Generic info + const { client } = await loadDeps(); + const ism = await client.wasm.queryContractSmart(ism_addr, { + ism: { + module_type: {}, + }, + }); + switch (ism.type) { + case "message_id_multisig": + const msig = await client.wasm.queryContractSmart(ism_addr, { + multisig_ism: { + enrolled_validators: { + domain: Number(originDomain), + }, + }, + }); + const owner = await client.wasm.queryContractSmart(ism_addr, { + ownable: { get_owner: {} }, + }); + console.log(msig, owner); + break; + + default: + break; + } + }; + } +} diff --git a/scripts/action/link.ts b/scripts/action/link.ts deleted file mode 100644 index bf6acda4..00000000 --- a/scripts/action/link.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { loadContext } from "../src/load_context"; -import { config, getSigningClient } from "../src/config"; - -import HplWarpNative from "../src/contracts/hpl_warp_native"; -import HplIsmMultisig from "../src/contracts/hpl_ism_multisig"; -import { ContractFetcher } from "./fetch"; -import { addPad } from "../src/conv"; - -async function main() { - const client = await getSigningClient(config); - - const ctx = loadContext(config.network.id); - - const fetcher = new ContractFetcher(ctx, client); - - const ism_multisig = fetcher.get(HplIsmMultisig, "hpl_ism_multisig"); - - let res; - - res = await ism_multisig.execute({ - enroll_validator: { - set: { - domain: 5, - validator: client.signer, - validator_pubkey: client.signer_pubkey, - }, - }, - }); - console.log(res.events.filter((v) => v.type.startsWith("wasm"))); - - const warp_native_ibc = fetcher.get(HplWarpNative, "hpl_warp_native_ibc"); - - res = await warp_native_ibc.execute({ - router: { - set_route: { - set: { - domain: 5, - route: addPad("0xaB7011fa44868E023C869635eE33875629Aec8db"), - }, - }, - }, - }); - console.log(res.events.filter((v) => v.type.startsWith("wasm"))); -} - -main().catch(console.error); diff --git a/scripts/action/mailbox.ts b/scripts/action/mailbox.ts index 5aba751f..2509708d 100644 --- a/scripts/action/mailbox.ts +++ b/scripts/action/mailbox.ts @@ -1,16 +1,18 @@ import { Command } from "commander"; +import { ExecuteResult } from "@cosmjs/cosmwasm-stargate"; import { version } from "../package.json"; import { config, getSigningClient } from "../src/config"; -import HplMailbox from "../src/contracts/hpl_mailbox"; import { addPad } from "../src/conv"; import { loadContext } from "../src/load_context"; import { ContractFetcher } from "./fetch"; -import { ExecuteResult } from "@cosmjs/cosmwasm-stargate"; -import HplIsmAggregate from "../src/contracts/hpl_ism_aggregate"; -import HplIgp from "../src/contracts/hpl_igp"; -import HplIgpGasOracle from "../src/contracts/hpl_igp_oracle"; -import HplHookMerkle from "../src/contracts/hpl_hook_merkle"; +import { + HplMailbox, + HplIgp, + HplIgpGasOracle, + HplHookMerkle, + HplIsmAggregate, +} from "../src/contracts"; const program = new Command(); diff --git a/scripts/action/warp.ts b/scripts/action/warp.ts index 42d5d9f7..0e1587fe 100644 --- a/scripts/action/warp.ts +++ b/scripts/action/warp.ts @@ -20,6 +20,12 @@ program ) .action(create); +program + .command("set-ism") + .argument("
", "address of internal warp route") + .argument("", "address of ISM") + .action(setIsm); + program .command("link") .argument("
", "address of internal warp route") @@ -81,9 +87,25 @@ async function create( } } +async function setIsm(address: string, ism: string) { + const client = await getSigningClient(config); + const resp = await client.wasm.execute( + client.signer, + address, + { + connection: { + set_ism: { + ism, + }, + }, + }, + "auto" + ); + console.log(parseWasmEventLog(resp)); + console.log(resp.transactionHash); +} async function link(address: string, domain: string, external_route: string) { const client = await getSigningClient(config); - const resp = await client.wasm.execute( client.signer, address, @@ -145,7 +167,7 @@ async function transfer( undefined, [ { amount, denom }, - { amount: "2500000", denom: "token" }, + { amount: "100", denom: "untrn" }, ] ); console.log(parseWasmEventLog(resp)); diff --git a/scripts/src/contracts/hpl_hook_aggregate.ts b/scripts/src/contracts/hpl_hook_aggregate.ts index d14a5819..cbbb00b0 100644 --- a/scripts/src/contracts/hpl_hook_aggregate.ts +++ b/scripts/src/contracts/hpl_hook_aggregate.ts @@ -1,5 +1,7 @@ +import { injectable } from "inversify"; import { BaseContract } from "../types"; -export default class HplHookAggregate extends BaseContract { +@injectable() +export class HplHookAggregate extends BaseContract { contractName: string = "hpl_hook_aggregate"; } diff --git a/scripts/src/contracts/hpl_hook_merkle.ts b/scripts/src/contracts/hpl_hook_merkle.ts index d216b8e8..81907d85 100644 --- a/scripts/src/contracts/hpl_hook_merkle.ts +++ b/scripts/src/contracts/hpl_hook_merkle.ts @@ -1,5 +1,7 @@ +import { injectable } from "inversify"; import { BaseContract } from "../types"; -export default class HplHookMerkle extends BaseContract { +@injectable() +export class HplHookMerkle extends BaseContract { contractName: string = "hpl_hook_merkle"; } diff --git a/scripts/src/contracts/hpl_hook_pausable.ts b/scripts/src/contracts/hpl_hook_pausable.ts index b32e3269..c6981d64 100644 --- a/scripts/src/contracts/hpl_hook_pausable.ts +++ b/scripts/src/contracts/hpl_hook_pausable.ts @@ -1,5 +1,7 @@ +import { injectable } from "inversify"; import { BaseContract } from "../types"; -export default class HplHookPausable extends BaseContract { +@injectable() +export class HplHookPausable extends BaseContract { contractName: string = "hpl_hook_pausable"; } diff --git a/scripts/src/contracts/hpl_hook_routing.ts b/scripts/src/contracts/hpl_hook_routing.ts index 35bcdd94..4531cbec 100644 --- a/scripts/src/contracts/hpl_hook_routing.ts +++ b/scripts/src/contracts/hpl_hook_routing.ts @@ -1,5 +1,7 @@ +import { injectable } from "inversify"; import { BaseContract } from "../types"; -export default class HplHookRouting extends BaseContract { +@injectable() +export class HplHookRouting extends BaseContract { contractName: string = "hpl_hook_routing"; } diff --git a/scripts/src/contracts/hpl_hook_routing_custom.ts b/scripts/src/contracts/hpl_hook_routing_custom.ts index 56133af4..dc6339be 100644 --- a/scripts/src/contracts/hpl_hook_routing_custom.ts +++ b/scripts/src/contracts/hpl_hook_routing_custom.ts @@ -1,5 +1,7 @@ +import { injectable } from "inversify"; import { BaseContract } from "../types"; -export default class HplHookRoutingCustom extends BaseContract { +@injectable() +export class HplHookRoutingCustom extends BaseContract { contractName: string = "hpl_hook_routing_custom"; } diff --git a/scripts/src/contracts/hpl_hook_routing_fallback.ts b/scripts/src/contracts/hpl_hook_routing_fallback.ts index a6b71741..2b474822 100644 --- a/scripts/src/contracts/hpl_hook_routing_fallback.ts +++ b/scripts/src/contracts/hpl_hook_routing_fallback.ts @@ -1,5 +1,7 @@ +import { injectable } from "inversify"; import { BaseContract } from "../types"; -export default class HplHookRoutingFallback extends BaseContract { +@injectable() +export class HplHookRoutingFallback extends BaseContract { contractName: string = "hpl_hook_routing_fallback"; } diff --git a/scripts/src/contracts/hpl_igp.ts b/scripts/src/contracts/hpl_igp.ts index 857448b8..c0505b82 100644 --- a/scripts/src/contracts/hpl_igp.ts +++ b/scripts/src/contracts/hpl_igp.ts @@ -1,5 +1,7 @@ +import { injectable } from "inversify"; import { BaseContract } from "../types"; -export default class HplIgp extends BaseContract { +@injectable() +export class HplIgp extends BaseContract { contractName: string = "hpl_igp"; } diff --git a/scripts/src/contracts/hpl_igp_oracle.ts b/scripts/src/contracts/hpl_igp_oracle.ts index 771e803c..b4381564 100644 --- a/scripts/src/contracts/hpl_igp_oracle.ts +++ b/scripts/src/contracts/hpl_igp_oracle.ts @@ -1,5 +1,7 @@ +import { injectable } from "inversify"; import { BaseContract } from "../types"; -export default class HplIgpGasOracle extends BaseContract { +@injectable() +export class HplIgpOracle extends BaseContract { contractName: string = "hpl_igp_oracle"; } diff --git a/scripts/src/contracts/hpl_ism_aggregate.ts b/scripts/src/contracts/hpl_ism_aggregate.ts index 17e4ae50..45dc95ec 100644 --- a/scripts/src/contracts/hpl_ism_aggregate.ts +++ b/scripts/src/contracts/hpl_ism_aggregate.ts @@ -1,5 +1,7 @@ +import { injectable } from "inversify"; import { BaseContract } from "../types"; -export default class HplIsmAggregate extends BaseContract { +@injectable() +export class HplIsmAggregate extends BaseContract { contractName: string = "hpl_ism_aggregate"; } diff --git a/scripts/src/contracts/hpl_ism_multisig.ts b/scripts/src/contracts/hpl_ism_multisig.ts index b1ea2a95..00a5382a 100644 --- a/scripts/src/contracts/hpl_ism_multisig.ts +++ b/scripts/src/contracts/hpl_ism_multisig.ts @@ -1,5 +1,7 @@ +import { injectable } from "inversify"; import { BaseContract } from "../types"; -export default class HplIsmMultisig extends BaseContract { +@injectable() +export class HplIsmMultisig extends BaseContract { contractName: string = "hpl_ism_multisig"; } diff --git a/scripts/src/contracts/hpl_ism_routing.ts b/scripts/src/contracts/hpl_ism_routing.ts index e61f8c89..2190eb83 100644 --- a/scripts/src/contracts/hpl_ism_routing.ts +++ b/scripts/src/contracts/hpl_ism_routing.ts @@ -1,5 +1,7 @@ +import { injectable } from "inversify"; import { BaseContract } from "../types"; -export default class HplIsmRouting extends BaseContract { +@injectable() +export class HplIsmRouting extends BaseContract { contractName: string = "hpl_ism_routing"; } diff --git a/scripts/src/contracts/hpl_mailbox.ts b/scripts/src/contracts/hpl_mailbox.ts index 9397420a..2dfb9230 100644 --- a/scripts/src/contracts/hpl_mailbox.ts +++ b/scripts/src/contracts/hpl_mailbox.ts @@ -1,5 +1,7 @@ +import { injectable } from "inversify"; import { BaseContract } from "../types"; -export default class HplMailbox extends BaseContract { +@injectable() +export class HplMailbox extends BaseContract { contractName: string = "hpl_mailbox"; } diff --git a/scripts/src/contracts/hpl_test_mock_hook.ts b/scripts/src/contracts/hpl_test_mock_hook.ts index 95eaed3c..a26e26a6 100644 --- a/scripts/src/contracts/hpl_test_mock_hook.ts +++ b/scripts/src/contracts/hpl_test_mock_hook.ts @@ -1,5 +1,7 @@ +import { injectable } from "inversify"; import { BaseContract } from "../types"; -export default class HplTestMockHook extends BaseContract { +@injectable() +export class HplTestMockHook extends BaseContract { contractName: string = "hpl_test_mock_hook"; } diff --git a/scripts/src/contracts/hpl_test_mock_msg_receiver.ts b/scripts/src/contracts/hpl_test_mock_msg_receiver.ts index fc6e38e5..4e84e79a 100644 --- a/scripts/src/contracts/hpl_test_mock_msg_receiver.ts +++ b/scripts/src/contracts/hpl_test_mock_msg_receiver.ts @@ -1,5 +1,7 @@ +import { injectable } from "inversify"; import { BaseContract } from "../types"; -export default class HplTestMockMsgReceiver extends BaseContract { +@injectable() +export class HplTestMockMsgReceiver extends BaseContract { contractName: string = "hpl_test_mock_msg_receiver"; } diff --git a/scripts/src/contracts/hpl_validator_announce.ts b/scripts/src/contracts/hpl_validator_announce.ts index ecccf80a..b887331a 100644 --- a/scripts/src/contracts/hpl_validator_announce.ts +++ b/scripts/src/contracts/hpl_validator_announce.ts @@ -1,5 +1,7 @@ +import { injectable } from "inversify"; import { BaseContract } from "../types"; -export default class HplValidatorAnnounce extends BaseContract { +@injectable() +export class HplValidatorAnnounce extends BaseContract { contractName: string = "hpl_validator_announce"; } diff --git a/scripts/src/contracts/hpl_warp_cw20.ts b/scripts/src/contracts/hpl_warp_cw20.ts index 0b79f9ad..cea3aa9e 100644 --- a/scripts/src/contracts/hpl_warp_cw20.ts +++ b/scripts/src/contracts/hpl_warp_cw20.ts @@ -1,5 +1,7 @@ +import { injectable } from "inversify"; import { BaseContract } from "../types"; -export default class HplWarpCw20 extends BaseContract { +@injectable() +export class HplWarpCw20 extends BaseContract { contractName: string = "hpl_warp_cw20"; } diff --git a/scripts/src/contracts/hpl_warp_native.ts b/scripts/src/contracts/hpl_warp_native.ts index 4fbd546a..36d7d3a1 100644 --- a/scripts/src/contracts/hpl_warp_native.ts +++ b/scripts/src/contracts/hpl_warp_native.ts @@ -1,5 +1,7 @@ +import { injectable } from "inversify"; import { BaseContract } from "../types"; -export default class HplWarpNative extends BaseContract { +@injectable() +export class HplWarpNative extends BaseContract { contractName: string = "hpl_warp_native"; } diff --git a/scripts/src/contracts/index.ts b/scripts/src/contracts/index.ts index 1e996108..8016e991 100644 --- a/scripts/src/contracts/index.ts +++ b/scripts/src/contracts/index.ts @@ -1,3 +1,21 @@ +export * from "./hpl_hook_aggregate"; +export * from "./hpl_hook_merkle"; +export * from "./hpl_hook_pausable"; +export * from "./hpl_hook_routing"; +export * from "./hpl_hook_routing_custom"; +export * from "./hpl_hook_routing_fallback"; +export * from "./hpl_igp"; +export * from "./hpl_igp_oracle"; +export * from "./hpl_ism_aggregate"; +export * from "./hpl_ism_multisig"; +export * from "./hpl_ism_routing"; +export * from "./hpl_mailbox"; +export * from "./hpl_test_mock_hook"; +export * from "./hpl_test_mock_msg_receiver"; +export * from "./hpl_validator_announce"; +export * from "./hpl_warp_cw20"; +export * from "./hpl_warp_native"; + import { readdirSync } from "fs"; import { Context, Contract, ContractConstructor } from "../types"; import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate"; @@ -7,24 +25,43 @@ const contractNames: string[] = readdirSync(__dirname) .filter((f) => f !== "index.ts") .map((f) => f.replace(".ts", "")); -const contractHandlers: { [key: string]: ContractConstructor } = contractNames - .reduce((acc, cur) =>{ - acc[cur] = require(`./${cur}`).default - return acc; - }, {} as { [key: string]: ContractConstructor}); +const contractHandlers: { [key: string]: ContractConstructor } = + contractNames.reduce((acc, cur) => { + const className = cur + .split("_") + .map((v) => v[0].toUpperCase() + v.slice(1)) + .join(""); + acc[cur] = require(`./${cur}`)[className]; + return acc; + }, {} as { [key: string]: ContractConstructor }); export function getTargetContractName(): string[] { return contractNames; } -export function getTargetContract(ctx: Context, client: SigningCosmWasmClient, signer: string, container: Container): { [key: string]: Contract } { +export function getTargetContract( + ctx: Context, + client: SigningCosmWasmClient, + signer: string, + container: Container +): { [key: string]: Contract } { return Object.keys(contractHandlers).reduce((acc, cur) => { const { codeId, digest, address } = ctx.contracts[cur] || {}; - acc[cur] = new contractHandlers[cur](address, codeId, digest, signer, client); + + try { + acc[cur] = new contractHandlers[cur]( + address, + codeId, + digest, + signer, + client + ); + } catch (e) { + throw Error(`Failed to instantiate contract ${cur}: ${e}`); + } container.bind(contractHandlers[cur]).toConstantValue(acc[cur]); return acc; }, {} as { [key: string]: Contract }); } - diff --git a/scripts/src/deploy.ts b/scripts/src/deploy.ts new file mode 100644 index 00000000..29f23de4 --- /dev/null +++ b/scripts/src/deploy.ts @@ -0,0 +1,153 @@ +import HplMailbox from "./contracts/hpl_mailbox"; +import HplValidatorAnnounce from "./contracts/hpl_validator_announce"; +import HplHookAggregate from "./contracts/hpl_hook_aggregate"; +import HplHookMerkle from "./contracts/hpl_hook_merkle"; +import HplHookPausable from "./contracts/hpl_hook_pausable"; +import HplHookRouting from "./contracts/hpl_hook_routing"; +import HplHookRoutingCustom from "./contracts/hpl_hook_routing_custom"; +import HplIgp from "./contracts/hpl_igp"; +import HplIgpGasOracle from "./contracts/hpl_igp_oracle"; +import HplIsmAggregate from "./contracts/hpl_ism_aggregate"; +import HplIsmMultisig from "./contracts/hpl_ism_multisig"; +import HplIsmRouting from "./contracts/hpl_ism_routing"; +import HplTestMockHook from "./contracts/hpl_test_mock_hook"; +import HplTestMockMsgReceiver from "./contracts/hpl_test_mock_msg_receiver"; +import HplWarpCw20 from "./contracts/hpl_warp_cw20"; +import HplWarpNative from "./contracts/hpl_warp_native"; + +import { + Client, + IsmType, +} from "../src/config"; +import { addPad } from "./conv"; + +export type Contracts = { + core: { + mailbox: HplMailbox; + va: HplValidatorAnnounce; + }; + hooks: { + aggregate: HplHookAggregate; + merkle: HplHookMerkle; + pausable: HplHookPausable; + routing: HplHookRouting; + routing_custom: HplHookRoutingCustom; + routing_fallback: HplHookRoutingCustom; + }; + igp: { + core: HplIgp; + oracle: HplIgpGasOracle; + }; + isms: { + aggregate: HplIsmAggregate; + multisig: HplIsmMultisig; + routing: HplIsmRouting; + }; + mocks: { + hook: HplTestMockHook; + receiver: HplTestMockMsgReceiver; + }; + warp: { + cw20: HplWarpCw20; + native: HplWarpNative; + }; + }; + +const name = (c: any) => c.contractName; + +export const deploy_ism = async ( + client: Client, + ism: IsmType, + contracts: Contracts +): Promise => { + const { isms } = contracts; + + switch (ism.type) { + case "multisig": + console.log('Instantiate Multisig ISM contract') + const multisig_ism_res = await isms.multisig.instantiate({ + owner: ism.owner === "" ? client.signer : ism.owner, + }); + + console.log('Enroll validators') + console.log(ism) + console.log(Object.entries(ism.validators).flatMap(([domain, validator]) => + validator.addrs.map((v) => ({ + domain: Number(domain), + validator: v, + })) + )) + await client.wasm.execute( + client.signer, + multisig_ism_res.address!, + { + enroll_validators: { + set: Object.entries(ism.validators).flatMap(([domain, validator]) => + validator.addrs.map((v) => ({ + domain: Number(domain), + validator: v, + })) + ), + }, + }, + "auto" + ); + + console.log('Set thresholds') + await client.wasm.execute( + client.signer, + multisig_ism_res.address!, + { + set_thresholds: { + set: Object.entries(ism.validators).map( + ([domain, { threshold }]) => ({ + domain: Number(domain), + threshold, + }) + ), + }, + }, + "auto" + ); + + return multisig_ism_res.address!; + + case "aggregate": + const aggregate_ism_res = await isms.aggregate.instantiate({ + owner: ism.owner === "" ? client.signer : ism.owner, + isms: await Promise.all( + ism.isms.map((v) => deploy_ism(client, v, contracts)) + ), + }); + + return aggregate_ism_res.address!; + case "routing": + const routing_ism_res = await isms.routing.instantiate({ + owner: ism.owner === "" ? client.signer : ism.owner, + }); + + await client.wasm.execute( + client.signer, + routing_ism_res.address!, + { + router: { + set_routes: { + set: await Promise.all( + Object.entries(ism.isms).map(async ([domain, v]) => { + const route = await deploy_ism(client, v, contracts); + return { domain, route }; + }) + ), + }, + }, + }, + "auto" + ); + + return routing_ism_res.address!; + + default: + throw new Error("invalid ism type"); + } +}; + diff --git a/scripts/src/index.ts b/scripts/src/index.ts index f2317383..b3e9b860 100644 --- a/scripts/src/index.ts +++ b/scripts/src/index.ts @@ -1,3 +1,5 @@ +import "reflect-metadata"; + import colors from "colors"; import { loadWasmFileDigest, getWasmPath } from "./load_wasm"; import { loadContext, saveContext } from "./load_context"; diff --git a/scripts/src/load_wasm.ts b/scripts/src/load_wasm.ts index e269aab5..a1f9cdeb 100644 --- a/scripts/src/load_wasm.ts +++ b/scripts/src/load_wasm.ts @@ -53,5 +53,5 @@ export async function loadWasmFileDigest() { } export function getWasmPath(contractName: string): string { - return path.join(directoryPath, `${contractName}-aarch64.wasm`); + return path.join(directoryPath, `${contractName}.wasm`); } diff --git a/scripts/src/migrations/InitializeStandalone.ts b/scripts/src/migrations/InitializeStandalone.ts new file mode 100644 index 00000000..920cc5b4 --- /dev/null +++ b/scripts/src/migrations/InitializeStandalone.ts @@ -0,0 +1,74 @@ +import { injectable } from "inversify"; +import { Context, Migration } from "../types"; +import { + HplMailbox, + HplHookMerkle, + HplIgpGasOracle, + HplIsmMultisig, + HplTestMockHook, +} from "../contracts"; + +@injectable() +export default class InitializeStandalone implements Migration { + name: string = "initialize_standalone"; + after: string = ""; + + constructor( + private ctx: Context, + private mailbox: HplMailbox, + private hook_merkle: HplHookMerkle, + private igp: HplIgp, + private igp_oracle: HplIgpGasOracle, + private ism_multisig: HplIsmMultisig, + private test_mock_hook: HplTestMockHook + ) {} + + run = async (): Promise => { + // init mailbox + this.ctx.contracts[this.mailbox.contractName] = + await this.mailbox.instantiate({ + hrp: "dual", + owner: this.ctx.address!, + domain: 33333, + }); + + // init merkle hook - (required hook) + this.ctx.contracts[this.hook_merkle.contractName] = + await this.hook_merkle.instantiate({ + owner: this.ctx.address!, + mailbox: this.ctx.contracts[this.mailbox.contractName].address, + }); + + // init mock hook - (default hook) + this.ctx.contracts[this.test_mock_hook.contractName] = + await this.test_mock_hook.instantiate({}); + + // init igp oracle + this.ctx.contracts[this.igp_oracle.contractName] = + await this.igp_oracle.instantiate({ + owner: this.ctx.address!, + }); + + // init igp + this.ctx.contracts[this.igp.contractName] = await this.igp.instantiate({ + hrp: "dual", + owner: this.ctx.address!, + mailbox: this.ctx.contracts[this.mailbox.contractName].address, + gas_token: "token", + beneficiary: this.ctx.address!, + }); + + // init ism multisig + this.ctx.contracts[this.ism_multisig.contractName] = + await this.ism_multisig.instantiate({ + hrp: "dual", + owner: this.ctx.address!, + }); + + return this.ctx; + }; + + setContext = (ctx: Context) => { + this.ctx = ctx; + }; +} diff --git a/scripts/src/migrations/index.ts b/scripts/src/migrations/index.ts index adb2161a..b298a1bb 100644 --- a/scripts/src/migrations/index.ts +++ b/scripts/src/migrations/index.ts @@ -1,17 +1,15 @@ import { readdirSync } from "fs"; import { CONTAINER } from "../ioc"; -import { Context, Migration } from "../types"; -import 'reflect-metadata' +import { Context } from "../types"; import { saveContext } from "../load_context"; - -const MIGRATIONS: {[key: string]: any } = readdirSync(__dirname) +const MIGRATIONS: { [key: string]: any } = readdirSync(__dirname) .filter((f) => f !== "index.ts") .map((f) => f.replace(".ts", "")) - .reduce((acc, cur) =>{ - acc[cur] = require(`./${cur}`).default + .reduce((acc, cur) => { + acc[cur] = require(`./${cur}`).default; return acc; - }, {} as {[key: string]: any}); + }, {} as { [key: string]: any }); export async function runMigrations(network: string, dryRun: boolean) { const migraiotnMap: { [key: string]: any } = {}; @@ -26,28 +24,33 @@ export async function runMigrations(network: string, dryRun: boolean) { migraiotnMap[obj.name] = obj; }); - // find initials - const availableInitials = Object.keys(migraiotnMap).map((key) => migraiotnMap[key]).filter((obj) => obj.after === ""); - if(availableInitials.length != 1) { + const availableInitials = Object.keys(migraiotnMap) + .map((key) => migraiotnMap[key]) + .filter((obj) => obj.after === ""); + if (availableInitials.length != 1) { throw new Error("There must be one initial migration"); } let current = availableInitials[0]; let migrationOrder = [current]; - while(true) { + while (true) { let next: any | undefined = undefined; Object.keys(migraiotnMap).forEach((key) => { const obj = migraiotnMap[key]; - if(obj.after === current.name) { - (next === undefined) ? (next = obj) : (() => { throw new Error("There must be one next migration") })(); + if (obj.after === current.name) { + next === undefined + ? (next = obj) + : (() => { + throw new Error("There must be one next migration"); + })(); } }); - if(next === undefined) break; + if (next === undefined) break; migrationOrder.push(next); current = next; @@ -55,7 +58,8 @@ export async function runMigrations(network: string, dryRun: boolean) { // get contexts and check after; let ctx = CONTAINER.get(Context); - const migStartAt = migrationOrder.findIndex((obj) => obj.name === ctx.latestMigration) + 1; + const migStartAt = + migrationOrder.findIndex((obj) => obj.name === ctx.latestMigration) + 1; // migrate if (migrationOrder.slice(migStartAt).length === 0) { @@ -63,9 +67,9 @@ export async function runMigrations(network: string, dryRun: boolean) { return; } - console.log("\nrun migrations...\n") + console.log("\nrun migrations...\n"); - for(let obj of migrationOrder.slice(migStartAt)) { + for (let obj of migrationOrder.slice(migStartAt)) { process.stdout.write("[MIGRATION]".gray); process.stdout.write(` ${obj.name} ... `); @@ -82,21 +86,19 @@ export async function runMigrations(network: string, dryRun: boolean) { saveContext(network, ctx); console.log("OK".green); - } catch(err) { + } catch (err) { console.log("FAIL".red); throw err; } } console.log("\n[INFO]".gray, "All migrations are done."); - console.log("\n============= Migration Result =============\n") + console.log("\n============= Migration Result =============\n"); Object.keys(ctx.contracts).forEach((key) => { const contract = ctx.contracts[key]; - console.log(`${key}`.padEnd(30), '=>', `${contract.address}`); + console.log(`${key}`.padEnd(30), "=>", `${contract.address}`); }); } -export async function runContract(network: string) { - -} +export async function runContract(network: string) {} diff --git a/scripts/src/migrations/initialize.ts b/scripts/src/migrations/initialize.ts index 9e48f4f1..0e1772a7 100644 --- a/scripts/src/migrations/initialize.ts +++ b/scripts/src/migrations/initialize.ts @@ -1,11 +1,13 @@ import { injectable } from "inversify"; import { Context, Migration } from "../types"; -import HplMailbox from "../contracts/hpl_mailbox"; -import HplIgpGasOracle from "../contracts/hpl_igp_oracle"; -import HplIgpCore from "../contracts/hpl_igp"; -import HplIsmMultisig from "../contracts/hpl_ism_multisig"; -import HplHookMerkle from "../contracts/hpl_hook_merkle"; -import HplTestMockHook from "../contracts/hpl_test_mock_hook"; +import { + HplMailbox, + HplHookMerkle, + HplIgpOracle, + HplIsmMultisig, + HplTestMockHook, + HplIgp, +} from "../contracts"; @injectable() export default class InitializeStandalone implements Migration { @@ -16,8 +18,8 @@ export default class InitializeStandalone implements Migration { private ctx: Context, private mailbox: HplMailbox, private hook_merkle: HplHookMerkle, - private igp: HplIgpCore, - private igp_oracle: HplIgpGasOracle, + private igp: HplIgp, + private igp_oracle: HplIgpOracle, private ism_multisig: HplIsmMultisig, private test_mock_hook: HplTestMockHook ) {} diff --git a/scripts/src/migrations/mailbox.ts b/scripts/src/migrations/mailbox.ts index 05d86b2c..aa79c3d2 100644 --- a/scripts/src/migrations/mailbox.ts +++ b/scripts/src/migrations/mailbox.ts @@ -1,8 +1,6 @@ import { injectable } from "inversify"; import { Context, HplMailboxInstantiateMsg, Migration } from "../types"; -import HplMailbox from "../contracts/hpl_mailbox"; -import HplIsmMultisig from "../contracts/hpl_ism_multisig"; - +import { HplIsmMultisig, HplMailbox } from "../contracts"; @injectable() export default class MailboxMigration implements Migration { @@ -12,7 +10,7 @@ export default class MailboxMigration implements Migration { constructor( private ctx: Context, private mailbox: HplMailbox, - private ism_multisig: HplIsmMultisig, + private ism_multisig: HplIsmMultisig ) {} run = async (): Promise => { @@ -20,10 +18,13 @@ export default class MailboxMigration implements Migration { owner: this.ctx.address!, hrp: "dual", domain: 33333, - } - this.ctx.contracts[this.mailbox.contractName] = await this.mailbox.instantiate(mailboxInit); + }; + this.ctx.contracts[this.mailbox.contractName] = + await this.mailbox.instantiate(mailboxInit); return this.ctx; - } + }; - setContext = (ctx: Context) => { this.ctx = ctx } + setContext = (ctx: Context) => { + this.ctx = ctx; + }; } diff --git a/scripts/src/migrations/mailbox_related.ts b/scripts/src/migrations/mailbox_related.ts index ef9a383f..dd9b3aeb 100644 --- a/scripts/src/migrations/mailbox_related.ts +++ b/scripts/src/migrations/mailbox_related.ts @@ -1,14 +1,6 @@ import { injectable } from "inversify"; -import { - Context, - HplIsmRoutingInstantiateMsg, - HplMulticallInstantiateMsg, - HplValidatorAnnounceInstantiateMsg, - Migration, -} from "../types"; -import HplMailbox from "../contracts/hpl_mailbox"; -import HplIsmRouting from "../contracts/hpl_ism_routing"; -import HplValidatorAnnounce from "../contracts/hpl_validator_announce"; +import { Context, Migration } from "../types"; +import { HplIsmRouting, HplMailbox, HplValidatorAnnounce } from "../contracts"; @injectable() export default class MailboxMigration implements Migration { diff --git a/scripts/src/types.ts b/scripts/src/types.ts index 75f01aca..b28f9edc 100644 --- a/scripts/src/types.ts +++ b/scripts/src/types.ts @@ -50,25 +50,13 @@ export interface Contract { export abstract class BaseContract implements Contract { contractName: string; - address: string | undefined; - codeId: number | undefined; - digest: string; - client: SigningCosmWasmClient; - signer: string; - constructor( - address: string | undefined, - codeId: number | undefined, - digest: string, - signer: string, - client: SigningCosmWasmClient - ) { - this.address = address; - this.client = client; - this.digest = digest; - this.codeId = codeId; - this.signer = signer; - } + public address: string | undefined, + public codeId: number | undefined, + public digest: string, + public signer: string, + public client: SigningCosmWasmClient + ) {} public getContractContext(): ContractContext { return { From c7e3d076005331c00b8b98c613555b45bc1df58b Mon Sep 17 00:00:00 2001 From: ByeongSu Hong Date: Tue, 31 Oct 2023 23:18:49 +0900 Subject: [PATCH 21/53] feat(igp): default gas works (#64) * feat(igp): default gas works * fix test * test(igp): gas config * feat: new codes * feat: add migration * revert: roll back igp-oracle migration * chore: with migration * feat: conv rev * migrate * feat: add default gas * feat(igp): add gas query * feat: store igp --- contracts/core/va/src/contract.rs | 5 + contracts/igps/core/src/contract.rs | 30 +++- contracts/igps/core/src/event.rs | 32 ++++ contracts/igps/core/src/execute.rs | 66 ++++++- contracts/igps/core/src/lib.rs | 18 +- contracts/igps/core/src/query.rs | 52 +++++- contracts/igps/core/src/tests/contract.rs | 190 ++++++++++++++++++++- contracts/igps/core/src/tests/mod.rs | 1 + contracts/isms/multisig/src/contract.rs | 7 +- contracts/warp/cw20/Cargo.toml | 2 + contracts/warp/cw20/src/contract.rs | 18 +- contracts/warp/native/Cargo.toml | 2 + integration-test/tests/contracts/cw/igp.rs | 1 + packages/interface/src/igp/core.rs | 35 ++++ packages/interface/src/types/bech32.rs | 9 + scripts/action/migrate.ts | 55 +++++- scripts/context/duality-devnet.json | 6 +- scripts/context/neutron-1.json | 16 +- scripts/src/deploy.ts | 120 +++++++------ 19 files changed, 563 insertions(+), 102 deletions(-) diff --git a/contracts/core/va/src/contract.rs b/contracts/core/va/src/contract.rs index 7948c0df..76b567af 100644 --- a/contracts/core/va/src/contract.rs +++ b/contracts/core/va/src/contract.rs @@ -198,6 +198,11 @@ fn announce( )) } +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate(_deps: DepsMut, _env: Env, _msg: Empty) -> Result { + Ok(Response::new()) +} + #[cfg(test)] mod test { use cosmwasm_std::{ diff --git a/contracts/igps/core/src/contract.rs b/contracts/igps/core/src/contract.rs index e9df7605..16cfb454 100644 --- a/contracts/igps/core/src/contract.rs +++ b/contracts/igps/core/src/contract.rs @@ -1,13 +1,15 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{Deps, DepsMut, Env, Event, MessageInfo, QueryResponse, Response}; +use cosmwasm_std::{Deps, DepsMut, Empty, Env, Event, MessageInfo, QueryResponse, Response}; use hpl_interface::hook::HookQueryMsg; use hpl_interface::igp::core::{ExecuteMsg, IgpQueryMsg, InstantiateMsg, QueryMsg}; use hpl_interface::igp::oracle::IgpGasOracleQueryMsg; use hpl_interface::to_binary; -use crate::{ContractError, BENEFICIARY, CONTRACT_NAME, CONTRACT_VERSION, GAS_TOKEN, HRP}; +use crate::{ + ContractError, BENEFICIARY, CONTRACT_NAME, CONTRACT_VERSION, DEFAULT_GAS_USAGE, GAS_TOKEN, HRP, +}; fn new_event(name: &str) -> Event { Event::new(format!("hpl_igp_core::{}", name)) @@ -31,12 +33,14 @@ pub fn instantiate( GAS_TOKEN.save(deps.storage, &msg.gas_token)?; HRP.save(deps.storage, &msg.hrp)?; + DEFAULT_GAS_USAGE.save(deps.storage, &msg.default_gas_usage)?; Ok(Response::new().add_event( new_event("initialize") .add_attribute("sender", info.sender) .add_attribute("owner", msg.owner) - .add_attribute("beneficiary", msg.beneficiary), + .add_attribute("beneficiary", msg.beneficiary) + .add_attribute("default_gas", msg.default_gas_usage.to_string()), )) } @@ -54,6 +58,12 @@ pub fn execute( ExecuteMsg::Router(msg) => Ok(hpl_router::handle(deps, env, info, msg)?), ExecuteMsg::PostDispatch(msg) => Ok(execute::post_dispatch(deps, info, msg)?), + ExecuteMsg::SetDefaultGas { gas } => execute::set_default_gas(deps, info, gas), + ExecuteMsg::SetGasForDomain { config } => execute::set_gas_for_domain(deps, info, config), + ExecuteMsg::UnsetGasForDomain { domains } => { + execute::unset_gas_for_domain(deps, info, domains) + } + ExecuteMsg::SetBeneficiary { beneficiary } => { execute::set_beneficiary(deps, info, beneficiary) } @@ -93,7 +103,16 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result match msg { + IgpQueryMsg::DefaultGas {} => to_binary(get_default_gas(deps)), + IgpQueryMsg::GasForDomain { domains } => to_binary(get_gas_for_domain(deps, domains)), + IgpQueryMsg::ListGasForDomains { + offset, + limit, + order, + } => to_binary(list_gas_for_domains(deps, offset, limit, order)), + IgpQueryMsg::Beneficiary {} => to_binary(get_beneficiary(deps)), + IgpQueryMsg::QuoteGasPayment { dest_domain, gas_amount, @@ -101,3 +120,8 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result Result { + Ok(Response::new()) +} diff --git a/contracts/igps/core/src/event.rs b/contracts/igps/core/src/event.rs index 4a8b496a..ad981201 100644 --- a/contracts/igps/core/src/event.rs +++ b/contracts/igps/core/src/event.rs @@ -1,5 +1,37 @@ use cosmwasm_std::{Addr, Coin, Event, HexBinary, Uint128, Uint256}; +pub fn emit_set_default_gas(owner: Addr, default_gas: u128) -> Event { + Event::new("igp-core-set-default-gas") + .add_attribute("owner", owner) + .add_attribute("default-gas", default_gas.to_string()) +} + +pub fn emit_set_gas_for_domain(owner: Addr, gas_for_domain: Vec<(u32, u128)>) -> Event { + Event::new("igp-core-set-gas-for-domain") + .add_attribute("owner", owner) + .add_attribute( + "domains", + gas_for_domain + .into_iter() + .map(|v| v.0.to_string()) + .collect::>() + .join(","), + ) +} + +pub fn emit_unset_gas_for_domain(owner: Addr, domains: Vec) -> Event { + Event::new("igp-core-unset-gas-for-domain") + .add_attribute("owner", owner) + .add_attribute( + "domains", + domains + .into_iter() + .map(|v| v.to_string()) + .collect::>() + .join(","), + ) +} + pub fn emit_set_beneficiary(owner: Addr, beneficiary: String) -> Event { Event::new("igp-core-set-beneficiary") .add_attribute("owner", owner) diff --git a/contracts/igps/core/src/execute.rs b/contracts/igps/core/src/execute.rs index 49097b7e..1578ab78 100644 --- a/contracts/igps/core/src/execute.rs +++ b/contracts/igps/core/src/execute.rs @@ -1,6 +1,11 @@ -use crate::event::{emit_claim, emit_pay_for_gas, emit_post_dispatch, emit_set_beneficiary}; +use crate::event::{ + emit_claim, emit_pay_for_gas, emit_post_dispatch, emit_set_beneficiary, emit_set_default_gas, + emit_set_gas_for_domain, emit_unset_gas_for_domain, +}; use crate::query::quote_gas_price; -use crate::{ContractError, BENEFICIARY, DEFAULT_GAS_USAGE, GAS_TOKEN, HRP}; +use crate::{ + get_default_gas, ContractError, BENEFICIARY, DEFAULT_GAS_USAGE, GAS_FOR_DOMAIN, GAS_TOKEN, HRP, +}; use cosmwasm_std::{ coins, ensure, ensure_eq, BankMsg, DepsMut, Env, HexBinary, MessageInfo, Response, Uint128, @@ -14,6 +19,58 @@ use hpl_ownable::get_owner; use std::str::FromStr; +pub fn set_default_gas( + deps: DepsMut, + info: MessageInfo, + default_gas: u128, +) -> Result { + ensure_eq!( + info.sender, + get_owner(deps.storage)?, + ContractError::Unauthorized {} + ); + + DEFAULT_GAS_USAGE.save(deps.storage, &default_gas)?; + + Ok(Response::new().add_event(emit_set_default_gas(info.sender, default_gas))) +} + +pub fn set_gas_for_domain( + deps: DepsMut, + info: MessageInfo, + config: Vec<(u32, u128)>, +) -> Result { + ensure_eq!( + info.sender, + get_owner(deps.storage)?, + ContractError::Unauthorized {} + ); + + for (domain, custom_gas) in config.clone() { + GAS_FOR_DOMAIN.save(deps.storage, domain, &custom_gas)?; + } + + Ok(Response::new().add_event(emit_set_gas_for_domain(info.sender, config))) +} + +pub fn unset_gas_for_domain( + deps: DepsMut, + info: MessageInfo, + domains: Vec, +) -> Result { + ensure_eq!( + info.sender, + get_owner(deps.storage)?, + ContractError::Unauthorized {} + ); + + for domain in domains.clone() { + GAS_FOR_DOMAIN.remove(deps.storage, domain); + } + + Ok(Response::new().add_event(emit_unset_gas_for_domain(info.sender, domains))) +} + pub fn set_beneficiary( deps: DepsMut, info: MessageInfo, @@ -59,7 +116,10 @@ pub fn post_dispatch( let hrp = HRP.load(deps.storage)?; let (gas_limit, refund_address) = match req.metadata.to_vec().len() < 32 { - true => (Uint256::from(DEFAULT_GAS_USAGE), message.sender_addr(&hrp)?), + true => ( + Uint256::from(get_default_gas(deps.storage, message.dest_domain)?), + message.sender_addr(&hrp)?, + ), false => { let igp_metadata: IGPMetadata = req.metadata.clone().into(); ( diff --git a/contracts/igps/core/src/lib.rs b/contracts/igps/core/src/lib.rs index 02d0d3ff..cedd7a79 100644 --- a/contracts/igps/core/src/lib.rs +++ b/contracts/igps/core/src/lib.rs @@ -7,8 +7,8 @@ pub mod query; #[cfg(test)] pub mod tests; -use cosmwasm_std::Addr; -use cw_storage_plus::Item; +use cosmwasm_std::{Addr, StdResult, Storage}; +use cw_storage_plus::{Item, Map}; pub use error::ContractError; // version info for migration info @@ -17,7 +17,6 @@ pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); // constants pub const TOKEN_EXCHANGE_RATE_SCALE: u128 = 10_000_000_000; -pub const DEFAULT_GAS_USAGE: u128 = 25_000; pub const HRP_KEY: &str = "hrp"; pub const HRP: Item = Item::new(HRP_KEY); @@ -25,5 +24,18 @@ pub const HRP: Item = Item::new(HRP_KEY); pub const GAS_TOKEN_KEY: &str = "gas_token"; pub const GAS_TOKEN: Item = Item::new(GAS_TOKEN_KEY); +pub const DEFAULT_GAS_USAGE_KEY: &str = "default_gas_usage"; +pub const DEFAULT_GAS_USAGE: Item = Item::new(DEFAULT_GAS_USAGE_KEY); + +pub const GAS_FOR_DOMAIN_PREFIX: &str = "gas_for_domain"; +pub const GAS_FOR_DOMAIN: Map = Map::new(GAS_FOR_DOMAIN_PREFIX); + pub const BENEFICAIRY_KEY: &str = "beneficiary"; pub const BENEFICIARY: Item = Item::new(BENEFICAIRY_KEY); + +pub fn get_default_gas(storage: &dyn Storage, domain: u32) -> StdResult { + let custom_gas = GAS_FOR_DOMAIN.may_load(storage, domain)?; + let default_gas = DEFAULT_GAS_USAGE.load(storage)?; + + Ok(custom_gas.unwrap_or(default_gas)) +} diff --git a/contracts/igps/core/src/query.rs b/contracts/igps/core/src/query.rs index 51db48b7..6b672013 100644 --- a/contracts/igps/core/src/query.rs +++ b/contracts/igps/core/src/query.rs @@ -1,11 +1,14 @@ use crate::error::ContractError; -use crate::{BENEFICIARY, DEFAULT_GAS_USAGE, GAS_TOKEN, TOKEN_EXCHANGE_RATE_SCALE}; +use crate::{BENEFICIARY, DEFAULT_GAS_USAGE, GAS_FOR_DOMAIN, GAS_TOKEN, TOKEN_EXCHANGE_RATE_SCALE}; -use cosmwasm_std::{coin, Addr, Deps, QuerierWrapper, Storage, Uint256}; +use cosmwasm_std::{coin, Addr, Deps, QuerierWrapper, StdResult, Storage, Uint256}; use hpl_interface::hook::{MailboxResponse, QuoteDispatchMsg, QuoteDispatchResponse}; -use hpl_interface::igp::core::{BeneficiaryResponse, QuoteGasPaymentResponse}; +use hpl_interface::igp::core::{ + BeneficiaryResponse, DefaultGasResponse, GasForDomainResponse, QuoteGasPaymentResponse, +}; use hpl_interface::igp::oracle::{self, GetExchangeRateAndGasPriceResponse, IgpGasOracleQueryMsg}; use hpl_interface::types::{IGPMetadata, Message}; +use hpl_interface::Order; pub fn get_mailbox(_deps: Deps) -> Result { Ok(MailboxResponse { @@ -13,6 +16,40 @@ pub fn get_mailbox(_deps: Deps) -> Result { }) } +pub fn get_default_gas(deps: Deps) -> Result { + let default_gas = DEFAULT_GAS_USAGE.load(deps.storage)?; + + Ok(DefaultGasResponse { gas: default_gas }) +} + +pub fn get_gas_for_domain( + deps: Deps, + domains: Vec, +) -> Result { + Ok(GasForDomainResponse { + gas: domains + .into_iter() + .map(|v| Ok((v, GAS_FOR_DOMAIN.load(deps.storage, v)?))) + .collect::>()?, + }) +} + +pub fn list_gas_for_domains( + deps: Deps, + offset: Option, + limit: Option, + order: Option, +) -> Result { + let ((min, max), limit, order) = hpl_interface::range_option(offset, limit, order)?; + + let gas = GAS_FOR_DOMAIN + .range(deps.storage, min, max, order.into()) + .take(limit) + .collect::>>()?; + + Ok(GasForDomainResponse { gas }) +} + pub fn get_beneficiary(deps: Deps) -> Result { let beneficairy = BENEFICIARY.load(deps.storage)?; @@ -58,16 +95,19 @@ pub fn quote_dispatch( deps: Deps, req: QuoteDispatchMsg, ) -> Result { + let igp_message: Message = req.message.into(); + let gas_limit = match req.metadata.len() < 32 { - true => Uint256::from(DEFAULT_GAS_USAGE), + true => Uint256::from(crate::get_default_gas( + deps.storage, + igp_message.dest_domain, + )?), false => { let igp_metadata: IGPMetadata = req.metadata.clone().into(); igp_metadata.gas_limit } }; - let igp_message: Message = req.message.into(); - let gas_amount = quote_gas_payment(deps, igp_message.dest_domain, gas_limit)?.gas_needed; let gas_amount = if !gas_amount.is_zero() { Some(coin( diff --git a/contracts/igps/core/src/tests/contract.rs b/contracts/igps/core/src/tests/contract.rs index e539cfc8..daf61c7a 100644 --- a/contracts/igps/core/src/tests/contract.rs +++ b/contracts/igps/core/src/tests/contract.rs @@ -1,11 +1,17 @@ use cosmwasm_std::{ coin, from_binary, - testing::{mock_dependencies, mock_env}, - to_binary, Addr, BankMsg, Coin, ContractResult, HexBinary, QuerierResult, SubMsg, SystemResult, - Uint128, Uint256, WasmQuery, + testing::{mock_dependencies, mock_env, mock_info}, + to_binary, Addr, BankMsg, Coin, ContractResult, HexBinary, Order, QuerierResult, StdResult, + SubMsg, SystemResult, Uint128, Uint256, WasmQuery, }; use hpl_interface::{ - igp::{core::GasOracleConfig, oracle}, + igp::{ + core::{ + DefaultGasResponse, ExecuteMsg, GasForDomainResponse, GasOracleConfig, IgpQueryMsg, + QueryMsg, + }, + oracle, + }, types::{IGPMetadata, Message}, }; use hpl_ownable::get_owner; @@ -13,7 +19,7 @@ use hpl_router::get_routes; use ibcx_test_utils::{addr, gen_bz}; use rstest::{fixture, rstest}; -use crate::{BENEFICIARY, DEFAULT_GAS_USAGE, GAS_TOKEN, HRP}; +use crate::{get_default_gas, BENEFICIARY, DEFAULT_GAS_USAGE, GAS_TOKEN, HRP}; use super::IGP; @@ -299,7 +305,10 @@ fn test_post_dispatch( .parse::() .unwrap(); - assert_eq!(gas_limit.unwrap_or(DEFAULT_GAS_USAGE), gas_amount_log); + assert_eq!( + gas_limit.unwrap_or(DEFAULT_GAS_USAGE.load(igp.deps.as_mut().storage).unwrap()), + gas_amount_log + ); } #[rstest] @@ -321,3 +330,172 @@ fn test_claim(mut igp: IGP, #[case] sender: Addr, #[case] funds: Vec) { }) ) } + +#[rstest] +#[case(addr("owner"))] +#[should_panic(expected = "unauthorized")] +#[case(addr("someone"))] +fn test_set_default_gas(mut igp: IGP, #[case] sender: Addr) { + let _resp = igp + .execute( + mock_info(sender.as_str(), &[]), + ExecuteMsg::SetDefaultGas { gas: 99_999 }, + ) + .map_err(|v| v.to_string()) + .unwrap(); + + let storage = igp.deps.as_ref().storage; + + assert_eq!(crate::DEFAULT_GAS_USAGE.load(storage).unwrap(), 99_999); +} + +#[rstest] +#[case(addr("owner"))] +#[should_panic(expected = "unauthorized")] +#[case(addr("someone"))] +fn test_set_gas_for_domain(mut igp: IGP, #[case] sender: Addr) { + let config = (1u32..5u32) + .map(|i| (i, (i * 100_000) as u128)) + .collect::>(); + + let _resp = igp + .execute( + mock_info(sender.as_str(), &[]), + ExecuteMsg::SetGasForDomain { + config: config.clone(), + }, + ) + .map_err(|v| v.to_string()) + .unwrap(); + + let storage = igp.deps.as_ref().storage; + + assert_eq!( + crate::GAS_FOR_DOMAIN + .range(storage, None, None, Order::Ascending) + .collect::>>() + .unwrap(), + config + ); +} + +#[rstest] +#[case(addr("owner"))] +#[should_panic(expected = "unauthorized")] +#[case(addr("someone"))] +fn test_unset_gas_for_domain(mut igp: IGP, #[case] sender: Addr) { + let config = (1u32..5u32) + .map(|i| (i, (i * 100_000) as u128)) + .collect::>(); + + let _resp = igp + .execute( + mock_info("owner", &[]), + ExecuteMsg::SetGasForDomain { + config: config.clone(), + }, + ) + .map_err(|v| v.to_string()) + .unwrap(); + + let _resp = igp + .execute( + mock_info(sender.as_str(), &[]), + ExecuteMsg::UnsetGasForDomain { + domains: config.iter().map(|v| v.0).collect(), + }, + ) + .map_err(|v| v.to_string()) + .unwrap(); + + let storage = igp.deps.as_ref().storage; + + assert!(crate::GAS_FOR_DOMAIN.is_empty(storage)); +} + +#[rstest] +fn test_get_default_gas(mut igp: IGP) { + let _resp = igp + .execute( + mock_info("owner", &[]), + ExecuteMsg::SetGasForDomain { + config: vec![(1, 123_456)], + }, + ) + .map_err(|v| v.to_string()) + .unwrap(); + + let storage = igp.deps.as_ref().storage; + + assert_eq!(get_default_gas(storage, 1).unwrap(), 123_456); + assert_eq!(get_default_gas(storage, 2).unwrap(), 250_000); +} + +#[rstest] +fn test_gas_query(mut igp: IGP) { + let config = (1u32..100u32) + .map(|i| (i, (i * 100_000) as u128)) + .collect::>(); + + let _resp = igp + .execute( + mock_info("owner", &[]), + ExecuteMsg::SetGasForDomain { config }, + ) + .map_err(|v| v.to_string()) + .unwrap(); + + let DefaultGasResponse { gas: default_gas } = igp + .query(QueryMsg::Igp(IgpQueryMsg::DefaultGas {})) + .unwrap(); + assert_eq!(default_gas, 250_000); + + let domain_range = 1u32..4u32; + let GasForDomainResponse { + gas: gas_for_domain, + } = igp + .query(QueryMsg::Igp(IgpQueryMsg::GasForDomain { + domains: domain_range.clone().collect(), + })) + .unwrap(); + assert_eq!( + domain_range + .map(|i| (i, (i * 100_000) as u128)) + .collect::>(), + gas_for_domain + ); + + let domain_range = 1u32..11u32; + let GasForDomainResponse { + gas: gas_for_domain, + } = igp + .query(QueryMsg::Igp(IgpQueryMsg::ListGasForDomains { + offset: None, + limit: None, + order: None, + })) + .unwrap(); + assert_eq!( + domain_range + .map(|i| (i, (i * 100_000) as u128)) + .collect::>(), + gas_for_domain + ); + + let domain_range = (90u32..100u32).rev(); + let GasForDomainResponse { + gas: gas_for_domain, + } = igp + .query(QueryMsg::Igp(IgpQueryMsg::ListGasForDomains { + offset: None, + limit: None, + order: Some(hpl_interface::Order::Desc), + })) + .unwrap(); + assert_eq!( + domain_range + .map(|i| (i, (i * 100_000) as u128)) + .collect::>(), + gas_for_domain + ); +} diff --git a/contracts/igps/core/src/tests/mod.rs b/contracts/igps/core/src/tests/mod.rs index d0dd96f3..29c4cd51 100644 --- a/contracts/igps/core/src/tests/mod.rs +++ b/contracts/igps/core/src/tests/mod.rs @@ -54,6 +54,7 @@ impl IGP { owner: owner.to_string(), gas_token: gas_token.to_string(), beneficiary: beneficiary.to_string(), + default_gas_usage: 250_000, }, ) } diff --git a/contracts/isms/multisig/src/contract.rs b/contracts/isms/multisig/src/contract.rs index 4b70daed..15a60f11 100644 --- a/contracts/isms/multisig/src/contract.rs +++ b/contracts/isms/multisig/src/contract.rs @@ -1,6 +1,6 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{Deps, DepsMut, Env, MessageInfo, QueryResponse, Response}; +use cosmwasm_std::{Deps, DepsMut, Empty, Env, MessageInfo, QueryResponse, Response}; use cw2::set_contract_version; use hpl_interface::{ ism::{ @@ -89,3 +89,8 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result Result { + Ok(Response::new()) +} diff --git a/contracts/warp/cw20/Cargo.toml b/contracts/warp/cw20/Cargo.toml index 0c2de70e..10cbe744 100644 --- a/contracts/warp/cw20/Cargo.toml +++ b/contracts/warp/cw20/Cargo.toml @@ -44,6 +44,8 @@ hpl-router.workspace = true hpl-interface.workspace = true [dev-dependencies] +serde-json-wasm.workspace = true + osmosis-test-tube.workspace = true ibcx-test-utils.workspace = true rstest.workspace = true diff --git a/contracts/warp/cw20/src/contract.rs b/contracts/warp/cw20/src/contract.rs index cd5a0e8d..defe13b2 100644 --- a/contracts/warp/cw20/src/contract.rs +++ b/contracts/warp/cw20/src/contract.rs @@ -529,15 +529,26 @@ mod test { &sender, ExecuteMsg::TransferRemote { dest_domain: domain, - recipient: route.clone(), + recipient: recipient.clone(), amount: Uint128::new(100), }, vec![], ); let msgs = res.messages.into_iter().map(|v| v.msg).collect::>(); + let transfer_from_msg = wasm_execute( + TOKEN, + &Cw20ExecuteMsg::TransferFrom { + owner: sender.to_string(), + recipient: mock_env().contract.address.to_string(), + amount: Uint128::new(100), + }, + vec![], + ) + .unwrap(); + let warp_msg = warp::Message { - recipient, + recipient: recipient, amount: Uint256::from_u128(100), metadata: HexBinary::default(), }; @@ -550,6 +561,7 @@ mod test { assert_eq!( cosmwasm_std::to_binary(&msgs).unwrap(), cosmwasm_std::to_binary(&vec![ + transfer_from_msg.into(), CosmosMsg::from(conv::to_burn_msg(TOKEN, Uint128::new(100)).unwrap()), dispatch_msg, ]) @@ -559,7 +571,7 @@ mod test { TokenModeMsg::Collateral(_) => { assert_eq!( cosmwasm_std::to_binary(&msgs).unwrap(), - cosmwasm_std::to_binary(&vec![dispatch_msg]).unwrap(), + cosmwasm_std::to_binary(&vec![transfer_from_msg.into(), dispatch_msg]).unwrap(), ); } } diff --git a/contracts/warp/native/Cargo.toml b/contracts/warp/native/Cargo.toml index c17dce97..f621ef34 100644 --- a/contracts/warp/native/Cargo.toml +++ b/contracts/warp/native/Cargo.toml @@ -45,6 +45,8 @@ hpl-router.workspace = true hpl-interface.workspace = true [dev-dependencies] +serde-json-wasm.workspace = true + ibcx-test-utils.workspace = true rstest.workspace = true anyhow.workspace = true diff --git a/integration-test/tests/contracts/cw/igp.rs b/integration-test/tests/contracts/cw/igp.rs index a1ccaadb..62447f27 100644 --- a/integration-test/tests/contracts/cw/igp.rs +++ b/integration-test/tests/contracts/cw/igp.rs @@ -36,6 +36,7 @@ impl Igp { owner: owner.address(), gas_token: self.gas_token, beneficiary: self.beneficiary, + default_gas_usage: 25_000, }, Some(deployer.address().as_str()), Some("cw-hpl-igp"), diff --git a/packages/interface/src/igp/core.rs b/packages/interface/src/igp/core.rs index 01e488f4..b71287cf 100644 --- a/packages/interface/src/igp/core.rs +++ b/packages/interface/src/igp/core.rs @@ -5,6 +5,7 @@ use crate::{ hook::{HookQueryMsg, PostDispatchMsg}, ownable::{OwnableMsg, OwnableQueryMsg}, router::{RouterMsg, RouterQuery}, + Order, }; use super::oracle::IgpGasOracleQueryMsg; @@ -15,6 +16,7 @@ pub struct InstantiateMsg { pub owner: String, pub gas_token: String, pub beneficiary: String, + pub default_gas_usage: u128, } #[cw_serde] @@ -49,6 +51,16 @@ pub enum ExecuteMsg { PostDispatch(PostDispatchMsg), // base + SetDefaultGas { + gas: u128, + }, + SetGasForDomain { + config: Vec<(u32, u128)>, + }, + UnsetGasForDomain { + domains: Vec, + }, + SetBeneficiary { beneficiary: String, }, @@ -78,6 +90,19 @@ pub enum QueryMsg { #[cw_serde] #[derive(QueryResponses)] pub enum IgpQueryMsg { + #[returns(DefaultGasResponse)] + DefaultGas {}, + + #[returns(GasForDomainResponse)] + GasForDomain { domains: Vec }, + + #[returns(GasForDomainResponse)] + ListGasForDomains { + offset: Option, + limit: Option, + order: Option, + }, + #[returns(BeneficiaryResponse)] Beneficiary {}, @@ -94,6 +119,16 @@ impl IgpQueryMsg { } } +#[cw_serde] +pub struct DefaultGasResponse { + pub gas: u128, +} + +#[cw_serde] +pub struct GasForDomainResponse { + pub gas: Vec<(u32, u128)>, +} + #[cw_serde] pub struct BeneficiaryResponse { pub beneficiary: String, diff --git a/packages/interface/src/types/bech32.rs b/packages/interface/src/types/bech32.rs index 8ad751d7..b2441e8d 100644 --- a/packages/interface/src/types/bech32.rs +++ b/packages/interface/src/types/bech32.rs @@ -74,6 +74,15 @@ mod tests { println!("{}", addr_hex); } + #[test] + fn conv_rev() { + let addr_hex = "f3aa0d652226e21ae35cd9035c492ae41725edc9036edf0d6a48701b153b90a0"; + let addr_bin = HexBinary::from_hex(addr_hex).unwrap(); + let addr = bech32_encode("dual", &addr_bin).unwrap(); + + println!("{}", addr); + } + #[rstest] #[case( "osmo", diff --git a/scripts/action/migrate.ts b/scripts/action/migrate.ts index d26d99d9..05d8a16a 100644 --- a/scripts/action/migrate.ts +++ b/scripts/action/migrate.ts @@ -1,3 +1,5 @@ +import "reflect-metadata"; + import { Event } from "@cosmjs/cosmwasm-stargate"; import { config, getSigningClient } from "../src/config"; import { loadContext } from "../src/load_context"; @@ -17,14 +19,57 @@ async function main() { const contracts = new ContractFetcher(ctx, client).getContracts(); - const migrate_resp = await client.wasm.migrate( + const migrations: [string, number][] = [ + [ + "neutron1q75ky8reksqzh0lkhk9k3csvjwv74jjquahrj233xc7dvzz5fv4qtvw0qg", + contracts.isms.multisig.codeId!, + ], + [ + "neutron12p8wntzra3vpfcqv05scdx5sa3ftaj6gjcmtm7ynkl0e6crtt4ns8cnrmx", + contracts.igp.core.codeId!, + ], + [ + "neutron17w4q6efzym3p4c6umyp4cjf2ustjtmwfqdhd7rt2fpcpk9fmjzsq0kj0f8", + contracts.core.va.codeId!, + ], + ]; + + for (const [addr, code_id] of migrations) { + const contract_info = await client.wasm.getContract(addr); + + if (!contract_info.admin) { + console.log(`skipping ${addr} as it has no admin`); + continue; + } + + if (contract_info.admin !== client.signer) { + console.log( + `skipping ${addr} as it is not admin. actual: ${contract_info.admin}` + ); + continue; + } + + const migrate_resp = await client.wasm.migrate( + client.signer, + addr, + code_id, + {}, + "auto" + ); + console.log(parseEventLog(migrate_resp.events)); + } + + const set_gas_resp = await client.wasm.execute( client.signer, - "dual1rvtgvc38sfd9zehtgsp3eh8k269naq949u5qdcqm3x35mjg2uctqfdn3yq", - contracts.mocks.receiver.codeId!, - {}, + "neutron12p8wntzra3vpfcqv05scdx5sa3ftaj6gjcmtm7ynkl0e6crtt4ns8cnrmx", + { + set_default_gas: { + gas: "200000", + }, + }, "auto" ); - console.log(parseEventLog(migrate_resp.events)); + console.log(parseEventLog(set_gas_resp.events)); } main().catch(console.error); diff --git a/scripts/context/duality-devnet.json b/scripts/context/duality-devnet.json index 495030cd..e0614116 100644 --- a/scripts/context/duality-devnet.json +++ b/scripts/context/duality-devnet.json @@ -6,8 +6,8 @@ }, "hpl_igp": { "address": "dual14cu2z6xw62cumt7hmfw7977jyaymr26jazxdpvfp7ag2dss29q2q700yum", - "codeId": 136, - "digest": "92e97402efe3aedcc84ef33608d5199f7a6506a61d13afc472e4b1f6740393cd" + "codeId": 147, + "digest": "ed02f45ba7566523428c483a99840ec803b4a8c4a61504c85c70713adfe4d912" }, "hpl_igp_oracle": { "address": "dual1alp24grk7e5f3msv0apk68hehspglpr7y7upfrvkpdfu7c84sqtq8ze5p2", @@ -94,4 +94,4 @@ } }, "address": "dual1dwnrgwsf5c9vqjxsax04pdm0mx007yrraj2dgn" -} +} \ No newline at end of file diff --git a/scripts/context/neutron-1.json b/scripts/context/neutron-1.json index 96906131..6659dd82 100644 --- a/scripts/context/neutron-1.json +++ b/scripts/context/neutron-1.json @@ -25,8 +25,8 @@ "digest": "6fcf09c7d50fd5bfddbdeb0020625bdab2094237dca4f29e93ae12371a550f6f" }, "hpl_igp": { - "codeId": 411, - "digest": "92e97402efe3aedcc84ef33608d5199f7a6506a61d13afc472e4b1f6740393cd" + "codeId": 433, + "digest": "016156ac0887daca0545eddd7d2adad91ee7049485abe567efbbf8476282a2ef" }, "hpl_igp_oracle": { "codeId": 412, @@ -37,8 +37,8 @@ "digest": "3a4adfaed525c40845768e05da436f1ea39f6c9d7e9d47ec2de8457034a229c1" }, "hpl_ism_multisig": { - "codeId": 414, - "digest": "168a875843bd6b13420e289e6a089e1c566d62f95e9b9fb4bcec0c61966940f4" + "codeId": 431, + "digest": "e233448e7b3fa54b528740ec882a93a59300fda7c828e829ad04c44f5344e259" }, "hpl_ism_routing": { "codeId": 415, @@ -57,8 +57,8 @@ "digest": "d0f8bc913e07df303911222970e09dd3abeef97399cb1c554d76cf98caa7f37a" }, "hpl_validator_announce": { - "codeId": 419, - "digest": "db7afc67aa15c4f287af98c7836b3acf2016b96c6aa1eee01458bc11e6732178" + "codeId": 432, + "digest": "5985741956f51e1353095939c413b5e03d03670f97482783cfd629a72c1b240d" }, "hpl_warp_cw20": { "codeId": 420, @@ -69,5 +69,5 @@ "digest": "983943571c74276e36a36f62ef9c6c54dc6de2c57fb76af4a0de37a0b5fef3ec" } }, - "address": "neutron1ugu6ktzqwrmr0pgw603kakuhvj46a7mmrl6pw3" -} + "address": "neutron1dwnrgwsf5c9vqjxsax04pdm0mx007yrre4yyvm" +} \ No newline at end of file diff --git a/scripts/src/deploy.ts b/scripts/src/deploy.ts index 29f23de4..8f176c41 100644 --- a/scripts/src/deploy.ts +++ b/scripts/src/deploy.ts @@ -1,57 +1,54 @@ -import HplMailbox from "./contracts/hpl_mailbox"; -import HplValidatorAnnounce from "./contracts/hpl_validator_announce"; -import HplHookAggregate from "./contracts/hpl_hook_aggregate"; -import HplHookMerkle from "./contracts/hpl_hook_merkle"; -import HplHookPausable from "./contracts/hpl_hook_pausable"; -import HplHookRouting from "./contracts/hpl_hook_routing"; -import HplHookRoutingCustom from "./contracts/hpl_hook_routing_custom"; -import HplIgp from "./contracts/hpl_igp"; -import HplIgpGasOracle from "./contracts/hpl_igp_oracle"; -import HplIsmAggregate from "./contracts/hpl_ism_aggregate"; -import HplIsmMultisig from "./contracts/hpl_ism_multisig"; -import HplIsmRouting from "./contracts/hpl_ism_routing"; -import HplTestMockHook from "./contracts/hpl_test_mock_hook"; -import HplTestMockMsgReceiver from "./contracts/hpl_test_mock_msg_receiver"; -import HplWarpCw20 from "./contracts/hpl_warp_cw20"; -import HplWarpNative from "./contracts/hpl_warp_native"; - +import { Client, IsmType } from "../src/config"; import { - Client, - IsmType, -} from "../src/config"; -import { addPad } from "./conv"; + HplMailbox, + HplValidatorAnnounce, + HplHookAggregate, + HplHookMerkle, + HplHookPausable, + HplHookRouting, + HplHookRoutingCustom, + HplIgp, + HplIsmAggregate, + HplIsmMultisig, + HplIsmRouting, + HplTestMockHook, + HplTestMockMsgReceiver, + HplWarpCw20, + HplWarpNative, + HplIgpOracle, +} from "./contracts"; export type Contracts = { - core: { - mailbox: HplMailbox; - va: HplValidatorAnnounce; - }; - hooks: { - aggregate: HplHookAggregate; - merkle: HplHookMerkle; - pausable: HplHookPausable; - routing: HplHookRouting; - routing_custom: HplHookRoutingCustom; - routing_fallback: HplHookRoutingCustom; - }; - igp: { - core: HplIgp; - oracle: HplIgpGasOracle; - }; - isms: { - aggregate: HplIsmAggregate; - multisig: HplIsmMultisig; - routing: HplIsmRouting; - }; - mocks: { - hook: HplTestMockHook; - receiver: HplTestMockMsgReceiver; - }; - warp: { - cw20: HplWarpCw20; - native: HplWarpNative; - }; + core: { + mailbox: HplMailbox; + va: HplValidatorAnnounce; + }; + hooks: { + aggregate: HplHookAggregate; + merkle: HplHookMerkle; + pausable: HplHookPausable; + routing: HplHookRouting; + routing_custom: HplHookRoutingCustom; + routing_fallback: HplHookRoutingCustom; + }; + igp: { + core: HplIgp; + oracle: HplIgpOracle; + }; + isms: { + aggregate: HplIsmAggregate; + multisig: HplIsmMultisig; + routing: HplIsmRouting; + }; + mocks: { + hook: HplTestMockHook; + receiver: HplTestMockMsgReceiver; }; + warp: { + cw20: HplWarpCw20; + native: HplWarpNative; + }; +}; const name = (c: any) => c.contractName; @@ -64,19 +61,21 @@ export const deploy_ism = async ( switch (ism.type) { case "multisig": - console.log('Instantiate Multisig ISM contract') + console.log("Instantiate Multisig ISM contract"); const multisig_ism_res = await isms.multisig.instantiate({ owner: ism.owner === "" ? client.signer : ism.owner, }); - console.log('Enroll validators') - console.log(ism) - console.log(Object.entries(ism.validators).flatMap(([domain, validator]) => - validator.addrs.map((v) => ({ - domain: Number(domain), - validator: v, - })) - )) + console.log("Enroll validators"); + console.log(ism); + console.log( + Object.entries(ism.validators).flatMap(([domain, validator]) => + validator.addrs.map((v) => ({ + domain: Number(domain), + validator: v, + })) + ) + ); await client.wasm.execute( client.signer, multisig_ism_res.address!, @@ -93,7 +92,7 @@ export const deploy_ism = async ( "auto" ); - console.log('Set thresholds') + console.log("Set thresholds"); await client.wasm.execute( client.signer, multisig_ism_res.address!, @@ -150,4 +149,3 @@ export const deploy_ism = async ( throw new Error("invalid ism type"); } }; - From 1565457356fc1d7bbf24d2a347ce9d70e903219d Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Tue, 7 Nov 2023 21:39:24 +0700 Subject: [PATCH 22/53] fix: quote gas --- contracts/core/mailbox/src/contract.rs | 4 +- contracts/core/mailbox/src/error.rs | 3 + contracts/core/mailbox/src/execute.rs | 111 ++++++++------------ contracts/core/mailbox/src/query.rs | 47 +++++---- contracts/hooks/aggregate/src/error.rs | 3 + contracts/hooks/aggregate/src/lib.rs | 44 ++++---- contracts/hooks/merkle/src/lib.rs | 4 +- contracts/hooks/pausable/src/lib.rs | 4 +- contracts/hooks/routing-custom/src/lib.rs | 15 ++- contracts/hooks/routing-fallback/src/lib.rs | 17 +-- contracts/hooks/routing/src/lib.rs | 15 ++- contracts/igps/core/src/query.rs | 8 +- contracts/mocks/mock-hook/src/contract.rs | 4 +- contracts/warp/cw20/src/contract.rs | 2 +- packages/interface/src/core/mailbox.rs | 2 +- packages/interface/src/hook/mod.rs | 2 +- 16 files changed, 147 insertions(+), 138 deletions(-) diff --git a/contracts/core/mailbox/src/contract.rs b/contracts/core/mailbox/src/contract.rs index e74ca14f..e39e328f 100644 --- a/contracts/core/mailbox/src/contract.rs +++ b/contracts/core/mailbox/src/contract.rs @@ -71,7 +71,9 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result Ok(hpl_ownable::handle_query(deps, env, msg)?), QueryMsg::Hook(msg) => match msg { - MailboxHookQueryMsg::QuoteDispatch(msg) => to_binary(quote_dispatch(deps, msg)), + MailboxHookQueryMsg::QuoteDispatch { sender, msg } => { + to_binary(quote_dispatch(deps, sender, msg)) + } }, QueryMsg::Mailbox(msg) => match msg { Hrp {} => to_binary(get_hrp(deps)), diff --git a/contracts/core/mailbox/src/error.rs b/contracts/core/mailbox/src/error.rs index cdf4805a..29709f78 100644 --- a/contracts/core/mailbox/src/error.rs +++ b/contracts/core/mailbox/src/error.rs @@ -9,6 +9,9 @@ pub enum ContractError { #[error("{0}")] Payment(#[from] cw_utils::PaymentError), + #[error("{0}")] + CoinsError(#[from] cosmwasm_std::CoinsError), + #[error("unauthorized")] Unauthorized {}, diff --git a/contracts/core/mailbox/src/execute.rs b/contracts/core/mailbox/src/execute.rs index 2170c952..e6e24369 100644 --- a/contracts/core/mailbox/src/execute.rs +++ b/contracts/core/mailbox/src/execute.rs @@ -1,14 +1,13 @@ use cosmwasm_std::{ - coin, ensure, ensure_eq, to_binary, wasm_execute, Coin, Deps, DepsMut, Env, HexBinary, - MessageInfo, Response, + ensure, ensure_eq, to_binary, wasm_execute, BankMsg, Coins, DepsMut, Env, HexBinary, + MessageInfo, Response, StdResult, }; -use cw_utils::PaymentError; use hpl_interface::{ core::{ mailbox::{DispatchMsg, DispatchResponse}, HandleMsg, }, - hook::{post_dispatch, quote_dispatch}, + hook::{self, post_dispatch}, ism, types::Message, }; @@ -24,53 +23,6 @@ use crate::{ ContractError, MAILBOX_VERSION, }; -fn get_required_value( - deps: Deps, - info: &MessageInfo, - hook: impl Into, - metadata: HexBinary, - msg_body: HexBinary, -) -> Result<(Option, Option), ContractError> { - let required = quote_dispatch(&deps.querier, hook, metadata, msg_body)?.gas_amount; - let required = match required { - Some(v) => v, - None => return Ok((None, None)), - }; - - if info.funds.is_empty() { - return Ok((None, None)); - } - - if info.funds.len() > 1 { - return Err(PaymentError::MultipleDenoms {}.into()); - } - - let received = &info.funds[0]; - - deps.api.debug(&format!( - "mailbox::dispatch: required: {:?}, received: {:?}", - required, received - )); - - ensure_eq!( - &received.denom, - &required.denom, - PaymentError::ExtraDenom(received.clone().denom) - ); - - if received.amount <= required.amount { - return Ok((Some(received.clone()), None)); - } - - Ok(( - Some(required.clone()), - Some(coin( - (received.amount - required.amount).u128(), - required.denom, - )), - )) -} - pub fn set_default_ism( deps: DepsMut, info: MessageInfo, @@ -156,6 +108,7 @@ pub fn dispatch( ); // calculate gas + let default_hook = config.get_default_hook(); let required_hook = config.get_required_hook(); let msg = @@ -164,17 +117,33 @@ pub fn dispatch( .to_msg(MAILBOX_VERSION, nonce, config.local_domain, &info.sender)?; let msg_id = msg.id(); - let (required_hook_value, hook_value) = get_required_value( - deps.as_ref(), - &info, - required_hook.as_str(), + let base_gas = hook::quote_dispatch( + &deps.querier, + dispatch_msg.get_hook_addr(deps.api, default_hook)?, + dispatch_msg.metadata.clone().unwrap_or_default(), + msg.clone(), + )? + .gas_amount; + + let required_gas = hook::quote_dispatch( + &deps.querier, + &required_hook, dispatch_msg.metadata.clone().unwrap_or_default(), - msg.clone().into(), + msg.clone(), + )? + .gas_amount; + + // assert gas received is satisfies required gas + let mut total_gas = required_gas.clone().into_iter().try_fold( + Coins::try_from(base_gas.clone())?, + |mut acc, gas| { + acc.add(gas)?; + StdResult::Ok(acc) + }, )?; - let (required_hook_value, hook_value) = ( - required_hook_value.map(|v| vec![v]), - hook_value.map(|v| vec![v]), - ); + for fund in info.funds { + total_gas.sub(fund)?; + } // interaction let hook = dispatch_msg.get_hook_addr(deps.api, config.get_default_hook())?; @@ -190,16 +159,22 @@ pub fn dispatch( required_hook, hook_metadata.clone(), msg.clone(), - required_hook_value, + Some(required_gas), )?, - post_dispatch(hook, hook_metadata, msg.clone(), hook_value)?, + post_dispatch(hook, hook_metadata, msg.clone(), Some(base_gas))?, ]; + let refund_msg = BankMsg::Send { + to_address: info.sender.to_string(), + amount: total_gas.to_vec(), + }; + Ok(Response::new() .add_event(emit_dispatch_id(msg_id.clone())) .add_event(emit_dispatch(msg)) .set_data(to_binary(&DispatchResponse { message_id: msg_id })?) - .add_messages(post_dispatch_msgs)) + .add_messages(post_dispatch_msgs) + .add_message(refund_msg)) } pub fn process( @@ -279,7 +254,7 @@ pub fn process( #[cfg(test)] mod tests { use cosmwasm_std::{ - from_binary, + coin, from_binary, testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, Addr, ContractResult, OwnedDeps, QuerierResult, SystemResult, WasmQuery, }; @@ -316,15 +291,17 @@ mod tests { _ => unreachable!("wrong query type"), }; - let mut gas_amount = None; + let mut gas_amount = Coins::default(); if !req.metadata.is_empty() { let parsed_gas = u32::from_be_bytes(req.metadata.as_slice().try_into().unwrap()); - gas_amount = Some(coin(parsed_gas as u128, "utest")); + gas_amount = Coins::from(coin(parsed_gas as u128, "utest")); } - let res = QuoteDispatchResponse { gas_amount }; + let res = QuoteDispatchResponse { + gas_amount: gas_amount.to_vec(), + }; let res = cosmwasm_std::to_binary(&res).unwrap(); SystemResult::Ok(ContractResult::Ok(res)) } diff --git a/contracts/core/mailbox/src/query.rs b/contracts/core/mailbox/src/query.rs index c1c0bfaf..cda8ef86 100644 --- a/contracts/core/mailbox/src/query.rs +++ b/contracts/core/mailbox/src/query.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{Deps, HexBinary}; +use cosmwasm_std::{Coins, Deps, HexBinary, StdResult}; use hpl_interface::{ core::mailbox::{ DefaultHookResponse, DefaultIsmResponse, DispatchMsg, HrpResponse, @@ -11,7 +11,7 @@ use hpl_interface::{ use crate::{ state::{CONFIG, DELIVERIES, LATEST_DISPATCHED_ID, NONCE}, - ContractError, + ContractError, MAILBOX_VERSION, }; pub fn get_hrp(deps: Deps) -> Result { @@ -87,36 +87,47 @@ pub fn get_latest_dispatch_id(deps: Deps) -> Result Result { + let sender = deps.api.addr_validate(&sender)?; + let config = CONFIG.load(deps.storage)?; + let nonce = NONCE.load(deps.storage)?; - let target_hook = msg.get_hook_addr(deps.api, config.get_default_hook())?; + let msg = dispatch_msg + .clone() + .to_msg(MAILBOX_VERSION, nonce, config.local_domain, sender)?; + + let default_hook = config.get_default_hook(); let required_hook = config.get_required_hook(); - let mut required_gas = hook::quote_dispatch( + let base_gas = hook::quote_dispatch( &deps.querier, - required_hook, - msg.metadata.clone().unwrap(), - msg.msg_body.clone(), + dispatch_msg.get_hook_addr(deps.api, default_hook)?, + dispatch_msg.metadata.clone().unwrap_or_default(), + msg.clone(), )? - .gas_amount - .expect("failed to quote required gas"); + .gas_amount; - let target_gas = hook::quote_dispatch( + let required_gas = hook::quote_dispatch( &deps.querier, - target_hook, - msg.metadata.clone().unwrap(), - msg.msg_body, + required_hook, + dispatch_msg.metadata.unwrap_or_default(), + msg, )? .gas_amount; - if let Some(gas) = target_gas { - required_gas.amount += gas.amount; - } + let total_gas = + required_gas + .into_iter() + .try_fold(Coins::try_from(base_gas)?, |mut acc, gas| { + acc.add(gas)?; + StdResult::Ok(acc) + })?; Ok(QuoteDispatchResponse { - gas_amount: Some(required_gas), + gas_amount: total_gas.to_vec(), }) } diff --git a/contracts/hooks/aggregate/src/error.rs b/contracts/hooks/aggregate/src/error.rs index babe75a3..85d89172 100644 --- a/contracts/hooks/aggregate/src/error.rs +++ b/contracts/hooks/aggregate/src/error.rs @@ -8,6 +8,9 @@ pub enum ContractError { #[error("{0}")] PaymentError(#[from] cw_utils::PaymentError), + #[error("{0}")] + CoinsError(#[from] cosmwasm_std::CoinsError), + #[error("unauthorized")] Unauthorized {}, diff --git a/contracts/hooks/aggregate/src/lib.rs b/contracts/hooks/aggregate/src/lib.rs index 4ffcb579..adbedd9b 100644 --- a/contracts/hooks/aggregate/src/lib.rs +++ b/contracts/hooks/aggregate/src/lib.rs @@ -3,7 +3,7 @@ mod error; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - ensure_eq, Addr, Coin, CosmosMsg, Deps, DepsMut, Env, Event, HexBinary, MessageInfo, + ensure_eq, Addr, Coins, CosmosMsg, Deps, DepsMut, Env, Event, HexBinary, MessageInfo, QueryResponse, Response, StdResult, }; use cw_storage_plus::Item; @@ -84,7 +84,7 @@ pub fn execute( v, metadata.clone(), message.clone(), - quote.gas_amount.map(|v| vec![v]), + Some(quote.gas_amount), )? .into(); @@ -151,28 +151,26 @@ fn quote_dispatch( ) -> Result { let hooks = HOOKS.load(deps.storage)?; - let mut total: Option = None; - - for hook in hooks { - let res = hpl_interface::hook::quote_dispatch( - &deps.querier, - hook, - metadata.clone(), - message.clone(), - )?; - - if let Some(gas_amount) = res.gas_amount { - total = match total { - Some(mut v) => { - v.amount += gas_amount.amount; - Some(v) - } - None => Some(gas_amount), - }; - } - } + let total = hooks + .into_iter() + .try_fold(Coins::default(), |mut acc, hook| { + let res = hpl_interface::hook::quote_dispatch( + &deps.querier, + hook, + metadata.clone(), + message.clone(), + )?; + + for gas in res.gas_amount { + acc.add(gas)?; + } - Ok(QuoteDispatchResponse { gas_amount: total }) + Ok::<_, ContractError>(acc) + })?; + + Ok(QuoteDispatchResponse { + gas_amount: total.to_vec(), + }) } fn get_hooks(deps: Deps) -> Result { diff --git a/contracts/hooks/merkle/src/lib.rs b/contracts/hooks/merkle/src/lib.rs index 765ed396..8517112a 100644 --- a/contracts/hooks/merkle/src/lib.rs +++ b/contracts/hooks/merkle/src/lib.rs @@ -143,7 +143,7 @@ fn get_mailbox(deps: Deps) -> Result { } fn quote_dispatch() -> Result { - Ok(QuoteDispatchResponse { gas_amount: None }) + Ok(QuoteDispatchResponse { gas_amount: vec![] }) } fn get_tree_count(deps: Deps) -> Result { @@ -328,7 +328,7 @@ mod test { deps.as_ref(), QueryMsg::Hook(HookQueryMsg::QuoteDispatch(QuoteDispatchMsg::default())), ); - assert_eq!(res.gas_amount, None); + assert_eq!(res.gas_amount, vec![]); let res: merkle::CountResponse = test_query( deps.as_ref(), diff --git a/contracts/hooks/pausable/src/lib.rs b/contracts/hooks/pausable/src/lib.rs index fe80c63c..4dcb699c 100644 --- a/contracts/hooks/pausable/src/lib.rs +++ b/contracts/hooks/pausable/src/lib.rs @@ -96,7 +96,7 @@ fn get_mailbox(_deps: Deps) -> Result { } fn quote_dispatch() -> Result { - Ok(QuoteDispatchResponse { gas_amount: None }) + Ok(QuoteDispatchResponse { gas_amount: vec![] }) } #[cfg(test)] @@ -182,6 +182,6 @@ mod test { deps.as_ref(), QueryMsg::Hook(HookQueryMsg::QuoteDispatch(QuoteDispatchMsg::default())), ); - assert_eq!(res.gas_amount, None); + assert_eq!(res.gas_amount, vec![]); } } diff --git a/contracts/hooks/routing-custom/src/lib.rs b/contracts/hooks/routing-custom/src/lib.rs index 90d93fe5..62642d74 100644 --- a/contracts/hooks/routing-custom/src/lib.rs +++ b/contracts/hooks/routing-custom/src/lib.rs @@ -297,7 +297,7 @@ mod test { use cosmwasm_std::{ coin, from_binary, testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, - ContractResult, OwnedDeps, QuerierResult, SystemResult, WasmQuery, + Coins, ContractResult, OwnedDeps, QuerierResult, SystemResult, WasmQuery, }; use hpl_interface::{build_test_querier, hook::ExpectedHookQueryMsg, router::DomainRouteSet}; use hpl_ownable::get_owner; @@ -338,15 +338,17 @@ mod test { _ => unreachable!("wrong query type"), }; - let mut gas_amount = None; + let mut gas_amount = Coins::default(); if !req.metadata.is_empty() { let parsed_gas = u32::from_be_bytes(req.metadata.as_slice().try_into().unwrap()); - gas_amount = Some(coin(parsed_gas as u128, "utest")); + gas_amount = Coins::from(coin(parsed_gas as u128, "utest")); } - let res = QuoteDispatchResponse { gas_amount }; + let res = QuoteDispatchResponse { + gas_amount: gas_amount.to_vec(), + }; let res = cosmwasm_std::to_binary(&res).unwrap(); SystemResult::Ok(ContractResult::Ok(res)) } @@ -610,6 +612,9 @@ mod test { message: rand_msg.into(), })), ); - assert_eq!(res.gas_amount.map(|v| v.amount.u128() as u32), expected_gas); + assert_eq!( + res.gas_amount.first().map(|v| v.amount.u128() as u32), + expected_gas + ); } } diff --git a/contracts/hooks/routing-fallback/src/lib.rs b/contracts/hooks/routing-fallback/src/lib.rs index ae0bbc16..aeba18e8 100644 --- a/contracts/hooks/routing-fallback/src/lib.rs +++ b/contracts/hooks/routing-fallback/src/lib.rs @@ -159,7 +159,7 @@ mod test { use cosmwasm_std::{ coin, from_binary, testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, - ContractResult, OwnedDeps, QuerierResult, SystemResult, WasmQuery, + Coins, ContractResult, OwnedDeps, QuerierResult, SystemResult, WasmQuery, }; use hpl_interface::{build_test_querier, hook::ExpectedHookQueryMsg, router::DomainRouteSet}; use hpl_ownable::get_owner; @@ -193,19 +193,21 @@ mod test { _ => unreachable!("wrong query type"), }; - let mut gas_amount = None; + let mut gas_amount = Coins::default(); if !req.metadata.is_empty() { let parsed_gas = u32::from_be_bytes(req.metadata.as_slice().try_into().unwrap()); - gas_amount = Some(coin(parsed_gas as u128, "utest")); + gas_amount = Coins::from(coin(parsed_gas as u128, "utest")); } if addr == FALLBACK_HOOK { - gas_amount = None; + gas_amount = Coins::default(); } - let res = QuoteDispatchResponse { gas_amount }; + let res = QuoteDispatchResponse { + gas_amount: gas_amount.to_vec(), + }; let res = cosmwasm_std::to_binary(&res).unwrap(); SystemResult::Ok(ContractResult::Ok(res)) } @@ -331,6 +333,9 @@ mod test { message: rand_msg.into(), })), ); - assert_eq!(res.gas_amount.map(|v| v.amount.u128() as u32), expected_gas); + assert_eq!( + res.gas_amount.first().map(|v| v.amount.u128() as u32), + expected_gas + ); } } diff --git a/contracts/hooks/routing/src/lib.rs b/contracts/hooks/routing/src/lib.rs index 7e97cc82..2e6c2086 100644 --- a/contracts/hooks/routing/src/lib.rs +++ b/contracts/hooks/routing/src/lib.rs @@ -140,7 +140,7 @@ mod test { use cosmwasm_std::{ coin, from_binary, testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, - ContractResult, OwnedDeps, QuerierResult, SystemResult, WasmQuery, + Coins, ContractResult, OwnedDeps, QuerierResult, SystemResult, WasmQuery, }; use hpl_interface::{build_test_querier, hook::ExpectedHookQueryMsg, router::DomainRouteSet}; use hpl_ownable::get_owner; @@ -172,15 +172,17 @@ mod test { _ => unreachable!("wrong query type"), }; - let mut gas_amount = None; + let mut gas_amount = Coins::default(); if !req.metadata.is_empty() { let parsed_gas = u32::from_be_bytes(req.metadata.as_slice().try_into().unwrap()); - gas_amount = Some(coin(parsed_gas as u128, "utest")); + gas_amount = Coins::from(coin(parsed_gas as u128, "utest")); } - let res = QuoteDispatchResponse { gas_amount }; + let res = QuoteDispatchResponse { + gas_amount: gas_amount.to_vec(), + }; let res = cosmwasm_std::to_binary(&res).unwrap(); SystemResult::Ok(ContractResult::Ok(res)) } @@ -303,6 +305,9 @@ mod test { message: rand_msg.into(), })), ); - assert_eq!(res.gas_amount.map(|v| v.amount.u128() as u32), expected_gas); + assert_eq!( + res.gas_amount.first().map(|v| v.amount.u128() as u32), + expected_gas + ); } } diff --git a/contracts/igps/core/src/query.rs b/contracts/igps/core/src/query.rs index 6b672013..8b1ea71e 100644 --- a/contracts/igps/core/src/query.rs +++ b/contracts/igps/core/src/query.rs @@ -1,7 +1,7 @@ use crate::error::ContractError; use crate::{BENEFICIARY, DEFAULT_GAS_USAGE, GAS_FOR_DOMAIN, GAS_TOKEN, TOKEN_EXCHANGE_RATE_SCALE}; -use cosmwasm_std::{coin, Addr, Deps, QuerierWrapper, StdResult, Storage, Uint256}; +use cosmwasm_std::{coins, Addr, Deps, QuerierWrapper, StdResult, Storage, Uint256}; use hpl_interface::hook::{MailboxResponse, QuoteDispatchMsg, QuoteDispatchResponse}; use hpl_interface::igp::core::{ BeneficiaryResponse, DefaultGasResponse, GasForDomainResponse, QuoteGasPaymentResponse, @@ -110,12 +110,12 @@ pub fn quote_dispatch( let gas_amount = quote_gas_payment(deps, igp_message.dest_domain, gas_limit)?.gas_needed; let gas_amount = if !gas_amount.is_zero() { - Some(coin( + coins( gas_amount.to_string().parse::()?, GAS_TOKEN.load(deps.storage)?, - )) + ) } else { - None + vec![] }; Ok(QuoteDispatchResponse { gas_amount }) diff --git a/contracts/mocks/mock-hook/src/contract.rs b/contracts/mocks/mock-hook/src/contract.rs index eb1f8f2d..75fed40e 100644 --- a/contracts/mocks/mock-hook/src/contract.rs +++ b/contracts/mocks/mock-hook/src/contract.rs @@ -2,7 +2,7 @@ use cosmwasm_schema::cw_serde; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - coin, to_binary, Deps, DepsMut, Env, Event, MessageInfo, QueryResponse, Response, StdResult, + coins, to_binary, Deps, DepsMut, Env, Event, MessageInfo, QueryResponse, Response, StdResult, Uint256, }; use cw2::set_contract_version; @@ -92,7 +92,7 @@ pub fn query(deps: Deps, _env: Env, msg: ExpectedHookQueryMsg) -> StdResult().unwrap(), gas_token)), + gas_amount: coins(gas.to_string().parse::().unwrap(), gas_token), })?) } HookQueryMsg::Mailbox {} => { diff --git a/contracts/warp/cw20/src/contract.rs b/contracts/warp/cw20/src/contract.rs index defe13b2..b68ca430 100644 --- a/contracts/warp/cw20/src/contract.rs +++ b/contracts/warp/cw20/src/contract.rs @@ -548,7 +548,7 @@ mod test { .unwrap(); let warp_msg = warp::Message { - recipient: recipient, + recipient, amount: Uint256::from_u128(100), metadata: HexBinary::default(), }; diff --git a/packages/interface/src/core/mailbox.rs b/packages/interface/src/core/mailbox.rs index b5aa8c7e..edebe0b1 100644 --- a/packages/interface/src/core/mailbox.rs +++ b/packages/interface/src/core/mailbox.rs @@ -154,7 +154,7 @@ pub enum QueryMsg { #[derive(QueryResponses)] pub enum MailboxHookQueryMsg { #[returns(QuoteDispatchResponse)] - QuoteDispatch(DispatchMsg), + QuoteDispatch { sender: String, msg: DispatchMsg }, } #[cw_serde] diff --git a/packages/interface/src/hook/mod.rs b/packages/interface/src/hook/mod.rs index 9d2a036d..a900551b 100644 --- a/packages/interface/src/hook/mod.rs +++ b/packages/interface/src/hook/mod.rs @@ -69,7 +69,7 @@ pub struct MailboxResponse { #[cw_serde] pub struct QuoteDispatchResponse { - pub gas_amount: Option, + pub gas_amount: Vec, } pub fn post_dispatch( From 49781b7cda64832a51a48cebdcdf087606affdb9 Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Wed, 1 Nov 2023 14:23:14 +0900 Subject: [PATCH 23/53] fix(mock): update mock hook to set gas to None --- contracts/mocks/mock-hook/src/contract.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/contracts/mocks/mock-hook/src/contract.rs b/contracts/mocks/mock-hook/src/contract.rs index eb1f8f2d..3d86b07c 100644 --- a/contracts/mocks/mock-hook/src/contract.rs +++ b/contracts/mocks/mock-hook/src/contract.rs @@ -27,7 +27,7 @@ pub struct InstantiateMsg {} #[cw_serde] pub enum ExecuteMsg { - SetGasAmount { gas: Uint256 }, + SetGasAmount { gas: Option }, PostDispatch(PostDispatchMsg), } @@ -75,7 +75,10 @@ pub fn execute( ), )), ExecuteMsg::SetGasAmount { gas } => { - GAS.save(deps.storage, &gas)?; + match gas { + Some(gas) => GAS.save(deps.storage, &gas)?, + None => GAS.remove(deps.storage), + } Ok(Response::new()) } @@ -88,12 +91,14 @@ pub fn query(deps: Deps, _env: Env, msg: ExpectedHookQueryMsg) -> StdResult match msg { HookQueryMsg::QuoteDispatch(_) => { - let gas = GAS.load(deps.storage)?; + let gas = GAS + .may_load(deps.storage)? + .map(|v| v.to_string().parse::().unwrap()); let gas_token = GAS_TOKEN.load(deps.storage)?; - Ok(to_binary(&QuoteDispatchResponse { - gas_amount: Some(coin(gas.to_string().parse::().unwrap(), gas_token)), - })?) + let gas_amount = gas.map(|v| coin(v, gas_token)); + + Ok(to_binary(&QuoteDispatchResponse { gas_amount })?) } HookQueryMsg::Mailbox {} => { unimplemented!("mailbox query not implemented on mock hook") From e9734a853c1236ec8186ffed5e015daa276198bf Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Wed, 8 Nov 2023 14:57:06 +0000 Subject: [PATCH 24/53] validation --- contracts/core/mailbox/src/contract.rs | 8 +++++- contracts/core/mailbox/src/error.rs | 11 +++++++++ contracts/igps/core/Cargo.toml | 2 ++ contracts/igps/core/src/contract.rs | 34 ++++++++++++++++++++++++-- contracts/igps/core/src/error.rs | 11 +++++++++ contracts/igps/core/src/lib.rs | 1 + contracts/igps/core/src/proto.rs | 24 ++++++++++++++++++ contracts/igps/oracle/src/contract.rs | 7 +++++- contracts/igps/oracle/src/error.rs | 13 +++++++++- 9 files changed, 106 insertions(+), 5 deletions(-) create mode 100644 contracts/igps/core/src/proto.rs diff --git a/contracts/core/mailbox/src/contract.rs b/contracts/core/mailbox/src/contract.rs index e74ca14f..2d83d8c7 100644 --- a/contracts/core/mailbox/src/contract.rs +++ b/contracts/core/mailbox/src/contract.rs @@ -1,6 +1,6 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{Deps, DepsMut, Empty, Env, MessageInfo, QueryResponse, Response}; +use cosmwasm_std::{ensure, Deps, DepsMut, Empty, Env, MessageInfo, QueryResponse, Response}; use hpl_interface::{ core::mailbox::{ExecuteMsg, InstantiateMsg, MailboxHookQueryMsg, MailboxQueryMsg, QueryMsg}, @@ -23,6 +23,12 @@ pub fn instantiate( ) -> Result { cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + // check hrp is lowercase + ensure!( + msg.hrp.chars().all(|v| v.is_lowercase()), + ContractError::invalid_config("hrp must be lowercase") + ); + let config = Config { hrp: msg.hrp, local_domain: msg.domain, diff --git a/contracts/core/mailbox/src/error.rs b/contracts/core/mailbox/src/error.rs index cdf4805a..09bd1bfe 100644 --- a/contracts/core/mailbox/src/error.rs +++ b/contracts/core/mailbox/src/error.rs @@ -15,6 +15,9 @@ pub enum ContractError { #[error("ism verify failed")] VerifyFailed {}, + #[error("invalid config. reason: {reason:?}")] + InvalidConfig { reason: String }, + #[error("invalid address length: {len:?}")] InvalidAddressLength { len: usize }, @@ -33,3 +36,11 @@ pub enum ContractError { #[error("message not found")] MessageNotFound {}, } + +impl ContractError { + pub fn invalid_config(reason: &str) -> Self { + Self::InvalidConfig { + reason: reason.to_string(), + } + } +} diff --git a/contracts/igps/core/Cargo.toml b/contracts/igps/core/Cargo.toml index 738bd5e3..0e4548bc 100644 --- a/contracts/igps/core/Cargo.toml +++ b/contracts/igps/core/Cargo.toml @@ -27,6 +27,8 @@ cw-storage-plus.workspace = true cw2.workspace = true cw-utils.workspace = true +serde.workspace = true +prost.workspace = true schemars.workspace = true thiserror.workspace = true diff --git a/contracts/igps/core/src/contract.rs b/contracts/igps/core/src/contract.rs index 16cfb454..a98f181c 100644 --- a/contracts/igps/core/src/contract.rs +++ b/contracts/igps/core/src/contract.rs @@ -1,14 +1,19 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{Deps, DepsMut, Empty, Env, Event, MessageInfo, QueryResponse, Response}; +use cosmwasm_std::{ + ensure, Binary, Deps, DepsMut, Empty, Env, Event, MessageInfo, QueryRequest, QueryResponse, + Response, +}; use hpl_interface::hook::HookQueryMsg; use hpl_interface::igp::core::{ExecuteMsg, IgpQueryMsg, InstantiateMsg, QueryMsg}; use hpl_interface::igp::oracle::IgpGasOracleQueryMsg; use hpl_interface::to_binary; +use prost::Message; use crate::{ - ContractError, BENEFICIARY, CONTRACT_NAME, CONTRACT_VERSION, DEFAULT_GAS_USAGE, GAS_TOKEN, HRP, + proto, ContractError, BENEFICIARY, CONTRACT_NAME, CONTRACT_VERSION, DEFAULT_GAS_USAGE, + GAS_TOKEN, HRP, }; fn new_event(name: &str) -> Event { @@ -24,6 +29,31 @@ pub fn instantiate( ) -> Result { cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + // check hrp is lowercase + ensure!( + msg.hrp.chars().all(|v| v.is_lowercase()), + ContractError::invalid_config("hrp must be lowercase") + ); + + // check gas token exists + if let Err(e) = deps + .querier + .query::(&QueryRequest::Stargate { + path: "/cosmos.bank.v1beta1.QuerySupplyOfRequest".to_string(), + data: Binary( + proto::QuerySupplyOfRequest { + denom: msg.gas_token.clone(), + } + .encode_to_vec(), + ), + }) + { + return Err(ContractError::invalid_config(&format!( + "gas_token {} does not exist: {e}", + msg.gas_token, + ))); + } + let owner = deps.api.addr_validate(&msg.owner)?; let beneficiary = deps.api.addr_validate(&msg.beneficiary)?; diff --git a/contracts/igps/core/src/error.rs b/contracts/igps/core/src/error.rs index f1c49455..267149d4 100644 --- a/contracts/igps/core/src/error.rs +++ b/contracts/igps/core/src/error.rs @@ -20,6 +20,17 @@ pub enum ContractError { gas_needed: Uint256, }, + #[error("invalid config. reason: {reason:?}")] + InvalidConfig { reason: String }, + #[error("gas oracle not found for {0}")] GasOracleNotFound(u32), } + +impl ContractError { + pub fn invalid_config(reason: &str) -> Self { + Self::InvalidConfig { + reason: reason.to_string(), + } + } +} diff --git a/contracts/igps/core/src/lib.rs b/contracts/igps/core/src/lib.rs index cedd7a79..8b259f3f 100644 --- a/contracts/igps/core/src/lib.rs +++ b/contracts/igps/core/src/lib.rs @@ -2,6 +2,7 @@ pub mod contract; mod error; mod event; pub mod execute; +mod proto; pub mod query; #[cfg(test)] diff --git a/contracts/igps/core/src/proto.rs b/contracts/igps/core/src/proto.rs new file mode 100644 index 00000000..13bd68d0 --- /dev/null +++ b/contracts/igps/core/src/proto.rs @@ -0,0 +1,24 @@ +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(::prost::Message, ::serde::Serialize, ::serde::Deserialize)] +pub struct QuerySupplyOfRequest { + /// denom is the coin denom to query balances for. + #[prost(string, tag = "1")] + pub denom: ::prost::alloc::string::String, +} + +/// QuerySupplyOfResponse is the response type for the Query/SupplyOf RPC method. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(::prost::Message, ::serde::Serialize, ::serde::Deserialize)] +pub struct QuerySupplyOfResponse { + /// amount is the supply of the coin. + #[prost(message, optional, tag = "1")] + pub amount: ::core::option::Option, +} + +#[derive(serde::Serialize, serde::Deserialize, ::prost::Message)] +pub struct Coin { + #[prost(string, tag = "1")] + pub denom: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub amount: ::prost::alloc::string::String, +} diff --git a/contracts/igps/oracle/src/contract.rs b/contracts/igps/oracle/src/contract.rs index c9901d46..9a0ead3a 100644 --- a/contracts/igps/oracle/src/contract.rs +++ b/contracts/igps/oracle/src/contract.rs @@ -1,7 +1,7 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - ensure_eq, to_binary, Deps, DepsMut, Env, Event, MessageInfo, QueryResponse, Response, + ensure, ensure_eq, to_binary, Deps, DepsMut, Env, Event, MessageInfo, QueryResponse, Response, }; use hpl_interface::igp::oracle::{ @@ -50,6 +50,11 @@ pub fn execute( let mut domains = vec![]; for config in configs { + ensure!( + !config.token_exchange_rate.is_zero(), + ContractError::invalid_config("exchange rate must be non-zero") + ); + domains.push(config.remote_domain.to_string()); insert_gas_data(deps.storage, config)?; } diff --git a/contracts/igps/oracle/src/error.rs b/contracts/igps/oracle/src/error.rs index dc19f103..1dd46ab6 100644 --- a/contracts/igps/oracle/src/error.rs +++ b/contracts/igps/oracle/src/error.rs @@ -6,6 +6,17 @@ pub enum ContractError { #[error("{0}")] Std(#[from] StdError), - #[error("Unauthorized")] + #[error("unauthorized")] Unauthorized {}, + + #[error("invalid config. reason: {reason:?}")] + InvalidConfig { reason: String }, +} + +impl ContractError { + pub fn invalid_config(reason: &str) -> Self { + Self::InvalidConfig { + reason: reason.to_string(), + } + } } From 43a7ea3ddc0beb9cbb57d0a35b2525e229ea04d6 Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Thu, 9 Nov 2023 03:38:50 +0000 Subject: [PATCH 25/53] validate address --- contracts/hooks/routing-custom/src/lib.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/contracts/hooks/routing-custom/src/lib.rs b/contracts/hooks/routing-custom/src/lib.rs index 90d93fe5..b13eeb6c 100644 --- a/contracts/hooks/routing-custom/src/lib.rs +++ b/contracts/hooks/routing-custom/src/lib.rs @@ -34,6 +34,9 @@ pub enum ContractError { #[error("route not found for {0}")] RouteNotFound(u32), + + #[error("invalid arguments. reason: {reason:?}")] + InvalidArguments { reason: String }, } // version info for migration info @@ -178,6 +181,15 @@ fn register( ); for msg in msgs.clone() { + let recipient = HexBinary::from_hex(&msg.recipient)?; + ensure_eq!( + recipient.len(), + 32, + ContractError::InvalidArguments { + reason: "recipient must be 32 bytes long".into() + } + ); + CUSTOM_HOOKS.save( deps.storage, ( From 4f6fc7ceb64b85f129b0d360e191b34cbbcd1c81 Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Thu, 9 Nov 2023 06:32:41 +0000 Subject: [PATCH 26/53] fix --- contracts/isms/multisig/src/contract.rs | 61 ++++- contracts/isms/multisig/src/error.rs | 9 + contracts/isms/multisig/src/event.rs | 31 --- contracts/isms/multisig/src/execute.rs | 319 ------------------------ contracts/isms/multisig/src/lib.rs | 2 - packages/interface/src/ism/multisig.rs | 20 +- 6 files changed, 69 insertions(+), 373 deletions(-) delete mode 100644 contracts/isms/multisig/src/event.rs delete mode 100644 contracts/isms/multisig/src/execute.rs diff --git a/contracts/isms/multisig/src/contract.rs b/contracts/isms/multisig/src/contract.rs index 15a60f11..ce4075a1 100644 --- a/contracts/isms/multisig/src/contract.rs +++ b/contracts/isms/multisig/src/contract.rs @@ -1,6 +1,8 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; -use cosmwasm_std::{Deps, DepsMut, Empty, Env, MessageInfo, QueryResponse, Response}; +use cosmwasm_std::{ + ensure, ensure_eq, Deps, DepsMut, Empty, Env, Event, MessageInfo, QueryResponse, Response, +}; use cw2::set_contract_version; use hpl_interface::{ ism::{ @@ -11,10 +13,10 @@ use hpl_interface::{ }, to_binary, }; +use hpl_ownable::get_owner; use crate::{ error::ContractError, - execute, state::{THRESHOLD, VALIDATORS}, CONTRACT_NAME, CONTRACT_VERSION, }; @@ -47,14 +49,55 @@ pub fn execute( match msg { Ownable(msg) => Ok(hpl_ownable::handle(deps, env, info, msg)?), - EnrollValidator { set: msg } => execute::enroll_validator(deps, info, msg), - EnrollValidators { set: validators } => execute::enroll_validators(deps, info, validators), - UnenrollValidator { + SetValidators { domain, - validator: vald, - } => execute::unenroll_validator(deps, info, domain, vald), - SetThreshold { set: threshold } => execute::set_threshold(deps, info, threshold), - SetThresholds { set: thresholds } => execute::set_thresholds(deps, info, thresholds), + threshold, + validators, + } => { + ensure_eq!( + info.sender, + get_owner(deps.storage)?, + ContractError::Unauthorized {} + ); + ensure!( + validators.iter().all(|v| v.len() == 20), + ContractError::invalid_addr("length should be 20") + ); + ensure!( + validators.len() > threshold as usize && threshold > 0, + ContractError::invalid_args(&format!( + "threshold not in range. 0 < <= {}", + validators.len(), + )) + ); + + VALIDATORS.save(deps.storage, domain, &validators)?; + THRESHOLD.save(deps.storage, domain, &threshold)?; + + Ok(Response::new().add_event( + Event::new("ism_multisig_set_validators") + .add_attribute("sender", info.sender) + .add_attribute("domain", domain.to_string()) + .add_attribute("validators", validators.len().to_string()) + .add_attribute("threshold", threshold.to_string()), + )) + } + UnsetDomain { domain } => { + ensure_eq!( + info.sender, + get_owner(deps.storage)?, + ContractError::Unauthorized {} + ); + + VALIDATORS.remove(deps.storage, domain); + THRESHOLD.remove(deps.storage, domain); + + Ok(Response::new().add_event( + Event::new("ism_multisig_unset_domain") + .add_attribute("sener", info.sender) + .add_attribute("domain", domain.to_string()), + )) + } } } diff --git a/contracts/isms/multisig/src/error.rs b/contracts/isms/multisig/src/error.rs index 70db89bc..7ccf0159 100644 --- a/contracts/isms/multisig/src/error.rs +++ b/contracts/isms/multisig/src/error.rs @@ -24,6 +24,9 @@ pub enum ContractError { #[error("invalid address. reason: {0}")] InvalidAddress(String), + #[error("invalid arguments. reason: {reason:?}")] + InvalidArguments { reason: String }, + #[error("duplicate validator")] ValidatorDuplicate, @@ -35,4 +38,10 @@ impl ContractError { pub fn invalid_addr(reason: &str) -> Self { ContractError::InvalidAddress(reason.into()) } + + pub fn invalid_args(reason: &str) -> Self { + ContractError::InvalidArguments { + reason: reason.into(), + } + } } diff --git a/contracts/isms/multisig/src/event.rs b/contracts/isms/multisig/src/event.rs deleted file mode 100644 index 17561622..00000000 --- a/contracts/isms/multisig/src/event.rs +++ /dev/null @@ -1,31 +0,0 @@ -use cosmwasm_std::{Addr, Event}; - -pub fn emit_init_transfer_ownership(next_owner: String) -> Event { - Event::new("ism_multisig_init_transfer_ownership").add_attribute("next_owner", next_owner) -} - -pub fn emit_finish_transfer_ownership(owner: Addr) -> Event { - Event::new("ism_multisig_finish_transfer_owner").add_attribute("owner", owner) -} - -pub fn emit_revoke_transfer_ownership() -> Event { - Event::new("ism_multisig_revoke_transfer_ownership") -} - -pub fn emit_enroll_validator(domain: u32, validator: String) -> Event { - Event::new("ism_multisig_enroll_validator") - .add_attribute("domain", domain.to_string()) - .add_attribute("validator", validator) -} - -pub fn emit_unenroll_validator(domain: u32, validator: String) -> Event { - Event::new("ism_multisig_unenroll_validator") - .add_attribute("domain", domain.to_string()) - .add_attribute("validator", validator) -} - -pub fn emit_set_threshold(domain: u32, threshold: u8) -> Event { - Event::new("ism_multisig_set_threshold") - .add_attribute("domain", domain.to_string()) - .add_attribute("threshold", threshold.to_string()) -} diff --git a/contracts/isms/multisig/src/execute.rs b/contracts/isms/multisig/src/execute.rs deleted file mode 100644 index d4661fbb..00000000 --- a/contracts/isms/multisig/src/execute.rs +++ /dev/null @@ -1,319 +0,0 @@ -use cosmwasm_std::{ensure_eq, DepsMut, Event, HexBinary, MessageInfo, Response, StdResult}; -use hpl_interface::ism::multisig::{ThresholdSet, ValidatorSet as MsgValidatorSet}; -use hpl_ownable::get_owner; - -use crate::{ - event::{emit_enroll_validator, emit_set_threshold, emit_unenroll_validator}, - state::{THRESHOLD, VALIDATORS}, - ContractError, -}; - -pub fn set_threshold( - deps: DepsMut, - info: MessageInfo, - threshold: ThresholdSet, -) -> Result { - ensure_eq!( - get_owner(deps.storage)?, - info.sender, - ContractError::Unauthorized - ); - THRESHOLD.save(deps.storage, threshold.domain, &threshold.threshold)?; - - Ok(Response::new().add_event(emit_set_threshold(threshold.domain, threshold.threshold))) -} - -pub fn set_thresholds( - deps: DepsMut, - info: MessageInfo, - thresholds: Vec, -) -> Result { - ensure_eq!( - get_owner(deps.storage)?, - info.sender, - ContractError::Unauthorized - ); - - let events: Vec = thresholds - .into_iter() - .map(|v| { - THRESHOLD.save(deps.storage, v.domain, &v.threshold)?; - Ok(emit_set_threshold(v.domain, v.threshold)) - }) - .collect::>()?; - - Ok(Response::new().add_events(events)) -} - -pub fn enroll_validator( - deps: DepsMut, - info: MessageInfo, - msg: MsgValidatorSet, -) -> Result { - ensure_eq!( - info.sender, - get_owner(deps.storage)?, - ContractError::Unauthorized {} - ); - - ensure_eq!( - msg.validator.len(), - 20, - ContractError::invalid_addr("length should be 20") - ); - - let validator_state = VALIDATORS.may_load(deps.storage, msg.domain)?; - - if let Some(mut validators) = validator_state { - if validators.contains(&msg.validator) { - return Err(ContractError::ValidatorDuplicate {}); - } - - validators.push(msg.validator.clone()); - validators.sort(); - - VALIDATORS.save(deps.storage, msg.domain, &validators)?; - } else { - VALIDATORS.save(deps.storage, msg.domain, &vec![msg.validator.clone()])?; - } - - Ok(Response::new().add_event(emit_enroll_validator(msg.domain, msg.validator.to_hex()))) -} - -pub fn enroll_validators( - deps: DepsMut, - info: MessageInfo, - validators: Vec, -) -> Result { - ensure_eq!( - info.sender, - get_owner(deps.storage)?, - ContractError::Unauthorized {} - ); - - let mut events: Vec = Vec::new(); - - for msg in validators.into_iter() { - ensure_eq!( - msg.validator.len(), - 20, - ContractError::invalid_addr("length should be 20") - ); - - let validators_state = VALIDATORS.may_load(deps.storage, msg.domain)?; - - if let Some(mut validators) = validators_state { - if validators.contains(&msg.validator) { - return Err(ContractError::ValidatorDuplicate {}); - } - - validators.push(msg.validator.clone()); - validators.sort(); - - VALIDATORS.save(deps.storage, msg.domain, &validators)?; - events.push(emit_enroll_validator(msg.domain, msg.validator.to_hex())); - } else { - VALIDATORS.save(deps.storage, msg.domain, &vec![msg.validator.clone()])?; - events.push(emit_enroll_validator(msg.domain, msg.validator.to_hex())); - } - } - - Ok(Response::new().add_events(events)) -} - -pub fn unenroll_validator( - deps: DepsMut, - info: MessageInfo, - domain: u32, - validator: HexBinary, -) -> Result { - ensure_eq!( - info.sender, - get_owner(deps.storage)?, - ContractError::Unauthorized {} - ); - - let validators = VALIDATORS - .load(deps.storage, domain) - .map_err(|_| ContractError::ValidatorNotExist {})?; - - if !validators.contains(&validator) { - return Err(ContractError::ValidatorNotExist {}); - } - - let mut validator_list: Vec = - validators.into_iter().filter(|v| v != &validator).collect(); - - validator_list.sort(); - - VALIDATORS.save(deps.storage, domain, &validator_list)?; - - Ok(Response::new().add_event(emit_unenroll_validator(domain, validator.to_hex()))) -} - -#[cfg(test)] -mod test { - use cosmwasm_std::{ - testing::{mock_dependencies, mock_info}, - Addr, HexBinary, Storage, - }; - use hpl_interface::{ - build_test_executor, build_test_querier, - ism::multisig::{ExecuteMsg, ValidatorSet}, - }; - use ibcx_test_utils::{addr, hex}; - use rstest::rstest; - - use crate::state::VALIDATORS; - - build_test_executor!(crate::contract::execute); - build_test_querier!(crate::contract::query); - - use super::*; - const ADDR1_VAULE: &str = "addr1"; - const ADDR2_VAULE: &str = "addr2"; - - fn mock_owner(storage: &mut dyn Storage, owner: Addr) { - hpl_ownable::initialize(storage, &owner).unwrap(); - } - - #[test] - fn test_set_threshold() { - let mut deps = mock_dependencies(); - let owner = Addr::unchecked(ADDR1_VAULE); - mock_owner(deps.as_mut().storage, owner.clone()); - - let threshold = ThresholdSet { - domain: 1u32, - threshold: 8u8, - }; - - // set_threshold failure test - let info = mock_info(ADDR2_VAULE, &[]); - let fail_result = set_threshold(deps.as_mut(), info, threshold.clone()).unwrap_err(); - - assert!(matches!(fail_result, ContractError::Unauthorized {})); - - // set_threshold success test - let info = mock_info(owner.as_str(), &[]); - let result = set_threshold(deps.as_mut(), info, threshold.clone()).unwrap(); - - assert_eq!( - result.events, - vec![emit_set_threshold(threshold.domain, threshold.threshold)] - ); - - // check it actually saved - let saved_threshold = THRESHOLD.load(&deps.storage, threshold.domain).unwrap(); - assert_eq!(saved_threshold, threshold.threshold); - } - - #[test] - fn test_set_thresholds() { - let mut deps = mock_dependencies(); - let owner = Addr::unchecked(ADDR1_VAULE); - mock_owner(deps.as_mut().storage, owner.clone()); - - let thresholds: Vec = vec![ - ThresholdSet { - domain: 1u32, - threshold: 8u8, - }, - ThresholdSet { - domain: 2u32, - threshold: 7u8, - }, - ThresholdSet { - domain: 3u32, - threshold: 6u8, - }, - ]; - - // set_threshold failure test - let info = mock_info(ADDR2_VAULE, &[]); - let fail_result = set_thresholds(deps.as_mut(), info, thresholds.clone()).unwrap_err(); - - assert!(matches!(fail_result, ContractError::Unauthorized {})); - - // set_threshold success test - let info = mock_info(owner.as_str(), &[]); - let result = set_thresholds(deps.as_mut(), info, thresholds.clone()).unwrap(); - - assert_eq!( - result.events, - vec![ - emit_set_threshold(1u32, 8u8), - emit_set_threshold(2u32, 7u8), - emit_set_threshold(3u32, 6u8), - ] - ); - - // check it actually saved - for threshold in thresholds { - let saved_threshold = THRESHOLD.load(&deps.storage, threshold.domain).unwrap(); - assert_eq!(saved_threshold, threshold.threshold); - } - } - - #[rstest] - #[case("owner", vec![hex(&"deadbeef".repeat(5))])] - #[should_panic(expected = "unauthorized")] - #[case("someone", vec![hex(&"deadbeef".repeat(5))])] - #[should_panic(expected = "duplicate validator")] - #[case("owner", vec![hex(&"deadbeef".repeat(5)),hex(&"deadbeef".repeat(5))])] - fn test_enroll(#[case] sender: &str, #[case] validators: Vec) { - let mut deps = mock_dependencies(); - - hpl_ownable::initialize(deps.as_mut().storage, &addr("owner")).unwrap(); - - for validator in validators.clone() { - test_execute( - deps.as_mut(), - &addr(sender), - ExecuteMsg::EnrollValidator { - set: ValidatorSet { - domain: 1, - validator, - }, - }, - vec![], - ); - } - - assert_eq!( - VALIDATORS.load(deps.as_ref().storage, 1).unwrap(), - validators - ); - } - - #[rstest] - #[case("owner", hex("deadbeef"))] - #[should_panic(expected = "unauthorized")] - #[case("someone", hex("deadbeef"))] - #[should_panic(expected = "validator not exist")] - #[case("owner", hex("debeefed"))] - fn test_unenroll(#[case] sender: &str, #[case] target: HexBinary) { - let mut deps = mock_dependencies(); - - hpl_ownable::initialize(deps.as_mut().storage, &addr("owner")).unwrap(); - - VALIDATORS - .save(deps.as_mut().storage, 1, &vec![hex("deadbeef")]) - .unwrap(); - - test_execute( - deps.as_mut(), - &addr(sender), - ExecuteMsg::UnenrollValidator { - domain: 1, - validator: target, - }, - vec![], - ); - - assert!(VALIDATORS - .load(deps.as_ref().storage, 1) - .unwrap() - .is_empty()); - } -} diff --git a/contracts/isms/multisig/src/lib.rs b/contracts/isms/multisig/src/lib.rs index aba3b52f..0519c95a 100644 --- a/contracts/isms/multisig/src/lib.rs +++ b/contracts/isms/multisig/src/lib.rs @@ -1,7 +1,5 @@ pub mod contract; mod error; -pub mod event; -pub mod execute; pub mod query; pub mod state; diff --git a/packages/interface/src/ism/multisig.rs b/packages/interface/src/ism/multisig.rs index 4f91ac67..dda6d9d8 100644 --- a/packages/interface/src/ism/multisig.rs +++ b/packages/interface/src/ism/multisig.rs @@ -18,22 +18,18 @@ pub struct ValidatorSet { pub validator: HexBinary, } -#[cw_serde] -pub struct ThresholdSet { - pub domain: u32, - pub threshold: u8, -} - #[cw_serde] pub enum ExecuteMsg { Ownable(OwnableMsg), - EnrollValidator { set: ValidatorSet }, - EnrollValidators { set: Vec }, - UnenrollValidator { domain: u32, validator: HexBinary }, - - SetThreshold { set: ThresholdSet }, - SetThresholds { set: Vec }, + SetValidators { + domain: u32, + threshold: u8, + validators: Vec, // should be 20 lenghted + }, + UnsetDomain { + domain: u32, + }, } #[cw_serde] From 0adb31f8daf50b41f090fe47963aaa7f96693c2a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Nov 2023 07:15:48 +0000 Subject: [PATCH 27/53] chore(deps): bump protobufjs from 6.11.3 to 6.11.4 in /scripts Bumps [protobufjs](https://github.com/protobufjs/protobuf.js) from 6.11.3 to 6.11.4. - [Release notes](https://github.com/protobufjs/protobuf.js/releases) - [Changelog](https://github.com/protobufjs/protobuf.js/blob/master/CHANGELOG.md) - [Commits](https://github.com/protobufjs/protobuf.js/commits) --- updated-dependencies: - dependency-name: protobufjs dependency-type: indirect ... Signed-off-by: dependabot[bot] --- scripts/pnpm-lock.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/pnpm-lock.yaml b/scripts/pnpm-lock.yaml index 3ef0d902..c5c0cb75 100644 --- a/scripts/pnpm-lock.yaml +++ b/scripts/pnpm-lock.yaml @@ -74,7 +74,7 @@ packages: resolution: {integrity: sha512-wB6uo+3A50m0sW/EWcU64xpV/8wShZ6bMTa7pF8eYsTrSkQA7oLUIJcs/wb8g4y2Oyq701BaGiO6n/ak5WXO1w==} dependencies: '@noble/hashes': 1.3.1 - protobufjs: 6.11.3 + protobufjs: 6.11.4 dev: false /@cosmjs/amino@0.25.6: @@ -275,7 +275,7 @@ packages: '@cosmjs/utils': 0.31.0 cosmjs-types: 0.8.0 long: 4.0.0 - protobufjs: 6.11.3 + protobufjs: 6.11.4 xstream: 11.14.0 transitivePeerDependencies: - bufferutil @@ -721,7 +721,7 @@ packages: resolution: {integrity: sha512-Q2Mj95Fl0PYMWEhA2LuGEIhipF7mQwd9gTQ85DdP9jjjopeoGaDxvmPa5nakNzsq7FnO1DMTatXTAx6bxMH7Lg==} dependencies: long: 4.0.0 - protobufjs: 6.11.3 + protobufjs: 6.11.4 dev: false /create-require@1.1.1: @@ -999,8 +999,8 @@ packages: resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==} dev: false - /protobufjs@6.11.3: - resolution: {integrity: sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==} + /protobufjs@6.11.4: + resolution: {integrity: sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==} hasBin: true requiresBuild: true dependencies: From fcef4ba7fc9d4e48bdb0c696b72b9042421059f3 Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Thu, 23 Nov 2023 15:05:51 +0700 Subject: [PATCH 28/53] rename gas_amount to fees --- contracts/core/mailbox/src/execute.rs | 32 ++++++++++----------- contracts/core/mailbox/src/query.rs | 18 ++++++------ contracts/hooks/aggregate/src/lib.rs | 16 ++++------- contracts/hooks/merkle/src/lib.rs | 4 +-- contracts/hooks/pausable/src/lib.rs | 4 +-- contracts/hooks/routing-custom/src/lib.rs | 14 ++++----- contracts/hooks/routing-fallback/src/lib.rs | 16 +++++------ contracts/hooks/routing/src/lib.rs | 14 ++++----- contracts/igps/core/src/query.rs | 16 +++++------ contracts/mocks/mock-hook/src/contract.rs | 2 +- packages/interface/src/hook/mod.rs | 2 +- 11 files changed, 67 insertions(+), 71 deletions(-) diff --git a/contracts/core/mailbox/src/execute.rs b/contracts/core/mailbox/src/execute.rs index e6e24369..fa20506d 100644 --- a/contracts/core/mailbox/src/execute.rs +++ b/contracts/core/mailbox/src/execute.rs @@ -117,32 +117,32 @@ pub fn dispatch( .to_msg(MAILBOX_VERSION, nonce, config.local_domain, &info.sender)?; let msg_id = msg.id(); - let base_gas = hook::quote_dispatch( + let base_fee = hook::quote_dispatch( &deps.querier, dispatch_msg.get_hook_addr(deps.api, default_hook)?, dispatch_msg.metadata.clone().unwrap_or_default(), msg.clone(), )? - .gas_amount; + .fees; - let required_gas = hook::quote_dispatch( + let required_fee = hook::quote_dispatch( &deps.querier, &required_hook, dispatch_msg.metadata.clone().unwrap_or_default(), msg.clone(), )? - .gas_amount; + .fees; // assert gas received is satisfies required gas - let mut total_gas = required_gas.clone().into_iter().try_fold( - Coins::try_from(base_gas.clone())?, - |mut acc, gas| { - acc.add(gas)?; + let mut total_fee = required_fee.clone().into_iter().try_fold( + Coins::try_from(base_fee.clone())?, + |mut acc, fee| { + acc.add(fee)?; StdResult::Ok(acc) }, )?; for fund in info.funds { - total_gas.sub(fund)?; + total_fee.sub(fund)?; } // interaction @@ -159,14 +159,14 @@ pub fn dispatch( required_hook, hook_metadata.clone(), msg.clone(), - Some(required_gas), + Some(required_fee), )?, - post_dispatch(hook, hook_metadata, msg.clone(), Some(base_gas))?, + post_dispatch(hook, hook_metadata, msg.clone(), Some(base_fee))?, ]; let refund_msg = BankMsg::Send { to_address: info.sender.to_string(), - amount: total_gas.to_vec(), + amount: total_fee.to_vec(), }; Ok(Response::new() @@ -291,16 +291,16 @@ mod tests { _ => unreachable!("wrong query type"), }; - let mut gas_amount = Coins::default(); + let mut fees = Coins::default(); if !req.metadata.is_empty() { - let parsed_gas = u32::from_be_bytes(req.metadata.as_slice().try_into().unwrap()); + let parsed_fee = u32::from_be_bytes(req.metadata.as_slice().try_into().unwrap()); - gas_amount = Coins::from(coin(parsed_gas as u128, "utest")); + fees = Coins::from(coin(parsed_fee as u128, "utest")); } let res = QuoteDispatchResponse { - gas_amount: gas_amount.to_vec(), + fees: fees.to_vec(), }; let res = cosmwasm_std::to_binary(&res).unwrap(); SystemResult::Ok(ContractResult::Ok(res)) diff --git a/contracts/core/mailbox/src/query.rs b/contracts/core/mailbox/src/query.rs index cda8ef86..0302b01e 100644 --- a/contracts/core/mailbox/src/query.rs +++ b/contracts/core/mailbox/src/query.rs @@ -102,32 +102,32 @@ pub fn quote_dispatch( let default_hook = config.get_default_hook(); let required_hook = config.get_required_hook(); - let base_gas = hook::quote_dispatch( + let base_fee = hook::quote_dispatch( &deps.querier, dispatch_msg.get_hook_addr(deps.api, default_hook)?, dispatch_msg.metadata.clone().unwrap_or_default(), msg.clone(), )? - .gas_amount; + .fees; - let required_gas = hook::quote_dispatch( + let required_fee = hook::quote_dispatch( &deps.querier, required_hook, dispatch_msg.metadata.unwrap_or_default(), msg, )? - .gas_amount; + .fees; - let total_gas = - required_gas + let total_fee = + required_fee .into_iter() - .try_fold(Coins::try_from(base_gas)?, |mut acc, gas| { - acc.add(gas)?; + .try_fold(Coins::try_from(base_fee)?, |mut acc, fee| { + acc.add(fee)?; StdResult::Ok(acc) })?; Ok(QuoteDispatchResponse { - gas_amount: total_gas.to_vec(), + fees: total_fee.to_vec(), }) } diff --git a/contracts/hooks/aggregate/src/lib.rs b/contracts/hooks/aggregate/src/lib.rs index adbedd9b..dc91e7ca 100644 --- a/contracts/hooks/aggregate/src/lib.rs +++ b/contracts/hooks/aggregate/src/lib.rs @@ -80,13 +80,9 @@ pub fn execute( metadata.clone(), message.clone(), )?; - let msg = post_dispatch( - v, - metadata.clone(), - message.clone(), - Some(quote.gas_amount), - )? - .into(); + let msg = + post_dispatch(v, metadata.clone(), message.clone(), Some(quote.fees))? + .into(); Ok(msg) }) @@ -161,15 +157,15 @@ fn quote_dispatch( message.clone(), )?; - for gas in res.gas_amount { - acc.add(gas)?; + for fee in res.fees { + acc.add(fee)?; } Ok::<_, ContractError>(acc) })?; Ok(QuoteDispatchResponse { - gas_amount: total.to_vec(), + fees: total.to_vec(), }) } diff --git a/contracts/hooks/merkle/src/lib.rs b/contracts/hooks/merkle/src/lib.rs index 8517112a..cef63c2d 100644 --- a/contracts/hooks/merkle/src/lib.rs +++ b/contracts/hooks/merkle/src/lib.rs @@ -143,7 +143,7 @@ fn get_mailbox(deps: Deps) -> Result { } fn quote_dispatch() -> Result { - Ok(QuoteDispatchResponse { gas_amount: vec![] }) + Ok(QuoteDispatchResponse { fees: vec![] }) } fn get_tree_count(deps: Deps) -> Result { @@ -328,7 +328,7 @@ mod test { deps.as_ref(), QueryMsg::Hook(HookQueryMsg::QuoteDispatch(QuoteDispatchMsg::default())), ); - assert_eq!(res.gas_amount, vec![]); + assert_eq!(res.fees, vec![]); let res: merkle::CountResponse = test_query( deps.as_ref(), diff --git a/contracts/hooks/pausable/src/lib.rs b/contracts/hooks/pausable/src/lib.rs index 4dcb699c..e072ad23 100644 --- a/contracts/hooks/pausable/src/lib.rs +++ b/contracts/hooks/pausable/src/lib.rs @@ -96,7 +96,7 @@ fn get_mailbox(_deps: Deps) -> Result { } fn quote_dispatch() -> Result { - Ok(QuoteDispatchResponse { gas_amount: vec![] }) + Ok(QuoteDispatchResponse { fees: vec![] }) } #[cfg(test)] @@ -182,6 +182,6 @@ mod test { deps.as_ref(), QueryMsg::Hook(HookQueryMsg::QuoteDispatch(QuoteDispatchMsg::default())), ); - assert_eq!(res.gas_amount, vec![]); + assert_eq!(res.fees, vec![]); } } diff --git a/contracts/hooks/routing-custom/src/lib.rs b/contracts/hooks/routing-custom/src/lib.rs index 62642d74..fcf2a6d7 100644 --- a/contracts/hooks/routing-custom/src/lib.rs +++ b/contracts/hooks/routing-custom/src/lib.rs @@ -338,16 +338,16 @@ mod test { _ => unreachable!("wrong query type"), }; - let mut gas_amount = Coins::default(); + let mut fees = Coins::default(); if !req.metadata.is_empty() { - let parsed_gas = u32::from_be_bytes(req.metadata.as_slice().try_into().unwrap()); + let parsed_fee = u32::from_be_bytes(req.metadata.as_slice().try_into().unwrap()); - gas_amount = Coins::from(coin(parsed_gas as u128, "utest")); + fees = Coins::from(coin(parsed_fee as u128, "utest")); } let res = QuoteDispatchResponse { - gas_amount: gas_amount.to_vec(), + fees: fees.to_vec(), }; let res = cosmwasm_std::to_binary(&res).unwrap(); SystemResult::Ok(ContractResult::Ok(res)) @@ -596,7 +596,7 @@ mod test { fn test_quote_dispatch( deps_custom_routes: (TestDeps, Routes, CustomRoutes), #[case] test_domain: u32, - #[case] expected_gas: Option, + #[case] expected_fee: Option, ) { let (mut deps, _, _) = deps_custom_routes; @@ -613,8 +613,8 @@ mod test { })), ); assert_eq!( - res.gas_amount.first().map(|v| v.amount.u128() as u32), - expected_gas + res.fees.first().map(|v| v.amount.u128() as u32), + expected_fee ); } } diff --git a/contracts/hooks/routing-fallback/src/lib.rs b/contracts/hooks/routing-fallback/src/lib.rs index aeba18e8..e38770e1 100644 --- a/contracts/hooks/routing-fallback/src/lib.rs +++ b/contracts/hooks/routing-fallback/src/lib.rs @@ -193,20 +193,20 @@ mod test { _ => unreachable!("wrong query type"), }; - let mut gas_amount = Coins::default(); + let mut fees = Coins::default(); if !req.metadata.is_empty() { - let parsed_gas = u32::from_be_bytes(req.metadata.as_slice().try_into().unwrap()); + let parsed_fee = u32::from_be_bytes(req.metadata.as_slice().try_into().unwrap()); - gas_amount = Coins::from(coin(parsed_gas as u128, "utest")); + fees = Coins::from(coin(parsed_fee as u128, "utest")); } if addr == FALLBACK_HOOK { - gas_amount = Coins::default(); + fees = Coins::default(); } let res = QuoteDispatchResponse { - gas_amount: gas_amount.to_vec(), + fees: fees.to_vec(), }; let res = cosmwasm_std::to_binary(&res).unwrap(); SystemResult::Ok(ContractResult::Ok(res)) @@ -317,7 +317,7 @@ mod test { fn test_quote_dispatch( deps_routes: (TestDeps, Routes), #[case] test_domain: u32, - #[case] expected_gas: Option, + #[case] expected_fee: Option, ) { let (mut deps, _) = deps_routes; @@ -334,8 +334,8 @@ mod test { })), ); assert_eq!( - res.gas_amount.first().map(|v| v.amount.u128() as u32), - expected_gas + res.fees.first().map(|v| v.amount.u128() as u32), + expected_fee ); } } diff --git a/contracts/hooks/routing/src/lib.rs b/contracts/hooks/routing/src/lib.rs index 2e6c2086..048e91e8 100644 --- a/contracts/hooks/routing/src/lib.rs +++ b/contracts/hooks/routing/src/lib.rs @@ -172,16 +172,16 @@ mod test { _ => unreachable!("wrong query type"), }; - let mut gas_amount = Coins::default(); + let mut fees = Coins::default(); if !req.metadata.is_empty() { - let parsed_gas = u32::from_be_bytes(req.metadata.as_slice().try_into().unwrap()); + let parsed_fee = u32::from_be_bytes(req.metadata.as_slice().try_into().unwrap()); - gas_amount = Coins::from(coin(parsed_gas as u128, "utest")); + fees = Coins::from(coin(parsed_fee as u128, "utest")); } let res = QuoteDispatchResponse { - gas_amount: gas_amount.to_vec(), + fees: fees.to_vec(), }; let res = cosmwasm_std::to_binary(&res).unwrap(); SystemResult::Ok(ContractResult::Ok(res)) @@ -289,7 +289,7 @@ mod test { fn test_quote_dispatch( deps_routes: (TestDeps, Routes), #[case] test_domain: u32, - #[case] expected_gas: Option, + #[case] expected_fee: Option, ) { let (mut deps, _) = deps_routes; @@ -306,8 +306,8 @@ mod test { })), ); assert_eq!( - res.gas_amount.first().map(|v| v.amount.u128() as u32), - expected_gas + res.fees.first().map(|v| v.amount.u128() as u32), + expected_fee ); } } diff --git a/contracts/igps/core/src/query.rs b/contracts/igps/core/src/query.rs index 8b1ea71e..5e761a5a 100644 --- a/contracts/igps/core/src/query.rs +++ b/contracts/igps/core/src/query.rs @@ -62,7 +62,7 @@ pub fn quote_gas_price( storage: &dyn Storage, querier: &QuerierWrapper, dest_domain: u32, - gas_amount: Uint256, + fees: Uint256, ) -> Result { let gas_oracle_set = hpl_router::get_route::(storage, dest_domain)?; let gas_oracle = gas_oracle_set @@ -74,7 +74,7 @@ pub fn quote_gas_price( &oracle::QueryMsg::Oracle(IgpGasOracleQueryMsg::GetExchangeRateAndGasPrice { dest_domain }), )?; - let dest_gas_cost = gas_amount * Uint256::from(gas_price_resp.gas_price); + let dest_gas_cost = fees * Uint256::from(gas_price_resp.gas_price); let gas_needed = (dest_gas_cost * Uint256::from(gas_price_resp.exchange_rate)) / Uint256::from(TOKEN_EXCHANGE_RATE_SCALE); @@ -84,9 +84,9 @@ pub fn quote_gas_price( pub fn quote_gas_payment( deps: Deps, dest_domain: u32, - gas_amount: Uint256, + fees: Uint256, ) -> Result { - let gas_needed = quote_gas_price(deps.storage, &deps.querier, dest_domain, gas_amount)?; + let gas_needed = quote_gas_price(deps.storage, &deps.querier, dest_domain, fees)?; Ok(QuoteGasPaymentResponse { gas_needed }) } @@ -108,17 +108,17 @@ pub fn quote_dispatch( } }; - let gas_amount = quote_gas_payment(deps, igp_message.dest_domain, gas_limit)?.gas_needed; - let gas_amount = if !gas_amount.is_zero() { + let fees = quote_gas_payment(deps, igp_message.dest_domain, gas_limit)?.gas_needed; + let fees = if !fees.is_zero() { coins( - gas_amount.to_string().parse::()?, + fees.to_string().parse::()?, GAS_TOKEN.load(deps.storage)?, ) } else { vec![] }; - Ok(QuoteDispatchResponse { gas_amount }) + Ok(QuoteDispatchResponse { fees }) } pub fn get_exchange_rate_and_gas_price( diff --git a/contracts/mocks/mock-hook/src/contract.rs b/contracts/mocks/mock-hook/src/contract.rs index 75fed40e..02118402 100644 --- a/contracts/mocks/mock-hook/src/contract.rs +++ b/contracts/mocks/mock-hook/src/contract.rs @@ -92,7 +92,7 @@ pub fn query(deps: Deps, _env: Env, msg: ExpectedHookQueryMsg) -> StdResult().unwrap(), gas_token), + fees: coins(gas.to_string().parse::().unwrap(), gas_token), })?) } HookQueryMsg::Mailbox {} => { diff --git a/packages/interface/src/hook/mod.rs b/packages/interface/src/hook/mod.rs index a900551b..0c368b0b 100644 --- a/packages/interface/src/hook/mod.rs +++ b/packages/interface/src/hook/mod.rs @@ -69,7 +69,7 @@ pub struct MailboxResponse { #[cw_serde] pub struct QuoteDispatchResponse { - pub gas_amount: Vec, + pub fees: Vec, } pub fn post_dispatch( From 7a5dba7ba817d4f929fb0056dbabc418651887ae Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Wed, 1 Nov 2023 14:15:55 +0900 Subject: [PATCH 29/53] fix(ism): threshold validation --- contracts/isms/aggregate/src/error.rs | 3 +++ contracts/isms/aggregate/src/lib.rs | 14 +++++++++++--- packages/interface/src/ism/aggregate.rs | 3 ++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/contracts/isms/aggregate/src/error.rs b/contracts/isms/aggregate/src/error.rs index 6ceb0a7d..00d05fd8 100644 --- a/contracts/isms/aggregate/src/error.rs +++ b/contracts/isms/aggregate/src/error.rs @@ -14,4 +14,7 @@ pub enum ContractError { #[error("route not found")] RouteNotFound {}, + + #[error("invalid threshold. reason: {0}")] + InvalidThreshold(String), } diff --git a/contracts/isms/aggregate/src/lib.rs b/contracts/isms/aggregate/src/lib.rs index 45d4e34e..4037c767 100644 --- a/contracts/isms/aggregate/src/lib.rs +++ b/contracts/isms/aggregate/src/lib.rs @@ -4,8 +4,8 @@ pub use crate::error::ContractError; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - ensure_eq, Addr, Deps, DepsMut, Empty, Env, Event, HexBinary, MessageInfo, QueryResponse, - Response, StdResult, + ensure, ensure_eq, Addr, Deps, DepsMut, Empty, Env, Event, HexBinary, MessageInfo, + QueryResponse, Response, StdResult, }; use cw2::set_contract_version; use cw_storage_plus::Item; @@ -72,12 +72,18 @@ pub fn execute( ) -> Result { match msg { ExecuteMsg::Ownable(msg) => Ok(hpl_ownable::handle(deps, env, info, msg)?), - ExecuteMsg::SetIsms { isms } => { + ExecuteMsg::SetIsms { isms, threshold } => { ensure_eq!( get_owner(deps.storage)?, info.sender, ContractError::Unauthorized ); + ensure!( + isms.len() >= threshold as usize, + ContractError::InvalidThreshold( + "threshold should be less than ism count".to_string() + ) + ); let parsed_isms = isms .iter() @@ -85,6 +91,7 @@ pub fn execute( .collect::>()?; ISMS.save(deps.storage, &parsed_isms)?; + THRESHOLD.save(deps.storage, &threshold)?; Ok(Response::new() .add_event(new_event("set_isms").add_attribute("isms", isms.join(",")))) @@ -116,6 +123,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result }, + SetIsms { isms: Vec, threshold: u8 }, } #[cw_serde] @@ -39,6 +39,7 @@ pub enum AggregateIsmQueryMsg { #[cw_serde] pub struct IsmsResponse { pub isms: Vec, + pub threshold: u8, } #[cfg(test)] From 7828af6e9fd08aaa0af044c4a1fba21e49c5b0b6 Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Thu, 9 Nov 2023 03:42:09 +0000 Subject: [PATCH 30/53] zero check --- contracts/isms/aggregate/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contracts/isms/aggregate/src/lib.rs b/contracts/isms/aggregate/src/lib.rs index 4037c767..f982d272 100644 --- a/contracts/isms/aggregate/src/lib.rs +++ b/contracts/isms/aggregate/src/lib.rs @@ -78,6 +78,10 @@ pub fn execute( info.sender, ContractError::Unauthorized ); + ensure!( + threshold > 0, + ContractError::InvalidThreshold("threshold must not be zero".to_string()) + ); ensure!( isms.len() >= threshold as usize, ContractError::InvalidThreshold( From e72014e429c93b31591ee2063da05ec8e88c584b Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Wed, 8 Nov 2023 10:16:10 +0000 Subject: [PATCH 31/53] remove hpl_pausable usage --- contracts/core/mailbox/Cargo.toml | 1 - contracts/core/mailbox/src/contract.rs | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/contracts/core/mailbox/Cargo.toml b/contracts/core/mailbox/Cargo.toml index e39ae0e1..4f982939 100644 --- a/contracts/core/mailbox/Cargo.toml +++ b/contracts/core/mailbox/Cargo.toml @@ -34,7 +34,6 @@ bech32.workspace = true thiserror.workspace = true hpl-ownable.workspace = true -hpl-pausable.workspace = true hpl-interface.workspace = true [dev-dependencies] diff --git a/contracts/core/mailbox/src/contract.rs b/contracts/core/mailbox/src/contract.rs index e74ca14f..e5cb94c0 100644 --- a/contracts/core/mailbox/src/contract.rs +++ b/contracts/core/mailbox/src/contract.rs @@ -31,12 +31,12 @@ pub fn instantiate( required_hook: None, }; + let owner = deps.api.addr_validate(&msg.owner)?; + CONFIG.save(deps.storage, &config)?; NONCE.save(deps.storage, &0u32)?; - let owner = deps.api.addr_validate(&msg.owner)?; hpl_ownable::initialize(deps.storage, &owner)?; - hpl_pausable::initialize(deps.storage, &false)?; Ok(Response::new().add_event(emit_instantiated(owner))) } From 8a7fc53dc95a75e7faacce38425835338cff5333 Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Thu, 23 Nov 2023 11:08:42 +0000 Subject: [PATCH 32/53] error message --- contracts/core/va/src/contract.rs | 2 +- contracts/core/va/src/error.rs | 8 ++++++-- contracts/hooks/merkle/src/lib.rs | 12 +++++++++--- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/contracts/core/va/src/contract.rs b/contracts/core/va/src/contract.rs index 76b567af..b6c9fd9d 100644 --- a/contracts/core/va/src/contract.rs +++ b/contracts/core/va/src/contract.rs @@ -150,7 +150,7 @@ fn announce( let replay_id = replay_hash(&validator, &storage_location)?; ensure!( !REPLAY_PROTECITONS.has(deps.storage, replay_id.to_vec()), - ContractError::Unauthorized {} + ContractError::unauthorized("replay protection triggered") ); REPLAY_PROTECITONS.save(deps.storage, replay_id.to_vec(), &Empty {})?; diff --git a/contracts/core/va/src/error.rs b/contracts/core/va/src/error.rs index 1cd84e7f..2650f6b9 100644 --- a/contracts/core/va/src/error.rs +++ b/contracts/core/va/src/error.rs @@ -9,8 +9,8 @@ pub enum ContractError { #[error("{0}")] RecoverPubkeyError(#[from] RecoverPubkeyError), - #[error("unauthorized")] - Unauthorized {}, + #[error("unauthorized. reason: {0}")] + Unauthorized(String), #[error("invalid address. reason: {0}")] InvalidAddress(String), @@ -20,6 +20,10 @@ pub enum ContractError { } impl ContractError { + pub fn unauthorized(reason: &str) -> Self { + ContractError::Unauthorized(reason.into()) + } + pub fn invalid_addr(reason: &str) -> Self { ContractError::InvalidAddress(reason.into()) } diff --git a/contracts/hooks/merkle/src/lib.rs b/contracts/hooks/merkle/src/lib.rs index 765ed396..7d97754b 100644 --- a/contracts/hooks/merkle/src/lib.rs +++ b/contracts/hooks/merkle/src/lib.rs @@ -23,13 +23,19 @@ pub enum ContractError { #[error("{0}")] PaymentError(#[from] cw_utils::PaymentError), - #[error("unauthorized")] - Unauthorized {}, + #[error("unauthorized. reason: {0}")] + Unauthorized(String), #[error("hook paused")] Paused {}, } +impl ContractError { + pub fn unauthorized(reason: &str) -> Self { + ContractError::Unauthorized(reason.into()) + } +} + // version info for migration info pub const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -94,7 +100,7 @@ pub fn execute( ensure_eq!( latest_dispatch_id, decoded_msg.id(), - ContractError::Unauthorized {} + ContractError::unauthorized("message is not dispatching") ); let mut tree = MESSAGE_TREE.load(deps.storage)?; From 39e6f485640e3b09920cfc69239ff15c24aa708a Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Thu, 23 Nov 2023 11:12:10 +0000 Subject: [PATCH 33/53] use tree_depth --- packages/interface/src/types/merkle.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/interface/src/types/merkle.rs b/packages/interface/src/types/merkle.rs index fd7f404a..5d0978f2 100644 --- a/packages/interface/src/types/merkle.rs +++ b/packages/interface/src/types/merkle.rs @@ -8,7 +8,7 @@ pub const TREE_DEPTH: usize = 32; pub const MAX_LEAVES: u128 = (2_u128.pow(TREE_DEPTH as u32)) - 1; pub const ZERO_BYTES: &str = "0000000000000000000000000000000000000000000000000000000000000000"; -pub const ZERO_HASHES: [&str; HASH_LENGTH] = [ +pub const ZERO_HASHES: [&str; TREE_DEPTH] = [ "0000000000000000000000000000000000000000000000000000000000000000", "ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5", "b4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30", From a77c745a5b44c1f70d67b1498c70d5367d62ace8 Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Thu, 23 Nov 2023 11:13:45 +0000 Subject: [PATCH 34/53] remove debugs --- contracts/core/mailbox/src/execute.rs | 7 ------- contracts/isms/multisig/src/query.rs | 5 ----- contracts/isms/routing/src/contract.rs | 5 ----- 3 files changed, 17 deletions(-) diff --git a/contracts/core/mailbox/src/execute.rs b/contracts/core/mailbox/src/execute.rs index 2170c952..6132cfd1 100644 --- a/contracts/core/mailbox/src/execute.rs +++ b/contracts/core/mailbox/src/execute.rs @@ -47,11 +47,6 @@ fn get_required_value( let received = &info.funds[0]; - deps.api.debug(&format!( - "mailbox::dispatch: required: {:?}, received: {:?}", - required, received - )); - ensure_eq!( &received.denom, &required.denom, @@ -232,8 +227,6 @@ pub fn process( let id = decoded_msg.id(); let ism = ism::recipient(&deps.querier, &recipient)?.unwrap_or(config.get_default_ism()); - deps.api.debug(&format!("mailbox::process: ism: {}", &ism)); - ensure!( !DELIVERIES.has(deps.storage, id.to_vec()), ContractError::AlreadyDeliveredMessage {} diff --git a/contracts/isms/multisig/src/query.rs b/contracts/isms/multisig/src/query.rs index ac848b91..2c265092 100644 --- a/contracts/isms/multisig/src/query.rs +++ b/contracts/isms/multisig/src/query.rs @@ -21,11 +21,6 @@ pub fn verify_message( raw_metadata: HexBinary, raw_message: HexBinary, ) -> Result { - deps.api.debug(&format!( - "ism_multisig::verify: metadata: {:?}, message: {:?}", - raw_metadata, raw_message - )); - let metadata: MessageIdMultisigIsmMetadata = raw_metadata.into(); let message: Message = raw_message.into(); diff --git a/contracts/isms/routing/src/contract.rs b/contracts/isms/routing/src/contract.rs index 3679fb38..ac6b63ce 100644 --- a/contracts/isms/routing/src/contract.rs +++ b/contracts/isms/routing/src/contract.rs @@ -81,11 +81,6 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result { - deps.api.debug(&format!( - "ism_routing::verify: metadata: {:?}, message: {:?}", - metadata, message - )); - let decoded = Message::from(message.clone()); let ism = MODULES From 14e4ff980337fbf7d4f1c95ad975f00cc80f29e2 Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Thu, 23 Nov 2023 11:22:16 +0000 Subject: [PATCH 35/53] duplicate --- contracts/isms/multisig/src/execute.rs | 72 ++++++++++++-------------- 1 file changed, 32 insertions(+), 40 deletions(-) diff --git a/contracts/isms/multisig/src/execute.rs b/contracts/isms/multisig/src/execute.rs index d4661fbb..255bf572 100644 --- a/contracts/isms/multisig/src/execute.rs +++ b/contracts/isms/multisig/src/execute.rs @@ -1,4 +1,6 @@ -use cosmwasm_std::{ensure_eq, DepsMut, Event, HexBinary, MessageInfo, Response, StdResult}; +use cosmwasm_std::{ + ensure_eq, DepsMut, Event, HexBinary, MessageInfo, Response, StdResult, Storage, +}; use hpl_interface::ism::multisig::{ThresholdSet, ValidatorSet as MsgValidatorSet}; use hpl_ownable::get_owner; @@ -45,38 +47,48 @@ pub fn set_thresholds( Ok(Response::new().add_events(events)) } -pub fn enroll_validator( - deps: DepsMut, - info: MessageInfo, - msg: MsgValidatorSet, -) -> Result { - ensure_eq!( - info.sender, - get_owner(deps.storage)?, - ContractError::Unauthorized {} - ); - +fn enroll( + storage: &mut dyn Storage, + domain: u32, + validator: HexBinary, +) -> Result<(), ContractError> { ensure_eq!( - msg.validator.len(), + validator.len(), 20, ContractError::invalid_addr("length should be 20") ); - let validator_state = VALIDATORS.may_load(deps.storage, msg.domain)?; + let validator_state = VALIDATORS.may_load(storage, domain)?; if let Some(mut validators) = validator_state { - if validators.contains(&msg.validator) { + if validators.contains(&validator) { return Err(ContractError::ValidatorDuplicate {}); } - validators.push(msg.validator.clone()); + validators.push(validator); validators.sort(); - VALIDATORS.save(deps.storage, msg.domain, &validators)?; + VALIDATORS.save(storage, domain, &validators)?; } else { - VALIDATORS.save(deps.storage, msg.domain, &vec![msg.validator.clone()])?; + VALIDATORS.save(storage, domain, &vec![validator])?; } + Ok(()) +} + +pub fn enroll_validator( + deps: DepsMut, + info: MessageInfo, + msg: MsgValidatorSet, +) -> Result { + ensure_eq!( + info.sender, + get_owner(deps.storage)?, + ContractError::Unauthorized {} + ); + + enroll(deps.storage, msg.domain, msg.validator.clone())?; + Ok(Response::new().add_event(emit_enroll_validator(msg.domain, msg.validator.to_hex()))) } @@ -94,28 +106,8 @@ pub fn enroll_validators( let mut events: Vec = Vec::new(); for msg in validators.into_iter() { - ensure_eq!( - msg.validator.len(), - 20, - ContractError::invalid_addr("length should be 20") - ); - - let validators_state = VALIDATORS.may_load(deps.storage, msg.domain)?; - - if let Some(mut validators) = validators_state { - if validators.contains(&msg.validator) { - return Err(ContractError::ValidatorDuplicate {}); - } - - validators.push(msg.validator.clone()); - validators.sort(); - - VALIDATORS.save(deps.storage, msg.domain, &validators)?; - events.push(emit_enroll_validator(msg.domain, msg.validator.to_hex())); - } else { - VALIDATORS.save(deps.storage, msg.domain, &vec![msg.validator.clone()])?; - events.push(emit_enroll_validator(msg.domain, msg.validator.to_hex())); - } + enroll(deps.storage, msg.domain, msg.validator.clone())?; + events.push(emit_enroll_validator(msg.domain, msg.validator.to_hex())); } Ok(Response::new().add_events(events)) From dbb74da7d4865efb90553da509f9cd9e46e554ce Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Thu, 23 Nov 2023 11:24:41 +0000 Subject: [PATCH 36/53] prune event / error --- contracts/core/mailbox/src/error.rs | 8 +------- contracts/hooks/aggregate/src/error.rs | 3 --- contracts/isms/aggregate/src/error.rs | 3 --- contracts/isms/multisig/src/event.rs | 14 +------------- 4 files changed, 2 insertions(+), 26 deletions(-) diff --git a/contracts/core/mailbox/src/error.rs b/contracts/core/mailbox/src/error.rs index cdf4805a..f17c4ce9 100644 --- a/contracts/core/mailbox/src/error.rs +++ b/contracts/core/mailbox/src/error.rs @@ -1,4 +1,4 @@ -use cosmwasm_std::{Coin, StdError}; +use cosmwasm_std::StdError; use thiserror::Error; #[derive(Error, Debug, PartialEq)] @@ -24,12 +24,6 @@ pub enum ContractError { #[error("invalid destination domain: {domain:?}")] InvalidDestinationDomain { domain: u32 }, - #[error("insufficient funds. required: {required}, received: {received}")] - InsufficientFunds { required: Coin, received: Coin }, - #[error("message already delivered")] AlreadyDeliveredMessage {}, - - #[error("message not found")] - MessageNotFound {}, } diff --git a/contracts/hooks/aggregate/src/error.rs b/contracts/hooks/aggregate/src/error.rs index babe75a3..494229a2 100644 --- a/contracts/hooks/aggregate/src/error.rs +++ b/contracts/hooks/aggregate/src/error.rs @@ -10,7 +10,4 @@ pub enum ContractError { #[error("unauthorized")] Unauthorized {}, - - #[error("hook paused")] - Paused {}, } diff --git a/contracts/isms/aggregate/src/error.rs b/contracts/isms/aggregate/src/error.rs index 00d05fd8..6bc64a01 100644 --- a/contracts/isms/aggregate/src/error.rs +++ b/contracts/isms/aggregate/src/error.rs @@ -12,9 +12,6 @@ pub enum ContractError { #[error("unauthorized")] Unauthorized, - #[error("route not found")] - RouteNotFound {}, - #[error("invalid threshold. reason: {0}")] InvalidThreshold(String), } diff --git a/contracts/isms/multisig/src/event.rs b/contracts/isms/multisig/src/event.rs index 17561622..4e7ab405 100644 --- a/contracts/isms/multisig/src/event.rs +++ b/contracts/isms/multisig/src/event.rs @@ -1,16 +1,4 @@ -use cosmwasm_std::{Addr, Event}; - -pub fn emit_init_transfer_ownership(next_owner: String) -> Event { - Event::new("ism_multisig_init_transfer_ownership").add_attribute("next_owner", next_owner) -} - -pub fn emit_finish_transfer_ownership(owner: Addr) -> Event { - Event::new("ism_multisig_finish_transfer_owner").add_attribute("owner", owner) -} - -pub fn emit_revoke_transfer_ownership() -> Event { - Event::new("ism_multisig_revoke_transfer_ownership") -} +use cosmwasm_std::Event; pub fn emit_enroll_validator(domain: u32, validator: String) -> Event { Event::new("ism_multisig_enroll_validator") From 38db7110deaecf62119e77fc889c5c26509d0f22 Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Thu, 23 Nov 2023 11:42:21 +0000 Subject: [PATCH 37/53] implement remove --- contracts/isms/routing/src/contract.rs | 13 +++++++++++++ packages/interface/src/ism/routing.rs | 1 + 2 files changed, 14 insertions(+) diff --git a/contracts/isms/routing/src/contract.rs b/contracts/isms/routing/src/contract.rs index ac6b63ce..159cde89 100644 --- a/contracts/isms/routing/src/contract.rs +++ b/contracts/isms/routing/src/contract.rs @@ -65,6 +65,19 @@ pub fn execute( &deps.api.addr_validate(&ism.address)?, )?; + Ok(Response::default()) + } + Unset { domains } => { + ensure_eq!( + get_owner(deps.storage)?, + info.sender, + ContractError::Unauthorized {} + ); + + for domain in domains { + MODULES.remove(deps.storage, domain); + } + Ok(Response::default()) } } diff --git a/packages/interface/src/ism/routing.rs b/packages/interface/src/ism/routing.rs index a877532e..27c444ff 100644 --- a/packages/interface/src/ism/routing.rs +++ b/packages/interface/src/ism/routing.rs @@ -24,6 +24,7 @@ pub enum ExecuteMsg { Ownable(OwnableMsg), Set { ism: IsmSet }, + Unset { domains: Vec }, } #[cw_serde] From 9902005e9b4dbbe633cd2be10ff1961be52c76f6 Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Thu, 23 Nov 2023 12:01:15 +0000 Subject: [PATCH 38/53] remove wrap as possible --- contracts/core/mailbox/src/query.rs | 4 ++-- contracts/hooks/routing-custom/src/lib.rs | 24 +++++++++++++++++++++-- contracts/mocks/mock-hook/src/contract.rs | 13 +++++++++--- contracts/warp/cw20/src/contract.rs | 9 +++++++-- contracts/warp/native/src/contract.rs | 9 +++++++-- 5 files changed, 48 insertions(+), 11 deletions(-) diff --git a/contracts/core/mailbox/src/query.rs b/contracts/core/mailbox/src/query.rs index c1c0bfaf..db978e3c 100644 --- a/contracts/core/mailbox/src/query.rs +++ b/contracts/core/mailbox/src/query.rs @@ -97,7 +97,7 @@ pub fn quote_dispatch( let mut required_gas = hook::quote_dispatch( &deps.querier, required_hook, - msg.metadata.clone().unwrap(), + msg.metadata.clone().unwrap_or_default(), msg.msg_body.clone(), )? .gas_amount @@ -106,7 +106,7 @@ pub fn quote_dispatch( let target_gas = hook::quote_dispatch( &deps.querier, target_hook, - msg.metadata.clone().unwrap(), + msg.metadata.clone().unwrap_or_default(), msg.msg_body, )? .gas_amount; diff --git a/contracts/hooks/routing-custom/src/lib.rs b/contracts/hooks/routing-custom/src/lib.rs index 90d93fe5..c917f7e8 100644 --- a/contracts/hooks/routing-custom/src/lib.rs +++ b/contracts/hooks/routing-custom/src/lib.rs @@ -34,6 +34,9 @@ pub enum ContractError { #[error("route not found for {0}")] RouteNotFound(u32), + + #[error("invalid arguments. reason: {reason:?}")] + InvalidArguments { reason: String }, } // version info for migration info @@ -178,6 +181,15 @@ fn register( ); for msg in msgs.clone() { + let recipient = HexBinary::from_hex(&msg.recipient)?; + ensure_eq!( + recipient.len(), + 32, + ContractError::InvalidArguments { + reason: "recipient must be 32 bytes long".into() + } + ); + CUSTOM_HOOKS.save( deps.storage, ( @@ -199,7 +211,11 @@ fn register( .map(|v| format!("{}:{}", v.dest_domain, v.recipient)) .collect::>(), ) - .unwrap(), + .map_err(|e| { + ContractError::Std(StdError::generic_err(format!( + "failed to marshal keys. reason: {e}", + ))) + })?, ), )) } @@ -236,7 +252,11 @@ fn clear( .map(|v| format!("{}:{}", v.dest_domain, v.recipient)) .collect::>(), ) - .unwrap(), + .map_err(|e| { + ContractError::Std(StdError::generic_err(format!( + "failed to marshal keys. reason: {e}", + ))) + })?, ), )) } diff --git a/contracts/mocks/mock-hook/src/contract.rs b/contracts/mocks/mock-hook/src/contract.rs index 3d86b07c..56037676 100644 --- a/contracts/mocks/mock-hook/src/contract.rs +++ b/contracts/mocks/mock-hook/src/contract.rs @@ -2,8 +2,8 @@ use cosmwasm_schema::cw_serde; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - coin, to_binary, Deps, DepsMut, Env, Event, MessageInfo, QueryResponse, Response, StdResult, - Uint256, + coin, to_binary, Deps, DepsMut, Env, Event, MessageInfo, QueryResponse, Response, StdError, + StdResult, Uint256, }; use cw2::set_contract_version; use cw_storage_plus::Item; @@ -93,7 +93,14 @@ pub fn query(deps: Deps, _env: Env, msg: ExpectedHookQueryMsg) -> StdResult { let gas = GAS .may_load(deps.storage)? - .map(|v| v.to_string().parse::().unwrap()); + .map(|v| { + v.to_string().parse::().map_err(|e| { + StdError::generic_err(format!( + "failed to parse Uint256 gas. reason: {e}" + )) + }) + }) + .transpose()?; let gas_token = GAS_TOKEN.load(deps.storage)?; let gas_amount = gas.map(|v| coin(v, gas_token)); diff --git a/contracts/warp/cw20/src/contract.rs b/contracts/warp/cw20/src/contract.rs index defe13b2..cee98d6c 100644 --- a/contracts/warp/cw20/src/contract.rs +++ b/contracts/warp/cw20/src/contract.rs @@ -2,7 +2,7 @@ use cosmwasm_std::entry_point; use cosmwasm_std::{ ensure_eq, wasm_execute, CosmosMsg, Deps, DepsMut, Env, HexBinary, MessageInfo, QueryResponse, - Reply, Response, SubMsg, Uint128, Uint256, WasmMsg, + Reply, Response, StdError, SubMsg, Uint128, Uint256, WasmMsg, }; use cw20::Cw20ExecuteMsg; @@ -107,7 +107,12 @@ pub fn execute( pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result { match msg.id { REPLY_ID_CREATE_DENOM => { - let reply_data = msg.result.unwrap().data.unwrap(); + let reply_data = msg + .result + .into_result() + .map_err(StdError::generic_err)? + .data + .ok_or(StdError::generic_err("no reply data"))?; let init_resp = cw_utils::parse_instantiate_response_data(&reply_data)?; let init_addr = deps.api.addr_validate(&init_resp.contract_address)?; diff --git a/contracts/warp/native/src/contract.rs b/contracts/warp/native/src/contract.rs index da89fa0d..5a78e40f 100644 --- a/contracts/warp/native/src/contract.rs +++ b/contracts/warp/native/src/contract.rs @@ -2,7 +2,7 @@ use cosmwasm_std::entry_point; use cosmwasm_std::{ ensure, ensure_eq, CosmosMsg, Deps, DepsMut, Empty, Env, HexBinary, MessageInfo, QueryResponse, - Reply, Response, SubMsg, Uint128, Uint256, + Reply, Response, StdError, SubMsg, Uint128, Uint256, }; use hpl_connection::{get_hook, get_ism}; use hpl_interface::{ @@ -106,7 +106,12 @@ pub fn execute( #[cfg_attr(not(feature = "library"), entry_point)] pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result { - let reply_data = msg.result.unwrap().data.unwrap(); + let reply_data = msg + .result + .into_result() + .map_err(StdError::generic_err)? + .data + .ok_or(StdError::generic_err("no reply data"))?; match msg.id { REPLY_ID_CREATE_DENOM => { From 7c0a1e8e54fbee3f477b7348d54942c3fcb857aa Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Thu, 23 Nov 2023 12:13:56 +0000 Subject: [PATCH 39/53] to factory denom --- contracts/warp/native/src/contract.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/contracts/warp/native/src/contract.rs b/contracts/warp/native/src/contract.rs index 5a78e40f..27948f10 100644 --- a/contracts/warp/native/src/contract.rs +++ b/contracts/warp/native/src/contract.rs @@ -64,7 +64,10 @@ pub fn instantiate( ))); } - (msgs, token.denom) + ( + msgs, + format!("factory/{}/{}", env.contract.address, token.denom), + ) } // use denom directly if token is native TokenModeMsg::Collateral(token) => { From b49b0536a5ecba93e0bb51f2f5c5c53d643048f1 Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Thu, 23 Nov 2023 12:14:43 +0000 Subject: [PATCH 40/53] remove config --- contracts/isms/multisig/src/state.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/contracts/isms/multisig/src/state.rs b/contracts/isms/multisig/src/state.rs index 25c8564b..ddb09d3a 100644 --- a/contracts/isms/multisig/src/state.rs +++ b/contracts/isms/multisig/src/state.rs @@ -1,13 +1,6 @@ -use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, HexBinary}; +use cosmwasm_std::HexBinary; use cw_storage_plus::Map; -#[cw_serde] -pub struct Config { - pub owner: Addr, - pub addr_prefix: String, -} - pub const VALIDATORS_PREFIX: &str = "validators"; pub const VALIDATORS: Map> = Map::new(VALIDATORS_PREFIX); From 09e89a3dd160d6b55f6ca97e6dbd40af5378a636 Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Thu, 23 Nov 2023 12:15:45 +0000 Subject: [PATCH 41/53] beneficiary --- contracts/igps/core/src/lib.rs | 4 ++-- contracts/igps/core/src/query.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/igps/core/src/lib.rs b/contracts/igps/core/src/lib.rs index cedd7a79..6085f7f3 100644 --- a/contracts/igps/core/src/lib.rs +++ b/contracts/igps/core/src/lib.rs @@ -30,8 +30,8 @@ pub const DEFAULT_GAS_USAGE: Item = Item::new(DEFAULT_GAS_USAGE_KEY); pub const GAS_FOR_DOMAIN_PREFIX: &str = "gas_for_domain"; pub const GAS_FOR_DOMAIN: Map = Map::new(GAS_FOR_DOMAIN_PREFIX); -pub const BENEFICAIRY_KEY: &str = "beneficiary"; -pub const BENEFICIARY: Item = Item::new(BENEFICAIRY_KEY); +pub const BENEFICIARY_KEY: &str = "beneficiary"; +pub const BENEFICIARY: Item = Item::new(BENEFICIARY_KEY); pub fn get_default_gas(storage: &dyn Storage, domain: u32) -> StdResult { let custom_gas = GAS_FOR_DOMAIN.may_load(storage, domain)?; diff --git a/contracts/igps/core/src/query.rs b/contracts/igps/core/src/query.rs index 6b672013..7a0d4b9c 100644 --- a/contracts/igps/core/src/query.rs +++ b/contracts/igps/core/src/query.rs @@ -51,10 +51,10 @@ pub fn list_gas_for_domains( } pub fn get_beneficiary(deps: Deps) -> Result { - let beneficairy = BENEFICIARY.load(deps.storage)?; + let beneficiary = BENEFICIARY.load(deps.storage)?; Ok(BeneficiaryResponse { - beneficiary: beneficairy.into(), + beneficiary: beneficiary.into(), }) } From 0e736fe2bd6ed9730d98beb0c223b307ce62218e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Nov 2023 12:17:03 +0000 Subject: [PATCH 42/53] chore(deps): bump axios from 1.4.0 to 1.6.0 in /scripts Bumps [axios](https://github.com/axios/axios) from 1.4.0 to 1.6.0. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.4.0...v1.6.0) --- updated-dependencies: - dependency-name: axios dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- scripts/pnpm-lock.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/pnpm-lock.yaml b/scripts/pnpm-lock.yaml index c5c0cb75..86f5a8c8 100644 --- a/scripts/pnpm-lock.yaml +++ b/scripts/pnpm-lock.yaml @@ -31,7 +31,7 @@ dependencies: version: 0.31.0 axios: specifier: ^1.4.0 - version: 1.4.0 + version: 1.6.0 colors: specifier: ^1.4.0 version: 1.4.0 @@ -660,8 +660,8 @@ packages: - debug dev: false - /axios@1.4.0: - resolution: {integrity: sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==} + /axios@1.6.0: + resolution: {integrity: sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==} dependencies: follow-redirects: 1.15.2 form-data: 4.0.0 From 57af03b22be7791f4074714f9fe089a803281e55 Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Fri, 24 Nov 2023 08:14:16 +0000 Subject: [PATCH 43/53] bump --- Cargo.toml | 24 +++++++------- contracts/core/mailbox/src/execute.rs | 21 ++++++------ contracts/core/mailbox/src/query.rs | 4 +-- contracts/core/va/src/contract.rs | 4 +-- contracts/hooks/merkle/src/lib.rs | 10 +++--- contracts/hooks/pausable/src/lib.rs | 8 ++--- contracts/hooks/routing-custom/src/lib.rs | 8 ++--- contracts/hooks/routing-fallback/src/lib.rs | 8 ++--- contracts/hooks/routing/src/lib.rs | 8 ++--- contracts/igps/core/src/tests/contract.rs | 10 +++--- contracts/igps/core/src/tests/mod.rs | 4 +-- contracts/igps/oracle/src/contract.rs | 4 +-- contracts/igps/oracle/src/tests/mod.rs | 4 +-- contracts/isms/aggregate/src/lib.rs | 6 ++-- contracts/isms/routing/src/contract.rs | 10 +++--- contracts/isms/routing/src/tests/contract.rs | 4 +-- contracts/isms/routing/src/tests/mod.rs | 4 +-- contracts/mocks/mock-hook/src/contract.rs | 12 +++---- contracts/mocks/mock-ism/src/contract.rs | 8 ++--- .../mocks/mock-msg-receiver/src/contract.rs | 4 +-- contracts/warp/cw20/src/contract.rs | 32 +++++++++---------- contracts/warp/native/src/contract.rs | 12 +++---- integration-test/tests/validator.rs | 1 + packages/connection/src/lib.rs | 10 +++--- packages/interface/src/lib.rs | 9 +++--- packages/interface/src/macros.rs | 6 ++-- packages/ownable/src/lib.rs | 6 ++-- packages/ownable/src/test.rs | 4 +-- packages/pausable/src/lib.rs | 4 +-- packages/pausable/src/test.rs | 4 +-- packages/router/src/lib.rs | 10 +++--- packages/router/src/test.rs | 4 +-- 32 files changed, 132 insertions(+), 135 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 283425bb..4261c4d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,17 +42,17 @@ keywords = ["hyperlane", "cosmos", "cosmwasm"] [workspace.dependencies] # cosmwasm -cosmwasm-std = { version = "1.2.7", features = ["stargate"] } -cosmwasm-storage = "1.2.7" -cosmwasm-schema = "1.2.7" -cosmwasm-crypto = "1.2.7" +cosmwasm-std = { version = "1.5.0", features = ["stargate"] } +cosmwasm-storage = "1.5.0" +cosmwasm-schema = "1.5.0" +cosmwasm-crypto = "1.5.0" # cosmwasm extension -cw-storage-plus = "1.1.0" -cw-utils = "1.0.1" -cw2 = "1.0.0" -cw20 = "1.1.0" -cw20-base = { version = "1.1.0", features = ["library"] } +cw-storage-plus = "1.2.0" +cw-utils = "1.0.3" +cw2 = "1.1.2" +cw20 = "1.1.2" +cw20-base = { version = "1.1.2", features = ["library"] } # utilities thiserror = { version = "1.0.37" } @@ -80,10 +80,10 @@ k256 = { version = "0.13.1", default-features = false, features = ["ecdsa"] } digest = { version = "0.10.7" } # testing -cw-multi-test = "0.17.0" +cw-multi-test = "0.18.1" rstest = "0.18.2" -test-tube = { version = "0.1.7" } -osmosis-test-tube = { version = "19.2.0" } +test-tube = { version = "0.2.0" } +osmosis-test-tube = { version = "20.1.2" } ibcx-test-utils = { version = "0.1.2" } tokio = { version = "1", features = ["full"] } diff --git a/contracts/core/mailbox/src/execute.rs b/contracts/core/mailbox/src/execute.rs index 2170c952..65f75356 100644 --- a/contracts/core/mailbox/src/execute.rs +++ b/contracts/core/mailbox/src/execute.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{ - coin, ensure, ensure_eq, to_binary, wasm_execute, Coin, Deps, DepsMut, Env, HexBinary, + coin, ensure, ensure_eq, to_json_binary, wasm_execute, Coin, Deps, DepsMut, Env, HexBinary, MessageInfo, Response, }; use cw_utils::PaymentError; @@ -198,7 +198,7 @@ pub fn dispatch( Ok(Response::new() .add_event(emit_dispatch_id(msg_id.clone())) .add_event(emit_dispatch(msg)) - .set_data(to_binary(&DispatchResponse { message_id: msg_id })?) + .set_data(to_json_binary(&DispatchResponse { message_id: msg_id })?) .add_messages(post_dispatch_msgs)) } @@ -279,9 +279,9 @@ pub fn process( #[cfg(test)] mod tests { use cosmwasm_std::{ - from_binary, + from_json, testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, - Addr, ContractResult, OwnedDeps, QuerierResult, SystemResult, WasmQuery, + to_json_binary, Addr, ContractResult, OwnedDeps, QuerierResult, SystemResult, WasmQuery, }; use hpl_interface::{ @@ -307,7 +307,7 @@ mod tests { fn mock_query_handler(req: &WasmQuery) -> QuerierResult { let (req, _addr) = match req { - WasmQuery::Smart { msg, contract_addr } => (from_binary(msg).unwrap(), contract_addr), + WasmQuery::Smart { msg, contract_addr } => (from_json(msg).unwrap(), contract_addr), _ => unreachable!("wrong query type"), }; @@ -325,7 +325,7 @@ mod tests { } let res = QuoteDispatchResponse { gas_amount }; - let res = cosmwasm_std::to_binary(&res).unwrap(); + let res = to_json_binary(&res).unwrap(); SystemResult::Ok(ContractResult::Ok(res)) } @@ -495,14 +495,13 @@ mod tests { fn test_process_query_handler(query: &WasmQuery) -> QuerierResult { match query { WasmQuery::Smart { contract_addr, msg } => { - if let Ok(req) = cosmwasm_std::from_binary::(msg) - { + if let Ok(req) = cosmwasm_std::from_json::(msg) { match req { hpl_interface::ism::ExpectedIsmSpecifierQueryMsg::IsmSpecifier( ism::IsmSpecifierQueryMsg::InterchainSecurityModule(), ) => { return SystemResult::Ok( - cosmwasm_std::to_binary(&ism::InterchainSecurityModuleResponse { + to_json_binary(&ism::InterchainSecurityModuleResponse { ism: Some(addr("default_ism")), }) .into(), @@ -511,13 +510,13 @@ mod tests { } } - if let Ok(req) = cosmwasm_std::from_binary::(msg) { + if let Ok(req) = cosmwasm_std::from_json::(msg) { assert_eq!(contract_addr, &addr("default_ism")); match req { ism::ExpectedIsmQueryMsg::Ism(IsmQueryMsg::Verify { metadata, .. }) => { return SystemResult::Ok( - cosmwasm_std::to_binary(&ism::VerifyResponse { + to_json_binary(&ism::VerifyResponse { verified: metadata[0] == 1, }) .into(), diff --git a/contracts/core/mailbox/src/query.rs b/contracts/core/mailbox/src/query.rs index c1c0bfaf..9a06cf67 100644 --- a/contracts/core/mailbox/src/query.rs +++ b/contracts/core/mailbox/src/query.rs @@ -124,7 +124,7 @@ pub fn quote_dispatch( mod test { use cosmwasm_std::{ - from_binary, + from_json, testing::{mock_dependencies, mock_env}, Addr, }; @@ -141,7 +141,7 @@ mod test { use super::*; fn query(deps: Deps, req: MailboxQueryMsg) -> T { - from_binary(&contract::query(deps, mock_env(), req.wrap()).unwrap()).unwrap() + from_json(contract::query(deps, mock_env(), req.wrap()).unwrap()).unwrap() } fn query_hrp(deps: Deps) -> HrpResponse { diff --git a/contracts/core/va/src/contract.rs b/contracts/core/va/src/contract.rs index 76b567af..bc3a7ab6 100644 --- a/contracts/core/va/src/contract.rs +++ b/contracts/core/va/src/contract.rs @@ -207,7 +207,7 @@ pub fn migrate(_deps: DepsMut, _env: Env, _msg: Empty) -> Result QuerierResult { SystemResult::Ok(ContractResult::Ok( - cosmwasm_std::to_binary(&mailbox::LocalDomainResponse { + to_json_binary(&mailbox::LocalDomainResponse { local_domain: 26657, }) .unwrap(), diff --git a/contracts/hooks/merkle/src/lib.rs b/contracts/hooks/merkle/src/lib.rs index 765ed396..b1cedea4 100644 --- a/contracts/hooks/merkle/src/lib.rs +++ b/contracts/hooks/merkle/src/lib.rs @@ -200,7 +200,7 @@ mod test { use super::*; use cosmwasm_std::{ - from_binary, + from_json, testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, HexBinary, OwnedDeps, WasmQuery, }; @@ -267,12 +267,10 @@ mod test { #[case] message: Option, ) { deps.querier.update_wasm(|query| { - use cosmwasm_std::{to_binary, ContractResult, SystemResult}; + use cosmwasm_std::{to_json_binary, ContractResult, SystemResult}; let (_contract_addr, msg) = match query { - WasmQuery::Smart { contract_addr, msg } => { - (contract_addr, from_binary(msg).unwrap()) - } + WasmQuery::Smart { contract_addr, msg } => (contract_addr, from_json(msg).unwrap()), _ => unreachable!("noo"), }; @@ -283,7 +281,7 @@ mod test { "a6d8af738f99da8a0a8a3611e6c777bc9ebf42b1f685a5ff6b1ff1f2b7b70f45", ), }; - SystemResult::Ok(ContractResult::Ok(to_binary(&res).unwrap())) + SystemResult::Ok(ContractResult::Ok(to_json_binary(&res).unwrap())) } _ => unreachable!("unwrap noo"), } diff --git a/contracts/hooks/pausable/src/lib.rs b/contracts/hooks/pausable/src/lib.rs index fe80c63c..dadecf27 100644 --- a/contracts/hooks/pausable/src/lib.rs +++ b/contracts/hooks/pausable/src/lib.rs @@ -103,9 +103,9 @@ fn quote_dispatch() -> Result { mod test { use cosmwasm_schema::serde::{de::DeserializeOwned, Serialize}; use cosmwasm_std::{ - from_binary, + from_json, testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, - Addr, HexBinary, OwnedDeps, + to_json_binary, Addr, HexBinary, OwnedDeps, }; use hpl_interface::hook::{PostDispatchMsg, QuoteDispatchMsg}; use hpl_ownable::get_owner; @@ -118,9 +118,9 @@ mod test { type TestDeps = OwnedDeps; fn query(deps: Deps, msg: S) -> T { - let req: QueryMsg = from_binary(&cosmwasm_std::to_binary(&msg).unwrap()).unwrap(); + let req: QueryMsg = from_json(to_json_binary(&msg).unwrap()).unwrap(); let res = crate::query(deps, mock_env(), req).unwrap(); - from_binary(&res).unwrap() + from_json(res).unwrap() } #[fixture] diff --git a/contracts/hooks/routing-custom/src/lib.rs b/contracts/hooks/routing-custom/src/lib.rs index 90d93fe5..f1510839 100644 --- a/contracts/hooks/routing-custom/src/lib.rs +++ b/contracts/hooks/routing-custom/src/lib.rs @@ -295,9 +295,9 @@ fn quote_dispatch( #[cfg(test)] mod test { use cosmwasm_std::{ - coin, from_binary, + coin, from_json, testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, - ContractResult, OwnedDeps, QuerierResult, SystemResult, WasmQuery, + to_json_binary, ContractResult, OwnedDeps, QuerierResult, SystemResult, WasmQuery, }; use hpl_interface::{build_test_querier, hook::ExpectedHookQueryMsg, router::DomainRouteSet}; use hpl_ownable::get_owner; @@ -329,7 +329,7 @@ mod test { fn mock_query_handler(req: &WasmQuery) -> QuerierResult { let (req, _addr) = match req { - WasmQuery::Smart { msg, contract_addr } => (from_binary(msg).unwrap(), contract_addr), + WasmQuery::Smart { msg, contract_addr } => (from_json(msg).unwrap(), contract_addr), _ => unreachable!("wrong query type"), }; @@ -347,7 +347,7 @@ mod test { } let res = QuoteDispatchResponse { gas_amount }; - let res = cosmwasm_std::to_binary(&res).unwrap(); + let res = to_json_binary(&res).unwrap(); SystemResult::Ok(ContractResult::Ok(res)) } diff --git a/contracts/hooks/routing-fallback/src/lib.rs b/contracts/hooks/routing-fallback/src/lib.rs index ae0bbc16..85df33f0 100644 --- a/contracts/hooks/routing-fallback/src/lib.rs +++ b/contracts/hooks/routing-fallback/src/lib.rs @@ -157,9 +157,9 @@ pub fn quote_dispatch( #[cfg(test)] mod test { use cosmwasm_std::{ - coin, from_binary, + coin, from_json, testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, - ContractResult, OwnedDeps, QuerierResult, SystemResult, WasmQuery, + to_json_binary, ContractResult, OwnedDeps, QuerierResult, SystemResult, WasmQuery, }; use hpl_interface::{build_test_querier, hook::ExpectedHookQueryMsg, router::DomainRouteSet}; use hpl_ownable::get_owner; @@ -184,7 +184,7 @@ mod test { fn mock_query_handler(req: &WasmQuery) -> QuerierResult { let (req, addr) = match req { - WasmQuery::Smart { msg, contract_addr } => (from_binary(msg).unwrap(), contract_addr), + WasmQuery::Smart { msg, contract_addr } => (from_json(msg).unwrap(), contract_addr), _ => unreachable!("wrong query type"), }; @@ -206,7 +206,7 @@ mod test { } let res = QuoteDispatchResponse { gas_amount }; - let res = cosmwasm_std::to_binary(&res).unwrap(); + let res = to_json_binary(&res).unwrap(); SystemResult::Ok(ContractResult::Ok(res)) } diff --git a/contracts/hooks/routing/src/lib.rs b/contracts/hooks/routing/src/lib.rs index 7e97cc82..65b107c6 100644 --- a/contracts/hooks/routing/src/lib.rs +++ b/contracts/hooks/routing/src/lib.rs @@ -138,9 +138,9 @@ fn post_dispatch( #[cfg(test)] mod test { use cosmwasm_std::{ - coin, from_binary, + coin, from_json, testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, - ContractResult, OwnedDeps, QuerierResult, SystemResult, WasmQuery, + to_json_binary, ContractResult, OwnedDeps, QuerierResult, SystemResult, WasmQuery, }; use hpl_interface::{build_test_querier, hook::ExpectedHookQueryMsg, router::DomainRouteSet}; use hpl_ownable::get_owner; @@ -163,7 +163,7 @@ mod test { fn mock_query_handler(req: &WasmQuery) -> QuerierResult { let (req, _addr) = match req { - WasmQuery::Smart { msg, contract_addr } => (from_binary(msg).unwrap(), contract_addr), + WasmQuery::Smart { msg, contract_addr } => (from_json(msg).unwrap(), contract_addr), _ => unreachable!("wrong query type"), }; @@ -181,7 +181,7 @@ mod test { } let res = QuoteDispatchResponse { gas_amount }; - let res = cosmwasm_std::to_binary(&res).unwrap(); + let res = to_json_binary(&res).unwrap(); SystemResult::Ok(ContractResult::Ok(res)) } diff --git a/contracts/igps/core/src/tests/contract.rs b/contracts/igps/core/src/tests/contract.rs index daf61c7a..4b81d8db 100644 --- a/contracts/igps/core/src/tests/contract.rs +++ b/contracts/igps/core/src/tests/contract.rs @@ -1,8 +1,8 @@ use cosmwasm_std::{ - coin, from_binary, + coin, from_json, testing::{mock_dependencies, mock_env, mock_info}, - to_binary, Addr, BankMsg, Coin, ContractResult, HexBinary, Order, QuerierResult, StdResult, - SubMsg, SystemResult, Uint128, Uint256, WasmQuery, + to_json_binary, Addr, BankMsg, Coin, ContractResult, HexBinary, Order, QuerierResult, + StdResult, SubMsg, SystemResult, Uint128, Uint256, WasmQuery, }; use hpl_interface::{ igp::{ @@ -28,7 +28,7 @@ const DEC_9: u128 = 10u128.pow(9); fn test_mock_querier(v: &WasmQuery) -> QuerierResult { let (contract_addr, msg) = match v { - WasmQuery::Smart { contract_addr, msg } => (contract_addr, from_binary(msg).unwrap()), + WasmQuery::Smart { contract_addr, msg } => (contract_addr, from_json(msg).unwrap()), _ => unreachable!("only smart query"), }; @@ -42,7 +42,7 @@ fn test_mock_querier(v: &WasmQuery) -> QuerierResult { let gas_price = split.pop().unwrap().parse::().unwrap(); let exchange_rate = split.pop().unwrap().parse::().unwrap(); - let res = to_binary(&oracle::GetExchangeRateAndGasPriceResponse { + let res = to_json_binary(&oracle::GetExchangeRateAndGasPriceResponse { gas_price: Uint128::new(gas_price * DEC_9), // 150 gwei gas price exchange_rate: Uint128::new(exchange_rate * DEC_9), // 0.2 exchange rate (remote token less valuable) }) diff --git a/contracts/igps/core/src/tests/mod.rs b/contracts/igps/core/src/tests/mod.rs index 29c4cd51..6bb55faf 100644 --- a/contracts/igps/core/src/tests/mod.rs +++ b/contracts/igps/core/src/tests/mod.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{ - from_binary, + from_json, testing::{mock_info, MockApi, MockQuerier, MockStorage}, Addr, Coin, Deps, DepsMut, Empty, Env, HexBinary, MessageInfo, OwnedDeps, Response, }; @@ -65,7 +65,7 @@ impl IGP { fn query(&self, msg: QueryMsg) -> Result { query(self.deps.as_ref(), self.env.clone(), msg) - .map(|v| from_binary::(&v))? + .map(|v| from_json::(&v))? .map_err(|e| e.into()) } diff --git a/contracts/igps/oracle/src/contract.rs b/contracts/igps/oracle/src/contract.rs index c9901d46..6def8460 100644 --- a/contracts/igps/oracle/src/contract.rs +++ b/contracts/igps/oracle/src/contract.rs @@ -1,7 +1,7 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - ensure_eq, to_binary, Deps, DepsMut, Env, Event, MessageInfo, QueryResponse, Response, + ensure_eq, to_json_binary, Deps, DepsMut, Env, Event, MessageInfo, QueryResponse, Response, }; use hpl_interface::igp::oracle::{ @@ -84,7 +84,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result { let gas_data = REMOTE_GAS_DATA.load(deps.storage, dest_domain)?; - Ok(to_binary(&GetExchangeRateAndGasPriceResponse { + Ok(to_json_binary(&GetExchangeRateAndGasPriceResponse { gas_price: gas_data.gas_price, exchange_rate: gas_data.token_exchange_rate, })?) diff --git a/contracts/igps/oracle/src/tests/mod.rs b/contracts/igps/oracle/src/tests/mod.rs index 80a2f821..d1c39c39 100644 --- a/contracts/igps/oracle/src/tests/mod.rs +++ b/contracts/igps/oracle/src/tests/mod.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{ - from_binary, testing::mock_info, Addr, Api, Empty, Env, MessageInfo, OwnedDeps, Querier, + from_json, testing::mock_info, Addr, Api, Empty, Env, MessageInfo, OwnedDeps, Querier, Response, Storage, }; use hpl_interface::igp::oracle::{ @@ -51,7 +51,7 @@ where fn query(&self, msg: QueryMsg) -> Result { query(self.deps.as_ref(), self.env.clone(), msg) - .map(|v| from_binary::(&v))? + .map(|v| from_json::(&v))? .map_err(|e| e.into()) } diff --git a/contracts/isms/aggregate/src/lib.rs b/contracts/isms/aggregate/src/lib.rs index f982d272..44f4ec94 100644 --- a/contracts/isms/aggregate/src/lib.rs +++ b/contracts/isms/aggregate/src/lib.rs @@ -4,8 +4,8 @@ pub use crate::error::ContractError; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - ensure, ensure_eq, Addr, Deps, DepsMut, Empty, Env, Event, HexBinary, MessageInfo, - QueryResponse, Response, StdResult, + ensure, ensure_eq, to_json_binary, Addr, Deps, DepsMut, Empty, Env, Event, HexBinary, + MessageInfo, QueryResponse, Response, StdResult, }; use cw2::set_contract_version; use cw_storage_plus::Item; @@ -121,7 +121,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result match msg { - AggregateIsmQueryMsg::Isms {} => Ok(cosmwasm_std::to_binary(&IsmsResponse { + AggregateIsmQueryMsg::Isms {} => Ok(to_json_binary(&IsmsResponse { isms: ISMS .load(deps.storage)? .into_iter() diff --git a/contracts/isms/routing/src/contract.rs b/contracts/isms/routing/src/contract.rs index 3679fb38..f4e73e0d 100644 --- a/contracts/isms/routing/src/contract.rs +++ b/contracts/isms/routing/src/contract.rs @@ -1,7 +1,7 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - ensure_eq, to_binary, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, + ensure_eq, to_json_binary, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, }; use cw2::set_contract_version; use hpl_interface::{ @@ -77,7 +77,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result Ok(hpl_ownable::handle_query(deps, env, msg)?), QueryMsg::Ism(msg) => match msg { - ModuleType {} => Ok(to_binary(&ModuleTypeResponse { + ModuleType {} => Ok(to_json_binary(&ModuleTypeResponse { typ: hpl_interface::ism::IsmType::Routing, })?), Verify { metadata, message } => { @@ -96,7 +96,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result { let decoded = Message::from(message.clone()); @@ -109,7 +109,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result match msg { @@ -121,7 +121,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result anyhow::Result<()> { // register mock handler ism.deps.querier.update_wasm(|v| match v { WasmQuery::Smart { contract_addr, .. } => SystemResult::Ok(ContractResult::Ok( - to_binary(&VerifyResponse { + to_json_binary(&VerifyResponse { verified: contract_addr == "ism1", }) .unwrap(), diff --git a/contracts/isms/routing/src/tests/mod.rs b/contracts/isms/routing/src/tests/mod.rs index daa9a2f8..42f3da73 100644 --- a/contracts/isms/routing/src/tests/mod.rs +++ b/contracts/isms/routing/src/tests/mod.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{ - from_binary, + from_json, testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, Addr, Empty, Env, HexBinary, MessageInfo, OwnedDeps, Response, }; @@ -59,7 +59,7 @@ impl IsmRouting { fn query(&self, msg: QueryMsg) -> Result { query(self.deps.as_ref(), self.env.clone(), msg) - .map(|v| from_binary::(&v))? + .map(|v| from_json::(&v))? .map_err(|e| e.into()) } diff --git a/contracts/mocks/mock-hook/src/contract.rs b/contracts/mocks/mock-hook/src/contract.rs index 3d86b07c..3052a0aa 100644 --- a/contracts/mocks/mock-hook/src/contract.rs +++ b/contracts/mocks/mock-hook/src/contract.rs @@ -2,8 +2,8 @@ use cosmwasm_schema::cw_serde; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - coin, to_binary, Deps, DepsMut, Env, Event, MessageInfo, QueryResponse, Response, StdResult, - Uint256, + coin, to_json_binary, Deps, DepsMut, Env, Event, MessageInfo, QueryResponse, Response, + StdResult, Uint256, }; use cw2::set_contract_version; use cw_storage_plus::Item; @@ -98,7 +98,7 @@ pub fn query(deps: Deps, _env: Env, msg: ExpectedHookQueryMsg) -> StdResult { unimplemented!("mailbox query not implemented on mock hook") @@ -109,7 +109,7 @@ pub fn query(deps: Deps, _env: Env, msg: ExpectedHookQueryMsg) -> StdResult StdResult match msg { - ModuleType {} => Ok(to_binary(&IsmType::Null)?), - Verify { .. } => Ok(to_binary(&VerifyResponse { verified: true })?), - VerifyInfo { .. } => Ok(to_binary(&VerifyInfoResponse { + ModuleType {} => Ok(to_json_binary(&IsmType::Null)?), + Verify { .. } => Ok(to_json_binary(&VerifyResponse { verified: true })?), + VerifyInfo { .. } => Ok(to_json_binary(&VerifyInfoResponse { threshold: 1u8, validators: vec![], })?), diff --git a/contracts/mocks/mock-msg-receiver/src/contract.rs b/contracts/mocks/mock-msg-receiver/src/contract.rs index b127e108..4f916778 100644 --- a/contracts/mocks/mock-msg-receiver/src/contract.rs +++ b/contracts/mocks/mock-msg-receiver/src/contract.rs @@ -2,7 +2,7 @@ use cosmwasm_schema::cw_serde; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - attr, to_binary, Deps, DepsMut, Empty, Env, Event, MessageInfo, QueryResponse, Response, + attr, to_json_binary, Deps, DepsMut, Empty, Env, Event, MessageInfo, QueryResponse, Response, StdResult, }; use cw2::set_contract_version; @@ -68,7 +68,7 @@ pub fn query( match msg { ism::ExpectedIsmSpecifierQueryMsg::IsmSpecifier( ism::IsmSpecifierQueryMsg::InterchainSecurityModule(), - ) => Ok(to_binary(&ism::InterchainSecurityModuleResponse { + ) => Ok(to_json_binary(&ism::InterchainSecurityModuleResponse { ism: None, })?), } diff --git a/contracts/warp/cw20/src/contract.rs b/contracts/warp/cw20/src/contract.rs index defe13b2..19f75b2a 100644 --- a/contracts/warp/cw20/src/contract.rs +++ b/contracts/warp/cw20/src/contract.rs @@ -1,8 +1,8 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - ensure_eq, wasm_execute, CosmosMsg, Deps, DepsMut, Env, HexBinary, MessageInfo, QueryResponse, - Reply, Response, SubMsg, Uint128, Uint256, WasmMsg, + ensure_eq, to_json_binary, wasm_execute, CosmosMsg, Deps, DepsMut, Env, HexBinary, MessageInfo, + QueryResponse, Reply, Response, SubMsg, Uint128, Uint256, WasmMsg, }; use cw20::Cw20ExecuteMsg; @@ -56,7 +56,7 @@ pub fn instantiate( WasmMsg::Instantiate { admin: Some(env.contract.address.to_string()), code_id: token.code_id, - msg: cosmwasm_std::to_binary(&token_init_msg)?, + msg: to_json_binary(&token_init_msg)?, funds: vec![], label: "token warp cw20".to_string(), }, @@ -240,11 +240,11 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result to_binary(get_token_type(deps)), TokenMode {} => to_binary(get_token_mode(deps)), }, - QueryMsg::IsmSpecifier(IsmSpecifierQueryMsg::InterchainSecurityModule()) => Ok( - cosmwasm_std::to_binary(&InterchainSecurityModuleResponse { + QueryMsg::IsmSpecifier(IsmSpecifierQueryMsg::InterchainSecurityModule()) => { + Ok(to_json_binary(&InterchainSecurityModuleResponse { ism: get_ism(deps.storage)?, - })?, - ), + })?) + } } } @@ -417,7 +417,7 @@ mod test { CosmosMsg::Wasm(WasmMsg::Instantiate { admin: Some(mock_env().contract.address.to_string()), code_id: v.code_id, - msg: cosmwasm_std::to_binary(&v.init_msg).unwrap(), + msg: to_json_binary(&v.init_msg).unwrap(), funds: vec![], label: "token warp cw20".to_string() }) @@ -474,8 +474,8 @@ mod test { match token_mode { TokenModeMsg::Bridged(_) => { assert_eq!( - cosmwasm_std::to_binary(msg).unwrap(), - cosmwasm_std::to_binary(&CosmosMsg::::Wasm( + to_json_binary(msg).unwrap(), + to_json_binary(&CosmosMsg::::Wasm( conv::to_mint_msg( TOKEN, bech32_encode(hrp, warp_msg.recipient.as_slice()).unwrap(), @@ -488,8 +488,8 @@ mod test { } TokenModeMsg::Collateral(_) => { assert_eq!( - cosmwasm_std::to_binary(msg).unwrap(), - cosmwasm_std::to_binary(&CosmosMsg::::Wasm( + to_json_binary(msg).unwrap(), + to_json_binary(&CosmosMsg::::Wasm( conv::to_send_msg( TOKEN, bech32_encode(hrp, warp_msg.recipient.as_slice()).unwrap(), @@ -559,8 +559,8 @@ mod test { match token_mode { TokenModeMsg::Bridged(_) => { assert_eq!( - cosmwasm_std::to_binary(&msgs).unwrap(), - cosmwasm_std::to_binary(&vec![ + to_json_binary(&msgs).unwrap(), + to_json_binary(&vec![ transfer_from_msg.into(), CosmosMsg::from(conv::to_burn_msg(TOKEN, Uint128::new(100)).unwrap()), dispatch_msg, @@ -570,8 +570,8 @@ mod test { } TokenModeMsg::Collateral(_) => { assert_eq!( - cosmwasm_std::to_binary(&msgs).unwrap(), - cosmwasm_std::to_binary(&vec![transfer_from_msg.into(), dispatch_msg]).unwrap(), + to_json_binary(&msgs).unwrap(), + to_json_binary(&vec![transfer_from_msg.into(), dispatch_msg]).unwrap(), ); } } diff --git a/contracts/warp/native/src/contract.rs b/contracts/warp/native/src/contract.rs index da89fa0d..c0591f5b 100644 --- a/contracts/warp/native/src/contract.rs +++ b/contracts/warp/native/src/contract.rs @@ -1,8 +1,8 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - ensure, ensure_eq, CosmosMsg, Deps, DepsMut, Empty, Env, HexBinary, MessageInfo, QueryResponse, - Reply, Response, SubMsg, Uint128, Uint256, + ensure, ensure_eq, to_json_binary, CosmosMsg, Deps, DepsMut, Empty, Env, HexBinary, + MessageInfo, QueryResponse, Reply, Response, SubMsg, Uint128, Uint256, }; use hpl_connection::{get_hook, get_ism}; use hpl_interface::{ @@ -250,11 +250,11 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result to_binary(get_token_type(deps)), TokenMode {} => to_binary(get_token_mode(deps)), }, - QueryMsg::IsmSpecifier(IsmSpecifierQueryMsg::InterchainSecurityModule()) => Ok( - cosmwasm_std::to_binary(&InterchainSecurityModuleResponse { + QueryMsg::IsmSpecifier(IsmSpecifierQueryMsg::InterchainSecurityModule()) => { + Ok(to_json_binary(&InterchainSecurityModuleResponse { ism: get_ism(deps.storage)?, - })?, - ), + })?) + } } } diff --git a/integration-test/tests/validator.rs b/integration-test/tests/validator.rs index 05230e41..4fc8a427 100644 --- a/integration-test/tests/validator.rs +++ b/integration-test/tests/validator.rs @@ -22,6 +22,7 @@ pub struct TestValidator { pub pub_key: VerifyingKey, } +#[allow(dead_code)] impl TestValidator { fn random() -> Self { let priv_key = SigningKey::random(&mut OsRng); diff --git a/packages/connection/src/lib.rs b/packages/connection/src/lib.rs index 5506c9d8..91c7bdbd 100644 --- a/packages/connection/src/lib.rs +++ b/packages/connection/src/lib.rs @@ -1,6 +1,6 @@ use cosmwasm_std::{ - ensure_eq, Addr, CustomQuery, Deps, DepsMut, Env, Event, MessageInfo, QueryResponse, Response, - StdError, StdResult, Storage, + ensure_eq, to_json_binary, Addr, CustomQuery, Deps, DepsMut, Env, Event, MessageInfo, + QueryResponse, Response, StdError, StdResult, Storage, }; use cw_storage_plus::Item; use hpl_interface::connection::{ @@ -75,13 +75,13 @@ pub fn handle_query( msg: ConnectionQueryMsg, ) -> StdResult { match msg { - ConnectionQueryMsg::GetMailbox {} => Ok(cosmwasm_std::to_binary(&MailboxResponse { + ConnectionQueryMsg::GetMailbox {} => Ok(to_json_binary(&MailboxResponse { mailbox: get_mailbox(deps.storage)?.map(|v| v.into()), })?), - ConnectionQueryMsg::GetHook {} => Ok(cosmwasm_std::to_binary(&HookResponse { + ConnectionQueryMsg::GetHook {} => Ok(to_json_binary(&HookResponse { hook: get_hook(deps.storage)?.map(|v| v.into()), })?), - ConnectionQueryMsg::GetIsm {} => Ok(cosmwasm_std::to_binary(&IsmResponse { + ConnectionQueryMsg::GetIsm {} => Ok(to_json_binary(&IsmResponse { ism: get_ism(deps.storage)?.map(|v| v.into()), })?), } diff --git a/packages/interface/src/lib.rs b/packages/interface/src/lib.rs index 595fb188..eaf238f8 100644 --- a/packages/interface/src/lib.rs +++ b/packages/interface/src/lib.rs @@ -1,7 +1,7 @@ use std::error::Error; use cosmwasm_schema::cw_serde; -use cosmwasm_std::{QueryResponse, StdError, StdResult}; +use cosmwasm_std::{to_json_binary, QueryResponse, StdError, StdResult}; use cw_storage_plus::Bound; pub mod connection; @@ -84,17 +84,16 @@ pub fn range_option<'a, T: cw_storage_plus::PrimaryKey<'a>>( pub fn to_binary + From>( data: Result, ) -> Result { - data.map(|v| cosmwasm_std::to_binary(&v))? - .map_err(|err| err.into()) + data.map(|v| to_json_binary(&v))?.map_err(|err| err.into()) } #[cfg(test)] mod test { - use cosmwasm_std::{from_binary, to_binary}; + use cosmwasm_std::{from_json, to_json_binary}; use serde::{de::DeserializeOwned, Serialize}; pub fn msg_checker(input: Input) -> Output { - from_binary::(&to_binary(&input).unwrap()).unwrap() + from_json::(to_json_binary(&input).unwrap()).unwrap() } } diff --git a/packages/interface/src/macros.rs b/packages/interface/src/macros.rs index 9a7dc8ba..deb93c80 100644 --- a/packages/interface/src/macros.rs +++ b/packages/interface/src/macros.rs @@ -11,11 +11,11 @@ macro_rules! build_test_querier { let res = $handler( deps, cosmwasm_std::testing::mock_env(), - cosmwasm_std::from_binary(&cosmwasm_std::to_binary(&msg).unwrap()).unwrap(), + cosmwasm_std::from_json(cosmwasm_std::to_json_binary(&msg).unwrap()).unwrap(), ) .map_err(|e| e.to_string()) .unwrap(); - cosmwasm_std::from_binary(&res).unwrap() + cosmwasm_std::from_json(&res).unwrap() } }; } @@ -33,7 +33,7 @@ macro_rules! build_test_executor { deps, cosmwasm_std::testing::mock_env(), cosmwasm_std::testing::mock_info(sender.as_str(), &funds), - cosmwasm_std::from_binary(&cosmwasm_std::to_binary(&msg).unwrap()).unwrap(), + cosmwasm_std::from_json(cosmwasm_std::to_json_binary(&msg).unwrap()).unwrap(), ) .map_err(|e| e.to_string()) .unwrap() diff --git a/packages/ownable/src/lib.rs b/packages/ownable/src/lib.rs index 8fbcba50..a4d07821 100644 --- a/packages/ownable/src/lib.rs +++ b/packages/ownable/src/lib.rs @@ -2,7 +2,7 @@ mod test; use cosmwasm_std::{ - ensure, ensure_eq, to_binary, Addr, CustomQuery, Deps, DepsMut, Env, Event, MessageInfo, + ensure, ensure_eq, to_json_binary, Addr, CustomQuery, Deps, DepsMut, Env, Event, MessageInfo, QueryResponse, Response, StdError, StdResult, Storage, }; use cw_storage_plus::Item; @@ -123,10 +123,10 @@ pub fn handle_query( msg: OwnableQueryMsg, ) -> StdResult { match msg { - OwnableQueryMsg::GetOwner {} => to_binary(&OwnerResponse { + OwnableQueryMsg::GetOwner {} => to_json_binary(&OwnerResponse { owner: get_owner(deps.storage)?, }), - OwnableQueryMsg::GetPendingOwner {} => to_binary(&PendingOwnerResponse { + OwnableQueryMsg::GetPendingOwner {} => to_json_binary(&PendingOwnerResponse { pending_owner: get_pending_owner(deps.storage)?, }), } diff --git a/packages/ownable/src/test.rs b/packages/ownable/src/test.rs index af74b678..e75c7561 100644 --- a/packages/ownable/src/test.rs +++ b/packages/ownable/src/test.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{ - from_binary, + from_json, testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, Addr, CustomQuery, Empty, Env, MessageInfo, OwnedDeps, Response, StdError, StdResult, }; @@ -27,7 +27,7 @@ where } fn query(&self, msg: OwnableQueryMsg) -> StdResult { - from_binary(&handle_query(self.deps.as_ref(), self.env.clone(), msg)?) + from_json(handle_query(self.deps.as_ref(), self.env.clone(), msg)?) } pub fn init(&mut self, sender: &Addr, next_owner: &Addr) -> StdResult { diff --git a/packages/pausable/src/lib.rs b/packages/pausable/src/lib.rs index 99cd56c7..0c95cd4e 100644 --- a/packages/pausable/src/lib.rs +++ b/packages/pausable/src/lib.rs @@ -2,7 +2,7 @@ mod test; use cosmwasm_std::{ - ensure, ensure_eq, to_binary, Addr, CustomQuery, Deps, DepsMut, Env, Event, MessageInfo, + ensure, ensure_eq, to_json_binary, Addr, CustomQuery, Deps, DepsMut, Env, Event, MessageInfo, QueryResponse, Response, StdError, StdResult, Storage, }; use cw_storage_plus::Item; @@ -69,7 +69,7 @@ pub fn handle_query( msg: PausableQueryMsg, ) -> StdResult { match msg { - PausableQueryMsg::PauseInfo {} => to_binary(&PauseInfoResponse { + PausableQueryMsg::PauseInfo {} => to_json_binary(&PauseInfoResponse { paused: get_pause_info(deps.storage)?, }), } diff --git a/packages/pausable/src/test.rs b/packages/pausable/src/test.rs index 84e0f545..17fa189d 100644 --- a/packages/pausable/src/test.rs +++ b/packages/pausable/src/test.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{ - from_binary, + from_json, testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, Addr, CustomQuery, Empty, Env, MessageInfo, OwnedDeps, Response, StdError, StdResult, }; @@ -27,7 +27,7 @@ where } fn query(&self, msg: PausableQueryMsg) -> StdResult { - from_binary(&handle_query(self.deps.as_ref(), self.env.clone(), msg)?) + from_json(handle_query(self.deps.as_ref(), self.env.clone(), msg)?) } pub fn set_owner(&mut self, owner: &Addr) -> StdResult<()> { diff --git a/packages/router/src/lib.rs b/packages/router/src/lib.rs index 2d5c9c57..b11de618 100644 --- a/packages/router/src/lib.rs +++ b/packages/router/src/lib.rs @@ -2,8 +2,8 @@ mod test; use cosmwasm_std::{ - ensure_eq, to_binary, Addr, CustomQuery, Deps, DepsMut, Env, Event, MessageInfo, QueryResponse, - Response, StdError, StdResult, Storage, + ensure_eq, to_json_binary, Addr, CustomQuery, Deps, DepsMut, Env, Event, MessageInfo, + QueryResponse, Response, StdError, StdResult, Storage, }; use cw_storage_plus::Map; use hpl_interface::{ @@ -116,17 +116,17 @@ where T: Serialize + DeserializeOwned + Clone + Eq, { match msg { - RouterQuery::Domains {} => to_binary(&DomainsResponse { + RouterQuery::Domains {} => to_json_binary(&DomainsResponse { domains: get_domains::(deps.storage)?, }), - RouterQuery::GetRoute { domain } => to_binary(&RouteResponse:: { + RouterQuery::GetRoute { domain } => to_json_binary(&RouteResponse:: { route: get_route(deps.storage, domain)?, }), RouterQuery::ListRoutes { offset, limit, order, - } => to_binary(&RoutesResponse:: { + } => to_json_binary(&RoutesResponse:: { routes: get_routes(deps.storage, offset, limit, order)?, }), RouterQuery::Placeholder(_) => unreachable!(), diff --git a/packages/router/src/test.rs b/packages/router/src/test.rs index 0557112b..4c2fe17a 100644 --- a/packages/router/src/test.rs +++ b/packages/router/src/test.rs @@ -1,7 +1,7 @@ use std::marker::PhantomData; use cosmwasm_std::{ - from_binary, + from_json, testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, Addr, Binary, Empty, Env, MessageInfo, OwnedDeps, Response, StdResult, }; @@ -61,7 +61,7 @@ where } fn handle_query(&self, msg: RouterQuery) -> StdResult { - from_binary(&handle_query(self.deps.as_ref(), self.env.clone(), msg)?) + from_json(handle_query(self.deps.as_ref(), self.env.clone(), msg)?) } pub fn query_domains(&self) -> StdResult { From cdcceece33ae19687210c56d1ca7bf0caa848923 Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Thu, 21 Dec 2023 10:08:45 +0000 Subject: [PATCH 44/53] fix merge --- contracts/core/mailbox/src/execute.rs | 55 +++------------------ contracts/hooks/routing-custom/src/lib.rs | 6 ++- contracts/hooks/routing-fallback/src/lib.rs | 8 +-- contracts/hooks/routing/src/lib.rs | 6 ++- contracts/igps/oracle/src/contract.rs | 3 +- contracts/mocks/mock-hook/src/contract.rs | 9 ++-- contracts/warp/cw20/src/contract.rs | 2 +- contracts/warp/native/src/contract.rs | 4 +- packages/interface/src/lib.rs | 6 +-- packages/router/src/test.rs | 2 +- 10 files changed, 34 insertions(+), 67 deletions(-) diff --git a/contracts/core/mailbox/src/execute.rs b/contracts/core/mailbox/src/execute.rs index ba9ad750..3e43a62f 100644 --- a/contracts/core/mailbox/src/execute.rs +++ b/contracts/core/mailbox/src/execute.rs @@ -1,6 +1,6 @@ use cosmwasm_std::{ - coin, ensure, ensure_eq, to_json_binary, wasm_execute, Coin, Deps, DepsMut, Env, HexBinary, - MessageInfo, Response, + ensure, ensure_eq, to_json_binary, wasm_execute, BankMsg, Coins, DepsMut, Env, HexBinary, + MessageInfo, Response, StdResult, }; use hpl_interface::{ core::{ @@ -23,48 +23,6 @@ use crate::{ ContractError, MAILBOX_VERSION, }; -fn get_required_value( - deps: Deps, - info: &MessageInfo, - hook: impl Into, - metadata: HexBinary, - msg_body: HexBinary, -) -> Result<(Option, Option), ContractError> { - let required = quote_dispatch(&deps.querier, hook, metadata, msg_body)?.gas_amount; - let required = match required { - Some(v) => v, - None => return Ok((None, None)), - }; - - if info.funds.is_empty() { - return Ok((None, None)); - } - - if info.funds.len() > 1 { - return Err(PaymentError::MultipleDenoms {}.into()); - } - - let received = &info.funds[0]; - - ensure_eq!( - &received.denom, - &required.denom, - PaymentError::ExtraDenom(received.clone().denom) - ); - - if received.amount <= required.amount { - return Ok((Some(received.clone()), None)); - } - - Ok(( - Some(required.clone()), - Some(coin( - (received.amount - required.amount).u128(), - required.denom, - )), - )) -} - pub fn set_default_ism( deps: DepsMut, info: MessageInfo, @@ -215,7 +173,8 @@ pub fn dispatch( .add_event(emit_dispatch_id(msg_id.clone())) .add_event(emit_dispatch(msg)) .set_data(to_json_binary(&DispatchResponse { message_id: msg_id })?) - .add_messages(post_dispatch_msgs)) + .add_messages(post_dispatch_msgs) + .add_message(refund_msg)) } pub fn process( @@ -293,7 +252,7 @@ pub fn process( #[cfg(test)] mod tests { use cosmwasm_std::{ - from_json, + coin, from_json, testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, to_json_binary, Addr, ContractResult, OwnedDeps, QuerierResult, SystemResult, WasmQuery, }; @@ -338,7 +297,9 @@ mod tests { fees = Coins::from(coin(parsed_fee as u128, "utest")); } - let res = QuoteDispatchResponse { gas_amount }; + let res = QuoteDispatchResponse { + fees: fees.into_vec(), + }; let res = to_json_binary(&res).unwrap(); SystemResult::Ok(ContractResult::Ok(res)) diff --git a/contracts/hooks/routing-custom/src/lib.rs b/contracts/hooks/routing-custom/src/lib.rs index 29471821..70a5b03e 100644 --- a/contracts/hooks/routing-custom/src/lib.rs +++ b/contracts/hooks/routing-custom/src/lib.rs @@ -317,7 +317,7 @@ mod test { use cosmwasm_std::{ coin, from_json, testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, - to_json_binary, ContractResult, OwnedDeps, QuerierResult, SystemResult, WasmQuery, + to_json_binary, Coins, ContractResult, OwnedDeps, QuerierResult, SystemResult, WasmQuery, }; use hpl_interface::{build_test_querier, hook::ExpectedHookQueryMsg, router::DomainRouteSet}; use hpl_ownable::get_owner; @@ -366,7 +366,9 @@ mod test { fees = Coins::from(coin(parsed_fee as u128, "utest")); } - let res = QuoteDispatchResponse { gas_amount }; + let res = QuoteDispatchResponse { + fees: fees.into_vec(), + }; let res = to_json_binary(&res).unwrap(); SystemResult::Ok(ContractResult::Ok(res)) diff --git a/contracts/hooks/routing-fallback/src/lib.rs b/contracts/hooks/routing-fallback/src/lib.rs index 9541604a..0fd4ad46 100644 --- a/contracts/hooks/routing-fallback/src/lib.rs +++ b/contracts/hooks/routing-fallback/src/lib.rs @@ -159,7 +159,7 @@ mod test { use cosmwasm_std::{ coin, from_json, testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, - to_json_binary, ContractResult, OwnedDeps, QuerierResult, SystemResult, WasmQuery, + to_json_binary, Coins, ContractResult, OwnedDeps, QuerierResult, SystemResult, WasmQuery, }; use hpl_interface::{build_test_querier, hook::ExpectedHookQueryMsg, router::DomainRouteSet}; use hpl_ownable::get_owner; @@ -205,9 +205,11 @@ mod test { fees = Coins::default(); } - let res = QuoteDispatchResponse { gas_amount }; + let res = QuoteDispatchResponse { + fees: fees.into_vec(), + }; let res = to_json_binary(&res).unwrap(); - + SystemResult::Ok(ContractResult::Ok(res)) } diff --git a/contracts/hooks/routing/src/lib.rs b/contracts/hooks/routing/src/lib.rs index fb0174f9..dd4651f3 100644 --- a/contracts/hooks/routing/src/lib.rs +++ b/contracts/hooks/routing/src/lib.rs @@ -140,7 +140,7 @@ mod test { use cosmwasm_std::{ coin, from_json, testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, - to_json_binary, ContractResult, OwnedDeps, QuerierResult, SystemResult, WasmQuery, + to_json_binary, Coins, ContractResult, OwnedDeps, QuerierResult, SystemResult, WasmQuery, }; use hpl_interface::{build_test_querier, hook::ExpectedHookQueryMsg, router::DomainRouteSet}; use hpl_ownable::get_owner; @@ -180,7 +180,9 @@ mod test { fees = Coins::from(coin(parsed_fee as u128, "utest")); } - let res = QuoteDispatchResponse { gas_amount }; + let res = QuoteDispatchResponse { + fees: fees.into_vec(), + }; let res = to_json_binary(&res).unwrap(); SystemResult::Ok(ContractResult::Ok(res)) diff --git a/contracts/igps/oracle/src/contract.rs b/contracts/igps/oracle/src/contract.rs index 39d21f14..f9ef1d5c 100644 --- a/contracts/igps/oracle/src/contract.rs +++ b/contracts/igps/oracle/src/contract.rs @@ -1,7 +1,8 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - ensure_eq, to_json_binary, Deps, DepsMut, Env, Event, MessageInfo, QueryResponse, Response, + ensure, ensure_eq, to_json_binary, Deps, DepsMut, Env, Event, MessageInfo, QueryResponse, + Response, }; use hpl_interface::igp::oracle::{ diff --git a/contracts/mocks/mock-hook/src/contract.rs b/contracts/mocks/mock-hook/src/contract.rs index 99a852a9..86096802 100644 --- a/contracts/mocks/mock-hook/src/contract.rs +++ b/contracts/mocks/mock-hook/src/contract.rs @@ -2,8 +2,8 @@ use cosmwasm_schema::cw_serde; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - coin, to_json_binary, Deps, DepsMut, Env, Event, MessageInfo, QueryResponse, Response, - StdResult, Uint256, + coins, to_json_binary, Deps, DepsMut, Env, Event, MessageInfo, QueryResponse, Response, + StdError, StdResult, Uint256, }; use cw2::set_contract_version; use cw_storage_plus::Item; @@ -102,10 +102,9 @@ pub fn query(deps: Deps, _env: Env, msg: ExpectedHookQueryMsg) -> StdResult { unimplemented!("mailbox query not implemented on mock hook") diff --git a/contracts/warp/cw20/src/contract.rs b/contracts/warp/cw20/src/contract.rs index 840aea13..785be875 100644 --- a/contracts/warp/cw20/src/contract.rs +++ b/contracts/warp/cw20/src/contract.rs @@ -2,7 +2,7 @@ use cosmwasm_std::entry_point; use cosmwasm_std::{ ensure_eq, to_json_binary, wasm_execute, CosmosMsg, Deps, DepsMut, Env, HexBinary, MessageInfo, - QueryResponse, Reply, Response, SubMsg, Uint128, Uint256, WasmMsg, + QueryResponse, Reply, Response, StdError, SubMsg, Uint128, Uint256, WasmMsg, }; use cw20::Cw20ExecuteMsg; diff --git a/contracts/warp/native/src/contract.rs b/contracts/warp/native/src/contract.rs index 42162ed6..d9181be2 100644 --- a/contracts/warp/native/src/contract.rs +++ b/contracts/warp/native/src/contract.rs @@ -2,7 +2,7 @@ use cosmwasm_std::entry_point; use cosmwasm_std::{ ensure, ensure_eq, to_json_binary, CosmosMsg, Deps, DepsMut, Empty, Env, HexBinary, - MessageInfo, QueryResponse, Reply, Response, SubMsg, Uint128, Uint256, + MessageInfo, QueryResponse, Reply, Response, StdError, SubMsg, Uint128, Uint256, }; use hpl_connection::{get_hook, get_ism}; use hpl_interface::{ @@ -561,7 +561,7 @@ mod test { .into(), None, None, - vec![ + [ vec![coin(50, DENOM)], funds.into_iter().filter(|v| v.denom != DENOM).collect() ] diff --git a/packages/interface/src/lib.rs b/packages/interface/src/lib.rs index eaf238f8..119204ed 100644 --- a/packages/interface/src/lib.rs +++ b/packages/interface/src/lib.rs @@ -87,6 +87,9 @@ pub fn to_binary + From>( data.map(|v| to_json_binary(&v))?.map_err(|err| err.into()) } +#[cfg(test)] +pub use test::msg_checker; + #[cfg(test)] mod test { use cosmwasm_std::{from_json, to_json_binary}; @@ -96,6 +99,3 @@ mod test { from_json::(to_json_binary(&input).unwrap()).unwrap() } } - -#[cfg(test)] -pub use test::msg_checker; diff --git a/packages/router/src/test.rs b/packages/router/src/test.rs index 4c2fe17a..48b3d079 100644 --- a/packages/router/src/test.rs +++ b/packages/router/src/test.rs @@ -94,7 +94,7 @@ where Self { deps: mock_dependencies(), env: mock_env(), - _marker: PhantomData::::default(), + _marker: PhantomData::, } } } From a19e51383f9ef0767f3f1859ec3bffefe1dd3ec3 Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Thu, 21 Dec 2023 19:11:46 +0900 Subject: [PATCH 45/53] bump axios (for remedy security issue) --- .beaker/state.json | 1 - scripts/package.json | 2 +- scripts/pnpm-lock.yaml | 8 ++++---- 3 files changed, 5 insertions(+), 6 deletions(-) delete mode 100644 .beaker/state.json diff --git a/.beaker/state.json b/.beaker/state.json deleted file mode 100644 index 9e26dfee..00000000 --- a/.beaker/state.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/scripts/package.json b/scripts/package.json index 8b6796ec..e02d93f8 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -19,7 +19,7 @@ "@cosmjs/proto-signing": "^0.31.0", "@cosmjs/stargate": "^0.31.0", "@cosmjs/tendermint-rpc": "0.31.0", - "axios": "^1.4.0", + "axios": "^1.6.2", "colors": "^1.4.0", "commander": "^11.1.0", "inversify": "^6.0.1", diff --git a/scripts/pnpm-lock.yaml b/scripts/pnpm-lock.yaml index 86f5a8c8..66ec77a8 100644 --- a/scripts/pnpm-lock.yaml +++ b/scripts/pnpm-lock.yaml @@ -30,8 +30,8 @@ dependencies: specifier: 0.31.0 version: 0.31.0 axios: - specifier: ^1.4.0 - version: 1.6.0 + specifier: ^1.6.2 + version: 1.6.2 colors: specifier: ^1.4.0 version: 1.4.0 @@ -660,8 +660,8 @@ packages: - debug dev: false - /axios@1.6.0: - resolution: {integrity: sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==} + /axios@1.6.2: + resolution: {integrity: sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==} dependencies: follow-redirects: 1.15.2 form-data: 4.0.0 From 985d98e513a18512a54b8ea58c492bbf2f22b839 Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Fri, 22 Dec 2023 04:07:09 +0000 Subject: [PATCH 46/53] fix igp test + use cosmwasm_1_1 --- Cargo.toml | 8 +++---- contracts/igps/core/src/contract.rs | 20 +++------------- contracts/igps/core/src/tests/contract.rs | 6 ++--- integration-test/tests/contracts/cw/ism.rs | 28 +++++++++++----------- integration-test/tests/validator.rs | 21 ++++++++-------- 5 files changed, 34 insertions(+), 49 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4261c4d3..643fe642 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,7 @@ keywords = ["hyperlane", "cosmos", "cosmwasm"] [workspace.dependencies] # cosmwasm -cosmwasm-std = { version = "1.5.0", features = ["stargate"] } +cosmwasm-std = { version = "1.5.0", features = ["stargate", "cosmwasm_1_1"] } cosmwasm-storage = "1.5.0" cosmwasm-schema = "1.5.0" cosmwasm-crypto = "1.5.0" @@ -80,10 +80,10 @@ k256 = { version = "0.13.1", default-features = false, features = ["ecdsa"] } digest = { version = "0.10.7" } # testing -cw-multi-test = "0.18.1" +cw-multi-test = "0.20.0" rstest = "0.18.2" -test-tube = { version = "0.2.0" } -osmosis-test-tube = { version = "20.1.2" } +test-tube = { version = "0.3.0" } +osmosis-test-tube = { version = "21.0.0" } ibcx-test-utils = { version = "0.1.2" } tokio = { version = "1", features = ["full"] } diff --git a/contracts/igps/core/src/contract.rs b/contracts/igps/core/src/contract.rs index a98f181c..16c9caea 100644 --- a/contracts/igps/core/src/contract.rs +++ b/contracts/igps/core/src/contract.rs @@ -36,23 +36,9 @@ pub fn instantiate( ); // check gas token exists - if let Err(e) = deps - .querier - .query::(&QueryRequest::Stargate { - path: "/cosmos.bank.v1beta1.QuerySupplyOfRequest".to_string(), - data: Binary( - proto::QuerySupplyOfRequest { - denom: msg.gas_token.clone(), - } - .encode_to_vec(), - ), - }) - { - return Err(ContractError::invalid_config(&format!( - "gas_token {} does not exist: {e}", - msg.gas_token, - ))); - } + deps.querier.query_supply(&msg.gas_token).map_err(|e| { + ContractError::invalid_config(&format!("gas_token {} does not exist: {e}", msg.gas_token,)) + })?; let owner = deps.api.addr_validate(&msg.owner)?; let beneficiary = deps.api.addr_validate(&msg.beneficiary)?; diff --git a/contracts/igps/core/src/tests/contract.rs b/contracts/igps/core/src/tests/contract.rs index 4b81d8db..ddf0caaa 100644 --- a/contracts/igps/core/src/tests/contract.rs +++ b/contracts/igps/core/src/tests/contract.rs @@ -1,8 +1,8 @@ use cosmwasm_std::{ coin, from_json, - testing::{mock_dependencies, mock_env, mock_info}, - to_json_binary, Addr, BankMsg, Coin, ContractResult, HexBinary, Order, QuerierResult, - StdResult, SubMsg, SystemResult, Uint128, Uint256, WasmQuery, + testing::{mock_dependencies, mock_dependencies_with_balance, mock_env, mock_info}, + to_json_binary, Addr, BankMsg, Coin, ContractResult, Empty, HexBinary, Order, QuerierResult, + QueryRequest, StdResult, SubMsg, SystemResult, Uint128, Uint256, WasmQuery, }; use hpl_interface::{ igp::{ diff --git a/integration-test/tests/contracts/cw/ism.rs b/integration-test/tests/contracts/cw/ism.rs index 14d4a5ca..2b58e01f 100644 --- a/integration-test/tests/contracts/cw/ism.rs +++ b/integration-test/tests/contracts/cw/ism.rs @@ -1,5 +1,5 @@ -use cosmwasm_std::Empty; -use hpl_interface::ism::multisig::ThresholdSet; +use cosmwasm_std::{Empty, HexBinary, StdResult}; +use hpl_interface::types::pub_to_addr; use osmosis_test_tube::Wasm; use test_tube::{Account, Runner, SigningAccount}; @@ -69,18 +69,18 @@ impl Ism { wasm.execute( &multisig_ism, - &hpl_interface::ism::multisig::ExecuteMsg::EnrollValidators { set: set.to_set() }, - &[], - owner, - )?; - - wasm.execute( - &multisig_ism, - &hpl_interface::ism::multisig::ExecuteMsg::SetThreshold { - set: ThresholdSet { - domain: set.domain, - threshold: set.threshold, - }, + &hpl_interface::ism::multisig::ExecuteMsg::SetValidators { + domain: set.domain, + threshold: set.threshold, + validators: set + .validators + .iter() + .map(|v| { + pub_to_addr(HexBinary::from( + v.pub_key.to_encoded_point(true).as_bytes().to_vec(), + )) + }) + .collect::>>()?, }, &[], owner, diff --git a/integration-test/tests/validator.rs b/integration-test/tests/validator.rs index 4fc8a427..25cb8dcd 100644 --- a/integration-test/tests/validator.rs +++ b/integration-test/tests/validator.rs @@ -1,11 +1,11 @@ use cosmwasm_std::{ - testing::{mock_dependencies, mock_info}, + testing::{mock_dependencies, mock_env, mock_info}, Binary, HexBinary, }; use ethers::types::{Address, H160}; use ethers::utils::hex::FromHex; use hpl_interface::{ - ism::multisig::{ThresholdSet, ValidatorSet}, + ism::multisig::ValidatorSet, types::{ bech32_encode, eth_addr, eth_hash, pub_to_addr, Message, MessageIdMultisigIsmMetadata, }, @@ -177,19 +177,18 @@ fn test_validator() { hpl_ownable::initialize(deps.as_mut().storage, &owner).unwrap(); - hpl_ism_multisig::execute::enroll_validators( + hpl_ism_multisig::contract::execute( deps.as_mut(), + mock_env(), mock_info(owner.as_str(), &[]), - validators.to_set(), - ) - .unwrap(); - - hpl_ism_multisig::execute::set_threshold( - deps.as_mut(), - mock_info(owner.as_str(), &[]), - ThresholdSet { + hpl_interface::ism::multisig::ExecuteMsg::SetValidators { domain: validators.domain, threshold: validators.threshold, + validators: validators + .to_set() + .iter() + .map(|v| v.validator.clone()) + .collect(), }, ) .unwrap(); From eae10ecb6e38a9c6fee7145ecd54c65fa40f835a Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Fri, 22 Dec 2023 05:02:26 +0000 Subject: [PATCH 47/53] interface --- contracts/isms/aggregate/src/lib.rs | 13 ++++++++----- contracts/isms/multisig/src/contract.rs | 2 +- contracts/isms/multisig/src/query.rs | 10 +++++----- contracts/isms/routing/src/contract.rs | 4 ++-- contracts/mocks/mock-ism/src/contract.rs | 6 +++--- packages/interface/src/ism/aggregate.rs | 2 +- packages/interface/src/ism/mod.rs | 8 ++++---- packages/interface/src/ism/multisig.rs | 4 ++-- packages/interface/src/ism/routing.rs | 2 +- 9 files changed, 27 insertions(+), 24 deletions(-) diff --git a/contracts/isms/aggregate/src/lib.rs b/contracts/isms/aggregate/src/lib.rs index 44f4ec94..0df0c158 100644 --- a/contracts/isms/aggregate/src/lib.rs +++ b/contracts/isms/aggregate/src/lib.rs @@ -12,7 +12,7 @@ use cw_storage_plus::Item; use hpl_interface::{ ism::{ aggregate::{AggregateIsmQueryMsg, ExecuteMsg, InstantiateMsg, IsmsResponse, QueryMsg}, - IsmQueryMsg, IsmType, ModuleTypeResponse, VerifyInfoResponse, VerifyResponse, + IsmQueryMsg, IsmType, ModuleTypeResponse, ModulesAndThresholdResponse, VerifyResponse, }, to_binary, types::{bech32_decode, AggregateMetadata}, @@ -117,7 +117,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result to_binary(verify(deps, metadata, message)), - VerifyInfo { message } => to_binary(verify_info(deps, message)), + ModulesAndThreshold { message } => to_binary(verify_info(deps, message)), }, QueryMsg::AggregateIsm(msg) => match msg { @@ -159,10 +159,13 @@ fn verify( }) } -fn verify_info(deps: Deps, _message: HexBinary) -> Result { - Ok(VerifyInfoResponse { +fn verify_info( + deps: Deps, + _message: HexBinary, +) -> Result { + Ok(ModulesAndThresholdResponse { threshold: THRESHOLD.load(deps.storage)?, - validators: ISMS + modules: ISMS .load(deps.storage)? .into_iter() .map(|v| Ok(bech32_decode(v.as_str())?.into())) diff --git a/contracts/isms/multisig/src/contract.rs b/contracts/isms/multisig/src/contract.rs index ce4075a1..6bd1d79c 100644 --- a/contracts/isms/multisig/src/contract.rs +++ b/contracts/isms/multisig/src/contract.rs @@ -115,7 +115,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result to_binary(query::verify_message(deps, raw_metadata, raw_message)), - VerifyInfo { + ModulesAndThreshold { message: raw_message, } => to_binary(query::get_verify_info(deps, raw_message)), }, diff --git a/contracts/isms/multisig/src/query.rs b/contracts/isms/multisig/src/query.rs index 2c265092..86460c51 100644 --- a/contracts/isms/multisig/src/query.rs +++ b/contracts/isms/multisig/src/query.rs @@ -1,6 +1,6 @@ use cosmwasm_std::{Deps, HexBinary}; use hpl_interface::{ - ism::{IsmType, ModuleTypeResponse, VerifyInfoResponse, VerifyResponse}, + ism::{IsmType, ModuleTypeResponse, ModulesAndThresholdResponse, VerifyResponse}, types::{eth_addr, eth_hash, Message, MessageIdMultisigIsmMetadata}, }; @@ -63,15 +63,15 @@ pub fn verify_message( pub fn get_verify_info( deps: Deps, raw_message: HexBinary, -) -> Result { +) -> Result { let message: Message = raw_message.into(); let threshold = THRESHOLD.load(deps.storage, message.origin_domain)?; let validators = VALIDATORS.load(deps.storage, message.origin_domain)?; - Ok(VerifyInfoResponse { + Ok(ModulesAndThresholdResponse { threshold, - validators, + modules: validators, }) } @@ -157,7 +157,7 @@ mod test { let info = get_verify_info(deps.as_ref(), raw_message).unwrap(); - assert_eq!(info.validators, vec![addr]); + assert_eq!(info.modules, vec![addr]); assert_eq!(info.threshold, 1); } } diff --git a/contracts/isms/routing/src/contract.rs b/contracts/isms/routing/src/contract.rs index 093ebfde..5a7218fb 100644 --- a/contracts/isms/routing/src/contract.rs +++ b/contracts/isms/routing/src/contract.rs @@ -106,7 +106,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result { + ModulesAndThreshold { message } => { let decoded = Message::from(message.clone()); let ism = MODULES @@ -115,7 +115,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result StdResult match msg { ModuleType {} => Ok(to_json_binary(&IsmType::Null)?), Verify { .. } => Ok(to_json_binary(&VerifyResponse { verified: true })?), - VerifyInfo { .. } => Ok(to_json_binary(&VerifyInfoResponse { + ModulesAndThreshold { .. } => Ok(to_json_binary(&ModulesAndThresholdResponse { threshold: 1u8, - validators: vec![], + modules: vec![], })?), }, } diff --git a/packages/interface/src/ism/aggregate.rs b/packages/interface/src/ism/aggregate.rs index 708c166a..f96a4316 100644 --- a/packages/interface/src/ism/aggregate.rs +++ b/packages/interface/src/ism/aggregate.rs @@ -60,7 +60,7 @@ mod test { .wrap(), ); let _checked: QueryMsg = msg_checker( - IsmQueryMsg::VerifyInfo { + IsmQueryMsg::ModulesAndThreshold { message: HexBinary::default(), } .wrap(), diff --git a/packages/interface/src/ism/mod.rs b/packages/interface/src/ism/mod.rs index 13cccbe4..b9c8838b 100644 --- a/packages/interface/src/ism/mod.rs +++ b/packages/interface/src/ism/mod.rs @@ -30,8 +30,8 @@ pub enum IsmQueryMsg { message: HexBinary, }, - #[returns(VerifyInfoResponse)] - VerifyInfo { message: HexBinary }, + #[returns(ModulesAndThresholdResponse)] + ModulesAndThreshold { message: HexBinary }, } impl IsmQueryMsg { @@ -79,9 +79,9 @@ pub struct VerifyResponse { } #[cw_serde] -pub struct VerifyInfoResponse { +pub struct ModulesAndThresholdResponse { pub threshold: u8, - pub validators: Vec, + pub modules: Vec, } #[cw_serde] diff --git a/packages/interface/src/ism/multisig.rs b/packages/interface/src/ism/multisig.rs index dda6d9d8..0b0e5519 100644 --- a/packages/interface/src/ism/multisig.rs +++ b/packages/interface/src/ism/multisig.rs @@ -5,7 +5,7 @@ use crate::ownable::{OwnableMsg, OwnableQueryMsg}; use super::IsmQueryMsg; #[allow(unused_imports)] -use super::{ModuleTypeResponse, VerifyInfoResponse, VerifyResponse}; +use super::{ModuleTypeResponse, ModulesAndThresholdResponse, VerifyResponse}; #[cw_serde] pub struct InstantiateMsg { @@ -70,7 +70,7 @@ mod test { .wrap(), ); let _checked: QueryMsg = msg_checker( - IsmQueryMsg::VerifyInfo { + IsmQueryMsg::ModulesAndThreshold { message: HexBinary::default(), } .wrap(), diff --git a/packages/interface/src/ism/routing.rs b/packages/interface/src/ism/routing.rs index 27c444ff..c2392380 100644 --- a/packages/interface/src/ism/routing.rs +++ b/packages/interface/src/ism/routing.rs @@ -66,7 +66,7 @@ mod test { .wrap(), ); let _checked: QueryMsg = msg_checker( - IsmQueryMsg::VerifyInfo { + IsmQueryMsg::ModulesAndThreshold { message: HexBinary::default(), } .wrap(), From 49131aec8144398a222067403a99ba06fbb123b2 Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Fri, 22 Dec 2023 14:05:32 +0900 Subject: [PATCH 48/53] update function name --- contracts/isms/aggregate/src/lib.rs | 4 ++-- contracts/isms/multisig/src/contract.rs | 2 +- contracts/isms/multisig/src/query.rs | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/isms/aggregate/src/lib.rs b/contracts/isms/aggregate/src/lib.rs index 0df0c158..085a95dd 100644 --- a/contracts/isms/aggregate/src/lib.rs +++ b/contracts/isms/aggregate/src/lib.rs @@ -117,7 +117,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result to_binary(verify(deps, metadata, message)), - ModulesAndThreshold { message } => to_binary(verify_info(deps, message)), + ModulesAndThreshold { message } => to_binary(modules_and_threshold(deps, message)), }, QueryMsg::AggregateIsm(msg) => match msg { @@ -159,7 +159,7 @@ fn verify( }) } -fn verify_info( +fn modules_and_threshold( deps: Deps, _message: HexBinary, ) -> Result { diff --git a/contracts/isms/multisig/src/contract.rs b/contracts/isms/multisig/src/contract.rs index 6bd1d79c..dd52bdcb 100644 --- a/contracts/isms/multisig/src/contract.rs +++ b/contracts/isms/multisig/src/contract.rs @@ -117,7 +117,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result to_binary(query::verify_message(deps, raw_metadata, raw_message)), ModulesAndThreshold { message: raw_message, - } => to_binary(query::get_verify_info(deps, raw_message)), + } => to_binary(query::modules_and_threshold(deps, raw_message)), }, QueryMsg::MultisigIsm(msg) => match msg { MultisigIsmQueryMsg::EnrolledValidators { domain } => to_binary({ diff --git a/contracts/isms/multisig/src/query.rs b/contracts/isms/multisig/src/query.rs index 86460c51..662c78be 100644 --- a/contracts/isms/multisig/src/query.rs +++ b/contracts/isms/multisig/src/query.rs @@ -60,7 +60,7 @@ pub fn verify_message( }) } -pub fn get_verify_info( +pub fn modules_and_threshold( deps: Deps, raw_message: HexBinary, ) -> Result { @@ -87,7 +87,7 @@ mod test { use k256::{ecdsa::SigningKey, elliptic_curve::rand_core::OsRng}; use rstest::rstest; - use super::{get_module_type, get_verify_info, verify_message}; + use super::{get_module_type, modules_and_threshold, verify_message}; #[test] fn test_get_module_type() { @@ -155,7 +155,7 @@ mod test { .unwrap(); THRESHOLD.save(deps.as_mut().storage, 26658, &1u8).unwrap(); - let info = get_verify_info(deps.as_ref(), raw_message).unwrap(); + let info = modules_and_threshold(deps.as_ref(), raw_message).unwrap(); assert_eq!(info.modules, vec![addr]); assert_eq!(info.threshold, 1); From 1d4d758d2fabd19f893ee8ab1caa811e75181829 Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Fri, 29 Dec 2023 00:10:15 +0900 Subject: [PATCH 49/53] reinforce va query --- contracts/core/va/src/contract.rs | 16 +++++++++++++++- packages/interface/src/core/va.rs | 16 ++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/contracts/core/va/src/contract.rs b/contracts/core/va/src/contract.rs index 057cddb1..69bd6eb5 100644 --- a/contracts/core/va/src/contract.rs +++ b/contracts/core/va/src/contract.rs @@ -10,7 +10,7 @@ use hpl_interface::{ mailbox::{self, MailboxQueryMsg}, va::{ ExecuteMsg, GetAnnounceStorageLocationsResponse, GetAnnouncedValidatorsResponse, - InstantiateMsg, QueryMsg, + InstantiateMsg, LocalDomainResponse, MailboxResponse, QueryMsg, }, }, to_binary, @@ -77,6 +77,8 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result to_binary(get_validators(deps)), + QueryMsg::Mailbox {} => to_binary(get_mailbox(deps)), + QueryMsg::LocalDomain {} => to_binary(get_local_domain(deps)), } } @@ -106,6 +108,18 @@ fn get_validators(deps: Deps) -> Result Result { + Ok(MailboxResponse { + mailbox: HexBinary::from(MAILBOX.load(deps.storage)?).to_hex(), + }) +} + +fn get_local_domain(deps: Deps) -> Result { + Ok(LocalDomainResponse { + local_domain: LOCAL_DOMAIN.load(deps.storage)?, + }) +} + fn replay_hash(validator: &HexBinary, storage_location: &str) -> StdResult { Ok(keccak256_hash( [validator.to_vec(), storage_location.as_bytes().to_vec()] diff --git a/packages/interface/src/core/va.rs b/packages/interface/src/core/va.rs index 7a9ad92a..d94661cc 100644 --- a/packages/interface/src/core/va.rs +++ b/packages/interface/src/core/va.rs @@ -24,6 +24,12 @@ pub enum QueryMsg { #[returns(GetAnnouncedValidatorsResponse)] GetAnnouncedValidators {}, + + #[returns(MailboxResponse)] + Mailbox {}, + + #[returns(LocalDomainResponse)] + LocalDomain {}, } #[cw_serde] @@ -35,3 +41,13 @@ pub struct GetAnnounceStorageLocationsResponse { pub struct GetAnnouncedValidatorsResponse { pub validators: Vec, } + +#[cw_serde] +pub struct MailboxResponse { + pub mailbox: String, +} + +#[cw_serde] +pub struct LocalDomainResponse { + pub local_domain: u32, +} From 33b4d842c889abfff5ad9c8ed25b2e3fdaca9b3f Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Fri, 29 Dec 2023 00:16:22 +0900 Subject: [PATCH 50/53] event for routing ism --- contracts/isms/routing/src/contract.rs | 24 ++++++++++++++++++++---- contracts/isms/routing/src/lib.rs | 6 ++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/contracts/isms/routing/src/contract.rs b/contracts/isms/routing/src/contract.rs index 5a7218fb..974e6e7f 100644 --- a/contracts/isms/routing/src/contract.rs +++ b/contracts/isms/routing/src/contract.rs @@ -13,7 +13,7 @@ use hpl_interface::{ }; use hpl_ownable::get_owner; -use crate::{error::ContractError, state::MODULES, CONTRACT_NAME, CONTRACT_VERSION}; +use crate::{error::ContractError, new_event, state::MODULES, CONTRACT_NAME, CONTRACT_VERSION}; #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( @@ -65,7 +65,12 @@ pub fn execute( &deps.api.addr_validate(&ism.address)?, )?; - Ok(Response::default()) + Ok(Response::default().add_event( + new_event("set") + .add_attribute("sender", info.sender) + .add_attribute("domain", ism.domain.to_string()) + .add_attribute("ism", ism.address), + )) } Unset { domains } => { ensure_eq!( @@ -74,11 +79,22 @@ pub fn execute( ContractError::Unauthorized {} ); - for domain in domains { + for domain in domains.clone() { MODULES.remove(deps.storage, domain); } - Ok(Response::default()) + Ok(Response::default().add_event( + new_event("unset") + .add_attribute("sender", info.sender) + .add_attribute( + "domains", + domains + .into_iter() + .map(|v| v.to_string()) + .collect::>() + .join(","), + ), + )) } } } diff --git a/contracts/isms/routing/src/lib.rs b/contracts/isms/routing/src/lib.rs index 2cf2df8e..23d841b0 100644 --- a/contracts/isms/routing/src/lib.rs +++ b/contracts/isms/routing/src/lib.rs @@ -5,8 +5,14 @@ pub mod state; #[cfg(test)] mod tests; +use cosmwasm_std::Event; + pub use crate::error::ContractError; +fn new_event(name: &str) -> Event { + Event::new(format!("hpl_ism_routing::{}", name)) +} + // version info for migration info pub const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); From 2137cf87e669632332f6559842cc4169d8166c1c Mon Sep 17 00:00:00 2001 From: byeongsu-hong Date: Fri, 29 Dec 2023 00:20:43 +0900 Subject: [PATCH 51/53] cleanup following 985d98e --- contracts/igps/core/src/contract.rs | 7 ++----- contracts/igps/core/src/tests/contract.rs | 6 +++--- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/contracts/igps/core/src/contract.rs b/contracts/igps/core/src/contract.rs index 16c9caea..225b794f 100644 --- a/contracts/igps/core/src/contract.rs +++ b/contracts/igps/core/src/contract.rs @@ -1,19 +1,16 @@ #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - ensure, Binary, Deps, DepsMut, Empty, Env, Event, MessageInfo, QueryRequest, QueryResponse, - Response, + ensure, Deps, DepsMut, Empty, Env, Event, MessageInfo, QueryResponse, Response, }; use hpl_interface::hook::HookQueryMsg; use hpl_interface::igp::core::{ExecuteMsg, IgpQueryMsg, InstantiateMsg, QueryMsg}; use hpl_interface::igp::oracle::IgpGasOracleQueryMsg; use hpl_interface::to_binary; -use prost::Message; use crate::{ - proto, ContractError, BENEFICIARY, CONTRACT_NAME, CONTRACT_VERSION, DEFAULT_GAS_USAGE, - GAS_TOKEN, HRP, + ContractError, BENEFICIARY, CONTRACT_NAME, CONTRACT_VERSION, DEFAULT_GAS_USAGE, GAS_TOKEN, HRP, }; fn new_event(name: &str) -> Event { diff --git a/contracts/igps/core/src/tests/contract.rs b/contracts/igps/core/src/tests/contract.rs index ddf0caaa..4b81d8db 100644 --- a/contracts/igps/core/src/tests/contract.rs +++ b/contracts/igps/core/src/tests/contract.rs @@ -1,8 +1,8 @@ use cosmwasm_std::{ coin, from_json, - testing::{mock_dependencies, mock_dependencies_with_balance, mock_env, mock_info}, - to_json_binary, Addr, BankMsg, Coin, ContractResult, Empty, HexBinary, Order, QuerierResult, - QueryRequest, StdResult, SubMsg, SystemResult, Uint128, Uint256, WasmQuery, + testing::{mock_dependencies, mock_env, mock_info}, + to_json_binary, Addr, BankMsg, Coin, ContractResult, HexBinary, Order, QuerierResult, + StdResult, SubMsg, SystemResult, Uint128, Uint256, WasmQuery, }; use hpl_interface::{ igp::{ From 8266fb9d3217158c5e8518942ea846d29740af15 Mon Sep 17 00:00:00 2001 From: eddy Date: Thu, 4 Jan 2024 13:43:35 +0900 Subject: [PATCH 52/53] feat: add SimulateVerify --- contracts/isms/aggregate/src/lib.rs | 5 +++++ contracts/isms/multisig/src/contract.rs | 8 +++++++- contracts/isms/routing/src/contract.rs | 13 +++++++++++++ packages/interface/src/ism/aggregate.rs | 21 +++++++++++++++++++-- packages/interface/src/ism/mod.rs | 8 ++++++++ packages/interface/src/ism/multisig.rs | 12 +++++++++++- packages/interface/src/ism/routing.rs | 23 ++++++++++++++++++++--- 7 files changed, 83 insertions(+), 7 deletions(-) diff --git a/contracts/isms/aggregate/src/lib.rs b/contracts/isms/aggregate/src/lib.rs index 085a95dd..7da45373 100644 --- a/contracts/isms/aggregate/src/lib.rs +++ b/contracts/isms/aggregate/src/lib.rs @@ -100,6 +100,11 @@ pub fn execute( Ok(Response::new() .add_event(new_event("set_isms").add_attribute("isms", isms.join(",")))) } + ExecuteMsg::SimulateVerify { metadata, message } => { + verify(deps.as_ref(), metadata, message)?; + + Ok(Response::new()) + } } } diff --git a/contracts/isms/multisig/src/contract.rs b/contracts/isms/multisig/src/contract.rs index dd52bdcb..03016450 100644 --- a/contracts/isms/multisig/src/contract.rs +++ b/contracts/isms/multisig/src/contract.rs @@ -94,10 +94,16 @@ pub fn execute( Ok(Response::new().add_event( Event::new("ism_multisig_unset_domain") - .add_attribute("sener", info.sender) + .add_attribute("sender", info.sender) .add_attribute("domain", domain.to_string()), )) } + + SimulateVerify { metadata, message } => { + crate::query::verify_message(deps.as_ref(), metadata, message)?; + + Ok(Response::new()) + } } } diff --git a/contracts/isms/routing/src/contract.rs b/contracts/isms/routing/src/contract.rs index 974e6e7f..b38c2878 100644 --- a/contracts/isms/routing/src/contract.rs +++ b/contracts/isms/routing/src/contract.rs @@ -96,6 +96,19 @@ pub fn execute( ), )) } + SimulateVerify { metadata, message } => { + let decoded = Message::from(message.clone()); + + let ism = MODULES + .may_load(deps.storage, decoded.origin_domain)? + .ok_or(ContractError::RouteNotFound {})?; + + let _: VerifyResponse = deps + .querier + .query_wasm_smart(ism, &IsmQueryMsg::Verify { metadata, message }.wrap())?; + + Ok(Response::new()) + } } } diff --git a/packages/interface/src/ism/aggregate.rs b/packages/interface/src/ism/aggregate.rs index f96a4316..144f849e 100644 --- a/packages/interface/src/ism/aggregate.rs +++ b/packages/interface/src/ism/aggregate.rs @@ -1,4 +1,5 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; +use cosmwasm_std::HexBinary; use crate::ownable::{OwnableMsg, OwnableQueryMsg}; @@ -15,7 +16,15 @@ pub struct InstantiateMsg { pub enum ExecuteMsg { Ownable(OwnableMsg), - SetIsms { isms: Vec, threshold: u8 }, + SimulateVerify { + metadata: HexBinary, + message: HexBinary, + }, + + SetIsms { + isms: Vec, + threshold: u8, + }, } #[cw_serde] @@ -47,10 +56,18 @@ mod test { use cosmwasm_std::HexBinary; use super::*; - use crate::{ism::IsmQueryMsg, msg_checker}; + use crate::{ + ism::{ExpectedIsmMsg, IsmQueryMsg}, + msg_checker, + }; #[test] fn test_ism_interface() { + let _checked: ExecuteMsg = msg_checker(ExpectedIsmMsg::SimulateVerify { + metadata: HexBinary::default(), + message: HexBinary::default(), + }); + let _checked: QueryMsg = msg_checker(IsmQueryMsg::ModuleType {}.wrap()); let _checked: QueryMsg = msg_checker( IsmQueryMsg::Verify { diff --git a/packages/interface/src/ism/mod.rs b/packages/interface/src/ism/mod.rs index b9c8838b..795350ea 100644 --- a/packages/interface/src/ism/mod.rs +++ b/packages/interface/src/ism/mod.rs @@ -18,6 +18,14 @@ pub enum IsmType { CcipRead = 7, } +#[cw_serde] +pub enum ExpectedIsmMsg { + SimulateVerify { + metadata: HexBinary, + message: HexBinary, + }, +} + #[cw_serde] #[derive(QueryResponses)] pub enum IsmQueryMsg { diff --git a/packages/interface/src/ism/multisig.rs b/packages/interface/src/ism/multisig.rs index 0b0e5519..77f7253d 100644 --- a/packages/interface/src/ism/multisig.rs +++ b/packages/interface/src/ism/multisig.rs @@ -22,6 +22,11 @@ pub struct ValidatorSet { pub enum ExecuteMsg { Ownable(OwnableMsg), + SimulateVerify { + metadata: HexBinary, + message: HexBinary, + }, + SetValidators { domain: u32, threshold: u8, @@ -57,10 +62,15 @@ pub struct EnrolledValidatorsResponse { #[cfg(test)] mod test { use super::*; - use crate::msg_checker; + use crate::{ism::ExpectedIsmMsg, msg_checker}; #[test] fn test_ism_interface() { + let _checked: ExecuteMsg = msg_checker(ExpectedIsmMsg::SimulateVerify { + metadata: HexBinary::default(), + message: HexBinary::default(), + }); + let _checked: QueryMsg = msg_checker(IsmQueryMsg::ModuleType {}.wrap()); let _checked: QueryMsg = msg_checker( IsmQueryMsg::Verify { diff --git a/packages/interface/src/ism/routing.rs b/packages/interface/src/ism/routing.rs index c2392380..d722fb97 100644 --- a/packages/interface/src/ism/routing.rs +++ b/packages/interface/src/ism/routing.rs @@ -23,8 +23,17 @@ pub struct InstantiateMsg { pub enum ExecuteMsg { Ownable(OwnableMsg), - Set { ism: IsmSet }, - Unset { domains: Vec }, + SimulateVerify { + metadata: HexBinary, + message: HexBinary, + }, + + Set { + ism: IsmSet, + }, + Unset { + domains: Vec, + }, } #[cw_serde] @@ -53,10 +62,18 @@ mod test { use cosmwasm_std::HexBinary; use super::*; - use crate::{ism::IsmQueryMsg, msg_checker}; + use crate::{ + ism::{ExpectedIsmMsg, IsmQueryMsg}, + msg_checker, + }; #[test] fn test_ism_interface() { + let _checked: ExecuteMsg = msg_checker(ExpectedIsmMsg::SimulateVerify { + metadata: HexBinary::default(), + message: HexBinary::default(), + }); + let _checked: QueryMsg = msg_checker(IsmQueryMsg::ModuleType {}.wrap()); let _checked: QueryMsg = msg_checker( IsmQueryMsg::Verify { From f1d401afddc8956a7264c92ddf19237bb573dbc5 Mon Sep 17 00:00:00 2001 From: eddy Date: Thu, 4 Jan 2024 13:44:09 +0900 Subject: [PATCH 53/53] rc7 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 643fe642..273f52bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ panic = "abort" rpath = false [workspace.package] -version = "0.0.6-rc6" +version = "0.0.6-rc7" authors = [ "byeongsu-hong ", "Eric ",