Skip to content

Commit

Permalink
Remove collection address from key
Browse files Browse the repository at this point in the history
  • Loading branch information
josefleventon committed Mar 14, 2023
1 parent 0339946 commit e650c37
Show file tree
Hide file tree
Showing 10 changed files with 406 additions and 92 deletions.
20 changes: 8 additions & 12 deletions schema/cw-wager.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,10 @@
"type": "array",
"items": [
{
"type": "integer",
"format": "uint64",
"minimum": 0.0
"$ref": "#/definitions/Decimal"
},
{
"type": "integer",
"format": "uint64",
"minimum": 0.0
"$ref": "#/definitions/Decimal"
}
],
"maxItems": 2,
Expand All @@ -128,14 +124,10 @@
"type": "array",
"items": [
{
"type": "integer",
"format": "uint64",
"minimum": 0.0
"$ref": "#/definitions/Decimal"
},
{
"type": "integer",
"format": "uint64",
"minimum": 0.0
"$ref": "#/definitions/Decimal"
}
],
"maxItems": 2,
Expand Down Expand Up @@ -303,6 +295,10 @@
"mir"
]
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
"type": "string"
},
"ParamInfo": {
"type": "object",
"properties": {
Expand Down
41 changes: 24 additions & 17 deletions src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#[cfg(not(feature = "library"))]
use cosmwasm_std::entry_point;
use cosmwasm_std::{
to_binary, Binary, Decimal, Deps, DepsMut, Empty, Env, MessageInfo, Order, StdError, StdResult,
to_binary, Addr, Binary, Decimal, Deps, DepsMut, Empty, Env, MessageInfo, Order, StdError,
StdResult,
};
use cw2::set_contract_version;
use semver::Version;
Expand Down Expand Up @@ -154,17 +155,21 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
}

pub fn query_wagers(deps: Deps) -> StdResult<WagersResponse> {
let config = CONFIG.load(deps.storage)?;

let wagers = wagers()
.idx
.id
.range(deps.storage, None, None, Order::Ascending)
.map(|v| export_wager(v.unwrap().1))
.map(|v| export_wager(v.unwrap().1, config.collection_address.clone()))
.collect::<Vec<_>>();

Ok(WagersResponse { wagers })
}

pub fn query_wager(deps: Deps, token: Token) -> StdResult<WagerResponse> {
let config = CONFIG.load(deps.storage)?;

// Find the wager with the key containing the token and return it as WagerExport
let wager = wagers()
.idx
Expand All @@ -175,7 +180,7 @@ pub fn query_wager(deps: Deps, token: Token) -> StdResult<WagerResponse> {
.map(|(_, w)| w.id.0 == token || w.id.1 == token)
.unwrap_or(false)
})
.map(|v| export_wager(v.unwrap().1));
.map(|v| export_wager(v.unwrap().1, config.collection_address));

match wager {
Some(wager) => Ok(WagerResponse { wager }),
Expand All @@ -190,6 +195,8 @@ pub fn query_token_status(deps: Deps, token: Token) -> StdResult<TokenStatusResp
// If there is a MatchmakingItem for the token, return TokenStatus::Matchmaking(MatchmakingItem).
// If there is no Wager or MatchmakingItem for the token, return TokenStatus::None.

let config = CONFIG.load(deps.storage)?;

let wager = wagers()
.idx
.id
Expand All @@ -199,19 +206,20 @@ pub fn query_token_status(deps: Deps, token: Token) -> StdResult<TokenStatusResp
.map(|(_, w)| w.id.0 == token || w.id.1 == token)
.unwrap_or(false)
})
.map(|v| export_wager(v.unwrap().1));
.map(|v| export_wager(v.unwrap().1, config.collection_address.clone()));

if let Some(wager) = wager {
return Ok(TokenStatusResponse {
token_status: TokenStatus::Wager(wager),
});
}

let matchmaking_item = MATCHMAKING.may_load(deps.storage, token.clone())?.ok_or(
cosmwasm_std::StdError::NotFound {
kind: "matchmaking_item".into(),
},
);
let matchmaking_item =
MATCHMAKING
.may_load(deps.storage, token)?
.ok_or(cosmwasm_std::StdError::NotFound {
kind: "matchmaking_item".into(),
});

