Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add query for renewal price for multiple names [SG-1090] #255

Merged
merged 13 commits into from
Mar 6, 2024
125 changes: 125 additions & 0 deletions contracts/marketplace/schema/name-marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,35 @@
},
"additionalProperties": false
},
{
"description": "Get renewal price for multiple names",
"type": "object",
"required": [
"ask_renewal_prices"
],
"properties": {
"ask_renewal_prices": {
"type": "object",
"required": [
"current_time",
"token_ids"
],
"properties": {
"current_time": {
"$ref": "#/definitions/Timestamp"
},
"token_ids": {
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"description": "Get data for a specific bid",
"type": "object",
Expand Down Expand Up @@ -1126,6 +1155,102 @@
}
}
},
"ask_renewal_prices": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Array_of_AskRenewPriceResponse",
"type": "array",
"items": {
"$ref": "#/definitions/AskRenewPriceResponse"
},
"definitions": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
},
"AskRenewPriceResponse": {
"type": "object",
"required": [
"price",
"token_id"
],
"properties": {
"bid": {
"anyOf": [
{
"$ref": "#/definitions/Bid"
},
{
"type": "null"
}
]
},
"price": {
"$ref": "#/definitions/Coin"
},
"token_id": {
"type": "string"
}
},
"additionalProperties": false
},
"Bid": {
"description": "Represents a bid (offer) on the marketplace",
"type": "object",
"required": [
"amount",
"bidder",
"created_time",
"token_id"
],
"properties": {
"amount": {
"$ref": "#/definitions/Uint128"
},
"bidder": {
"$ref": "#/definitions/Addr"
},
"created_time": {
"$ref": "#/definitions/Timestamp"
},
"token_id": {
"type": "string"
}
},
"additionalProperties": false
},
"Coin": {
"type": "object",
"required": [
"amount",
"denom"
],
"properties": {
"amount": {
"$ref": "#/definitions/Uint128"
},
"denom": {
"type": "string"
}
}
},
"Timestamp": {
"description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```",
"allOf": [
{
"$ref": "#/definitions/Uint64"
}
]
},
"Uint128": {
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```",
"type": "string"
},
"Uint64": {
"description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```",
"type": "string"
}
}
},
"asks": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Array_of_Ask",
Expand Down
13 changes: 13 additions & 0 deletions contracts/marketplace/src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,13 @@ impl BidOffset {
}
}

#[cw_serde]
pub struct AskRenewPriceResponse {
pub token_id: TokenId,
pub price: Coin,
pub bid: Option<Bid>,
}

