diff --git a/.vscode/settings.json b/.vscode/settings.json index 4e0b25db..da526536 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,10 +4,4 @@ "prettier.jsxSingleQuote": false, "prettier.printWidth": 120, "typescript.tsdk": "node_modules/typescript/lib", - "rust-analyzer.cargo.extraEnv": { - "CARGO_TARGET_DIR": "${userHome}/.cargo/target/analyzer" - }, - "rust-analyzer.linkedProjects": [ - "./contracts/oraiswap_staking/Cargo.toml" - ] } \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index e81a94b2..f69288d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1318,6 +1318,9 @@ dependencies = [ "cosmwasm-std", "cosmwasm-storage", "cw-storage-plus 1.2.0", + "cw2 1.1.2", + "cw20", + "cw20-base", "oraiswap", "oraiswap-oracle", "oraiswap-pair", diff --git a/contracts/oraiswap_factory/Cargo.toml b/contracts/oraiswap_factory/Cargo.toml index 66f49642..85841fbf 100644 --- a/contracts/oraiswap_factory/Cargo.toml +++ b/contracts/oraiswap_factory/Cargo.toml @@ -18,6 +18,9 @@ crate-type = ["cdylib", "rlib"] backtraces = ["cosmwasm-std/backtraces"] [dependencies] +cw2 = { workspace = true } +cw20 = { workspace = true } +cw20-base = { workspace = true } cosmwasm-std = { workspace = true } cosmwasm-storage = { workspace = true } cw-storage-plus = { workspace = true, features = ["iterator"] } diff --git a/contracts/oraiswap_factory/src/contract.rs b/contracts/oraiswap_factory/src/contract.rs index 544b8f23..20ece7bb 100644 --- a/contracts/oraiswap_factory/src/contract.rs +++ b/contracts/oraiswap_factory/src/contract.rs @@ -4,18 +4,20 @@ use std::convert::TryFrom; use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_json_binary, Addr, Binary, CanonicalAddr, Deps, DepsMut, Env, MessageInfo, Reply, Response, - StdError, StdResult, SubMsg, WasmMsg, + to_json_binary, Addr, Binary, CanonicalAddr, Coin, CosmosMsg, Decimal, Deps, DepsMut, Env, + MessageInfo, Reply, Response, StdError, StdResult, SubMsg, WasmMsg, }; +use cw20::Cw20ExecuteMsg; use oraiswap::error::ContractError; use oraiswap::querier::query_pair_info_from_pair; use oraiswap::response::MsgInstantiateContractResponse; use crate::state::{read_pairs, Config, CONFIG, PAIRS}; -use oraiswap::asset::{pair_key, AssetInfo, PairInfo, PairInfoRaw}; +use oraiswap::asset::{self, pair_key, Asset, AssetInfo, PairInfo, PairInfoRaw}; use oraiswap::factory::{ - ConfigResponse, ExecuteMsg, InstantiateMsg, MigrateMsg, PairsResponse, QueryMsg, + ConfigResponse, ExecuteMsg, InstantiateMsg, MigrateMsg, PairsResponse, ProvideLiquidityParams, + QueryMsg, }; use oraiswap::pair::{ InstantiateMsg as PairInstantiateMsg, DEFAULT_COMMISSION_RATE, DEFAULT_OPERATOR_FEE, @@ -63,13 +65,26 @@ pub fn execute( asset_infos, pair_admin, operator, - } => execute_create_pair(deps, env, info, asset_infos, pair_admin, operator), + provide_liquidity, + } => execute_create_pair( + deps, + env, + info, + asset_infos, + pair_admin, + operator, + provide_liquidity, + ), ExecuteMsg::AddPair { pair_info } => execute_add_pair_manually(deps, env, info, pair_info), ExecuteMsg::MigrateContract { contract_addr, new_code_id, msg, } => migrate_pair(deps, env, info, contract_addr, new_code_id, msg), + ExecuteMsg::ProvideLiquidity { + assets, + receiver, + } => execute_provide_liquidity(deps, env, info, assets, receiver), } } @@ -135,10 +150,11 @@ pub fn execute_update_config( pub fn execute_create_pair( deps: DepsMut, env: Env, - _info: MessageInfo, + info: MessageInfo, asset_infos: [AssetInfo; 2], pair_admin: Option, operator: Option, + provide_liquidity: Option, ) -> Result { let config: Config = CONFIG.load(deps.storage)?; let raw_infos = [ @@ -169,6 +185,40 @@ pub fn execute_create_pair( let operator_addr = operator.map(|op| deps.api.addr_validate(&op)).transpose()?; + // if provide_liquidity is not None, transfer all cw20 tokens to this contract + let mut messages: Vec = vec![]; + + if let Some(ProvideLiquidityParams { + assets, + slippage_tolerance, + receiver, + }) = provide_liquidity + { + for asset in &assets { + // If the pool is token contract, then we need to execute TransferFrom msg to receive funds + if let AssetInfo::Token { contract_addr, .. } = &asset.info { + messages.push(CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: contract_addr.to_owned().into(), + msg: to_json_binary(&Cw20ExecuteMsg::TransferFrom { + owner: info.sender.to_string(), + recipient: env.contract.address.to_string(), + amount: asset.amount, + })?, + funds: vec![], + })); + } + } + + messages.push(CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: env.contract.address.to_string(), + msg: to_json_binary(&ExecuteMsg::ProvideLiquidity { + assets, + receiver, + })?, + funds: info.funds, + })); + } + Ok(Response::new() .add_submessage(SubMsg::reply_on_success( WasmMsg::Instantiate { @@ -191,10 +241,11 @@ pub fn execute_create_pair( .add_attributes(vec![ ("action", "create_pair"), ("pair", &format!("{}-{}", asset_infos[0], asset_infos[1])), - ])) + ]) + .add_messages(messages)) } -// Anyone can execute it to create swap pair +// Only owner can execute it pub fn execute_add_pair_manually( deps: DepsMut, _env: Env, @@ -245,6 +296,61 @@ pub fn execute_add_pair_manually( ])) } +pub fn execute_provide_liquidity( + deps: DepsMut, + _env: Env, + info: MessageInfo, + assets: [Asset; 2], + receiver: Option, +) -> Result { + let asset_infos = [assets[0].info.clone(), assets[1].info.clone()]; + let pair_key = pair_key(&asset_infos.map(|a| a.to_raw(deps.api).unwrap())); + let pair_raw = PAIRS.load(deps.storage, &pair_key)?; + let pair_contract = deps.api.addr_humanize(&pair_raw.contract_addr)?; + + let receiver = receiver.unwrap_or(info.sender.clone()); + + // Transfer native asset to pair contract + let mut funds: Vec = vec![]; + let mut cw20_msgs: Vec = vec![]; + for (_i, asset) in assets.iter().enumerate() { + match &asset.info { + AssetInfo::NativeToken { denom } => { + funds.push(Coin { + denom: denom.clone(), + amount: asset.amount, + }); + } + AssetInfo::Token { contract_addr, .. } => { + cw20_msgs.push(CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: contract_addr.to_owned().into(), + msg: to_json_binary(&Cw20ExecuteMsg::IncreaseAllowance { + spender: pair_contract.to_string(), + amount: asset.amount, + expires: None, + })?, + funds: vec![], + })); + } + } + } + + // Execute provide liquidity + let provide_msg = CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr: pair_contract.to_string(), + msg: to_json_binary(&oraiswap::pair::ExecuteMsg::ProvideLiquidity { + assets, + slippage_tolerance: None, + receiver: Some(receiver), + })?, + funds, + }); + + Ok(Response::new() + .add_messages(cw20_msgs) + .add_message(provide_msg)) +} + /// This just stores the result for future query #[cfg_attr(not(feature = "library"), entry_point)] pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result { diff --git a/packages/oraiswap/src/factory.rs b/packages/oraiswap/src/factory.rs index 95eec99a..9bd88235 100644 --- a/packages/oraiswap/src/factory.rs +++ b/packages/oraiswap/src/factory.rs @@ -1,7 +1,7 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; -use cosmwasm_std::{Addr, Binary}; +use cosmwasm_std::{Addr, Binary, Decimal}; -use crate::asset::{AssetInfo, PairInfo}; +use crate::asset::{Asset, AssetInfo, PairInfo}; #[cw_serde] pub struct InstantiateMsg { @@ -27,6 +27,7 @@ pub enum ExecuteMsg { asset_infos: [AssetInfo; 2], pair_admin: Option, operator: Option, + provide_liquidity: Option }, AddPair { pair_info: PairInfo, @@ -36,6 +37,10 @@ pub enum ExecuteMsg { new_code_id: u64, msg: Binary, }, + ProvideLiquidity { + assets: [Asset; 2], + receiver: Option, + } } #[cw_serde] @@ -70,3 +75,10 @@ pub struct MigrateMsg {} pub struct PairsResponse { pub pairs: Vec, } + +#[cw_serde] +pub struct ProvideLiquidityParams { + pub assets: [Asset; 2], + pub slippage_tolerance: Option, + pub receiver: Option, +} diff --git a/packages/oraiswap/src/pair.rs b/packages/oraiswap/src/pair.rs index cdf4c86b..7211b27c 100644 --- a/packages/oraiswap/src/pair.rs +++ b/packages/oraiswap/src/pair.rs @@ -1,7 +1,7 @@ use std::convert::TryInto; use crate::{ - asset::{Asset, AssetInfo, PairInfo, PairInfoRaw}, + asset::{Asset, AssetInfo, PairInfo}, error::ContractError, }; use cosmwasm_schema::{cw_serde, QueryResponses}; diff --git a/packages/oraiswap/src/testing.rs b/packages/oraiswap/src/testing.rs index 903b2043..b63b4e8d 100644 --- a/packages/oraiswap/src/testing.rs +++ b/packages/oraiswap/src/testing.rs @@ -143,6 +143,7 @@ impl MockApp { asset_infos: asset_infos.clone(), pair_admin: Some("admin".to_string()), operator: Some("operator".to_string()), + provide_liquidity: None, }, &[], )