Skip to content

Commit

Permalink
feat: add entry points
Browse files Browse the repository at this point in the history
  • Loading branch information
ibrizsabin committed Sep 27, 2024
1 parent ab0c14b commit 5859966
Show file tree
Hide file tree
Showing 9 changed files with 492 additions and 34 deletions.
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions contracts/cosmwasm-vm/cw-intents-v1/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ thiserror = { workspace=true}
common ={ git = "https://github.com/icon-project/IBC-Integration.git",branch="main" }
serde-json-wasm = {workspace=true}
hex="*"
cw20="*"

[dev-dependencies]
cosmwasm = "0.7.2"
Expand Down
313 changes: 308 additions & 5 deletions contracts/cosmwasm-vm/cw-intents-v1/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
use std::str::from_utf8;

use cosmwasm_std::Addr;
use crate::events::create_swap_order_event;
use common::{
rlp::{self, Decodable, Encodable},
utils::keccak256,
};
use cosmwasm_std::{to_binary, Addr, BankMsg, Coin};
use cw20::Cw20ExecuteMsg;
use events::create_send_message_event;

use super::*;

Expand All @@ -24,16 +31,312 @@ impl<'a> CwIntentV1Service<'a> {
Ok(Response::new())
}

pub fn swap(&self, order: SwapOrder, deps: DepsMut, env: Env, info: MessageInfo) {}
pub fn swap(
&self,
order: SwapOrder,
deps: DepsMut,
env: Env,
info: MessageInfo,
) -> Result<Response, ContractError> {
let event = create_swap_order_event(&order);
self.set_order(deps.storage, order.id, &order)?;
let mut res = self.receive_payment(deps.as_ref(), env, info, order.amount, order.token)?;
res = res.add_event(event);
Ok(res)
}

pub fn fill(
&self,
order: SwapOrder,
fill_amount: u128,
solver: String,
deps: DepsMut,
env: Env,
info: MessageInfo,
) -> Result<Response, ContractError> {
let stored = self.get_order(deps.storage, order.id)?;
let order_bytes = order.rlp_bytes();
let order_hash = keccak256(&order_bytes);
if keccak256(&stored.rlp_bytes()) != order_hash {
return Err(ContractError::DecodeError {
error: "Hash Mismatch".to_string(),
});
}

if self.is_order_finished(deps.storage, &order_hash) {
return Err(ContractError::OrderAlreadyComplete);
}

let mut remaining_amount = self
.get_pending_fill(deps.storage, &order_hash)
.unwrap_or(order.amount);

let payout = (order.amount * fill_amount) / order.min_receive;

if payout > remaining_amount {
return Err(ContractError::PayoutGreaterThanRemaining);
}

remaining_amount = remaining_amount - payout;

let mut close_order = false;
if remaining_amount == 0 {
self.remove_pending_fill(deps.storage, &order_hash);
self.set_order_finished(deps.storage, &order_hash, true)?;
close_order = true;
} else {
self.set_pending_fill(deps.storage, &order_hash, remaining_amount)?;
}

pub fn fill(&self, order: SwapOrder, deps: DepsMut, env: Env, info: MessageInfo) {}
let protocol_fee = self.get_protocol_fee(deps.storage).unwrap_or(0);
let fee = (fill_amount * protocol_fee as u128) / 10000;
let fee_handler = self.get_fee_handler(deps.storage)?;

pub fn receive_msg(&self, src_network: String, conn_sn: u128, order_msg: OrderMsg) {}
let fee_transfer = self.try_transfer(
deps.as_ref(),
env.contract.address.to_string(),
fee_handler.to_string(),
fee,
&order.to_token,
);

let receiver_transfer = self.try_transfer(
deps.as_ref(),
env.contract.address.to_string(),
order.destination_address.clone(),
(fill_amount - fee),
&order.to_token,
);

let order_fill = OrderFill {
id: order.id,
order_bytes: order.rlp_bytes().to_vec(),
solver_address: solver,
amount: payout,
closed: close_order,
};
let mut response = Response::new();

if order.src_nid == order.dst_nid {
response = self.resolve_fill(deps, env, order.src_nid, order_fill)?;
} else {
let msg = OrderMsg {
msg_type: ORDER_FILL,
message: order_fill.rlp_bytes().to_vec(),
};
let sn = self.get_next_conn_sn(deps.storage)?;
let event = create_send_message_event(order.src_nid, sn, msg.rlp_bytes().to_vec());
response = response.add_event(event);
}
response = response.add_messages(vec![fee_transfer, receiver_transfer]);
Ok(response)
}

pub fn receive_msg(
&self,
deps: DepsMut,
env: Env,
info: MessageInfo,
src_network: String,
conn_sn: u128,
order_msg: OrderMsg,
) -> Result<Response, ContractError> {
if self.have_received(deps.storage, (src_network.clone(), conn_sn)) {
return Err(ContractError::MessageAlreadyReceived);
}
self.set_received(deps.storage, (src_network.clone(), conn_sn), true)?;
match order_msg.msg_type {
ORDER_FILL => {
let fill = rlp::decode::<OrderFill>(&order_msg.message).map_err(|e| {
ContractError::DecodeError {
error: e.to_string(),
}
})?;
return self.resolve_fill(deps, env, src_network, fill);
}
ORDER_CANCEL => {
let cancel = rlp::decode::<OrderCancel>(&order_msg.message).map_err(|e| {
ContractError::DecodeError {
error: e.to_string(),
}
})?;
return self.resolve_cancel(deps, src_network, cancel.order_bytes);
}
_ => Err(ContractError::InvalidMessageType),
}
}

