Repository: CosmWasm/cosmwasm
Pull request #1703 (2023-05-31 14:37:38)

Add AllDenomMetadata BankQuery
Based on #1647 
I simplified the `PageRequest` and improved the mock

See also https://github.com/CosmWasm/wasmd/pull/1426 and https://github.com/CosmWasm/wasmvm/pull/430


Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 0335b7ae7..60bd62878 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -356,15 +356,15 @@ jobs:
       - run:
           name: Build library for native target (all features)
           working_directory: ~/project/packages/std
-          command: cargo build --locked --features abort,iterator,staking,stargate,cosmwasm_1_2
+          command: cargo build --locked --features abort,iterator,staking,stargate,cosmwasm_1_3
       - run:
           name: Build library for wasm target (all features)
           working_directory: ~/project/packages/std
-          command: cargo wasm --locked --features abort,iterator,staking,stargate,cosmwasm_1_2
+          command: cargo wasm --locked --features abort,iterator,staking,stargate,cosmwasm_1_3
       - run:
           name: Run unit tests (all features)
           working_directory: ~/project/packages/std
-          command: cargo test --locked --features abort,iterator,staking,stargate,cosmwasm_1_2
+          command: cargo test --locked --features abort,iterator,staking,stargate,cosmwasm_1_3
       - save_cache:
           paths:
             - /usr/local/cargo/registry
@@ -907,7 +907,7 @@ jobs:
       - run:
           name: Clippy linting on std (all feature flags)
           working_directory: ~/project/packages/std
-          command: cargo clippy --all-targets --features abort,iterator,staking,stargate,cosmwasm_1_2 -- -D warnings
+          command: cargo clippy --all-targets --features abort,iterator,staking,stargate,cosmwasm_1_3 -- -D warnings
       - run:
           name: Clippy linting on storage (no feature flags)
           working_directory: ~/project/packages/storage
@@ -984,7 +984,7 @@ jobs:
             CRYPTO=" cargo tarpaulin --skip-clean --out Xml --output-dir reports/crypto  --packages cosmwasm-crypto"
             DERIVE=" cargo tarpaulin --skip-clean --out Xml --output-dir reports/derive  --packages cosmwasm-derive"
             SCHEMA=" cargo tarpaulin --skip-clean --out Xml --output-dir reports/schema  --packages cosmwasm-schema"
-            STD="    cargo tarpaulin --skip-clean --out Xml --output-dir reports/std     --packages cosmwasm-std     --features abort,iterator,staking,stargate,cosmwasm_1_2"
+            STD="    cargo tarpaulin --skip-clean --out Xml --output-dir reports/std     --packages cosmwasm-std     --features abort,iterator,staking,stargate,cosmwasm_1_3"
             STORAGE="cargo tarpaulin --skip-clean --out Xml --output-dir reports/storage --packages cosmwasm-storage"
             docker run --security-opt seccomp=unconfined -v "${PWD}:/volume" xd009642/tarpaulin:0.21.0 \
               sh -c "$CRYPTO && $DERIVE && $SCHEMA && $STD && $STORAGE"
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 4fc9b2577..3105948a8 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,3 +1,3 @@
 {
-  "rust-analyzer.cargo.features": ["abort", "stargate", "staking", "cosmwasm_1_2"]
+  "rust-analyzer.cargo.features": ["abort", "stargate", "staking", "cosmwasm_1_3"]
 }
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b1682da85..155f6f08f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,16 +8,20 @@ and this project adheres to
 
 ### Added
 
