From 56b79b8cacd96b7ac0498fe2a77598dec7a51537 Mon Sep 17 00:00:00 2001 From: Christian Borst Date: Wed, 17 Apr 2024 14:28:18 -0400 Subject: [PATCH] Add nativedex governance proposals to DEX test --- .../test_runner/src/bootstrapping.rs | 7 + .../test_runner/src/dex_utils.rs | 97 +++- .../test_runner/src/tests/dex.rs | 483 +++++++++++++++++- .../test_runner/src/type_urls.rs | 13 + 4 files changed, 576 insertions(+), 24 deletions(-) diff --git a/integration_tests/test_runner/src/bootstrapping.rs b/integration_tests/test_runner/src/bootstrapping.rs index 84e03a57..3b7627a9 100644 --- a/integration_tests/test_runner/src/bootstrapping.rs +++ b/integration_tests/test_runner/src/bootstrapping.rs @@ -277,6 +277,7 @@ pub struct DexAddresses { pub query: EthAddress, pub impact: EthAddress, pub policy: EthAddress, + pub upgrade: EthAddress, } /// Parses the DEX contract addresses from the file created @@ -290,6 +291,7 @@ pub fn parse_dex_contract_addresses() -> DexAddresses { let mut query: EthAddress = EthAddress::default(); let mut impact: EthAddress = EthAddress::default(); let mut policy: EthAddress = EthAddress::default(); + let mut upgrade: EthAddress = EthAddress::default(); for line in output.lines() { if line.contains("CrocSwapDex deployed at Address -") { let address_string = line.split('-').last().unwrap(); @@ -307,6 +309,10 @@ pub fn parse_dex_contract_addresses() -> DexAddresses { let address_string = line.split('-').last().unwrap(); policy = address_string.trim().parse().unwrap(); info!("found policy address it is {}", address_string); + } else if line.contains("ColdPathUpgrade deployed at Address -") { + let address_string = line.split('-').last().unwrap(); + upgrade = address_string.trim().parse().unwrap(); + info!("found upgrade address it is {}", address_string); } } DexAddresses { @@ -314,6 +320,7 @@ pub fn parse_dex_contract_addresses() -> DexAddresses { query, impact, policy, + upgrade, } } diff --git a/integration_tests/test_runner/src/dex_utils.rs b/integration_tests/test_runner/src/dex_utils.rs index 9d804ec4..2ee86229 100644 --- a/integration_tests/test_runner/src/dex_utils.rs +++ b/integration_tests/test_runner/src/dex_utils.rs @@ -12,6 +12,24 @@ use web30::{ use crate::utils::OPERATION_TIMEOUT; +// Callpath Indices +pub const BOOT_PATH: u16 = 0; +pub const HOT_PROXY: u16 = 1; +pub const WARM_PATH: u16 = 2; +pub const COLD_PATH: u16 = 3; +pub const LONG_PATH: u16 = 4; +pub const MICRO_PATHS: u16 = 5; +pub const KNOCKOUT_LIQ_PATH: u16 = 7; +pub const KNOCKOUT_FLAG_PATH: u16 = 3500; +pub const SAFE_MODE_PATH: u16 = 9999; + +lazy_static! { + pub static ref MIN_TICK: Int256 = Int256::from(-665454i64); + pub static ref MAX_TICK: Int256 = Int256::from(831818i64); + pub static ref MIN_PRICE: Uint256 = Uint256::from(65538u32); + pub static ref MAX_PRICE: Uint256 = Uint256::from(21267430153580247136652501917186561137u128); +} + // ABI result parsing notes: // a struct with static fields is returned like a static tuple, (static fields have no tail) // enc(X1, ..., Xk) = head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(k)) = enc(X1) ... enc(Xk) @@ -464,6 +482,7 @@ pub async fn croc_query_conc_rewards( }) } +/// Specifies a swap() call on the DEX contract #[derive(Debug, Clone)] pub struct SwapArgs { pub base: EthAddress, @@ -524,11 +543,14 @@ pub async fn dex_swap( web30.wait_for_transaction(txhash, timeout, None).await } +/// Specifies a userCmd call to be made on the DEX contract #[derive(Debug, Clone)] pub struct UserCmdArgs { pub callpath: u16, pub cmd: Vec, } + +/// Calls any userCmd on the DEX contract pub async fn dex_user_cmd( web30: &Web3, dex_contract: EthAddress, @@ -555,11 +577,16 @@ pub async fn dex_user_cmd( .await?; web30.wait_for_transaction(txhash, timeout, None).await } + +/// Specifies a protocolCmd call to be made on the DEX contract +/// If CrocPolicy is set up then this call will fail #[derive(Debug, Clone)] pub struct ProtocolCmdArgs { pub callpath: u16, pub cmd: Vec, + pub sudo: bool, } +/// Calls any protocolCmd on the DEX contract pub async fn dex_protocol_cmd( web30: &Web3, dex_contract: EthAddress, @@ -573,8 +600,8 @@ pub async fn dex_protocol_cmd( let cmd = clarity::abi::encode_tokens(&cmd_args.cmd); let payload = clarity::abi::encode_call( - "protocolCmd(uint16,bytes)", - &[cmd_args.callpath.into(), cmd.into()], + "protocolCmd(uint16,bytes,bool)", + &[cmd_args.callpath.into(), cmd.into(), cmd_args.sudo.into()], )?; let native_in = native_in.unwrap_or(0u8.into()); let txhash = web30 @@ -586,3 +613,69 @@ pub async fn dex_protocol_cmd( .await?; web30.wait_for_transaction(txhash, timeout, None).await } + +/// Conveniently wraps dex_protocol_cmd() to invoke an authority transfer, useful when setting up CrocPolicy +pub async fn dex_authority_transfer( + web30: &Web3, + dex_contract: EthAddress, + new_auth: EthAddress, + wallet: PrivateKey, + timeout: Option, +) -> Result { + let code: Uint256 = 20u8.into(); + + // ABI: authorityTransfer (uint8 cmd_code, address auth) + let cmd_args = vec![code.into(), new_auth.into()]; + + dex_protocol_cmd( + web30, + dex_contract, + wallet, + ProtocolCmdArgs { + callpath: COLD_PATH, + cmd: cmd_args, + sudo: true, + }, + None, + timeout, + ) + .await +} + +pub async fn dex_query_safe_mode( + web30: &Web3, + dex_contract: EthAddress, + caller: Option, +) -> Result { + // ABI: bool internal inSafeMode_ + let caller = caller.unwrap_or(dex_contract); + let payload = clarity::abi::encode_call("safeMode()", &[])?; + + let query_res = web30 + .simulate_transaction( + TransactionRequest::quick_tx(caller, dex_contract, payload), + None, + ) + .await?; + + Ok(query_res[31] > 0u8) +} + +pub async fn dex_query_authority( + web30: &Web3, + dex_contract: EthAddress, + caller: Option, +) -> Result { + // ABI: bool internal inSafeMode_ + let caller = caller.unwrap_or(dex_contract); + let payload = clarity::abi::encode_call("authority()", &[])?; + + let query_res = web30 + .simulate_transaction( + TransactionRequest::quick_tx(caller, dex_contract, payload), + None, + ) + .await?; + + Ok(EthAddress::from_slice(&query_res[12..32])?) +} diff --git a/integration_tests/test_runner/src/tests/dex.rs b/integration_tests/test_runner/src/tests/dex.rs index bfb3d1dd..3e6d68b2 100644 --- a/integration_tests/test_runner/src/tests/dex.rs +++ b/integration_tests/test_runner/src/tests/dex.rs @@ -2,13 +2,34 @@ use crate::bootstrapping::DexAddresses; use crate::dex_utils::{ - croc_query_curve_tick, croc_query_dex, croc_query_pool_params, croc_query_range_position, - dex_user_cmd, UserCmdArgs, + croc_query_curve_tick, croc_query_dex, croc_query_pool_params, croc_query_price, + croc_query_range_position, dex_authority_transfer, dex_protocol_cmd, dex_query_authority, + dex_query_safe_mode, dex_swap, dex_user_cmd, ProtocolCmdArgs, SwapArgs, UserCmdArgs, COLD_PATH, + WARM_PATH, }; -use crate::utils::{EthermintUserKey, ValidatorKeys, OPERATION_TIMEOUT}; -use clarity::{Address as EthAddress, Uint256}; -use deep_space::Contact; +use crate::type_urls::{ + COLLECT_TREASURY_PROPOSAL_TYPE_URL, HOT_PATH_OPEN_PROPOSAL_TYPE_URL, + SET_SAFE_MODE_PROPOSAL_TYPE_URL, SET_TREASURY_PROPOSAL_TYPE_URL, + UPGRADE_PROXY_PROPOSAL_TYPE_URL, +}; +use crate::utils::{ + encode_any, one_atom, one_eth, vote_yes_on_proposals, wait_for_proposals_to_execute, + EthermintUserKey, ValidatorKeys, MINER_ETH_ADDRESS, MINER_PRIVATE_KEY, OPERATION_TIMEOUT, + STAKING_TOKEN, +}; +use althea_proto::althea::nativedex::v1::{ + CollectTreasuryMetadata, CollectTreasuryProposal, HotPathOpenMetadata, HotPathOpenProposal, + SetSafeModeMetadata, SetSafeModeProposal, SetTreasuryMetadata, SetTreasuryProposal, + TransferGovernanceMetadata, TransferGovernanceProposal, UpgradeProxyMetadata, + UpgradeProxyProposal, +}; +use althea_proto::cosmos_sdk_proto::cosmos::params::v1beta1::{ + ParamChange, ParameterChangeProposal, +}; +use clarity::{Address as EthAddress, PrivateKey, Uint256}; +use deep_space::{Coin, Contact}; use num::ToPrimitive; +use tokio::time::sleep; use web30::client::Web3; use web30::jsonrpc::error::Web3Error; use web30::types::TransactionResponse; @@ -18,9 +39,9 @@ lazy_static! { } pub async fn dex_test( - _contact: &Contact, + contact: &Contact, web3: &Web3, - _validator_keys: Vec, + validator_keys: Vec, evm_user_keys: Vec, erc20_contracts: Vec, dex_contracts: DexAddresses, @@ -41,6 +62,14 @@ pub async fn dex_test( (tokens[1], tokens[0]) }; + let safe_mode = dex_query_safe_mode(web3, dex, Some(evm_user.eth_address)) + .await + .expect("Unable to query safe mode"); + if safe_mode { + info!("Dex is in safe mode, disabling safe mode"); + submit_and_pass_safe_mode_proposal(contact, &validator_keys, false, true).await; + } + let pool = croc_query_pool_params( web3, dex_contracts.query, @@ -51,12 +80,32 @@ pub async fn dex_test( ) .await; if pool.is_ok() && pool.unwrap().tick_size != 0 { - info!("Pool already created"); + info!("Pool already created, approving use of base and quote tokens"); + web3.approve_erc20_transfers(pool_base, evm_privkey, dex, Some(OPERATION_TIMEOUT), vec![]) + .await + .expect("Could not approve base token"); + web3.approve_erc20_transfers( + pool_quote, + evm_privkey, + dex, + Some(OPERATION_TIMEOUT), + vec![], + ) + .await + .expect("Could not approve base token"); } else { info!("Creating pool"); - init_pool(web3, evm_user, dex_contracts.dex, pool_base, pool_quote) - .await - .expect("Could not create pool"); + init_pool( + web3, + evm_user.eth_privkey, + dex_contracts.dex, + pool_base, + pool_quote, + None, + None, + ) + .await + .expect("Could not create pool"); } let tick = croc_query_curve_tick( @@ -70,14 +119,27 @@ pub async fn dex_test( .await .expect("Could not get curve tick for pool"); + let price = croc_query_price( + web3, + dex_contracts.query, + Some(evm_user.eth_address), + pool_base, + pool_quote, + *POOL_IDX, + ) + .await + .expect("Could not get curve price for pool"); + let bid_tick = tick - 75u8.into(); let ask_tick = tick + 75u8.into(); + let min_price = price - 1000u128.into(); + let max_price = price + 1000u128.into(); // uint8 code, address base, address quote, uint256 poolIdx, // int24 bidTick, int24 askTick, uint128 liq, // uint128 limitLower, uint128 limitHigher, // uint8 reserveFlags, address lpConduit let mint_ranged_pos_args = UserCmdArgs { - callpath: 2, // Warm Path index + callpath: WARM_PATH, // Warm Path index cmd: vec![ Uint256::from(11u8).into(), // Mint Ranged Liq in base token code pool_base.into(), // base @@ -86,12 +148,13 @@ pub async fn dex_test( bid_tick.into(), // bid (lower) tick ask_tick.into(), // ask (upper) tick 1_000_000u128.into(), // liq (in base token) - 18446744073u128.into(), // limitLower - 18446744073709000u128.into(), // limitHigher + min_price.into(), // limitLower + max_price.into(), // limitHigher Uint256::from(0u8).into(), // reserveFlags EthAddress::default().into(), // lpConduit ], }; + info!("Minting position in pool: {mint_ranged_pos_args:?}"); dex_user_cmd( web3, dex_contracts.dex, @@ -119,38 +182,142 @@ pub async fn dex_test( info!("Range position: {:?}", range_pos); info!("Successfully minted position"); + + let current_auth = dex_query_authority(web3, dex_contracts.dex, Some(evm_user.eth_address)) + .await + .expect("Unable to query current authority"); + if current_auth != dex_contracts.policy { + // Transfer authority to the CrocPolicy contract, so nativedex governance can manage the DEX + dex_authority_transfer( + web3, + dex_contracts.dex, + dex_contracts.policy, + *MINER_PRIVATE_KEY, + Some(OPERATION_TIMEOUT), + ) + .await + .expect("Unable to transfer dex ownership to the CrocPolicy contract"); + info!("Transferred DEX authority_ address to CrocPolicy contract for nativedex governance control"); + } + + info!("Attempt to steal DEX control away from CrocPolicy contract"); + dex_protocol_cmd( + web3, + dex_contracts.dex, + *MINER_PRIVATE_KEY, + ProtocolCmdArgs { + callpath: COLD_PATH, + cmd: vec![Uint256::from(20u16).into(), (*MINER_ETH_ADDRESS).into()], + sudo: true, + }, + None, + None, + ) + .await + .expect_err("Miner should not be able to take control away from CrocPolicy"); + // Submit and pass ParamChangeProposal to use these contracts with the nativedex module + submit_and_pass_nativedex_config_proposal( + contact, + &validator_keys, + dex_contracts.dex, + dex_contracts.policy, + ) + .await; + + info!("Testing safe mode"); + submit_and_pass_safe_mode_proposal(contact, &validator_keys, true, false).await; + + let test_swap = dex_swap( + web3, + dex_contracts.dex, + evm_user_keys.first().unwrap().eth_privkey, + SwapArgs { + base: pool_base, + quote: pool_quote, + pool_idx: *POOL_IDX, + is_buy: false, + in_base_qty: true, + qty: one_eth(), + tip: 0u16, + limit_price: 18446744073u128.into(), + min_out: 0u8.into(), + reserve_flags: 0u8, + }, + None, + Some(OPERATION_TIMEOUT), + ) + .await; + test_swap.expect_err("Swap should fail in safe mode"); + + info!("Disabling safe mode"); + submit_and_pass_safe_mode_proposal(contact, &validator_keys, false, true).await; + + info!("Testing upgrade proposal"); + submit_and_pass_upgrade_proxy_proposal(contact, &validator_keys, 33, dex_contracts.upgrade) + .await; + + // Here we init a new pool using the callpath instaleld in the upgrade proposal (index 33). We want to use the same tokens, so we need to use a different + // pool index + init_pool( + web3, + evm_privkey, + dex_contracts.dex, + pool_base, + pool_quote, + Some(36001u64.into()), + Some(33), + ) + .await + .expect("Could not create pool"); + + // Now we query the pool params to ensure it was set up correctly, we use the new pool index to locate the right pool + let params = croc_query_pool_params( + web3, + dex_contracts.query, + Some(evm_user.eth_address), + pool_base, + pool_quote, + 36001u64.into(), + ) + .await + .expect("Could not query pool"); + assert!(params.tick_size != 0, "Pool not created"); } pub async fn init_pool( web3: &Web3, - evm_user: &EthermintUserKey, + evm_privkey: PrivateKey, dex: EthAddress, pool_base: EthAddress, pool_quote: EthAddress, + template_override: Option, + callpath_override: Option, ) -> Result { + let callpath: u16 = callpath_override.unwrap_or(COLD_PATH); + let template = template_override.unwrap_or(*POOL_IDX); if pool_base >= pool_quote { panic!("Base token must be lexically less than quote token"); } - let evm_privkey = evm_user.eth_privkey; - let price: Uint256 = (f64::sqrt(10f64.powf(-12.0)) * 2f64.powf(64.0)) .round() .to_u128() .unwrap() .into(); let init_pool_args = UserCmdArgs { - callpath: 3, // Cold Path index + callpath, cmd: vec![ 71u8.into(), // Init pool code pool_base.into(), pool_quote.into(), - (*POOL_IDX).into(), + template.into(), price.into(), ], }; - web3.approve_erc20_transfers(pool_base, evm_privkey, dex, Some(OPERATION_TIMEOUT), vec![]) - .await - .expect("Could not approve base token"); + if pool_base != EthAddress::default() { + web3.approve_erc20_transfers(pool_base, evm_privkey, dex, Some(OPERATION_TIMEOUT), vec![]) + .await + .expect("Could not approve base token"); + } web3.approve_erc20_transfers( pool_quote, evm_privkey, @@ -163,3 +330,275 @@ pub async fn init_pool( dex_user_cmd(web3, dex, evm_privkey, init_pool_args, None, None).await } + +/// Configures the nativedex module to use the given addresses as the CrocSwapDEX and CrocPolicy when executing gov proposals +pub async fn submit_and_pass_nativedex_config_proposal( + contact: &Contact, + keys: &[ValidatorKeys], + dex_contract: EthAddress, + policy_contract: EthAddress, +) { + let deposit = Coin { + amount: one_atom() * 100u8.into(), + denom: STAKING_TOKEN.clone(), + }; + let fee = Coin { + amount: 0u8.into(), + denom: STAKING_TOKEN.clone(), + }; + let res = contact + .submit_parameter_change_proposal( + ParameterChangeProposal { + title: "Configure nativedex module".to_string(), + description: "Configure nativedex module".to_string(), + changes: vec![ + // subspace defined at x/nativedex/types/keys.go + // keys defined at x/nativedex/types/genesis.go + ParamChange { + subspace: "nativedex".to_string(), + key: "VerifiedNativeDexAddress".to_string(), + value: format!("\"{}\"", dex_contract), + }, + ParamChange { + subspace: "nativedex".to_string(), + key: "VerifiedCrocPolicyAddress".to_string(), + value: format!("\"{}\"", policy_contract), + }, + ], + }, + deposit, + fee, + keys[0].validator_key, + Some(OPERATION_TIMEOUT), + ) + .await; + vote_yes_on_proposals(contact, keys, None).await; + wait_for_proposals_to_execute(contact).await; + info!("Gov proposal executed with {:?}", res); +} + +pub async fn submit_and_pass_upgrade_proxy_proposal( + contact: &Contact, + keys: &[ValidatorKeys], + callpath: u64, + contract_address: EthAddress, +) { + let deposit = Coin { + amount: one_atom() * 100u8.into(), + denom: STAKING_TOKEN.clone(), + }; + let fee = Coin { + amount: 0u8.into(), + denom: STAKING_TOKEN.clone(), + }; + + let proposal = UpgradeProxyProposal { + title: "Upgrade Proposal".to_string(), + description: "Upgrade proposal".to_string(), + metadata: Some(UpgradeProxyMetadata { + callpath_address: contract_address.to_string(), + callpath_index: callpath, + }), + }; + let any = encode_any(proposal, UPGRADE_PROXY_PROPOSAL_TYPE_URL.to_string()); + let res = contact + .create_gov_proposal( + any, + deposit, + fee, + keys.first().unwrap().validator_key, + Some(OPERATION_TIMEOUT), + ) + .await; + vote_yes_on_proposals(contact, keys, None).await; + wait_for_proposals_to_execute(contact).await; + info!("Gov proposal executed with {:?}", res); +} + +pub async fn submit_and_pass_collect_treasury_proposal( + contact: &Contact, + keys: &[ValidatorKeys], + token_address: EthAddress, + in_safe_mode: bool, +) { + let deposit = Coin { + amount: one_atom() * 100u8.into(), + denom: STAKING_TOKEN.clone(), + }; + let fee = Coin { + amount: 0u8.into(), + denom: STAKING_TOKEN.clone(), + }; + + let proposal = CollectTreasuryProposal { + title: "Collect Treasury Proposal".to_string(), + description: "Collect Treasury proposal".to_string(), + metadata: Some(CollectTreasuryMetadata { + token_address: token_address.to_string(), + }), + in_safe_mode, + }; + let any = encode_any(proposal, COLLECT_TREASURY_PROPOSAL_TYPE_URL.to_string()); + let res = contact + .create_gov_proposal( + any, + deposit, + fee, + keys.first().unwrap().validator_key, + Some(OPERATION_TIMEOUT), + ) + .await; + vote_yes_on_proposals(contact, keys, None).await; + wait_for_proposals_to_execute(contact).await; + info!("Gov proposal executed with {:?}", res); +} + +pub async fn submit_and_pass_set_treasury_proposal( + contact: &Contact, + keys: &[ValidatorKeys], + treasury_address: EthAddress, + in_safe_mode: bool, +) { + let deposit = Coin { + amount: one_atom() * 100u8.into(), + denom: STAKING_TOKEN.clone(), + }; + let fee = Coin { + amount: 0u8.into(), + denom: STAKING_TOKEN.clone(), + }; + + let proposal = SetTreasuryProposal { + title: "Set Treasury Proposal".to_string(), + description: "Set Treasury proposal".to_string(), + metadata: Some(SetTreasuryMetadata { + treasury_address: treasury_address.to_string(), + }), + in_safe_mode, + }; + let any = encode_any(proposal, SET_TREASURY_PROPOSAL_TYPE_URL.to_string()); + let res = contact + .create_gov_proposal( + any, + deposit, + fee, + keys.first().unwrap().validator_key, + Some(OPERATION_TIMEOUT), + ) + .await; + vote_yes_on_proposals(contact, keys, None).await; + wait_for_proposals_to_execute(contact).await; + info!("Gov proposal executed with {:?}", res); +} + +pub async fn submit_and_pass_hot_path_open_proposal( + contact: &Contact, + keys: &[ValidatorKeys], + hot_path_open: bool, + in_safe_mode: bool, +) { + let deposit = Coin { + amount: one_atom() * 100u8.into(), + denom: STAKING_TOKEN.clone(), + }; + let fee = Coin { + amount: 0u8.into(), + denom: STAKING_TOKEN.clone(), + }; + + let proposal = HotPathOpenProposal { + title: "Hot Path Open Proposal".to_string(), + description: "Hot Path Open proposal".to_string(), + metadata: Some(HotPathOpenMetadata { + open: hot_path_open, + }), + in_safe_mode, + }; + let any = encode_any(proposal, HOT_PATH_OPEN_PROPOSAL_TYPE_URL.to_string()); + let res = contact + .create_gov_proposal( + any, + deposit, + fee, + keys.first().unwrap().validator_key, + Some(OPERATION_TIMEOUT), + ) + .await; + vote_yes_on_proposals(contact, keys, None).await; + wait_for_proposals_to_execute(contact).await; + info!("Gov proposal executed with {:?}", res); +} + +pub async fn submit_and_pass_safe_mode_proposal( + contact: &Contact, + keys: &[ValidatorKeys], + lock_dex: bool, + in_safe_mode: bool, +) { + let deposit = Coin { + amount: one_atom() * 100u8.into(), + denom: STAKING_TOKEN.clone(), + }; + let fee = Coin { + amount: 0u8.into(), + denom: STAKING_TOKEN.clone(), + }; + + let proposal = SetSafeModeProposal { + title: "Set safe mode".to_string(), + description: "Set safe mode".to_string(), + metadata: Some(SetSafeModeMetadata { lock_dex }), + in_safe_mode, + }; + let any = encode_any(proposal, SET_SAFE_MODE_PROPOSAL_TYPE_URL.to_string()); + let res = contact + .create_gov_proposal( + any, + deposit, + fee, + keys.first().unwrap().validator_key, + Some(OPERATION_TIMEOUT), + ) + .await; + vote_yes_on_proposals(contact, keys, None).await; + wait_for_proposals_to_execute(contact).await; + info!("Gov proposal executed with {:?}", res); +} + +pub async fn submit_and_pass_transfer_governance_proposal( + contact: &Contact, + keys: &[ValidatorKeys], + ops: EthAddress, + emergency: EthAddress, +) { + let deposit = Coin { + amount: one_atom() * 100u8.into(), + denom: STAKING_TOKEN.clone(), + }; + let fee = Coin { + amount: 0u8.into(), + denom: STAKING_TOKEN.clone(), + }; + + let proposal = TransferGovernanceProposal { + title: "Transfer Governance Proposal".to_string(), + description: "Transfer Governance proposal".to_string(), + metadata: Some(TransferGovernanceMetadata { + ops: ops.to_string(), + emergency: emergency.to_string(), + }), + }; + let any = encode_any(proposal, HOT_PATH_OPEN_PROPOSAL_TYPE_URL.to_string()); + let res = contact + .create_gov_proposal( + any, + deposit, + fee, + keys.first().unwrap().validator_key, + Some(OPERATION_TIMEOUT), + ) + .await; + vote_yes_on_proposals(contact, keys, None).await; + wait_for_proposals_to_execute(contact).await; + info!("Gov proposal executed with {:?}", res); +} diff --git a/integration_tests/test_runner/src/type_urls.rs b/integration_tests/test_runner/src/type_urls.rs index 14846d33..f5f43fc9 100644 --- a/integration_tests/test_runner/src/type_urls.rs +++ b/integration_tests/test_runner/src/type_urls.rs @@ -1,3 +1,5 @@ +// Althea Msg Types + // microtx pub const MSG_MICROTX_TYPE_URL: &str = "/althea.microtx.v1.MsgMicrotx"; pub const MSG_LIQUIFY_TYPE_URL: &str = "/althea.microtx.v1.MsgLiquify"; @@ -8,6 +10,17 @@ pub const MSG_GRANT_TYPE_URL: &str = "/cosmos.authz.v1beta1.MsgGrant"; pub const GRANT_TYPE_URL: &str = "/cosmos.authz.v1beta1.Grant"; pub const MSG_EXEC_TYPE_URL: &str = "/cosmos.authz.v1beta1.MsgExec"; +// Althea Proposal Types +pub const UPGRADE_PROXY_PROPOSAL_TYPE_URL: &str = "/althea.nativedex.v1.UpgradeProxyProposal"; +pub const COLLECT_TREASURY_PROPOSAL_TYPE_URL: &str = "/althea.nativedex.v1.CollectTreasuryProposal"; +pub const SET_TREASURY_PROPOSAL_TYPE_URL: &str = "/althea.nativedex.v1.SetTreasuryProposal"; +pub const AUTHORITY_TRANSFER_PROPOSAL_TYPE_URL: &str = + "/althea.nativedex.v1.AuthorityTransferProposal"; +pub const HOT_PATH_OPEN_PROPOSAL_TYPE_URL: &str = "/althea.nativedex.v1.HotPathOpenProposal"; +pub const SET_SAFE_MODE_PROPOSAL_TYPE_URL: &str = "/althea.nativedex.v1.SetSafeModeProposal"; +pub const TRANSFER_GOVERNANCE_PROPOSAL_TYPE_URL: &str = + "/althea.nativedex.v1.TransferGovernanceProposal"; + // bank msgs pub const MSG_SEND_TYPE_URL: &str = "/cosmos.bank.v1beta1.MsgSend"; pub const MSG_MULTI_SEND_TYPE_URL: &str = "/cosmos.bank.v1beta1.MsgMultiSend";