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 the total supply of a coin #1356

Merged
merged 28 commits into from
Aug 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9de7624
add bank supply query
larry0x Jul 12, 2022
eb66d7a
export `SupplyResponse`
larry0x Jul 12, 2022
de275e8
add a helper function for querying supply
larry0x Jul 12, 2022
ddd4e3c
update schema
larry0x Jul 12, 2022
787cef0
update import
larry0x Jul 12, 2022
ce2577b
fix import
larry0x Jul 12, 2022
c1e0b78
add unit test for bank query helpers
larry0x Jul 13, 2022
0293397
add an entry to changelog
larry0x Jul 13, 2022
ecb68ea
Update packages/std/src/query/bank.rs
larry0x Jul 13, 2022
11cd9a5
clarify in comment the case if a denom doesn not exist
larry0x Jul 13, 2022
7d47853
add a unit test for querying the supply of a non-existing denom
larry0x Jul 13, 2022
d97d68b
revert changes made to `Cargo.toml` (not supposed to be pushed)
larry0x Jul 13, 2022
f15a25f
remove a contract unintentionally pushed (only used for local testing)
larry0x Jul 13, 2022
21e505c
format changelog
larry0x Jul 13, 2022
032c34c
revert unintential changes made to `Cargo.toml`
larry0x Jul 13, 2022
2aeca60
update schema
larry0x Jul 13, 2022
2cd39d1
resolve conflict
larry0x Jul 14, 2022
6f125e4
updat schema
larry0x Jul 14, 2022
abe4a4f
Update packages/std/src/query/bank.rs
webmaster128 Jul 14, 2022
0e619bc
Merge branch 'main' into larry/bank-query-total-supply
larry0x Jul 29, 2022
ef952c3
Merge branch 'main' into larry/bank-query-total-supply
uint Aug 23, 2022
162bcfd
fix poorly resolved merge conflict
uint Aug 23, 2022
61bcb49
hide BankQuery::Supply behind the `cosmwasm_1_1` feature
uint Aug 23, 2022
914f87c
fix Rust 1.63 lints
uint Aug 23, 2022
b440266
generate std schemas including cosmwasm_1_1 feature
uint Aug 23, 2022
3cdc9ef
test BankQuery::Supply using the reflect contract
uint Aug 23, 2022
74a68bc
update CI after introducing the `cosmwasm_1_1` feature
uint Aug 23, 2022
492e6d7
avoid BankQuerier.supplies ending up with invalid state
uint Aug 24, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -274,19 +274,19 @@ jobs:
- run:
name: Build library for native target (all features)
working_directory: ~/project/packages/std
command: cargo build --locked --features abort,iterator,staking,stargate
command: cargo build --locked --features abort,iterator,staking,stargate,cosmwasm_1_1
- run:
name: Build library for wasm target (all features)
working_directory: ~/project/packages/std
command: cargo wasm --locked --features abort,iterator,staking,stargate
command: cargo wasm --locked --features abort,iterator,staking,stargate,cosmwasm_1_1
- run:
name: Run unit tests (all features)
working_directory: ~/project/packages/std
command: cargo test --locked --features abort,iterator,staking,stargate
command: cargo test --locked --features abort,iterator,staking,stargate,cosmwasm_1_1
- run:
name: Build and run schema generator
working_directory: ~/project/packages/std
command: cargo schema --locked
command: cargo schema --features cosmwasm_1_1 --locked
- run:
name: Ensure schemas are up-to-date
command: |
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ and this project adheres to
on the other for all `Uint` and `Decimal` types
- cosmwasm-std: Implement `saturating_add`/`sub`/`mul` for
`Decimal`/`Decimal256`.
- cosmwasm-std: Implement `BankQuery::Supply` to allow querying the total supply
of a native token
- cosmwasm-std: Implement `MIN` const value for all `Uint` and `Decimal` types
- cosmwasm-std: Implement `checked_div_euclid` for `Uint256`/`Uint512`
- cosmwasm-std: Add `QuerierWrapper::query_wasm_contract_info` - this is just a
Expand Down
2 changes: 1 addition & 1 deletion contracts/reflect/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ backtraces = ["cosmwasm-std/backtraces", "cosmwasm-vm/backtraces"]

