From 7b38330dfdd64a66a7d80ce4e53799c843432719 Mon Sep 17 00:00:00 2001 From: Anshudhar Kumar Singh Date: Fri, 31 May 2024 14:38:13 +0530 Subject: [PATCH] WIP:Router ibc --- Cargo.lock | 4 +- contracts/hub/router/src/contract.rs | 68 ++------- contracts/hub/router/src/execute.rs | 165 +++++++-------------- contracts/hub/router/src/helpers.rs | 26 ---- contracts/hub/router/src/ibc.rs | 212 +-------------------------- contracts/hub/router/src/lib.rs | 1 - contracts/hub/router/src/query.rs | 30 +--- contracts/hub/router/src/reply.rs | 71 +++++++-- contracts/hub/router/src/state.rs | 57 ++----- contracts/hub/vlp/src/contract.rs | 19 ++- contracts/hub/vlp/src/execute.rs | 15 +- contracts/hub/vlp/src/state.rs | 3 - packages/euclid/Cargo.toml | 6 +- packages/euclid/src/error.rs | 3 + packages/euclid/src/msgs/mod.rs | 1 + packages/euclid/src/msgs/router.rs | 48 +----- packages/euclid/src/msgs/vlp.rs | 7 +- packages/euclid/src/token.rs | 98 ++++++++++++- 18 files changed, 277 insertions(+), 557 deletions(-) delete mode 100644 contracts/hub/router/src/helpers.rs diff --git a/Cargo.lock b/Cargo.lock index 56505f62..21bdc91c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -408,10 +408,12 @@ version = "0.1.0" dependencies = [ "cosmwasm-schema", "cosmwasm-std", - "cw-storage-plus 0.15.1", + "cw-storage-plus 1.2.0", "cw-utils", "cw20", "itertools 0.10.5", + "schemars", + "serde", "thiserror", ] diff --git a/contracts/hub/router/src/contract.rs b/contracts/hub/router/src/contract.rs index 0d418bdd..e48282c3 100644 --- a/contracts/hub/router/src/contract.rs +++ b/contracts/hub/router/src/contract.rs @@ -5,11 +5,10 @@ use cw2::set_contract_version; use euclid::error::ContractError; // use cw2::set_contract_version; -use crate::query::{query_all_pools, query_state}; -use crate::reply::INSTANTIATE_REPLY_ID; +use crate::reply::{self, VLP_INSTANTIATE_REPLY_ID, VLP_POOL_REGISTER_REPLY_ID}; use crate::state::{State, STATE}; -use crate::{execute, query, reply}; -use euclid::msgs::factory::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use crate::{execute, query}; +use euclid::msgs::router::{ExecuteMsg, InstantiateMsg, QueryMsg}; // version info for migration info const CONTRACT_NAME: &str = "crates.io:factory"; @@ -23,67 +22,28 @@ pub fn instantiate( msg: InstantiateMsg, ) -> Result { let state = State { - router_contract: msg.router_contract.clone(), - chain_id: env.block.chain_id, - admin: info.sender.clone().to_string(), - pool_code_id: msg.pool_code_id.clone(), + vlp_code_id: msg.vlp_code_id, + admin: info.sender.to_string(), }; - set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; STATE.save(deps.storage, &state)?; Ok(Response::new() .add_attribute("method", "instantiate") - .add_attribute("router_contract", msg.router_contract) - .add_attribute("chain_id", state.chain_id)) + .add_attribute("router_contract", env.contract.address)) } #[cfg_attr(not(feature = "library"), entry_point)] pub fn execute( deps: DepsMut, - env: Env, + _env: Env, info: MessageInfo, msg: ExecuteMsg, ) -> Result { match msg { - ExecuteMsg::RequestPoolCreation { pair_info, channel } => { - execute::execute_request_pool_creation(deps, env, info, pair_info, channel) - } - ExecuteMsg::ExecuteSwap { - asset, - asset_amount, - min_amount_out, - channel, - swap_id, - } => execute::execute_swap( - deps, - env, - info, - asset, - asset_amount, - min_amount_out, - channel, - swap_id, - ), - ExecuteMsg::AddLiquidity { - token_1_liquidity, - token_2_liquidity, - slippage_tolerance, - channel, - liquidity_id, - } => execute::execute_add_liquidity( - deps, - env, - info, - token_1_liquidity, - token_2_liquidity, - slippage_tolerance, - channel, - liquidity_id, - ), - ExecuteMsg::UpdatePoolCodeId { new_pool_code_id } => { - execute::execute_update_pool_code_id(deps, info, new_pool_code_id) + ExecuteMsg::UpdateVLPCodeId { new_vlp_code_id } => { + execute::execute_update_vlp_code_id(deps, info, new_vlp_code_id) } } } @@ -91,21 +51,17 @@ pub fn execute( #[cfg_attr(not(feature = "library"), entry_point)] pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result { match msg { - QueryMsg::GetPool { vlp } => query::get_pool(deps, vlp), - QueryMsg::GetState {} => query_state(deps), - QueryMsg::GetAllPools {} => query_all_pools(deps), + QueryMsg::GetState {} => query::query_state(deps), } } #[cfg_attr(not(feature = "library"), entry_point)] pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result { match msg.id { - INSTANTIATE_REPLY_ID => reply::on_pool_instantiate_reply(deps, msg), + VLP_INSTANTIATE_REPLY_ID => reply::on_vlp_instantiate_reply(deps, msg), + VLP_POOL_REGISTER_REPLY_ID => reply::on_pool_register_reply(deps, msg), id => Err(ContractError::Std(StdError::generic_err(format!( "Unknown reply id: {}", id )))), } } - -#[cfg(test)] -mod tests {} diff --git a/contracts/hub/router/src/execute.rs b/contracts/hub/router/src/execute.rs index b324d262..055d4d98 100644 --- a/contracts/hub/router/src/execute.rs +++ b/contracts/hub/router/src/execute.rs @@ -1,144 +1,89 @@ -use cosmwasm_std::{ - to_json_binary, CosmosMsg, DepsMut, Env, IbcMsg, IbcTimeout, MessageInfo, Response, Uint128, -}; -use euclid::{ - error::ContractError, - token::{PairInfo, Token}, -}; -use euclid_ibc::msg::IbcExecuteMsg; +use cosmwasm_std::{ensure, to_json_binary, DepsMut, Env, MessageInfo, Response, SubMsg, WasmMsg}; +use euclid::{error::ContractError, fee::Fee, msgs, token::PairInfo}; -use crate::state::{generate_pool_req, STATE}; +use crate::{ + reply::{VLP_INSTANTIATE_REPLY_ID, VLP_POOL_REGISTER_REPLY_ID}, + state::{STATE, VLPS}, +}; // Function to send IBC request to Router in VLS to create a new pool pub fn execute_request_pool_creation( deps: DepsMut, env: Env, - info: MessageInfo, + chain_id: String, + factory: String, pair_info: PairInfo, - channel: String, ) -> Result { - // Load the state let state = STATE.load(deps.storage)?; - let mut msgs: Vec = Vec::new(); + let pair = (pair_info.token_1.get_token(), pair_info.token_2.get_token()); - // Create a Request in state - let pool_request = generate_pool_req(deps, &info.sender, env.block.chain_id, channel.clone())?; + let vlp = VLPS.may_load(deps.storage, pair)?; - // Create IBC packet to send to Router - let ibc_packet = IbcMsg::SendPacket { - channel_id: channel.clone(), - data: to_json_binary(&IbcExecuteMsg::RequestPoolCreation { - pool_rq_id: pool_request.pool_rq_id, - chain: state.chain_id, - factory: env.contract.address.to_string(), - pair_info, - })?, + if vlp.is_none() { + let pair = (pair_info.token_2.get_token(), pair_info.token_1.get_token()); + ensure!( + VLPS.load(deps.storage, pair).is_err(), + ContractError::Generic { + err: "pair order is reversed".to_string() + } + ); + } - timeout: IbcTimeout::with_timestamp(env.block.time.plus_seconds(60)), + let register_msg = msgs::vlp::ExecuteMsg::RegisterPool { + chain_id, + factory, + pair_info: pair_info.clone(), }; - msgs.push(ibc_packet.into()); - - Ok(Response::new() - .add_attribute("method", "request_pool_creation") - .add_messages(msgs)) -} - -// Function to send IBC request to Router in VLS to perform a swap -pub fn execute_swap( - deps: DepsMut, - env: Env, - info: MessageInfo, - asset: Token, - asset_amount: Uint128, - min_amount_out: Uint128, - channel: String, - swap_id: String, -) -> Result { - // Load the state - let state = STATE.load(deps.storage)?; - - let pool_address = info.sender; - - // Create IBC packet to send to Router - let ibc_packet = IbcMsg::SendPacket { - channel_id: channel.clone(), - data: to_json_binary(&IbcExecuteMsg::Swap { - chain_id: state.chain_id, - asset, - asset_amount, - min_amount_out, - channel, - swap_id, - pool_address, - })?, - timeout: IbcTimeout::with_timestamp(env.block.time.plus_seconds(60)), - }; - - let msg = CosmosMsg::Ibc(ibc_packet); - - Ok(Response::new() - .add_attribute("method", "request_pool_creation") - .add_message(msg)) -} - -// Function to send IBC request to Router in VLS to add liquidity to a pool -pub fn execute_add_liquidity( - deps: DepsMut, - env: Env, - info: MessageInfo, - token_1_liquidity: Uint128, - token_2_liquidity: Uint128, - slippage_tolerance: u64, - channel: String, - liquidity_id: String, -) -> Result { - // Load the state - let state = STATE.load(deps.storage)?; - - let pool_address = info.sender.clone(); - - // Create IBC packet to send to Router - let ibc_packet = IbcMsg::SendPacket { - channel_id: channel.clone(), - data: to_json_binary(&IbcExecuteMsg::AddLiquidity { - chain_id: state.chain_id, - token_1_liquidity, - token_2_liquidity, - slippage_tolerance, - liquidity_id, - pool_address: pool_address.clone().to_string(), - })?, - timeout: IbcTimeout::with_timestamp(env.block.time.plus_seconds(60)), - }; - - let msg = CosmosMsg::Ibc(ibc_packet); - - Ok(Response::new() - .add_attribute("method", "add_liquidity_request") - .add_message(msg)) + if vlp.is_some() { + let msg = WasmMsg::Execute { + contract_addr: vlp.unwrap(), + msg: to_json_binary(®ister_msg)?, + funds: vec![], + }; + Ok(Response::new().add_submessage(SubMsg::reply_always(msg, VLP_POOL_REGISTER_REPLY_ID))) + } else { + let instantiate_msg = msgs::vlp::InstantiateMsg { + router: env.contract.address.to_string(), + pair: pair_info, + fee: Fee { + lp_fee: 0, + treasury_fee: 0, + staker_fee: 0, + }, + execute: Some(register_msg), + }; + let msg = WasmMsg::Instantiate { + admin: Some(env.contract.address.to_string()), + code_id: state.vlp_code_id, + msg: to_json_binary(&instantiate_msg)?, + funds: vec![], + label: "VLP".to_string(), + }; + Ok(Response::new().add_submessage(SubMsg::reply_always(msg, VLP_INSTANTIATE_REPLY_ID))) + } } // Function to update the pool code ID -pub fn execute_update_pool_code_id( +pub fn execute_update_vlp_code_id( deps: DepsMut, info: MessageInfo, - new_pool_code_id: u64, + new_vlp_code_id: u64, ) -> Result { // Load the state STATE.update(deps.storage, |mut state| -> Result<_, ContractError> { // Ensure that only the admin can update the pool code ID - if info.sender.to_string() != state.admin { + if info.sender != state.admin { return Err(ContractError::Unauthorized {}); } // Update the pool code ID - state.pool_code_id = new_pool_code_id; + state.vlp_code_id = new_vlp_code_id; Ok(state) })?; Ok(Response::new() .add_attribute("method", "update_pool_code_id") - .add_attribute("new_pool_code_id", new_pool_code_id.to_string())) + .add_attribute("new_vlp_code_id", new_vlp_code_id.to_string())) } diff --git a/contracts/hub/router/src/helpers.rs b/contracts/hub/router/src/helpers.rs deleted file mode 100644 index 935560d5..00000000 --- a/contracts/hub/router/src/helpers.rs +++ /dev/null @@ -1,26 +0,0 @@ -use euclid::msgs::factory::ExecuteMsg; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - -use cosmwasm_std::{to_json_binary, Addr, CosmosMsg, StdResult, WasmMsg}; - -/// CwTemplateContract is a wrapper around Addr that provides a lot of helpers -/// for working with this. -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] -pub struct CwTemplateContract(pub Addr); - -impl CwTemplateContract { - pub fn addr(&self) -> Addr { - self.0.clone() - } - - pub fn call>(&self, msg: T) -> StdResult { - let msg = to_json_binary(&msg.into())?; - Ok(WasmMsg::Execute { - contract_addr: self.addr().into(), - msg, - funds: vec![], - } - .into()) - } -} diff --git a/contracts/hub/router/src/ibc.rs b/contracts/hub/router/src/ibc.rs index 6692f52a..480d45c2 100644 --- a/contracts/hub/router/src/ibc.rs +++ b/contracts/hub/router/src/ibc.rs @@ -19,13 +19,11 @@ use euclid::{ use euclid_ibc::ack::make_ack_fail; use euclid_ibc::msg::{AcknowledgementMsg, IbcExecuteMsg}; -use crate::{ - reply::INSTANTIATE_REPLY_ID, - state::{CONNECTION_COUNTS, POOL_REQUESTS, STATE, TIMEOUT_COUNTS}, -}; - use euclid::msgs::pool::InstantiateMsg as PoolInstantiateMsg; +use crate::execute; +use crate::state::{CONNECTION_COUNTS, TIMEOUT_COUNTS}; + pub const IBC_VERSION: &str = "counter-1"; /// Handles the `OpenInit` and `OpenTry` parts of the IBC handshake. @@ -109,32 +107,16 @@ pub fn ibc_packet_ack( IbcExecuteMsg::RequestPoolCreation { pool_rq_id, pair_info, + chain, + factory, .. } => { // Process acknowledgment for pool creation let res: AcknowledgementMsg = from_json(ack.acknowledgement.data)?; - - execute_pool_creation(deps, pair_info, res, pool_rq_id) - } - IbcExecuteMsg::Swap { - swap_id, - pool_address, - .. - } => { - // Process acknowledgment for swap - let res: AcknowledgementMsg = from_json(ack.acknowledgement.data)?; - execute_swap_process(res, pool_address.to_string(), swap_id) - } - - IbcExecuteMsg::AddLiquidity { - liquidity_id, - pool_address, - .. - } => { - // Process acknowledgment for add liquidity - let res: AcknowledgementMsg = from_json(ack.acknowledgement.data)?; - execute_add_liquidity_process(res, pool_address, liquidity_id) + let response = + execute::execute_request_pool_creation(deps, pair_info, res, pool_rq_id)?; + Ok(IbcReceiveResponse::new().add_attributes(response.attributes)) } _ => Err(ContractError::Unauthorized {}), @@ -157,30 +139,6 @@ pub fn ibc_packet_timeout( let parsed_msg: IbcExecuteMsg = from_json(&msg.packet.data)?; let result = match parsed_msg { - IbcExecuteMsg::AddLiquidity { - liquidity_id, - pool_address, - .. - } => { - let fake_error_ack = AcknowledgementMsg::Error("Timeout".to_string()); - execute_add_liquidity_process(fake_error_ack, pool_address, liquidity_id) - } - IbcExecuteMsg::Swap { - swap_id, - pool_address, - .. - } => { - let fake_error_ack = AcknowledgementMsg::Error("Timeout".to_string()); - execute_swap_process(fake_error_ack, pool_address.to_string(), swap_id) - } - IbcExecuteMsg::RequestPoolCreation { - pool_rq_id, - pair_info, - .. - } => { - let fake_error_ack = AcknowledgementMsg::Error("Timeout".to_string()); - execute_pool_creation(deps, pair_info, fake_error_ack, pool_rq_id) - } _ => Ok(IbcBasicResponse::new().add_attribute("method", "ibc_packet_timeout")), }; result.or(Ok( @@ -225,157 +183,3 @@ pub fn validate_order_and_version( Ok(()) } - -// Function to create pool -pub fn execute_pool_creation( - deps: DepsMut, - pair_info: PairInfo, - res: AcknowledgementMsg, - pool_rq_id: String, -) -> Result { - let _existing_req = POOL_REQUESTS - .may_load(deps.storage, pool_rq_id.clone())? - .ok_or(ContractError::PoolRequestDoesNotExists { - req: pool_rq_id.clone(), - })?; - // Load the state - let state = STATE.load(deps.storage)?; - // Check whether res is an error or not - match res { - AcknowledgementMsg::Ok(data) => { - // Check if the pool was created successfully - // Prepare Instantiate Msg - let init_msg = PoolInstantiateMsg { - vlp_contract: data.vlp_contract.clone(), - pool: Pool { - chain: state.chain_id.clone(), - pair: pair_info, - reserve_1: Uint128::zero(), - reserve_2: Uint128::zero(), - }, - chain_id: state.chain_id.clone(), - }; - - let msg: CosmosMsg = CosmosMsg::Wasm(WasmMsg::Instantiate { - admin: None, - code_id: state.pool_code_id, - msg: to_json_binary(&init_msg)?, - funds: vec![], - label: "euclid-pool".to_string(), - }); - - // Create submsg with reply always from msg - let msg: SubMsg = SubMsg::reply_always(msg, INSTANTIATE_REPLY_ID); - // Remove pool request from MAP - POOL_REQUESTS.remove(deps.storage, pool_rq_id); - Ok(IbcBasicResponse::new() - .add_attribute("method", "pool_creation") - .add_submessage(msg)) - } - - AcknowledgementMsg::Error(err) => { - // Remove pool request from MAP - POOL_REQUESTS.remove(deps.storage, pool_rq_id); - Ok(IbcBasicResponse::new() - .add_attribute("method", "refund_pool_request") - .add_attribute("error", err.clone())) - } - } -} - -// Function to process swap acknowledgment -pub fn execute_swap_process( - res: AcknowledgementMsg, - pool_address: String, - swap_id: String, -) -> Result { - // Check whether res is an error or not - match res { - AcknowledgementMsg::Ok(data) => { - // Prepare callback to send to pool - let callback = CallbackExecuteMsg::CompleteSwap { - swap_response: data.clone(), - }; - let msg = PoolExecuteMsg::Callback(callback); - - let execute = WasmMsg::Execute { - contract_addr: pool_address.clone(), - msg: to_json_binary(&msg.clone())?, - funds: vec![], - }; - - Ok(IbcBasicResponse::new() - .add_attribute("method", "swap") - .add_message(execute)) - } - - AcknowledgementMsg::Error(err) => { - // Prepare error callback to send to pool - let callback = CallbackExecuteMsg::RejectSwap { - swap_id: swap_id.clone(), - error: Some(err.clone()), - }; - - let msg = PoolExecuteMsg::Callback(callback); - let execute = WasmMsg::Execute { - contract_addr: pool_address.clone(), - msg: to_json_binary(&msg.clone())?, - funds: vec![], - }; - - Ok(IbcBasicResponse::new() - .add_attribute("method", "swap") - .add_attribute("error", err.clone()) - .add_message(execute)) - } - } -} - -// Function to process add liquidity acknowledgment -pub fn execute_add_liquidity_process( - res: AcknowledgementMsg, - pool_address: String, - liquidity_id: String, -) -> Result { - // Check whether res is an error or not - match res { - AcknowledgementMsg::Ok(data) => { - // Prepare callback to send to pool - let callback = CallbackExecuteMsg::CompleteAddLiquidity { - liquidity_response: data.clone(), - liquidity_id: liquidity_id.clone(), - }; - let msg = PoolExecuteMsg::Callback(callback); - - let execute = WasmMsg::Execute { - contract_addr: pool_address.clone(), - msg: to_json_binary(&msg.clone())?, - funds: vec![], - }; - - Ok(IbcBasicResponse::new() - .add_attribute("method", "add_liquidity") - .add_message(execute)) - } - - AcknowledgementMsg::Error(err) => { - // Prepare error callback to send to pool - let callback = CallbackExecuteMsg::RejectAddLiquidity { - liquidity_id, - error: Some(err.clone()), - }; - - let msg = PoolExecuteMsg::Callback(callback); - let execute = WasmMsg::Execute { - contract_addr: pool_address.clone(), - msg: to_json_binary(&msg.clone())?, - funds: vec![], - }; - - Ok(IbcBasicResponse::new() - .add_attribute("method", "add_liquidity") - .add_attribute("error", err.clone()) - .add_message(execute)) - } - } -} diff --git a/contracts/hub/router/src/lib.rs b/contracts/hub/router/src/lib.rs index 4d4d819e..00c7a177 100644 --- a/contracts/hub/router/src/lib.rs +++ b/contracts/hub/router/src/lib.rs @@ -1,6 +1,5 @@ pub mod contract; pub mod execute; -pub mod helpers; pub mod ibc; pub mod migrate; pub mod query; diff --git a/contracts/hub/router/src/query.rs b/contracts/hub/router/src/query.rs index 90b472c0..9767e203 100644 --- a/contracts/hub/router/src/query.rs +++ b/contracts/hub/router/src/query.rs @@ -1,36 +1,12 @@ use cosmwasm_std::{to_json_binary, Binary, Deps}; -use euclid::{ - error::ContractError, - msgs::factory::{AllPoolsResponse, GetPoolResponse, PoolVlpResponse, StateResponse}, -}; +use euclid::{error::ContractError, msgs::router::StateResponse}; -use crate::state::{STATE, VLP_TO_POOL}; +use crate::state::STATE; -// Returns the Pair Info of the Pair in the pool -pub fn get_pool(deps: Deps, vlp: String) -> Result { - let pool = VLP_TO_POOL.load(deps.storage, vlp)?; - Ok(to_json_binary(&GetPoolResponse { pool })?) -} pub fn query_state(deps: Deps) -> Result { let state = STATE.load(deps.storage)?; Ok(to_json_binary(&StateResponse { - chain_id: state.chain_id, - router_contract: state.router_contract, admin: state.admin, - pool_code_id: state.pool_code_id, + vlp_code_id: state.vlp_code_id, })?) } -pub fn query_all_pools(deps: Deps) -> Result { - let pools = VLP_TO_POOL - .range(deps.storage, None, None, cosmwasm_std::Order::Ascending) - .map(|item| { - let item = item.unwrap(); - PoolVlpResponse { - pool: item.1.clone(), - vlp: item.0, - } - }) - .collect(); - - to_json_binary(&AllPoolsResponse { pools }).map_err(Into::into) -} diff --git a/contracts/hub/router/src/reply.rs b/contracts/hub/router/src/reply.rs index 046602fd..74520ba5 100644 --- a/contracts/hub/router/src/reply.rs +++ b/contracts/hub/router/src/reply.rs @@ -1,29 +1,68 @@ -use crate::state::VLP_TO_POOL; -use cosmwasm_std::{DepsMut, Reply, Response, SubMsgResult}; -use cw0::parse_reply_instantiate_data; -use euclid::error::ContractError; -use euclid::msgs::pool::{GetVLPResponse, QueryMsg as PoolQueryMessage}; +use cosmwasm_std::{from_json, to_json_binary, DepsMut, Reply, Response, SubMsgResult}; +use cw0::{parse_reply_execute_data, parse_reply_instantiate_data}; +use euclid::{error::ContractError, msgs, pool::PoolCreationResponse}; +use euclid_ibc::msg::AcknowledgementMsg; -pub const INSTANTIATE_REPLY_ID: u64 = 1u64; +use crate::state::VLPS; -pub fn on_pool_instantiate_reply(deps: DepsMut, msg: Reply) -> Result { +pub const VLP_INSTANTIATE_REPLY_ID: u64 = 1u64; +pub const VLP_POOL_REGISTER_REPLY_ID: u64 = 2u64; + +pub fn on_vlp_instantiate_reply(deps: DepsMut, msg: Reply) -> Result { match msg.result.clone() { - SubMsgResult::Err(err) => Err(ContractError::PoolInstantiateFailed { err }), + SubMsgResult::Err(err) => Err(ContractError::InstantiateError { err }), SubMsgResult::Ok(..) => { - let instantiate_data: cw0::MsgInstantiateContractResponse = + let instantiate_data = parse_reply_instantiate_data(msg).map_err(|res| ContractError::Generic { err: res.to_string(), })?; - let pool_address = instantiate_data.contract_address; - let vlp_address: GetVLPResponse = deps + let vlp_address = instantiate_data.contract_address; + + let liquidity: msgs::vlp::GetLiquidityResponse = deps .querier - .query_wasm_smart(pool_address.clone(), &PoolQueryMessage::GetVlp {})?; - VLP_TO_POOL.save(deps.storage, vlp_address.vlp.clone(), &pool_address)?; + .query_wasm_smart(vlp_address.clone(), &msgs::vlp::QueryMsg::Liquidity {})?; + + VLPS.save( + deps.storage, + ( + liquidity.pair.token_1.get_token(), + liquidity.pair.token_2.get_token(), + ), + &vlp_address, + )?; + + let pool_creation_response = + from_json::(instantiate_data.data.unwrap_or_default())?; + let ack = AcknowledgementMsg::Ok(pool_creation_response); + + Ok(Response::new() + .add_attribute("action", "reply_vlp_instantiate") + .add_attribute("vlp", vlp_address) + .set_data(to_json_binary(&ack)?)) + } + } +} + +pub fn on_pool_register_reply(_deps: DepsMut, msg: Reply) -> Result { + match msg.result.clone() { + SubMsgResult::Err(err) => Err(ContractError::Generic { err }), + SubMsgResult::Ok(..) => { + let execute_data = + parse_reply_execute_data(msg).map_err(|res| ContractError::Generic { + err: res.to_string(), + })?; + let pool_creation_response: PoolCreationResponse = + from_json(execute_data.data.unwrap_or_default())?; + + let vlp_address = pool_creation_response.vlp_contract.clone(); + + let ack = AcknowledgementMsg::Ok(pool_creation_response); + Ok(Response::new() - .add_attribute("action", "reply_pool_instantiate") - .add_attribute("pool", pool_address) - .add_attribute("vlp", vlp_address.vlp)) + .add_attribute("action", "reply_pool_register") + .add_attribute("vlp", vlp_address) + .set_data(to_json_binary(&ack)?)) } } } diff --git a/contracts/hub/router/src/state.rs b/contracts/hub/router/src/state.rs index c2b97d53..ddc3b20a 100644 --- a/contracts/hub/router/src/state.rs +++ b/contracts/hub/router/src/state.rs @@ -1,62 +1,27 @@ use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, DepsMut}; use cw_storage_plus::{Item, Map}; -use euclid::{ - error::ContractError, - pool::{generate_id, PoolRequest}, -}; +use euclid::token::Token; #[cw_serde] pub struct State { - // The Unique Chain Identifier - // THIS IS DIFFERENT THAN THE CHAIN_ID OF THE CHAIN, THIS REPRESENTS A UNIQUE IDENTIFIER FOR THE CHAIN - // IN THE EUCLID ECOSYSTEM - pub chain_id: String, - // The Router Contract Address on the Virtual Settlement Layer - pub router_contract: String, // Contract admin pub admin: String, // Pool Code ID - pub pool_code_id: u64, + pub vlp_code_id: u64, } pub const STATE: Item = Item::new("state"); +// Convert it to multi index map? +pub const VLPS: Map<(Token, Token), String> = Map::new("vlps"); + +// chain id to factory map +pub const FACTORIES: Map = Map::new("factories"); + +// chain id to channel +pub const CHANNELS: Map = Map::new("channels"); + /// (channel_id) -> count. Reset on channel closure. pub const CONNECTION_COUNTS: Map = Map::new("connection_counts"); /// (channel_id) -> timeout_count. Reset on channel closure. pub const TIMEOUT_COUNTS: Map = Map::new("timeout_count"); - -// Map VLP address to Pool address -pub const VLP_TO_POOL: Map = Map::new("vlp_to_pool"); - -// Map sender of Pool request to Pool address -pub const POOL_REQUESTS: Map = Map::new("request_to_pool"); - -// Pool Requests Counter -pub const POOL_REQUEST_COUNT: Map = Map::new("request_to_pool_count"); - -pub fn generate_pool_req( - deps: DepsMut, - sender: &Addr, - chain: String, - channel: String, -) -> Result { - let count = POOL_REQUEST_COUNT - .may_load(deps.storage, sender.to_string())? - .unwrap_or_default(); - - let pool_rq_id = generate_id(sender.as_str(), count); - let pool_request = PoolRequest { - chain, - channel, - pool_rq_id: pool_rq_id.clone(), - }; - // If a pool request already exist, throw error, else create a new request - POOL_REQUESTS.update(deps.storage, pool_rq_id, |existing| match existing { - Some(req) => Err(ContractError::PoolRequestAlreadyExists { req }), - None => Ok(pool_request.clone()), - })?; - POOL_REQUEST_COUNT.save(deps.storage, sender.to_string(), &count.wrapping_add(1))?; - Ok(pool_request) -} diff --git a/contracts/hub/vlp/src/contract.rs b/contracts/hub/vlp/src/contract.rs index f383bcfa..da4f7d40 100644 --- a/contracts/hub/vlp/src/contract.rs +++ b/contracts/hub/vlp/src/contract.rs @@ -3,6 +3,7 @@ use cosmwasm_std::entry_point; use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response, Uint128}; use cw2::set_contract_version; +use crate::execute; use crate::state::{State, STATE}; use euclid::error::ContractError; use euclid::msgs::vlp::{ExecuteMsg, InstantiateMsg, QueryMsg}; @@ -15,7 +16,7 @@ const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( deps: DepsMut, - _env: Env, + env: Env, info: MessageInfo, msg: InstantiateMsg, ) -> Result { @@ -31,20 +32,28 @@ pub fn instantiate( set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; STATE.save(deps.storage, &state)?; - Ok(Response::new() + + let response = msg.execute.map_or(Ok(Response::default()), |execute_msg| { + execute(deps, env, info, execute_msg) + })?; + Ok(response .add_attribute("method", "instantiate") .add_attribute("owner", info.sender)) } #[cfg_attr(not(feature = "library"), entry_point)] pub fn execute( - _deps: DepsMut, - _env: Env, + deps: DepsMut, + env: Env, _info: MessageInfo, msg: ExecuteMsg, ) -> Result { match msg { - // ExecuteMsg::RegisterPool {pool} => execute::register_pool(deps, info, pool), + ExecuteMsg::RegisterPool { + chain_id, + factory, + pair_info, + } => execute::register_pool(deps, env, chain_id, factory, pair_info), } } diff --git a/contracts/hub/vlp/src/execute.rs b/contracts/hub/vlp/src/execute.rs index 31891301..43233da2 100644 --- a/contracts/hub/vlp/src/execute.rs +++ b/contracts/hub/vlp/src/execute.rs @@ -1,6 +1,6 @@ use cosmwasm_std::{ ensure, to_json_binary, Decimal256, DepsMut, Env, IbcReceiveResponse, OverflowError, - OverflowOperation, Uint128, + OverflowOperation, Response, Uint128, }; use euclid::{ error::ContractError, @@ -12,7 +12,7 @@ use euclid_ibc::{ack::make_ack_success, msg::AcknowledgementMsg}; use crate::{ query::{assert_slippage_tolerance, calculate_lp_allocation, calculate_swap}, - state::{self, FACTORIES, POOLS, STATE}, + state::{self, POOLS, STATE}, }; /// Registers a new pool in the contract. Function called by Router Contract @@ -36,7 +36,7 @@ pub fn register_pool( chain_id: String, factory: String, pair_info: PairInfo, -) -> Result { +) -> Result { let state = STATE.load(deps.storage)?; // Verify that chain pool does not already exist @@ -60,18 +60,17 @@ pub fn register_pool( // Store the pool in the map POOLS.save(deps.storage, &chain_id, &pool)?; - FACTORIES.save(deps.storage, &chain_id, &factory)?; STATE.save(deps.storage, &state)?; - let ack = AcknowledgementMsg::Ok(PoolCreationResponse { + let ack = PoolCreationResponse { vlp_contract: env.contract.address.to_string(), - }); + }; - Ok(IbcReceiveResponse::new() + Ok(Response::new() .add_attribute("action", "register_pool") .add_attribute("pool_chain", pool.chain) - .set_ack(to_json_binary(&ack)?)) + .set_data(to_json_binary(&ack)?)) } /// Adds liquidity to the VLP diff --git a/contracts/hub/vlp/src/state.rs b/contracts/hub/vlp/src/state.rs index 37c8f82d..487f6c2d 100644 --- a/contracts/hub/vlp/src/state.rs +++ b/contracts/hub/vlp/src/state.rs @@ -30,9 +30,6 @@ pub const STATE: Item = Item::new("state"); // A map of chain-ids connected to the VLP to pools pub const POOLS: Map<&String, Pool> = Map::new("pools"); -// A map of chain-ids connected to the VLP to pools -pub const FACTORIES: Map<&String, String> = Map::new("factories"); - // Stores a snapshotMap in order to keep track of prices for blocks for charts and other purposes pub const BALANCES: SnapshotMap = SnapshotMap::new( "balances", diff --git a/packages/euclid/Cargo.toml b/packages/euclid/Cargo.toml index 7c7a0518..0ee569cf 100644 --- a/packages/euclid/Cargo.toml +++ b/packages/euclid/Cargo.toml @@ -6,10 +6,12 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -cosmwasm-std = { version = "1.1" } -cw-storage-plus = "0.15" +cosmwasm-std = { version = "1.5.3" } +cw-storage-plus = "1.1.0" itertools = "0.10" cosmwasm-schema = "1.1" cw-utils = "1.0" thiserror = { workspace = true } cw20 = { workspace = true } +serde = { workspace = true } +schemars = { workspace = true } diff --git a/packages/euclid/src/error.rs b/packages/euclid/src/error.rs index cdfe3578..4cb48046 100644 --- a/packages/euclid/src/error.rs +++ b/packages/euclid/src/error.rs @@ -26,6 +26,9 @@ pub enum ContractError { #[error("Unauthorized")] Unauthorized {}, + #[error("Instantiate Error - {err}")] + InstantiateError { err: String }, + #[error("Pool request already exist")] PoolRequestAlreadyExists {}, diff --git a/packages/euclid/src/msgs/mod.rs b/packages/euclid/src/msgs/mod.rs index ab9881da..f82d18aa 100644 --- a/packages/euclid/src/msgs/mod.rs +++ b/packages/euclid/src/msgs/mod.rs @@ -1,3 +1,4 @@ pub mod factory; pub mod pool; +pub mod router; pub mod vlp; diff --git a/packages/euclid/src/msgs/router.rs b/packages/euclid/src/msgs/router.rs index e402db0a..f5a46126 100644 --- a/packages/euclid/src/msgs/router.rs +++ b/packages/euclid/src/msgs/router.rs @@ -9,65 +9,21 @@ pub struct InstantiateMsg { #[cw_serde] pub enum ExecuteMsg { - // Request Pool Creation - RequestPoolCreation { - pair_info: PairInfo, - channel: String, - }, - ExecuteSwap { - asset: Token, - asset_amount: Uint128, - min_amount_out: Uint128, - channel: String, - swap_id: String, - }, - // Add Liquidity Request to the VLP - AddLiquidity { - token_1_liquidity: Uint128, - token_2_liquidity: Uint128, - slippage_tolerance: u64, - channel: String, - liquidity_id: String, - }, // Update Pool Code ID - UpdatePoolCodeId { - new_pool_code_id: u64, - }, + UpdateVLPCodeId { new_vlp_code_id: u64 }, } #[cw_serde] #[derive(QueryResponses)] pub enum QueryMsg { - #[returns(GetPoolResponse)] - GetPool { vlp: String }, #[returns(StateResponse)] GetState {}, - // Query to get all pools in the factory - #[returns(AllPoolsResponse)] - GetAllPools {}, -} - -#[cw_serde] -pub struct GetPoolResponse { - pub pool: String, } // We define a custom struct for each query response #[cw_serde] pub struct StateResponse { - pub chain_id: String, - pub router_contract: String, pub admin: String, - pub pool_code_id: u64, -} - -#[cw_serde] -pub struct AllPoolsResponse { - pub pools: Vec, // Assuming pool addresses are strings -} -#[cw_serde] -pub struct PoolVlpResponse { - pub pool: String, - pub vlp: String, + pub vlp_code_id: u64, } #[cw_serde] diff --git a/packages/euclid/src/msgs/vlp.rs b/packages/euclid/src/msgs/vlp.rs index 8ce14d39..378cb813 100644 --- a/packages/euclid/src/msgs/vlp.rs +++ b/packages/euclid/src/msgs/vlp.rs @@ -11,12 +11,17 @@ pub struct InstantiateMsg { pub router: String, pub pair: PairInfo, pub fee: Fee, + pub execute: Option, } #[cw_serde] pub enum ExecuteMsg { // Registers a new pool from a new chain to an already existing VLP - // RegisterPool { pool: Pool }, + RegisterPool { + chain_id: String, + factory: String, + pair_info: PairInfo, + }, /* // Update the fee for the VLP diff --git a/packages/euclid/src/token.rs b/packages/euclid/src/token.rs index e54c0ab9..07ce3452 100644 --- a/packages/euclid/src/token.rs +++ b/packages/euclid/src/token.rs @@ -2,7 +2,8 @@ use std::fmt; use cosmwasm_schema::cw_serde; use cosmwasm_std::{ - to_json_binary, BankMsg, Coin, CosmosMsg, StdError, StdResult, Uint128, WasmMsg, + forward_ref_partial_eq, to_json_binary, Addr, BankMsg, Coin, CosmosMsg, StdError, StdResult, + Uint128, WasmMsg, }; use cw_storage_plus::{Key, KeyDeserialize, Prefixer, PrimaryKey}; @@ -10,24 +11,24 @@ use crate::{cw20::Cw20ExecuteMsg, error::ContractError}; // Token asset that represents an identifier for a token #[cw_serde] -#[derive(Hash, Eq)] +#[derive(Eq, PartialOrd, Ord)] pub struct Token { pub id: String, } +forward_ref_partial_eq!(Token, Token); + impl Token { pub fn exists(&self, pool: Pair) -> bool { - self == &pool.token_1 || self == &pool.token_2 + self == pool.token_1 || self == pool.token_2 } } impl<'a> PrimaryKey<'a> for Token { type Prefix = (); - type SubPrefix = (); type Suffix = Self; - type SuperSuffix = Self; fn key(&self) -> Vec { @@ -60,11 +61,57 @@ impl fmt::Display for Token { // A pair is a set of two tokens #[cw_serde] +#[derive(Eq, PartialOrd, Ord)] pub struct Pair { pub token_1: Token, pub token_2: Token, } +forward_ref_partial_eq!(Pair, Pair); + +impl<'a> PrimaryKey<'a> for Pair { + type Prefix = Token; + type SubPrefix = (); + + type Suffix = Token; + type SuperSuffix = Self; + + fn key(&self) -> Vec { + let token_1_key_size = self.token_1.joined_key().len().to_be_bytes(); + let mut res = vec![]; + res.push(Key::Val64(token_1_key_size)); + res.extend(self.token_1.key()); + res.extend(self.token_2.key()); + res + } +} + +fn parse_length(value: &[u8]) -> StdResult { + Ok(usize::from_be_bytes(value.try_into().map_err(|err| { + StdError::generic_err(format!("{err:?}")) + })?)) +} + +impl KeyDeserialize for Pair { + type Output = Pair; + + #[inline(always)] + fn from_vec(mut value: Vec) -> StdResult { + let mut values = value.split_off(2); + let mut token_1_key_bytes = values.split_off(8); + + // Deserialize token_1 + let token_1_key_len = parse_length(&values)?; + let token_2_key_bytes = token_1_key_bytes.split_off(token_1_key_len + 2); + let token_1 = Token::from_vec(token_1_key_bytes[2..].to_vec())?; + + // Deserialize token_2 + let token_2 = Token::from_vec(token_2_key_bytes.to_vec())?; + + Ok(Pair { token_1, token_2 }) + } +} + // TokenInfo stores the native or smart contract token information from incoming chain #[cw_serde] pub enum TokenInfo { @@ -174,3 +221,44 @@ impl PairInfo { } } } + +// Struct to handle Acknowledgement Response for a Pool Creation Request +#[cw_serde] +pub struct PairRouter { + pub vlp_contract: Addr, + pub pair: Pair, +} + +#[cfg(test)] +mod tests { + use cosmwasm_std::testing::mock_dependencies; + + use super::*; + + #[test] + fn test_tuple_key_serialize_deserialzie() { + let mut owned_deps = mock_dependencies(); + let deps = owned_deps.as_mut(); + pub const PAIR_MAP: cw_storage_plus::Map = cw_storage_plus::Map::new("pair"); + + let token_1 = Token { + id: "token_1123".to_string(), + }; + let token_2 = Token { + id: "token_2".to_string(), + }; + + let pair = Pair { token_1, token_2 }; + + let vlp = "vlp_address".to_string(); + PAIR_MAP.save(deps.storage, pair.clone(), &vlp).unwrap(); + + assert_eq!(PAIR_MAP.load(deps.storage, pair.clone()).unwrap(), vlp); + + let list = PAIR_MAP + .range(deps.storage, None, None, cosmwasm_std::Order::Ascending) + .collect::, _>>() + .unwrap(); + assert_eq!(list[0], (pair, vlp)); + } +}