fn resolve_fill(
&self,
deps: DepsMut,
env: Env,
src_nid: String,
fill: OrderFill,
) -> Result<Response, ContractError> {
let order = self.get_order(deps.storage, fill.id)?;
if fill.order_bytes != order.rlp_bytes().to_vec() {
return Err(ContractError::InvalidFillOrder);
}

if order.dst_nid != src_nid {
return Err(ContractError::InvalidFillOrder);
}
let mut response = Response::new();
if fill.closed == true {
self.remove_order(deps.storage, order.id);
}
let transfer = self.try_transfer(
deps.as_ref(),
env.contract.address.to_string(),
fill.solver_address,
fill.amount,
&order.token,
);
response = response.add_message(transfer);
Ok(response)
}

fn resolve_cancel(
&self,
deps: DepsMut,
src_nid: String,
order_bytes: Vec<u8>,
) -> Result<Response, ContractError> {
let order_hash = keccak256(&order_bytes);
if self.is_order_finished(deps.storage, &order_hash) {
return Err(ContractError::OrderAlreadyComplete);
}
let mut response = Response::new();
let order: SwapOrder =
rlp::decode(&order_bytes).map_err(|e| ContractError::DecodeError {
error: e.to_string(),
})?;
if order.src_nid != src_nid {
return Err(ContractError::InvalidCancellation);
}

let remaining_amount = self
.get_pending_fill(deps.storage, &order_hash)
.unwrap_or(order.amount);
self.remove_pending_fill(deps.storage, &order_hash);
self.set_order_finished(deps.storage, &order_hash, true)?;
let fill = OrderFill {
id: order.id,
order_bytes,
solver_address: order.creator,
amount: remaining_amount,
closed: true,
};
let msg = OrderMsg {
msg_type: ORDER_CANCEL,
message: fill.rlp_bytes().to_vec(),
};
let conn_sn = self.get_next_conn_sn(deps.storage)?;
let event = create_send_message_event(order.src_nid, conn_sn, msg.rlp_bytes().to_vec());
response = response.add_event(event);

Ok(response)
}

fn receive_payment(
&self,
deps: Deps,
env: Env,
info: MessageInfo,
amount: u128,
denom: String,
) -> Result<Response, ContractError> {
let mut response = Response::new();
if self.is_native(deps, &denom) {
let sum: u128 = info.funds.into_iter().fold(0, |mut a, c| {
if c.denom == denom {
a = a + Into::<u128>::into(c.amount);
}
return a;
});
if sum < amount {
return Err(ContractError::InsufficientFunds);
}
} else {
let msg = self.token_transfer(
denom,
info.sender.to_string(),
env.contract.address.to_string(),
amount,
);
response = response.add_message(msg);
}

Ok(response)
}

fn token_transfer(
&self,
token_address: String,
from: String,
to: String,
amount: u128,
) -> CosmosMsg<Empty> {
let transfer_msg = Cw20ExecuteMsg::TransferFrom {
owner: from,
recipient: to,
amount: amount.into(),
};

CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: token_address,
msg: to_json_binary(&transfer_msg).unwrap(),
funds: vec![],
})
}

fn try_transfer(
&self,
deps: Deps,
from: String,
to: String,
amount: u128,
denom: &String,
) -> CosmosMsg<Empty> {
if self.is_native(deps, &denom) {
let msg = BankMsg::Send {
to_address: deps.api.addr_validate(&to).unwrap().to_string(),
amount: vec![Coin {
denom: denom.to_string(),
amount: amount.into(),
}],
};
CosmosMsg::Bank(msg)
} else {
self.token_transfer(denom.to_string(), from, to, amount)
}
}

pub fn is_contract(&self, deps: Deps, address: &Addr) -> bool {
deps.querier
.query_wasm_contract_info(address)
.map(|r| true)
.unwrap_or(false)
}

pub fn is_native(&self, deps: Deps, denom: &String) -> bool {
if let Some(addr) = deps.api.addr_validate(denom).ok() {
return self.is_contract(deps, &addr);
}
return false;
}

pub fn get_next_deposit_id(&self, storage: &mut dyn Storage) -> StdResult<u128> {
let id = self.get_deposit_id(storage)?;
let id = self.get_deposit_id(storage);
let new_id = id + 1;
self.set_deposit_id(storage, new_id)?;
Ok(new_id)
}

pub fn get_next_conn_sn(&self, storage: &mut dyn Storage) -> StdResult<u128> {
let id = self.get_conn_sn(storage);
let new_id = id + 1;
self.set_conn_sn(storage, new_id)?;
Ok(new_id)
}
}
25 changes: 15 additions & 10 deletions contracts/cosmwasm-vm/cw-intents-v1/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,19 @@ pub enum ContractError {
DecodeError { error: String },
#[error("RollBackMessageMismatch {sequence}")]
RollBackMismatch { sequence: u64 },
#[error("RevertFromDAPP")]
RevertFromDAPP,
#[error("ModuleAddressNotFound")]
ModuleAddressNotFound,
#[error("MisiingRollBack {sequence}")]
MisiingRollBack { sequence: u64 },
#[error("Connection Not Found {network_id}")]
ConnectionNotFound { network_id: String },
#[error("Invalid Address {address}")]
InvalidAddress { address: String },
#[error("InsufficientFunds")]
InsufficientFunds,
#[error("OrderAlreadyComplete")]
OrderAlreadyComplete,

#[error("PayoutGreaterThanRemaining")]
PayoutGreaterThanRemaining,
#[error("InvalidFillOrder")]
InvalidFillOrder,
#[error("InvalidCancellation")]
InvalidCancellation,
#[error("InvalidMessageType")]
InvalidMessageType,
#[error("MessageAlreadyReceived")]
MessageAlreadyReceived,
}
Loading

0 comments on commit 5859966

Please sign in to comment.