[dependencies]
cosmwasm-schema = { path = "../../packages/schema" }
cosmwasm-std = { path = "../../packages/std", default-features = false, features = ["staking", "stargate"] }
cosmwasm-std = { path = "../../packages/std", default-features = false, features = ["staking", "stargate", "cosmwasm_1_1"] }
cosmwasm-storage = { path = "../../packages/storage", default-features = false }
schemars = "0.8.1"
serde = { version = "=1.0.103", default-features = false, features = ["derive"] }
Expand Down
21 changes: 21 additions & 0 deletions contracts/reflect/schema/reflect.json
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,27 @@
"definitions": {
"BankQuery": {
"oneOf": [
{
"description": "This calls into the native bank module for querying the total supply of one denomination. It does the same as the SupplyOf call in Cosmos SDK's RPC API. Return value is of type SupplyResponse.",
"type": "object",
"required": [
"supply"
],
"properties": {
"supply": {
"type": "object",
"required": [
"denom"
],
"properties": {
"denom": {
"type": "string"
}
}
}
},
"additionalProperties": false
},
{
"description": "This calls into the native bank module for one denomination Return value is BalanceResponse",
"type": "object",
Expand Down
50 changes: 46 additions & 4 deletions contracts/reflect/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@
//! 4. Anywhere you see query(&deps, ...) you must replace it with query(&mut deps, ...)

use cosmwasm_std::{
coin, coins, from_binary, BankMsg, Binary, Coin, ContractResult, Event, Reply, Response,
StakingMsg, SubMsg, SubMsgResponse, SubMsgResult, SystemResult,
coin, coins, from_binary, BankMsg, BankQuery, Binary, Coin, ContractResult, Event,
QueryRequest, Reply, Response, StakingMsg, SubMsg, SubMsgResponse, SubMsgResult,
SupplyResponse, SystemResult,
};
use cosmwasm_vm::{
testing::{
Expand All @@ -30,8 +31,8 @@ use cosmwasm_vm::{
};

use reflect::msg::{
CapitalizedResponse, CustomMsg, ExecuteMsg, InstantiateMsg, OwnerResponse, QueryMsg,
SpecialQuery,
CapitalizedResponse, ChainResponse, CustomMsg, ExecuteMsg, InstantiateMsg, OwnerResponse,
QueryMsg, SpecialQuery,
};
use reflect::testing::custom_query_execute;

Expand All @@ -56,6 +57,19 @@ pub fn mock_dependencies_with_custom_querier(
}
}

pub fn mock_dependencies_with_custom_querier_and_balances(
balances: &[(&str, &[Coin])],
) -> Backend<MockApi, MockStorage, MockQuerier<SpecialQuery>> {
let custom_querier: MockQuerier<SpecialQuery> = MockQuerier::new(balances)
.with_custom_handler(|query| SystemResult::Ok(custom_query_execute(query)));

Backend {
api: MockApi::default(),
storage: MockStorage::default(),
querier: custom_querier,
}
}

#[test]
fn proper_initialization() {
let mut deps = mock_instance(WASM, &[]);
Expand Down Expand Up @@ -166,6 +180,34 @@ fn transfer_requires_owner() {
assert!(msg.contains("Permission denied: the sender is not the current owner"));
}

#[test]
fn supply_query() {
// stub gives us defaults. Consume it and override...
let custom = mock_dependencies_with_custom_querier_and_balances(&[
("ryan_reynolds", &[coin(5, "ATOM"), coin(10, "OSMO")]),
("huge_ackman", &[coin(15, "OSMO"), coin(5, "BTC")]),
]);
// we cannot use mock_instance, so we just copy and modify code from cosmwasm_vm::testing
let (instance_options, memory_limit) = mock_instance_options();
let mut deps = Instance::from_code(WASM, custom, instance_options, memory_limit).unwrap();

// we don't even initialize, just trigger a query
let res = query(
&mut deps,
mock_env(),
QueryMsg::Chain {
request: QueryRequest::Bank(BankQuery::Supply {
denom: "OSMO".to_string(),
}),
},
)
.unwrap();

let res: ChainResponse = from_binary(&res).unwrap();
let res: SupplyResponse = from_binary(&res.data).unwrap();
assert_eq!(res.amount, coin(25, "OSMO"));
}

#[test]
fn dispatch_custom_query() {
// stub gives us defaults. Consume it and override...
Expand Down
2 changes: 1 addition & 1 deletion devtools/check_workspace.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ cargo fmt
cargo wasm-debug
cargo wasm-debug --features iterator,staking,stargate
cargo clippy --all-targets --features iterator,staking,stargate -- -D warnings
cargo schema
cargo schema --features cosmwasm_1_1
)
(cd packages/storage && cargo build && cargo clippy --all-targets --features iterator -- -D warnings)
(cd packages/schema && cargo build && cargo clippy --all-targets -- -D warnings)
Expand Down
2 changes: 1 addition & 1 deletion packages/check/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use colored::Colorize;
use cosmwasm_vm::capabilities_from_csv;
use cosmwasm_vm::internals::{check_wasm, compile};

const DEFAULT_AVAILABLE_CAPABILITIES: &str = "iterator,staking,stargate";
const DEFAULT_AVAILABLE_CAPABILITIES: &str = "iterator,staking,stargate,cosmwasm_1_1";

pub fn main() {
let matches = App::new("Contract checking")
Expand Down
3 changes: 3 additions & 0 deletions packages/std/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ stargate = []
# ibc3 extends ibc messages with ibc-v3 only features. This should only be enabled on contracts
# that require these types. Without this, they get the smaller ibc-v1 API.
ibc3 = ["stargate"]
# This feature makes `BankQuery::Supply` available for the contract to call, but requires
# the host blockchain to run CosmWasm `1.1.0` or higher.
cosmwasm_1_1 = []

[dependencies]
base64 = "0.13.0"
Expand Down
21 changes: 21 additions & 0 deletions packages/std/schema/query_request.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,27 @@
"definitions": {
"BankQuery": {
"oneOf": [
{
"description": "This calls into the native bank module for querying the total supply of one denomination. It does the same as the SupplyOf call in Cosmos SDK's RPC API. Return value is of type SupplyResponse.",
"type": "object",
"required": [
"supply"
],
"properties": {
"supply": {
"type": "object",
"required": [
"denom"
],
"properties": {
"denom": {
"type": "string"
}
}
}
},
"additionalProperties": false
},
{
"description": "This calls into the native bank module for one denomination Return value is BalanceResponse",
"type": "object",
Expand Down
4 changes: 4 additions & 0 deletions packages/std/src/exports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ extern "C" fn requires_staking() -> () {}
#[no_mangle]
extern "C" fn requires_stargate() -> () {}

#[cfg(feature = "cosmwasm_1_1")]
#[no_mangle]
extern "C" fn requires_cosmwasm_1_1() -> () {}

/// interface_version_* exports mark which Wasm VM interface level this contract is compiled for.
/// They can be checked by cosmwasm_vm.
/// Update this whenever the Wasm VM interface breaks.
Expand Down
2 changes: 2 additions & 0 deletions packages/std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ pub use crate::math::{
Decimal, Decimal256, Decimal256RangeExceeded, DecimalRangeExceeded, Fraction, Isqrt, Uint128,
Uint256, Uint512, Uint64,
};
#[cfg(feature = "cosmwasm_1_1")]
pub use crate::query::SupplyResponse;
pub use crate::query::{
AllBalanceResponse, BalanceResponse, BankQuery, ContractInfoResponse, CustomQuery,
QueryRequest, WasmQuery,
Expand Down
101 changes: 96 additions & 5 deletions packages/std/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ use crate::ibc::{
IbcEndpoint, IbcOrder, IbcPacket, IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg,
IbcTimeoutBlock,
};
use crate::math::Uint128;
#[cfg(feature = "cosmwasm_1_1")]
use crate::query::SupplyResponse;
use crate::query::{
AllBalanceResponse, BalanceResponse, BankQuery, CustomQuery, QueryRequest, WasmQuery,
};
Expand Down Expand Up @@ -452,7 +455,7 @@ impl<C: DeserializeOwned> MockQuerier<C> {
addr: impl Into<String>,
balance: Vec<Coin>,
) -> Option<Vec<Coin>> {
self.bank.balances.insert(addr.into(), balance)
self.bank.update_balance(addr, balance)
}

#[cfg(feature = "staking")]
Expand Down Expand Up @@ -564,20 +567,68 @@ impl Default for WasmQuerier {

#[derive(Clone, Default)]
pub struct BankQuerier {
#[allow(dead_code)]
/// HashMap<denom, amount>
supplies: HashMap<String, Uint128>,
/// HashMap<address, coins>
balances: HashMap<String, Vec<Coin>>,
}

impl BankQuerier {
pub fn new(balances: &[(&str, &[Coin])]) -> Self {
let mut map = HashMap::new();
for (addr, coins) in balances.iter() {
map.insert(addr.to_string(), coins.to_vec());
let balances: HashMap<_, _> = balances
.iter()
.map(|(s, c)| (s.to_string(), c.to_vec()))
.collect();

BankQuerier {
supplies: Self::calculate_supplies(&balances),
balances,
}
BankQuerier { balances: map }
}

pub fn update_balance(
&mut self,
addr: impl Into<String>,
balance: Vec<Coin>,
) -> Option<Vec<Coin>> {
let result = self.balances.insert(addr.into(), balance);
self.supplies = Self::calculate_supplies(&self.balances);

result
}

fn calculate_supplies(balances: &HashMap<String, Vec<Coin>>) -> HashMap<String, Uint128> {
let mut supplies = HashMap::new();

let all_coins = balances.iter().flat_map(|(_, coins)| coins);

for coin in all_coins {
*supplies
.entry(coin.denom.clone())
.or_insert_with(Uint128::zero) += coin.amount;
}

supplies
}

pub fn query(&self, request: &BankQuery) -> QuerierResult {
let contract_result: ContractResult<Binary> = match request {
#[cfg(feature = "cosmwasm_1_1")]
BankQuery::Supply { denom } => {
let amount = self
.supplies
.get(denom)
.cloned()
.unwrap_or_else(Uint128::zero);
let bank_res = SupplyResponse {
amount: Coin {
amount,
denom: denom.to_string(),
},
};
to_binary(&bank_res).into()
}
BankQuery::Balance { address, denom } => {
// proper error on not found, serialize result on found
let amount = self
Expand Down Expand Up @@ -1060,6 +1111,46 @@ mod tests {
assert_eq!(res.unwrap_err(), VerificationError::InvalidPubkeyFormat);
}

#[cfg(feature = "cosmwasm_1_1")]
#[test]
fn bank_querier_supply() {
let addr1 = String::from("foo");
let balance1 = vec![coin(123, "ELF"), coin(777, "FLY")];

let addr2 = String::from("bar");
let balance2 = coins(321, "ELF");

let bank = BankQuerier::new(&[(&addr1, &balance1), (&addr2, &balance2)]);

let elf = bank
.query(&BankQuery::Supply {
denom: "ELF".to_string(),
})
.unwrap()
.unwrap();
let res: SupplyResponse = from_binary(&elf).unwrap();
assert_eq!(res.amount, coin(444, "ELF"));

let fly = bank
.query(&BankQuery::Supply {
denom: "FLY".to_string(),
})
.unwrap()
.unwrap();
let res: SupplyResponse = from_binary(&fly).unwrap();
assert_eq!(res.amount, coin(777, "FLY"));

// if a denom does not exist, should return zero amount, instead of throwing an error
let atom = bank
.query(&BankQuery::Supply {
denom: "ATOM".to_string(),
})
.unwrap()
.unwrap();
let res: SupplyResponse = from_binary(&atom).unwrap();
assert_eq!(res.amount, coin(0, "ATOM"));
}

#[test]
fn bank_querier_all_balances() {
let addr = String::from("foobar");
Expand Down
15 changes: 15 additions & 0 deletions packages/std/src/query/bank.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ use crate::Coin;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum BankQuery {
/// This calls into the native bank module for querying the total supply of one denomination.
/// It does the same as the SupplyOf call in Cosmos SDK's RPC API.
/// Return value is of type SupplyResponse.
#[cfg(feature = "cosmwasm_1_1")]
Supply { denom: String },
/// This calls into the native bank module for one denomination
/// Return value is BalanceResponse
Balance { address: String, denom: String },
Expand All @@ -16,6 +21,16 @@ pub enum BankQuery {
AllBalances { address: String },
}

#[cfg(feature = "cosmwasm_1_1")]
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
webmaster128 marked this conversation as resolved.
Show resolved Hide resolved
#[non_exhaustive]
pub struct SupplyResponse {
/// Always returns a Coin with the requested denom.
/// This will be of zero amount if the denom does not exist.
pub amount: Coin,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct BalanceResponse {
Expand Down
Loading