+- cosmwasm-std: Implement `BankQuery::AllDenomMetadata` to allow querying all
+  the denom metadata and `BankQuery::DenomMetadata` to query a specific one. In
+  order to use this query in a contract, the `cosmwasm_1_3` feature needs to be
+  enabled for the `cosmwasm_std` dependency. This makes the contract
+  incompatible with chains running anything lower than CosmWasm `1.3.0`.
+  ([#1647])
 - cosmwasm-vm: Add `Cache::save_wasm_unchecked` to save Wasm blobs that have
   been checked before. This is useful for state-sync where we know the Wasm code
   was checked when it was first uploaded. ([#1635])
 - cosmwasm-std: Add `FromStr` impl for `Coin`. ([#1684])
-- cosmwasm-std: Add `Decimal::bps` and `Decimal256::bps` to create a decimal
-  from a basis point value ([#1715]).
 
 [#1635]: https://github.com/CosmWasm/cosmwasm/pull/1635
+[#1647]: https://github.com/CosmWasm/cosmwasm/pull/1647
 [#1684]: https://github.com/CosmWasm/cosmwasm/pull/1684
-[#1715]: https://github.com/CosmWasm/cosmwasm/pull/1715
 
 ### Changed
 
@@ -37,8 +41,6 @@ and this project adheres to
 - cosmwasm-vm: Add `.wasm` extension to stored wasm files ([#1686]).
 - cosmwasm-vm: Upgrade Wasmer to version 3.3.0.
 - cosmwasm-check: Update clap dependency to version 4 ([#1677])
-- cosmwasm-std: Coin uses shorter `Coin { 123 "ucosm" }` format for Debug
-  ([#1704])
 
 [#1511]: https://github.com/CosmWasm/cosmwasm/issues/1511
 [#1629]: https://github.com/CosmWasm/cosmwasm/pull/1629
@@ -47,7 +49,6 @@ and this project adheres to
 [#1667]: https://github.com/CosmWasm/cosmwasm/pull/1667
 [#1677]: https://github.com/CosmWasm/cosmwasm/pull/1677
 [#1686]: https://github.com/CosmWasm/cosmwasm/pull/1686
-[#1704]: https://github.com/CosmWasm/cosmwasm/pull/1704
 
 ### Deprecated
 
diff --git a/MIGRATING.md b/MIGRATING.md
index 0000f999f..ac9d8dd34 100644
--- a/MIGRATING.md
+++ b/MIGRATING.md
@@ -4,6 +4,33 @@ This guide explains what is needed to upgrade contracts when migrating over
 major releases of `cosmwasm`. Note that you can also view the
 [complete CHANGELOG](./CHANGELOG.md) to understand the differences.
 
+## 1.2.x -> 1.3.0
+
+- Update `cosmwasm-*` dependencies in Cargo.toml (skip the ones you don't use):
+
+  ```
+  [dependencies]
+  cosmwasm-std = "1.3.0"
+  cosmwasm-storage = "1.3.0"
+  # ...
+
+  [dev-dependencies]
+  cosmwasm-schema = "1.3.0"
+  cosmwasm-vm = "1.3.0"
+  # ...
+  ```
+
+- If you want to use a feature that is only available on CosmWasm 1.3+ chains,
+  use this feature:
+
+  ```diff
+  -cosmwasm-std = { version = "1.3.0", features = ["stargate"] }
+  +cosmwasm-std = { version = "1.3.0", features = ["stargate", "cosmwasm_1_3"] }
+  ```
+
+  Please note that `cosmwasm_1_2` implies `cosmwasm_1_1`, and `cosmwasm_1_3`
+  implies `cosmwasm_1_2`, and so on, so there is no need to set multiple.
+
 ## 1.1.x -> 1.2.0
 
 - Update `cosmwasm-*` dependencies in Cargo.toml (skip the ones you don't use):
diff --git a/contracts/cyberpunk/Cargo.toml b/contracts/cyberpunk/Cargo.toml
index 67efa29d0..c2db096f2 100644
--- a/contracts/cyberpunk/Cargo.toml
+++ b/contracts/cyberpunk/Cargo.toml
@@ -30,7 +30,7 @@ backtraces = ["cosmwasm-std/backtraces", "cosmwasm-vm/backtraces"]
 
 [dependencies]
 cosmwasm-schema = { path = "../../packages/schema" }
-cosmwasm-std = { path = "../../packages/std", default-features = false, features = ["abort"] }
+cosmwasm-std = { path = "../../packages/std", default-features = false, features = ["abort", "cosmwasm_1_3"] }
 rust-argon2 = "0.8"
 thiserror = "1.0.26"
 
diff --git a/contracts/cyberpunk/schema/cyberpunk.json b/contracts/cyberpunk/schema/cyberpunk.json
index dcce96d60..ccb550b1a 100644
--- a/contracts/cyberpunk/schema/cyberpunk.json
+++ b/contracts/cyberpunk/schema/cyberpunk.json
@@ -199,12 +199,198 @@
           }
         },
         "additionalProperties": false
+      },
+      {
+        "description": "Queries `AllDenomMetadata` from the bank module repeatedly and returns all entries",
+        "type": "object",
+        "required": [
+          "denoms"
+        ],
+        "properties": {
+          "denoms": {
+            "type": "object",
+            "additionalProperties": false
+          }
+        },
+        "additionalProperties": false
+      },
+      {
+        "description": "Queries `DenomMetadata` from the bank module and returns the result",
+        "type": "object",
+        "required": [
+          "denom"
+        ],
+        "properties": {
+          "denom": {
+            "type": "object",
+            "required": [
+              "denom"
+            ],
+            "properties": {
+              "denom": {
+                "type": "string"
+              }
+            },
+            "additionalProperties": false
+          }
+        },
+        "additionalProperties": false
       }
     ]
   },
   "migrate": null,
   "sudo": null,
   "responses": {
+    "denom": {
+      "$schema": "http://json-schema.org/draft-07/schema#",
+      "title": "DenomMetadata",
+      "description": "Replicates the cosmos-sdk bank module Metadata type",
+      "type": "object",
+      "required": [
+        "base",
+        "denom_units",
+        "description",
+        "display",
+        "name",
+        "symbol",
+        "uri",
+        "uri_hash"
+      ],
+      "properties": {
+        "base": {
+          "type": "string"
+        },
+        "denom_units": {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/DenomUnit"
+          }
+        },
+        "description": {
+          "type": "string"
+        },
+        "display": {
+          "type": "string"
+        },
+        "name": {
+          "type": "string"
+        },
+        "symbol": {
+          "type": "string"
+        },
+        "uri": {
+          "type": "string"
+        },
+        "uri_hash": {
+          "type": "string"
+        }
+      },
+      "definitions": {
+        "DenomUnit": {
+          "description": "Replicates the cosmos-sdk bank module DenomUnit type",
+          "type": "object",
+          "required": [
+            "aliases",
+            "denom",
+            "exponent"
+          ],
+          "properties": {
+            "aliases": {
+              "type": "array",
+              "items": {
+                "type": "string"
+              }
+            },
+            "denom": {
+              "type": "string"
+            },
+            "exponent": {
+              "type": "integer",
+              "format": "uint32",
+              "minimum": 0.0
+            }
+          }
+        }
+      }
+    },
+    "denoms": {
+      "$schema": "http://json-schema.org/draft-07/schema#",
+      "title": "Array_of_DenomMetadata",
+      "type": "array",
+      "items": {
+        "$ref": "#/definitions/DenomMetadata"
+      },
+      "definitions": {
+        "DenomMetadata": {
+          "description": "Replicates the cosmos-sdk bank module Metadata type",
+          "type": "object",
+          "required": [
+            "base",
+            "denom_units",
+            "description",
+            "display",
+            "name",
+            "symbol",
+            "uri",
+            "uri_hash"
+          ],
+          "properties": {
+            "base": {
+              "type": "string"
+            },
+            "denom_units": {
+              "type": "array",
+              "items": {
+                "$ref": "#/definitions/DenomUnit"
+              }
+            },
+            "description": {
+              "type": "string"
+            },
+            "display": {
+              "type": "string"
+            },
+            "name": {
+              "type": "string"
+            },
+            "symbol": {
+              "type": "string"
+            },
+            "uri": {
+              "type": "string"
+            },
+            "uri_hash": {
+              "type": "string"
+            }
+          }
+        },
+        "DenomUnit": {
+          "description": "Replicates the cosmos-sdk bank module DenomUnit type",
+          "type": "object",
+          "required": [
+            "aliases",
+            "denom",
+            "exponent"
+          ],
+          "properties": {
+            "aliases": {
+              "type": "array",
+              "items": {
+                "type": "string"
+              }
+            },
+            "denom": {
+              "type": "string"
+            },
+            "exponent": {
+              "type": "integer",
+              "format": "uint32",
+              "minimum": 0.0
+            }
+          }
+        }
+      }
+    },
     "mirror_env": {
       "$schema": "http://json-schema.org/draft-07/schema#",
       "title": "Env",
diff --git a/contracts/cyberpunk/schema/raw/query.json b/contracts/cyberpunk/schema/raw/query.json
index 18c07a562..47b493442 100644
--- a/contracts/cyberpunk/schema/raw/query.json
+++ b/contracts/cyberpunk/schema/raw/query.json
@@ -15,6 +15,42 @@
         }
       },
       "additionalProperties": false
+    },
+    {
+      "description": "Queries `AllDenomMetadata` from the bank module repeatedly and returns all entries",
+      "type": "object",
+      "required": [
+        "denoms"
+      ],
+      "properties": {
+        "denoms": {
+          "type": "object",
+          "additionalProperties": false
+        }
+      },
+      "additionalProperties": false
+    },
+    {
+      "description": "Queries `DenomMetadata` from the bank module and returns the result",
+      "type": "object",
+      "required": [
+        "denom"
+      ],
+      "properties": {
+        "denom": {
+          "type": "object",
+          "required": [
+            "denom"
+          ],
+          "properties": {
+            "denom": {
+              "type": "string"
+            }
+          },
+          "additionalProperties": false
+        }
+      },
+      "additionalProperties": false
     }
   ]
 }
diff --git a/contracts/cyberpunk/schema/raw/response_to_denom.json b/contracts/cyberpunk/schema/raw/response_to_denom.json
new file mode 100644
index 000000000..b541da804
--- /dev/null
+++ b/contracts/cyberpunk/schema/raw/response_to_denom.json
@@ -0,0 +1,72 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "title": "DenomMetadata",
+  "description": "Replicates the cosmos-sdk bank module Metadata type",
+  "type": "object",
+  "required": [
+    "base",
+    "denom_units",
+    "description",
+    "display",
+    "name",
+    "symbol",
+    "uri",
+    "uri_hash"
+  ],
+  "properties": {
+    "base": {
+      "type": "string"
+    },
+    "denom_units": {
+      "type": "array",
+      "items": {
+        "$ref": "#/definitions/DenomUnit"
+      }
+    },
+    "description": {
+      "type": "string"
+    },
+    "display": {
+      "type": "string"
+    },
+    "name": {
+      "type": "string"
+    },
+    "symbol": {
+      "type": "string"
+    },
+    "uri": {
+      "type": "string"
+    },
+    "uri_hash": {
+      "type": "string"
+    }
+  },
+  "definitions": {
+    "DenomUnit": {
+      "description": "Replicates the cosmos-sdk bank module DenomUnit type",
+      "type": "object",
+      "required": [
+        "aliases",
+        "denom",
+        "exponent"
+      ],
+      "properties": {
+        "aliases": {
+          "type": "array",
+          "items": {
+            "type": "string"
+          }
+        },
+        "denom": {
+          "type": "string"
+        },
+        "exponent": {
+          "type": "integer",
+          "format": "uint32",
+          "minimum": 0.0
+        }
+      }
+    }
+  }
+}
diff --git a/contracts/cyberpunk/schema/raw/response_to_denoms.json b/contracts/cyberpunk/schema/raw/response_to_denoms.json
new file mode 100644
index 000000000..e8fcd9a16
--- /dev/null
+++ b/contracts/cyberpunk/schema/raw/response_to_denoms.json
@@ -0,0 +1,78 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "title": "Array_of_DenomMetadata",
+  "type": "array",
+  "items": {
+    "$ref": "#/definitions/DenomMetadata"
+  },
+  "definitions": {
+    "DenomMetadata": {
+      "description": "Replicates the cosmos-sdk bank module Metadata type",
+      "type": "object",
+      "required": [
+        "base",
+        "denom_units",
+        "description",
+        "display",
+        "name",
+        "symbol",
+        "uri",
+        "uri_hash"
+      ],
+      "properties": {
+        "base": {
+          "type": "string"
+        },
+        "denom_units": {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/DenomUnit"
+          }
+        },
+        "description": {
+          "type": "string"
+        },
+        "display": {
+          "type": "string"
+        },
+        "name": {
+          "type": "string"
+        },
+        "symbol": {
+          "type": "string"
+        },
+        "uri": {
+          "type": "string"
+        },
+        "uri_hash": {
+          "type": "string"
+        }
+      }
+    },
+    "DenomUnit": {
+      "description": "Replicates the cosmos-sdk bank module DenomUnit type",
+      "type": "object",
+      "required": [
+        "aliases",
+        "denom",
+        "exponent"
+      ],
+      "properties": {
+        "aliases": {
+          "type": "array",
+          "items": {
+            "type": "string"
+          }
+        },
+        "denom": {
+          "type": "string"
+        },
+        "exponent": {
+          "type": "integer",
+          "format": "uint32",
+          "minimum": 0.0
+        }
+      }
+    }
+  }
+}
diff --git a/contracts/cyberpunk/src/contract.rs b/contracts/cyberpunk/src/contract.rs
index 55acddaf6..7de7d80fd 100644
--- a/contracts/cyberpunk/src/contract.rs
+++ b/contracts/cyberpunk/src/contract.rs
@@ -1,6 +1,6 @@
 use cosmwasm_std::{
-    entry_point, to_binary, Api, Deps, DepsMut, Empty, Env, MessageInfo, QueryResponse, Response,
-    StdError, StdResult, WasmMsg,
+    entry_point, to_binary, Api, DenomMetadata, Deps, DepsMut, Empty, Env, MessageInfo,
+    PageRequest, QueryResponse, Response, StdError, StdResult, WasmMsg,
 };
 
 use crate::errors::ContractError;
@@ -179,11 +179,13 @@ fn execute_debug(api: &dyn Api) -> Result<Response, ContractError> {
 }
 
 #[entry_point]
-pub fn query(_deps: Deps, env: Env, msg: QueryMsg) -> StdResult<QueryResponse> {
+pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult<QueryResponse> {
     use QueryMsg::*;
 
     match msg {
         MirrorEnv {} => to_binary(&query_mirror_env(env)),
+        Denoms {} => to_binary(&query_denoms(deps)?),
+        Denom { denom } => to_binary(&query_denom(deps, denom)?),
     }
 }
 
@@ -191,13 +193,40 @@ fn query_mirror_env(env: Env) -> Env {
     env
 }
 
+fn query_denoms(deps: Deps) -> StdResult<Vec<DenomMetadata>> {
+    const PAGE_SIZE: u32 = 10;
+    let mut next_key = None;
+    let mut all_metadata = Vec::new();
+    loop {
+        let page = deps.querier.query_all_denom_metadata(PageRequest {
+            key: next_key,
+            limit: PAGE_SIZE,
+            reverse: false,
+        })?;
+
+        let len = page.metadata.len() as u32;
+        all_metadata.extend(page.metadata.into_iter());
+        next_key = page.next_key;
+
+        if next_key.is_none() || len < PAGE_SIZE {
+            break;
+        }
+    }
+
+    Ok(all_metadata)
+}
+
+fn query_denom(deps: Deps, denom: String) -> StdResult<DenomMetadata> {
+    deps.querier.query_denom_metadata(denom)
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
     use cosmwasm_std::testing::{
         mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage,
     };
-    use cosmwasm_std::OwnedDeps;
+    use cosmwasm_std::{from_binary, DenomMetadata, DenomUnit, OwnedDeps};
 
     fn setup() -> OwnedDeps<MockStorage, MockApi, MockQuerier> {
         let mut deps = mock_dependencies();
@@ -220,4 +249,47 @@ mod tests {
         let msg = ExecuteMsg::Debug {};
         execute(deps.as_mut(), mock_env(), mock_info("caller", &[]), msg).unwrap();
     }
+
+    #[test]
+    fn query_denoms_works() {
+        let mut deps = setup();
+
+        deps.querier.set_denom_metadata(
+            &(0..98)
+                .map(|i| DenomMetadata {
+                    symbol: format!("FOO{i}"),
+                    name: "Foo".to_string(),
+                    description: "Foo coin".to_string(),
+                    denom_units: vec![DenomUnit {
+                        denom: "ufoo".to_string(),
+                        exponent: 8,
+                        aliases: vec!["microfoo".to_string(), "foobar".to_string()],
+                    }],
+                    display: "FOO".to_string(),
+                    base: format!("ufoo{i}"),
+                    uri: "https://foo.bar".to_string(),
+                    uri_hash: "foo".to_string(),
+                })
+                .collect::<Vec<_>>(),
+        );
+
+        let symbols: Vec<DenomMetadata> =
+            from_binary(&query(deps.as_ref(), mock_env(), QueryMsg::Denoms {}).unwrap()).unwrap();
+
+        assert_eq!(symbols.len(), 98);
+
+        let denom: DenomMetadata = from_binary(
+            &query(
+                deps.as_ref(),
+                mock_env(),
+                QueryMsg::Denom {
+                    denom: "ufoo0".to_string(),
+                },
+            )
+            .unwrap(),
+        )
+        .unwrap();
+
+        assert_eq!(denom.symbol, "FOO0");
+    }
 }
diff --git a/contracts/cyberpunk/src/msg.rs b/contracts/cyberpunk/src/msg.rs
index 7abb88200..8e9ee80bf 100644
--- a/contracts/cyberpunk/src/msg.rs
+++ b/contracts/cyberpunk/src/msg.rs
@@ -1,4 +1,5 @@
 use cosmwasm_schema::{cw_serde, QueryResponses};
+use cosmwasm_std::DenomMetadata;
 
 #[cw_serde]
 pub enum ExecuteMsg {
@@ -38,4 +39,12 @@ pub enum QueryMsg {
     /// Returns the env for testing
     #[returns(cosmwasm_std::Env)]
     MirrorEnv {},
+
+    /// Queries `AllDenomMetadata` from the bank module repeatedly and returns all entries
+    #[returns(Vec<DenomMetadata>)]
+    Denoms {},
+
+    /// Queries `DenomMetadata` from the bank module and returns the result
+    #[returns(DenomMetadata)]
+    Denom { denom: String },
 }
diff --git a/docs/CAPABILITIES-BUILT-IN.md b/docs/CAPABILITIES-BUILT-IN.md
index 8fdd679fe..d82297180 100644
--- a/docs/CAPABILITIES-BUILT-IN.md
+++ b/docs/CAPABILITIES-BUILT-IN.md
@@ -15,3 +15,6 @@ might define others.
   CosmWasm `1.1.0` or higher support this.
 - `cosmwasm_1_2` enables the `GovMsg::VoteWeighted` and `WasmMsg::Instantiate2`
   messages. Only chains running CosmWasm `1.2.0` or higher support this.
+- `cosmwasm_1_3` enables the `BankQuery::AllDenomMetadata` and
+  `BankQuery::DenomMetadata` queries. Only chains running CosmWasm `1.3.0` or
+  higher support this.
diff --git a/docs/MESSAGE_TYPES.md b/docs/MESSAGE_TYPES.md
index 30d4ce91f..05ff61a0c 100644
--- a/docs/MESSAGE_TYPES.md
+++ b/docs/MESSAGE_TYPES.md
@@ -9,41 +9,38 @@ this reason, the CosmWasm standard library `cosmwasm-std` ships types that
 ensure good user experience in JSON. The following table shows both standard
 Rust types as well as `cosmwasm_std` types and how they are encoded in JSON.
 
-| Rust type           | JSON type[^1]                             | Example                                                                               | Note                                                                                                                                                                                   |
-| ------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| bool                | `true` or `false`                         | `true`                                                                                |                                                                                                                                                                                        |
-| u32/i32             | number                                    | `123`                                                                                 |                                                                                                                                                                                        |
-| u64/i64             | number                                    | `123456`                                                                              | Supported in Rust and Go. Other implementations (`jq`, `JavaScript`) do not support the full uint64/int64 range.                                                                       |
-| u128/i128           | string                                    | `"340282366920938463463374607431768211455", "-2766523308300312711084346401884294402"` | 🚫 Strongly discouraged because the JSON type in serde-json-wasm is wrong and will change. See [Dev Note #4: u128/i128 serialization][dev-note-4].                                     |
-| usize/isize         | number                                    | `123456`                                                                              | 🚫 Don't use this type because it has a different size in unit tests (64 bit) and Wasm (32 bit). Also it tends to issue float instructions such that the contracts cannot be uploaded. |
-| String              | string                                    | `"foo"`                                                                               |
-| &str                | string                                    | `"foo"`                                                                               | 🚫 Unsuppored since message types must be owned (DeserializeOwned)                                                                                                                     |
-| Option\<T\>         | `null` or JSON type of `T`                | `null`, `{"foo":12}`                                                                  |                                                                                                                                                                                        |
-| Vec\<T\>            | array of JSON type of `T`                 | `["one", "two", "three"]` (Vec\<String\>), `[true, false]` (Vec\<bool\>)              |
-| Vec\<u8\>           | array of numbers from 0 to 255            | `[187, 61, 11, 250]`                                                                  | ⚠️ Discouraged as this encoding is not as compact as it can be. See `Binary`.                                                                                                          |
-| struct MyType { … } | object                                    | `{"foo":12}`                                                                          |                                                                                                                                                                                        |
-| [Uint64]            | string containing number                  | `"1234321"`                                                                           | Used to support full uint64 range in all implementations                                                                                                                               |
-| [Uint128]           | string containing number                  | `"1234321"`                                                                           |                                                                                                                                                                                        |
-| [Uint256]           | string containing number                  | `"1234321"`                                                                           |                                                                                                                                                                                        |
-| [Uint512]           | string containing number                  | `"1234321"`                                                                           |                                                                                                                                                                                        |
-| [Decimal]           | string containing decimal number          | `"55.6584"`                                                                           |                                                                                                                                                                                        |
-| [Decimal256]        | string containing decimal number          | `"55.6584"`                                                                           |                                                                                                                                                                                        |
-| [Binary]            | string containing base64 data             | `"MTIzCg=="`                                                                          |                                                                                                                                                                                        |
-| [HexBinary]         | string containing hex data                | `"b5d7d24e428c"`                                                                      |                                                                                                                                                                                        |
-| [Timestamp]         | string containing nanoseconds since epoch | `"1677687687000000000"`                                                               |                                                                                                                                                                                        |
+| Rust type           | JSON type[^1]                    | Example                                                                               | Note                                                                                                                                                                                   |
+| ------------------- | -------------------------------- | ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| bool                | `true` or `false`                | `true`                                                                                |                                                                                                                                                                                        |
+| u32/i32             | number                           | `123`                                                                                 |                                                                                                                                                                                        |
+| u64/i64             | number                           | `123456`                                                                              | Supported in Rust and Go. Other implementations (`jq`, `JavaScript`) do not support the full uint64/int64 range.                                                                       |
+| u128/i128           | string                           | `"340282366920938463463374607431768211455", "-2766523308300312711084346401884294402"` | 🚫 Strongly discouraged because the JSON type in serde-json-wasm is wrong and will change. See [Dev Note #4: u128/i128 serialization][dev-note-4].                                     |
+| usize/isize         | number                           | `123456`                                                                              | 🚫 Don't use this type because it has a different size in unit tests (64 bit) and Wasm (32 bit). Also it tends to issue float instructions such that the contracts cannot be uploaded. |
+| String              | string                           | `"foo"`                                                                               |
+| &str                | string                           | `"foo"`                                                                               | 🚫 Unsuppored since message types must be owned (DeserializeOwned)                                                                                                                     |
+| Option\<T\>         | `null` or JSON type of `T`       | `null`, `{"foo":12}`                                                                  |                                                                                                                                                                                        |
+| Vec\<T\>            | array of JSON type of `T`        | `["one", "two", "three"]` (Vec\<String\>), `[true, false]` (Vec\<bool\>)              |
+| Vec\<u8\>           | array of numbers from 0 to 255   | `[187, 61, 11, 250]`                                                                  | ⚠️ Discouraged as this encoding is not as compact as it can be. See `Binary`.                                                                                                          |
+| struct MyType { … } | object                           | `{"foo":12}`                                                                          |                                                                                                                                                                                        |
+| [Uint64]            | string containing number         | `"1234321"`                                                                           | Used to support full uint64 range in all implementations                                                                                                                               |
+| [Uint128]           | string containing number         | `"1234321"`                                                                           |                                                                                                                                                                                        |
+| [Uint256]           | string containing number         | `"1234321"`                                                                           |                                                                                                                                                                                        |
+| [Uint512]           | string containing number         | `"1234321"`                                                                           |                                                                                                                                                                                        |
+| [Decimal]           | string containing decimal number | `"55.6584"`                                                                           |                                                                                                                                                                                        |
+| [Decimal256]        | string containing decimal number | `"55.6584"`                                                                           |                                                                                                                                                                                        |
+| [Binary]            | string containing base64 data    | `"MTIzCg=="`                                                                          |                                                                                                                                                                                        |
+| [HexBinary]         | string containing hex data       | `"b5d7d24e428c"`                                                                      |                                                                                                                                                                                        |
 
-[uint64]: https://docs.rs/cosmwasm-std/1.2.5/cosmwasm_std/struct.Uint64.html
-[uint128]: https://docs.rs/cosmwasm-std/1.2.5/cosmwasm_std/struct.Uint128.html
-[uint256]: https://docs.rs/cosmwasm-std/1.2.5/cosmwasm_std/struct.Uint256.html
-[uint512]: https://docs.rs/cosmwasm-std/1.2.5/cosmwasm_std/struct.Uint512.html
-[decimal]: https://docs.rs/cosmwasm-std/1.2.5/cosmwasm_std/struct.Decimal.html
+[uint64]: https://docs.rs/cosmwasm-std/1.1.1/cosmwasm_std/struct.Uint64.html
+[uint128]: https://docs.rs/cosmwasm-std/1.1.1/cosmwasm_std/struct.Uint128.html
+[uint256]: https://docs.rs/cosmwasm-std/1.1.1/cosmwasm_std/struct.Uint256.html
+[uint512]: https://docs.rs/cosmwasm-std/1.1.1/cosmwasm_std/struct.Uint512.html
+[decimal]: https://docs.rs/cosmwasm-std/1.1.1/cosmwasm_std/struct.Decimal.html
 [decimal256]:
-  https://docs.rs/cosmwasm-std/1.2.5/cosmwasm_std/struct.Decimal256.html
-[binary]: https://docs.rs/cosmwasm-std/1.2.5/cosmwasm_std/struct.Binary.html
+  https://docs.rs/cosmwasm-std/1.1.1/cosmwasm_std/struct.Decimal256.html
+[binary]: https://docs.rs/cosmwasm-std/1.1.1/cosmwasm_std/struct.Binary.html
 [hexbinary]:
-  https://docs.rs/cosmwasm-std/1.2.5/cosmwasm_std/struct.HexBinary.html
-[timestamp]:
-  https://docs.rs/cosmwasm-std/1.2.5/cosmwasm_std/struct.Timestamp.html
+  https://docs.rs/cosmwasm-std/1.1.1/cosmwasm_std/struct.HexBinary.html
 [dev-note-4]:
   https://medium.com/cosmwasm/dev-note-4-u128-i128-serialization-in-cosmwasm-90cb76784d44
 
diff --git a/docs/USING_COSMWASM_STD.md b/docs/USING_COSMWASM_STD.md
index 7da8fa6e9..36730f994 100644
--- a/docs/USING_COSMWASM_STD.md
+++ b/docs/USING_COSMWASM_STD.md
@@ -44,6 +44,7 @@ The libarary comes with the following features:
 | backtraces   |                    | Add backtraces to errors (for unit testing)                               |
 | cosmwasm_1_1 |                    | Features that require CosmWasm 1.1+ on the chain                          |
 | cosmwasm_1_2 |                    | Features that require CosmWasm 1.2+ on the chain                          |
+| cosmwasm_1_3 |                    | Features that require CosmWasm 1.3+ on the chain                          |
 
 ## The cosmwasm-std dependency for contract developers
 
diff --git a/packages/check/src/main.rs b/packages/check/src/main.rs
index 201aaa8b9..561992bb2 100644
--- a/packages/check/src/main.rs
+++ b/packages/check/src/main.rs
@@ -10,7 +10,8 @@ use colored::Colorize;
 use cosmwasm_vm::capabilities_from_csv;
 use cosmwasm_vm::internals::{check_wasm, compile};
 
-const DEFAULT_AVAILABLE_CAPABILITIES: &str = "iterator,staking,stargate,cosmwasm_1_1,cosmwasm_1_2";
+const DEFAULT_AVAILABLE_CAPABILITIES: &str =
+    "iterator,staking,stargate,cosmwasm_1_1,cosmwasm_1_2,cosmwasm_1_3";
 
 pub fn main() {
     let matches = Command::new("Contract checking")
diff --git a/packages/std/Cargo.toml b/packages/std/Cargo.toml
index 262969078..cb40abdb5 100644
--- a/packages/std/Cargo.toml
+++ b/packages/std/Cargo.toml
@@ -9,7 +9,7 @@ license = "Apache-2.0"
 readme = "README.md"
 
 [package.metadata.docs.rs]
-features = ["abort", "stargate", "staking", "ibc3", "cosmwasm_1_2"]
+features = ["abort", "stargate", "staking", "ibc3", "cosmwasm_1_3"]
 
 [features]
 default = ["iterator", "abort"]
@@ -39,6 +39,9 @@ cosmwasm_1_1 = []
 # This feature makes `GovMsg::VoteWeighted` available for the contract to call, but requires
 # the host blockchain to run CosmWasm `1.2.0` or higher.
 cosmwasm_1_2 = ["cosmwasm_1_1"]
+# This feature makes `BankQuery::DenomMetadata` available for the contract to call, but requires
+# the host blockchain to run CosmWasm `1.3.0` or higher.
+cosmwasm_1_3 = ["cosmwasm_1_2"]
 
 [dependencies]
 base64 = "0.13.0"
diff --git a/packages/std/src/coin.rs b/packages/std/src/coin.rs
index 0838958ef..b88a6da3e 100644
--- a/packages/std/src/coin.rs
+++ b/packages/std/src/coin.rs
@@ -4,7 +4,7 @@ use std::{fmt, str::FromStr};
 
 use crate::{errors::CoinFromStrError, math::Uint128};
 
-#[derive(Serialize, Deserialize, Clone, Default, PartialEq, Eq, JsonSchema)]
+#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq, JsonSchema)]
 pub struct Coin {
     pub denom: String,
     pub amount: Uint128,
@@ -19,12 +19,6 @@ impl Coin {
     }
 }
 
-impl fmt::Debug for Coin {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "Coin {{ {} \"{}\" }}", self.amount, self.denom)
-    }
-}
-
 impl FromStr for Coin {
     type Err = CoinFromStrError;
 
@@ -241,10 +235,4 @@ mod tests {
             "Invalid amount: number too large to fit in target type"
         );
     }
-
-    #[test]
-    fn debug_coin() {
-        let coin = Coin::new(123, "ucosm");
-        assert_eq!(format!("{:?}", coin), r#"Coin { 123 "ucosm" }"#);
-    }
 }
diff --git a/packages/std/src/exports.rs b/packages/std/src/exports.rs
index 5b4b30925..27c46768d 100644
--- a/packages/std/src/exports.rs
+++ b/packages/std/src/exports.rs
@@ -49,6 +49,10 @@ extern "C" fn requires_cosmwasm_1_1() -> () {}
 #[no_mangle]
 extern "C" fn requires_cosmwasm_1_2() -> () {}
 
+#[cfg(feature = "cosmwasm_1_3")]
+#[no_mangle]
+extern "C" fn requires_cosmwasm_1_3() -> () {}
+
 /// 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.
diff --git a/packages/std/src/iterator.rs b/packages/std/src/iterator.rs
index e74758160..00190aa5c 100644
--- a/packages/std/src/iterator.rs
+++ b/packages/std/src/iterator.rs
@@ -6,7 +6,7 @@ use crate::errors::StdError;
 /// allows contracts to reuse the type when deserializing database records.
 pub type Record<V = Vec<u8>> = (Vec<u8>, V);
 
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, PartialEq, Eq)]
 // We assign these to integers to provide a stable API for passing over FFI (to wasm and Go)
 pub enum Order {
     Ascending = 1,
diff --git a/packages/std/src/lib.rs b/packages/std/src/lib.rs
index dca5b3ba8..d2e156883 100644
--- a/packages/std/src/lib.rs
+++ b/packages/std/src/lib.rs
@@ -17,7 +17,9 @@ mod import_helpers;
 #[cfg(feature = "iterator")]
 mod iterator;
 mod math;
+mod metadata;
 mod never;
+mod pagination;
 mod panic;
 mod query;
 mod results;
@@ -56,7 +58,9 @@ pub use crate::math::{
     Decimal, Decimal256, Decimal256RangeExceeded, DecimalRangeExceeded, Fraction, Isqrt, Uint128,
     Uint256, Uint512, Uint64,
 };
+pub use crate::metadata::{DenomMetadata, DenomUnit};
 pub use crate::never::Never;
+pub use crate::pagination::PageRequest;
 #[cfg(feature = "cosmwasm_1_2")]
 pub use crate::query::CodeInfoResponse;
 #[cfg(feature = "cosmwasm_1_1")]
diff --git a/packages/std/src/math/decimal.rs b/packages/std/src/math/decimal.rs
index 9e4e3424a..3c3842128 100644
--- a/packages/std/src/math/decimal.rs
+++ b/packages/std/src/math/decimal.rs
@@ -76,11 +76,6 @@ impl Decimal {
         Self(((x as u128) * 1_000_000_000_000_000).into())
     }
 
-    /// Convert basis points (x/10000) into Decimal
-    pub fn bps(x: u64) -> Self {
-        Self(((x as u128) * 100_000_000_000_000).into())
-    }
-
     /// Creates a decimal from a number of atomic units and the number
     /// of decimal places. The inputs will be converted internally to form
     /// a decimal with 18 decimal places. So the input 123 and 2 will create
@@ -773,12 +768,6 @@ mod tests {
         assert_eq!(value.0, Decimal::DECIMAL_FRACTIONAL / Uint128::from(8u8));
     }
 
-    #[test]
-    fn decimal_bps() {
-        let value = Decimal::bps(125);
-        assert_eq!(value.0, Decimal::DECIMAL_FRACTIONAL / Uint128::from(80u8));
-    }
-
     #[test]
     fn decimal_from_atomics_works() {
         let one = Decimal::one();
diff --git a/packages/std/src/math/decimal256.rs b/packages/std/src/math/decimal256.rs
index 157e7397b..bd6109718 100644
--- a/packages/std/src/math/decimal256.rs
+++ b/packages/std/src/math/decimal256.rs
@@ -85,11 +85,6 @@ impl Decimal256 {
         Self(Uint256::from(x) * Uint256::from(1_000_000_000_000_000u128))
     }
 
-    /// Convert basis points (x/10000) into Decimal256
-    pub fn bps(x: u64) -> Self {
-        Self(Uint256::from(x) * Uint256::from(100_000_000_000_000u128))
-    }
-
     /// Creates a decimal from a number of atomic units and the number
     /// of decimal places. The inputs will be converted internally to form
     /// a decimal with 18 decimal places. So the input 123 and 2 will create
@@ -799,15 +794,6 @@ mod tests {
         assert_eq!(value.0, Decimal256::DECIMAL_FRACTIONAL / Uint256::from(8u8));
     }
 
-    #[test]
-    fn decimal256_bps() {
-        let value = Decimal256::bps(125);
-        assert_eq!(
-            value.0,
-            Decimal256::DECIMAL_FRACTIONAL / Uint256::from(80u8)
-        );
-    }
-
     #[test]
     fn decimal256_from_atomics_works() {
         let one = Decimal256::one();
diff --git a/packages/std/src/metadata.rs b/packages/std/src/metadata.rs
new file mode 100644
index 000000000..c0827ce72
--- /dev/null
+++ b/packages/std/src/metadata.rs
@@ -0,0 +1,23 @@
+use schemars::JsonSchema;
+use serde::{Deserialize, Serialize};
+
+/// Replicates the cosmos-sdk bank module Metadata type
+#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq, JsonSchema)]
+pub struct DenomMetadata {
+    pub description: String,
+    pub denom_units: Vec<DenomUnit>,
+    pub base: String,
+    pub display: String,
+    pub name: String,
+    pub symbol: String,
+    pub uri: String,
+    pub uri_hash: String,
+}
+
+/// Replicates the cosmos-sdk bank module DenomUnit type
+#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq, JsonSchema)]
+pub struct DenomUnit {
+    pub denom: String,
+    pub exponent: u32,
+    pub aliases: Vec<String>,
+}
diff --git a/packages/std/src/pagination.rs b/packages/std/src/pagination.rs
new file mode 100644
index 000000000..d9d1d8ef9
--- /dev/null
+++ b/packages/std/src/pagination.rs
@@ -0,0 +1,12 @@
+use schemars::JsonSchema;
+use serde::{Deserialize, Serialize};
+
+use crate::Binary;
+
+/// Simplified version of the PageRequest type for pagination from the cosmos-sdk
+#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq, JsonSchema)]
+pub struct PageRequest {
+    pub key: Option<Binary>,
+    pub limit: u32,
+    pub reverse: bool,
+}
diff --git a/packages/std/src/query/bank.rs b/packages/std/src/query/bank.rs
index b3a43e11d..74d8b2b9d 100644
--- a/packages/std/src/query/bank.rs
+++ b/packages/std/src/query/bank.rs
@@ -3,6 +3,9 @@ use serde::{Deserialize, Serialize};
 
 use crate::Coin;
 
+#[cfg(feature = "cosmwasm_1_3")]
+use crate::{Binary, DenomMetadata, PageRequest};
+
 use super::query_response::QueryResponseType;
 
 #[non_exhaustive]
@@ -21,6 +24,14 @@ pub enum BankQuery {
     /// Note that this may be much more expensive than Balance and should be avoided if possible.
     /// Return value is AllBalanceResponse.
     AllBalances { address: String },
+    /// This calls into the native bank module for querying metadata for a specific bank token.
+    /// Return value is DenomMetadataResponse
+    #[cfg(feature = "cosmwasm_1_3")]
+    DenomMetadata { denom: String },
+    /// This calls into the native bank module for querying metadata for all bank tokens that have a metadata entry.
+    /// Return value is AllDenomMetadataResponse
+    #[cfg(feature = "cosmwasm_1_3")]
+    AllDenomMetadata { pagination: Option<PageRequest> },
 }
 
 #[cfg(feature = "cosmwasm_1_1")]
@@ -54,3 +65,28 @@ pub struct AllBalanceResponse {
 }
 
 impl QueryResponseType for AllBalanceResponse {}
+
+#[cfg(feature = "cosmwasm_1_3")]
+#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+#[non_exhaustive]
+pub struct DenomMetadataResponse {
+    /// The metadata for the queried denom.
+    pub metadata: DenomMetadata,
+}
+
+#[cfg(feature = "cosmwasm_1_3")]
+impl QueryResponseType for DenomMetadataResponse {}
+
+#[cfg(feature = "cosmwasm_1_3")]
+#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+#[non_exhaustive]
+pub struct AllDenomMetadataResponse {
+    /// Always returns metadata for all token denoms on the base chain.
+    pub metadata: Vec<DenomMetadata>,
+    pub next_key: Option<Binary>,
+}
+
+#[cfg(feature = "cosmwasm_1_3")]
+impl QueryResponseType for AllDenomMetadataResponse {}
diff --git a/packages/std/src/query/mod.rs b/packages/std/src/query/mod.rs
index 50061df09..c88e209b8 100644
--- a/packages/std/src/query/mod.rs
+++ b/packages/std/src/query/mod.rs
@@ -14,6 +14,8 @@ mod wasm;
 #[cfg(feature = "cosmwasm_1_1")]
 pub use bank::SupplyResponse;
 pub use bank::{AllBalanceResponse, BalanceResponse, BankQuery};
+#[cfg(feature = "cosmwasm_1_3")]
+pub use bank::{AllDenomMetadataResponse, DenomMetadataResponse};
 #[cfg(feature = "stargate")]
 pub use ibc::{ChannelResponse, IbcQuery, ListChannelsResponse, PortIdResponse};
 #[cfg(feature = "staking")]
diff --git a/packages/std/src/traits.rs b/packages/std/src/traits.rs
index e3ba067b6..7181036cb 100644
--- a/packages/std/src/traits.rs
+++ b/packages/std/src/traits.rs
@@ -20,9 +20,13 @@ use crate::query::{
     AllDelegationsResponse, AllValidatorsResponse, BondedDenomResponse, Delegation,
     DelegationResponse, FullDelegation, StakingQuery, Validator, ValidatorResponse,
 };
+#[cfg(feature = "cosmwasm_1_3")]
+use crate::query::{AllDenomMetadataResponse, DenomMetadataResponse};
 use crate::results::{ContractResult, Empty, SystemResult};
 use crate::serde::{from_binary, to_binary, to_vec};
 use crate::ContractInfoResponse;
+#[cfg(feature = "cosmwasm_1_3")]
+use crate::{DenomMetadata, PageRequest};
 
 /// Storage provides read and write access to a persistent storage.
 /// If you only want to provide read access, provide `&Storage`
@@ -239,6 +243,28 @@ impl<'a, C: CustomQuery> QuerierWrapper<'a, C> {
         Ok(res.amount)
     }
 
+    #[cfg(feature = "cosmwasm_1_3")]
+    pub fn query_denom_metadata(&self, denom: impl Into<String>) -> StdResult<DenomMetadata> {
+        let request = BankQuery::DenomMetadata {
+            denom: denom.into(),
+        }
+        .into();
+        let res: DenomMetadataResponse = self.query(&request)?;
+        Ok(res.metadata)
+    }
+
+    #[cfg(feature = "cosmwasm_1_3")]
+    pub fn query_all_denom_metadata(
+        &self,
+        pagination: PageRequest,
+    ) -> StdResult<AllDenomMetadataResponse> {
+        let request = BankQuery::AllDenomMetadata {
+            pagination: Some(pagination),
+        }
+        .into();
+        self.query(&request)
+    }
+
     // this queries another wasm contract. You should know a priori the proper types for T and U
     // (response and request) based on the contract API
     pub fn query_wasm_smart<T: DeserializeOwned>(


Test patch:
diff --git a/packages/std/src/testing/mock.rs b/packages/std/src/testing/mock.rs
index d21923657..3b5ce6f25 100644
--- a/packages/std/src/testing/mock.rs
+++ b/packages/std/src/testing/mock.rs
@@ -1,8 +1,10 @@
 use serde::de::DeserializeOwned;
 #[cfg(feature = "stargate")]
 use serde::Serialize;
-use std::collections::HashMap;
+use std::collections::{BTreeMap, HashMap};
 use std::marker::PhantomData;
+#[cfg(feature = "cosmwasm_1_3")]
+use std::ops::Bound;
 
 use crate::addresses::{Addr, CanonicalAddr};
 use crate::binary::Binary;
@@ -32,7 +34,12 @@ use crate::storage::MemoryStorage;
 use crate::timestamp::Timestamp;
 use crate::traits::{Api, Querier, QuerierResult};
 use crate::types::{BlockInfo, ContractInfo, Env, MessageInfo, TransactionInfo};
-use crate::Attribute;
+#[cfg(feature = "cosmwasm_1_3")]
+use crate::{
+    query::{AllDenomMetadataResponse, DenomMetadataResponse},
+    PageRequest,
+};
+use crate::{Attribute, DenomMetadata};
 #[cfg(feature = "stargate")]
 use crate::{ChannelResponse, IbcQuery, ListChannelsResponse, PortIdResponse};
 
@@ -473,6 +480,10 @@ impl<C: DeserializeOwned> MockQuerier<C> {
         self.bank.update_balance(addr, balance)
     }
 
+    pub fn set_denom_metadata(&mut self, denom_metadata: &[DenomMetadata]) {
+        self.bank.set_denom_metadata(denom_metadata);
+    }
+
     #[cfg(feature = "staking")]
     pub fn update_staking(
         &mut self,
@@ -599,6 +610,8 @@ pub struct BankQuerier {
     supplies: HashMap<String, Uint128>,
     /// HashMap<address, coins>
     balances: HashMap<String, Vec<Coin>>,
+    /// Vec<Metadata>
+    denom_metadata: BTreeMap<Vec<u8>, DenomMetadata>,
 }
 
 impl BankQuerier {
@@ -611,6 +624,7 @@ impl BankQuerier {
         BankQuerier {
             supplies: Self::calculate_supplies(&balances),
             balances,
+            denom_metadata: BTreeMap::new(),
         }
     }
 
@@ -625,6 +639,13 @@ impl BankQuerier {
         result
     }
 
+    pub fn set_denom_metadata(&mut self, denom_metadata: &[DenomMetadata]) {
+        self.denom_metadata = denom_metadata
+            .iter()
+            .map(|d| (d.base.as_bytes().to_vec(), d.clone()))
+            .collect();
+    }
+
     fn calculate_supplies(balances: &HashMap<String, Vec<Coin>>) -> HashMap<String, Uint128> {
         let mut supplies = HashMap::new();
 
@@ -678,6 +699,59 @@ impl BankQuerier {
                 };
                 to_binary(&bank_res).into()
             }
+            #[cfg(feature = "cosmwasm_1_3")]
+            BankQuery::DenomMetadata { denom } => {
+                let denom_metadata = self.denom_metadata.get(denom.as_bytes());
+                match denom_metadata {
+                    Some(m) => {
+                        let metadata_res = DenomMetadataResponse {
+                            metadata: m.clone(),
+                        };
+                        to_binary(&metadata_res).into()
+                    }
+                    None => return SystemResult::Err(SystemError::Unknown {}),
+                }
+            }
+            #[cfg(feature = "cosmwasm_1_3")]
+            BankQuery::AllDenomMetadata { pagination } => {
+                let default_pagination = PageRequest {
+                    key: None,
+                    limit: 100,
+                    reverse: false,
+                };
+                let pagination = pagination.as_ref().unwrap_or(&default_pagination);
+
+                // range of all denoms after the given key (or until the key for reverse)
+                let range = match (pagination.reverse, &pagination.key) {
+                    (_, None) => (Bound::Unbounded, Bound::Unbounded),
+                    (true, Some(key)) => (Bound::Unbounded, Bound::Included(key.as_slice())),
+                    (false, Some(key)) => (Bound::Included(key.as_slice()), Bound::Unbounded),
+                };
+                let iter = self.denom_metadata.range::<[u8], _>(range);
+                // using dynamic dispatch here to reduce code duplication and since this is only testing code
+                let iter: Box<dyn Iterator<Item = _>> = if pagination.reverse {
+                    Box::new(iter.rev())
+                } else {
+                    Box::new(iter)
+                };
+
+                let mut metadata: Vec<_> = iter
+                    // take the requested amount + 1 to get the next key
+                    .take((pagination.limit.saturating_add(1)) as usize)
+                    .map(|(_, m)| m.clone())
+                    .collect();
+
+                // if we took more than requested, remove the last element (the next key),
+                // otherwise this is the last batch
+                let next_key = if metadata.len() > pagination.limit as usize {
+                    metadata.pop().map(|m| Binary::from(m.base.as_bytes()))
+                } else {
+                    None
+                };
+
+                let metadata_res = AllDenomMetadataResponse { metadata, next_key };
+                to_binary(&metadata_res).into()
+            }
         };
         // system result is always ok in the mock implementation
         SystemResult::Ok(contract_result)
@@ -835,6 +909,8 @@ pub fn mock_wasmd_attr(key: impl Into<String>, value: impl Into<String>) -> Attr
 #[cfg(test)]
 mod tests {
     use super::*;
+    #[cfg(feature = "cosmwasm_1_3")]
+    use crate::DenomUnit;
     use crate::{coin, coins, from_binary, to_binary, ContractInfoResponse, Response};
     #[cfg(feature = "staking")]
     use crate::{Decimal, Delegation};
@@ -1262,6 +1338,96 @@ mod tests {
         assert_eq!(res.amount, coin(0, "ELF"));
     }
 
+    #[cfg(feature = "cosmwasm_1_3")]
+    #[test]
+    fn bank_querier_metadata_works() {
+        let mut bank = BankQuerier::new(&[]);
+        bank.set_denom_metadata(
+            &(0..100)
+                .map(|i| DenomMetadata {
+                    symbol: format!("FOO{i}"),
+                    name: "Foo".to_string(),
+                    description: "Foo coin".to_string(),
+                    denom_units: vec![DenomUnit {
+                        denom: "ufoo".to_string(),
+                        exponent: 8,
+                        aliases: vec!["microfoo".to_string(), "foobar".to_string()],
+                    }],
+                    display: "FOO".to_string(),
+                    base: format!("ufoo{i}"),
+                    uri: "https://foo.bar".to_string(),
+                    uri_hash: "foo".to_string(),
+                })
+                .collect::<Vec<_>>(),
+        );
+
+        // querying first 10 should work
+        let res = bank
+            .query(&BankQuery::AllDenomMetadata {
+                pagination: Some(PageRequest {
+                    key: None,
+                    limit: 10,
+                    reverse: false,
+                }),
+            })
+            .unwrap()
+            .unwrap();
+        let res: AllDenomMetadataResponse = from_binary(&res).unwrap();
+        assert_eq!(res.metadata.len(), 10);
+        assert!(res.next_key.is_some());
+
+        // querying next 10 should also work
+        let res2 = bank
+            .query(&BankQuery::AllDenomMetadata {
+                pagination: Some(PageRequest {
+                    key: res.next_key,
+                    limit: 10,
+                    reverse: false,
+                }),
+            })
+            .unwrap()
+            .unwrap();
+        let res2: AllDenomMetadataResponse = from_binary(&res2).unwrap();
+        assert_eq!(res2.metadata.len(), 10);
+        assert_ne!(res.metadata.last(), res2.metadata.first());
+        // should have no overlap
+        for m in res.metadata {
+            assert!(!res2.metadata.contains(&m));
+        }
+
+        // querying all 100 should work
+        let res = bank
+            .query(&BankQuery::AllDenomMetadata {
+                pagination: Some(PageRequest {
+                    key: None,
+                    limit: 100,
+                    reverse: true,
+                }),
+            })
+            .unwrap()
+            .unwrap();
+        let res: AllDenomMetadataResponse = from_binary(&res).unwrap();
+        assert_eq!(res.metadata.len(), 100);
+        assert!(res.next_key.is_none(), "no more data should be available");
+        assert_eq!(res.metadata[0].symbol, "FOO99", "should have been reversed");
+
+        let more_res = bank
+            .query(&BankQuery::AllDenomMetadata {
+                pagination: Some(PageRequest {
+                    key: res.next_key,
+                    limit: u32::MAX,
+                    reverse: true,
+                }),
+            })
+            .unwrap()
+            .unwrap();
+        let more_res: AllDenomMetadataResponse = from_binary(&more_res).unwrap();
+        assert_eq!(
+            more_res.metadata, res.metadata,
+            "should be same as previous query"
+        );
+    }
+
     #[cfg(feature = "stargate")]
     #[test]
     fn ibc_querier_channel_existing() {
diff --git a/packages/vm/src/testing/instance.rs b/packages/vm/src/testing/instance.rs
index 14b85cf7c..6a4f11a37 100644
--- a/packages/vm/src/testing/instance.rs
+++ b/packages/vm/src/testing/instance.rs
@@ -98,7 +98,8 @@ pub struct MockInstanceOptions<'a> {
 impl MockInstanceOptions<'_> {
     fn default_capabilities() -> HashSet<String> {
         #[allow(unused_mut)]
-        let mut out = capabilities_from_csv("iterator,staking,cosmwasm_1_1,cosmwasm_1_2");
+        let mut out =
+            capabilities_from_csv("iterator,staking,cosmwasm_1_1,cosmwasm_1_2,cosmwasm_1_3");
         #[cfg(feature = "stargate")]
         out.insert("stargate".to_string());
         out