if matchmaking_item.is_ok() {
let MatchmakingItem {
Expand All @@ -224,8 +232,8 @@ pub fn query_token_status(deps: Deps, token: Token) -> StdResult<TokenStatusResp
Ok(TokenStatusResponse {
token_status: TokenStatus::Matchmaking(MatchmakingItemExport {
token: NFT {
collection: token.0,
token_id: token.1,
collection: config.collection_address,
token_id: token,
},
expires_at,
currency,
Expand All @@ -246,23 +254,22 @@ fn query_config(deps: Deps) -> StdResult<ConfigResponse> {
Ok(ConfigResponse { config })
}

#[allow(clippy::type_complexity)]
fn export_wager(v: Wager) -> WagerExport {
fn export_wager(v: Wager, collection: Addr) -> WagerExport {
WagerExport {
amount: v.amount,
expires_at: v.expires_at,
wagers: (
WagerInfo {
token: NFT {
collection: v.id.0 .0,
token_id: v.id.0 .1,
collection: collection.clone(),
token_id: v.id.0,
},
currency: v.currencies.0,
},
WagerInfo {
token: NFT {
collection: v.id.1 .0,
token_id: v.id.1 .1,
collection,
token_id: v.id.1,
},
currency: v.currencies.1,
},
Expand Down
66 changes: 25 additions & 41 deletions src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use crate::state::{
wagers, Currency, MatchmakingItem, Token, TokenStatus, Wager, CONFIG, MATCHMAKING,
};

#[allow(clippy::too_many_arguments)]
pub fn execute_wager(
deps: DepsMut,
env: Env,
Expand All @@ -23,15 +22,11 @@ pub fn execute_wager(
against_currencies: Vec<Currency>,
expiry: u64,
) -> Result<Response, ContractError> {
let (collection, token_id) = token.clone();
let token_id = token;

let amount = must_pay(&info, NATIVE_DENOM)?;

// Verify that the token's collection address is the same as the contract address in config
let config = CONFIG.load(deps.storage)?;
if collection != config.collection_address {
return Err(ContractError::Unauthorized {});
}

// Verify that the currencies do not exceed the maximum amount
if against_currencies.len() > config.max_currencies as usize {
Expand Down Expand Up @@ -62,15 +57,15 @@ pub fn execute_wager(
};

// Verify that the sender is the owner of the token
let token_owner = Cw721Contract(collection.clone())
let token_owner = Cw721Contract(config.collection_address)
.owner_of(&deps.querier, token_id.to_string(), true)?
.owner;
if info.sender != token_owner {
return Err(ContractError::Unauthorized {});
};

// Verify that the token is not already wagered or is not matchmaking
let token_status = query_token_status(deps.as_ref(), token.clone())?.token_status;
let token_status = query_token_status(deps.as_ref(), token)?.token_status;
if token_status != TokenStatus::None {
return Err(ContractError::AlreadyWagered {});
};
Expand Down Expand Up @@ -108,26 +103,23 @@ pub fn execute_wager(
},
) = matchmaking_item?;

let (_, token_id) = matchmaking_key;

let against_token: Token = (collection, token_id);
let against_token: Token = matchmaking_key;
let expires_at = env.block.time.plus_seconds(expiry);

let wager = Wager {
id: (token.clone(), against_token.clone()),
id: (token, against_token),
currencies: (currency, match_currency),
expires_at,
amount,
};

wagers().save(deps.storage, (token.clone(), against_token), &wager)?;
wagers().save(deps.storage, (token, against_token), &wager)?;

MATCHMAKING.remove(deps.storage, matchmaking_key);

Ok(Response::new()
.add_attribute("action", "wager")
.add_attribute("collection", token.0)
.add_attribute("token_id", token.1.to_string())
.add_attribute("token_id", token.to_string())
.add_attribute("expires_at", expires_at.to_string()))
} else {
let expires_at = env.block.time.plus_seconds(config.matchmaking_expiry);
Expand All @@ -139,12 +131,11 @@ pub fn execute_wager(
amount,
};

MATCHMAKING.save(deps.storage, token.clone(), &matchmaking_item)?;
MATCHMAKING.save(deps.storage, token, &matchmaking_item)?;

Ok(Response::new()
.add_attribute("action", "matchmake")
.add_attribute("collection", token.0)
.add_attribute("token_id", token.1.to_string())
.add_attribute("token_id", token.to_string())
.add_attribute("expires_at", expires_at.to_string()))
}
}
Expand All @@ -154,33 +145,28 @@ pub fn execute_cancel(
info: MessageInfo,
token: Token,
) -> Result<Response, ContractError> {
let (collection, token_id) = token.clone();
let token_id = token;

// Verify that the token's collection address is the same as the contract address in config
let config = CONFIG.load(deps.storage)?;
if collection != config.collection_address {
return Err(ContractError::Unauthorized {});
}

// Verify that the sender is the owner of the token
let token_owner = Cw721Contract(collection)
let token_owner = Cw721Contract(config.collection_address)
.owner_of(&deps.querier, token_id.to_string(), true)?
.owner;
if info.sender != token_owner {
return Err(ContractError::Unauthorized {});
};

let token_status = query_token_status(deps.as_ref(), token.clone())?.token_status;
let token_status = query_token_status(deps.as_ref(), token)?.token_status;

match token_status {
TokenStatus::Matchmaking(status) => {
MATCHMAKING.remove(deps.storage, token.clone());
MATCHMAKING.remove(deps.storage, token);
let msg = send_tokens(info.sender, coin(status.amount.u128(), NATIVE_DENOM))?;
Ok(Response::new()
.add_submessage(msg)
.add_attribute("action", "cancel")
.add_attribute("collection", token.0)
.add_attribute("token_id", token.1.to_string()))
.add_attribute("token_id", token.to_string()))
}
_ => Err(ContractError::NotMatchmaking {}),
}
Expand All @@ -194,22 +180,20 @@ pub fn execute_set_winner(
prev_prices: (Decimal, Decimal),
current_prices: (Decimal, Decimal),
) -> Result<Response, ContractError> {
// Verify that all token data conforms to the contract's config
let config = CONFIG.load(deps.storage)?;
if wager_key.0 .0 != config.collection_address || wager_key.1 .0 != config.collection_address {
return Err(ContractError::Unauthorized {});
}

// Get the wager info
let wager = wagers()
.load(deps.storage, wager_key.clone())
.or_else(|_| wagers().load(deps.storage, (wager_key.clone().1, wager_key.clone().0)))?;
.load(deps.storage, wager_key)
.or_else(|_| wagers().load(deps.storage, (wager_key.1, wager_key.0)))?;

// Verify that the wager has expired
if env.block.time < wager.expires_at {
return Err(ContractError::WagerActive {});
}

// Remove the wager
wagers().remove(deps.storage, wager_key.clone())?;
wagers().remove(deps.storage, wager_key)?;

// Determine the winner of the wager
let token_1_change = Decimal::from_ratio(current_prices.0.atomics(), prev_prices.0.atomics());
Expand All @@ -228,16 +212,16 @@ pub fn execute_set_winner(
let msgs = vec![
send_tokens(
deps.api.addr_validate(
&Cw721Contract(wager.id.0 .0)
.owner_of(&deps.querier, wager.id.0 .1.to_string(), true)?
&Cw721Contract(config.collection_address.clone())
.owner_of(&deps.querier, wager.id.0.to_string(), true)?
.owner,
)?,
coin(wager.amount.u128(), NATIVE_DENOM),
)?,
send_tokens(
deps.api.addr_validate(
&Cw721Contract(wager.id.1 .0)
.owner_of(&deps.querier, wager.id.1 .1.to_string(), true)?
&Cw721Contract(config.collection_address)
.owner_of(&deps.querier, wager.id.1.to_string(), true)?
.owner,
)?,
coin(wager.amount.u128(), NATIVE_DENOM),
Expand All @@ -246,8 +230,8 @@ pub fn execute_set_winner(
return Ok(res.add_submessages(msgs));
};

let winner_addr = Cw721Contract(winner.0)
.owner_of(&deps.querier, winner.1.to_string(), true)?
let winner_addr = Cw721Contract(config.collection_address)
.owner_of(&deps.querier, winner.to_string(), true)?
.owner;

// Pay out the winner
Expand Down
9 changes: 3 additions & 6 deletions src/multitest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ fn try_wager() {

// Submit a wager for matchmaking
let wager_msg = ExecuteMsg::Wager {
token: (collection.clone(), TOKEN1_ID as u64),
token: TOKEN1_ID as u64,
currency: crate::state::Currency::Atom,
against_currencies: vec![crate::state::Currency::Stars],
expiry: 60,
Expand All @@ -369,7 +369,7 @@ fn try_wager() {

// Submit a wager for matchmaking to `sender` from `peer`
let wager_msg = ExecuteMsg::Wager {
token: (collection.clone(), TOKEN2_ID as u64),
token: TOKEN2_ID as u64,
currency: crate::state::Currency::Stars,
against_currencies: vec![crate::state::Currency::Atom],
expiry: 60,
Expand All @@ -394,10 +394,7 @@ fn try_wager() {
// Attempt to set the wager as won, even thought it has not expired yet
// Expects: failure
let set_winner_msg = ExecuteMsg::SetWinner {
wager_key: (
(collection.clone(), TOKEN1_ID as u64),
(collection.clone(), TOKEN2_ID as u64),
),
wager_key: (TOKEN1_ID as u64, TOKEN2_ID as u64),
prev_prices: (
Decimal::from_str("100.0").unwrap(),
Decimal::from_str("100.0").unwrap(),
Expand Down
4 changes: 2 additions & 2 deletions src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ pub enum TokenStatus {
None,
}

pub type Token = (Addr, u64);
pub type Token = u64;
pub type WagerKey = (Token, Token);

pub struct WagerIndicies<'a> {
Expand All @@ -100,7 +100,7 @@ impl<'a> IndexList<Wager> for WagerIndicies<'a> {

pub fn wagers<'a>() -> IndexedMap<'a, WagerKey, Wager, WagerIndicies<'a>> {
let indexes = WagerIndicies {
id: UniqueIndex::new(|d| d.id.clone(), "wager_id"),
id: UniqueIndex::new(|d| d.id, "wager_id"),
};
IndexedMap::new("bids", indexes)
}
Expand Down
Loading

0 comments on commit e650c37

Please sign in to comment.