#[cw_serde]
#[derive(QueryResponses)]
pub enum QueryMsg {
Expand Down Expand Up @@ -159,6 +166,12 @@ pub enum QueryMsg {
current_time: Timestamp,
token_id: TokenId,
},
/// Get renewal price for multiple names
#[returns(Vec<AskRenewPriceResponse>)]
AskRenewalPrices {
current_time: Timestamp,
token_ids: Vec<TokenId>,
},
/// Get data for a specific bid
#[returns(Option<Bid>)]
Bid { token_id: TokenId, bidder: Bidder },
Expand Down
27 changes: 25 additions & 2 deletions contracts/marketplace/src/query.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::helpers::get_renewal_price_and_bid;
use crate::msg::{BidOffset, Bidder, ConfigResponse, QueryMsg};
use crate::msg::{AskRenewPriceResponse, BidOffset, Bidder, ConfigResponse, QueryMsg};
use crate::state::{
ask_key, asks, bid_key, bids, legacy_bids, Ask, AskKey, Bid, Id, SudoParams, TokenId,
ASK_COUNT, ASK_HOOKS, BID_HOOKS, NAME_COLLECTION, NAME_MINTER, RENEWAL_QUEUE, SALE_HOOKS,
Expand Down Expand Up @@ -51,6 +51,10 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
current_time,
token_id,
} => to_binary(&query_ask_renew_price(deps, current_time, token_id)?),
QueryMsg::AskRenewalPrices {
current_time,
token_ids,
} => to_binary(&query_ask_renew_prices(deps, current_time, token_ids)?),
QueryMsg::AskCount {} => to_binary(&query_ask_count(deps)?),
QueryMsg::Bid { token_id, bidder } => {
to_binary(&query_bid(deps, token_id, api.addr_validate(&bidder)?)?)
Expand Down Expand Up @@ -207,7 +211,7 @@ pub fn query_ask_renew_price(
let name_minter = NAME_MINTER.load(deps.storage)?;
let name_minter_params = deps
.querier
.query_wasm_smart::<NameMinterParams>(name_minter, &SgNameMinterQueryMsg::Params {})?;
.query_wasm_smart::<NameMinterParams>(name_minter, &(SgNameMinterQueryMsg::Params {}))?;

let (renewal_price, valid_bid) = get_renewal_price_and_bid(
deps,
Expand All @@ -221,6 +225,25 @@ pub fn query_ask_renew_price(
Ok((Some(coin(renewal_price.u128(), NATIVE_DENOM)), valid_bid))
}

pub fn query_ask_renew_prices(
deps: Deps,
current_time: Timestamp,
token_ids: Vec<String>,
) -> StdResult<Vec<AskRenewPriceResponse>> {
token_ids
.iter()
.map(|token_id| {
let (coin_option, bid_option) =
query_ask_renew_price(deps, current_time, token_id.to_string())?;
Ok(AskRenewPriceResponse {
token_id: token_id.to_string(),
price: coin_option.unwrap_or_default(),
bid: bid_option,
})
})
.collect::<StdResult<Vec<_>>>()
}

pub fn query_ask(deps: Deps, token_id: TokenId) -> StdResult<Option<Ask>> {
asks().may_load(deps.storage, ask_key(&token_id))
}
Expand Down
31 changes: 31 additions & 0 deletions contracts/name-minter/src/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ const BIDDER2: &str = "bidder2";
const ADMIN: &str = "admin";
const ADMIN2: &str = "admin2";
const NAME: &str = "bobo";
const NAME2: &str = "mccool";
const VERIFIER: &str = "verifier";
const OPERATOR: &str = "operator";

Expand Down Expand Up @@ -861,6 +862,7 @@ mod query {
use cosmwasm_std::coin;
use cosmwasm_std::Coin;
use cosmwasm_std::StdResult;
use name_marketplace::msg::AskRenewPriceResponse;
use name_marketplace::msg::BidOffset;
use name_marketplace::state::Ask;
use sg721_base::msg::CollectionInfoResponse;
Expand Down Expand Up @@ -1191,6 +1193,35 @@ mod query {
assert_eq!(renewal_price.unwrap().amount, expect_price);
}

#[test]
fn multiple_renew_prices() {
// test that QueryRenewPrices returns multiple entires
let mut app = instantiate_contracts(None, None, None);

mint_and_list(&mut app, NAME, USER, None).unwrap();
mint_and_list(&mut app, NAME2, USER, None).unwrap();

// Amount to make it just above the char price
let bid_amount = 1_000_000_000u128 * 201u128;

update_block_time(&mut app, SECONDS_PER_YEAR - (60 * 60 * 24 * 31));

bid(&mut app, NAME, BIDDER, bid_amount);
bid(&mut app, NAME2, BIDDER, bid_amount);

update_block_time(&mut app, 60 * 60 * 24 * 31);

let result = app.wrap().query_wasm_smart::<Vec<AskRenewPriceResponse>>(
MKT,
&MarketplaceQueryMsg::AskRenewalPrices {
current_time: app.block_info().time,
token_ids: vec![NAME.to_string(), NAME2.to_string()],
},
);
println!("{:?}", result);
assert!(result.is_ok());
}

#[test]
fn renew_execute_msg() {
let mut app = instantiate_contracts(None, None, None);
Expand Down
24 changes: 23 additions & 1 deletion ts/src/NameMarketplace.client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import { CosmWasmClient, SigningCosmWasmClient, ExecuteResult } from "@cosmjs/cosmwasm-stargate";
import { StdFee } from "@cosmjs/amino";
import { Uint128, Decimal, InstantiateMsg, ExecuteMsg, QueryMsg, Timestamp, Uint64, Addr, BidOffset, NullableAsk, Ask, HooksResponse, TupleOfNullable_CoinAndNullable_Bid, Coin, Bid, ArrayOfAsk, NullableBid, ArrayOfBid, ConfigResponse, SudoParams } from "./NameMarketplace.types";
import { Uint128, Decimal, InstantiateMsg, ExecuteMsg, QueryMsg, Timestamp, Uint64, Addr, BidOffset, NullableAsk, Ask, HooksResponse, TupleOfNullable_CoinAndNullable_Bid, Coin, Bid, ArrayOfAskRenewPriceResponse, AskRenewPriceResponse, ArrayOfAsk, NullableBid, ArrayOfBid, ConfigResponse, SudoParams } from "./NameMarketplace.types";
export interface NameMarketplaceReadOnlyInterface {
contractAddress: string;
ask: ({
Expand Down Expand Up @@ -47,6 +47,13 @@ export interface NameMarketplaceReadOnlyInterface {
currentTime: Timestamp;
tokenId: string;
}) => Promise<TupleOfNullableCoinAndNullableBid>;
askRenewalPrices: ({
currentTime,
tokenIds
}: {
currentTime: Timestamp;
tokenIds: string[];
}) => Promise<ArrayOfAskRenewPriceResponse>;
bid: ({
bidder,
tokenId
Expand Down Expand Up @@ -131,6 +138,7 @@ export class NameMarketplaceQueryClient implements NameMarketplaceReadOnlyInterf
this.asksBySeller = this.asksBySeller.bind(this);
this.asksByRenewTime = this.asksByRenewTime.bind(this);
this.askRenewPrice = this.askRenewPrice.bind(this);
this.askRenewalPrices = this.askRenewalPrices.bind(this);
this.bid = this.bid.bind(this);
this.bidsByBidder = this.bidsByBidder.bind(this);
this.bids = this.bids.bind(this);
Expand Down Expand Up @@ -225,6 +233,20 @@ export class NameMarketplaceQueryClient implements NameMarketplaceReadOnlyInterf
}
});
};
askRenewalPrices = async ({
currentTime,
tokenIds
}: {
currentTime: Timestamp;
tokenIds: string[];
}): Promise<ArrayOfAskRenewPriceResponse> => {
return this.client.queryContractSmart(this.contractAddress, {
ask_renewal_prices: {
current_time: currentTime,
token_ids: tokenIds
}
});
};
bid = async ({
bidder,
tokenId
Expand Down
2 changes: 1 addition & 1 deletion ts/src/NameMarketplace.message-composer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import { MsgExecuteContractEncodeObject } from "cosmwasm";
import { MsgExecuteContract } from "cosmjs-types/cosmwasm/wasm/v1/tx";
import { toUtf8 } from "@cosmjs/encoding";
import { Uint128, Decimal, InstantiateMsg, ExecuteMsg, QueryMsg, Timestamp, Uint64, Addr, BidOffset, NullableAsk, Ask, HooksResponse, TupleOfNullable_CoinAndNullable_Bid, Coin, Bid, ArrayOfAsk, NullableBid, ArrayOfBid, ConfigResponse, SudoParams } from "./NameMarketplace.types";
import { Uint128, Decimal, InstantiateMsg, ExecuteMsg, QueryMsg, Timestamp, Uint64, Addr, BidOffset, NullableAsk, Ask, HooksResponse, TupleOfNullable_CoinAndNullable_Bid, Coin, Bid, ArrayOfAskRenewPriceResponse, AskRenewPriceResponse, ArrayOfAsk, NullableBid, ArrayOfBid, ConfigResponse, SudoParams } from "./NameMarketplace.types";
export interface NameMarketplaceMessage {
contractAddress: string;
sender: string;
Expand Down
11 changes: 11 additions & 0 deletions ts/src/NameMarketplace.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ export type QueryMsg = {
current_time: Timestamp;
token_id: string;
};
} | {
ask_renewal_prices: {
current_time: Timestamp;
token_ids: string[];
};
} | {
bid: {
bidder: string;
Expand Down Expand Up @@ -185,6 +190,12 @@ export interface Bid {
created_time: Timestamp;
token_id: string;
}
export type ArrayOfAskRenewPriceResponse = AskRenewPriceResponse[];
export interface AskRenewPriceResponse {
bid?: Bid | null;
price: Coin;
token_id: string;
}
export type ArrayOfAsk = Ask[];
export type NullableBid = Bid | null;
export type ArrayOfBid = Bid[];
Expand Down
Loading