Repository: CosmWasm/cosmwasm
Pull request #1745 (2023-06-22 15:38:11)

Add AllDenomMetadata BankQuery (backport #1703)
This is an automatic backport of pull request #1703 done by [Mergify](https://mergify.com).
Cherry-pick of 4bac1e3cb3769570c91f2cd44bf1232111424ab4 has failed:
```
On branch mergify/bp/release/1.3/pr-1703
Your branch is up to date with 'origin/release/1.3'.

You are currently cherry-picking commit 4bac1e3c.
  (fix conflicts and run "git cherry-pick --continue")
  (use "git cherry-pick --skip" to skip this patch)
  (use "git cherry-pick --abort" to cancel the cherry-pick operation)

Changes to be committed:
	modified:   .circleci/config.yml
	modified:   MIGRATING.md
	modified:   docs/CAPABILITIES-BUILT-IN.md
	modified:   packages/check/src/main.rs
	modified:   packages/std/Cargo.toml
	modified:   packages/std/src/exports.rs
	modified:   packages/std/src/lib.rs
	new file:   packages/std/src/metadata.rs
	modified:   packages/std/src/query/bank.rs
	modified:   packages/std/src/query/mod.rs
	modified:   packages/std/src/testing/mock.rs
	modified:   packages/std/src/traits.rs
	modified:   packages/vm/src/testing/instance.rs

Unmerged paths:
  (use "git add <file>..." to mark resolution)
	both modified:   CHANGELOG.md
	both modified:   docs/USING_COSMWASM_STD.md

```


Cherry-pick of 7e96644e08d08f0e9f2fc16eb3b6b75670c1f97a has failed:
```
On branch mergify/bp/release/1.3/pr-1703
Your branch is ahead of 'origin/release/1.3' by 12 commits.
  (use "git push" to publish your local commits)

You are currently cherry-picking commit 7e96644e.
  (fix conflicts and run "git cherry-pick --continue")
  (use "git cherry-pick --skip" to skip this patch)
  (use "git cherry-pick --abort" to cancel the cherry-pick operation)

Changes to be committed:
	modified:   contracts/cyberpunk/Cargo.toml
	modified:   contracts/cyberpunk/src/msg.rs

Unmerged paths:
  (use "git add <file>..." to mark resolution)
	both modified:   contracts/cyberpunk/src/contract.rs

```


Cherry-pick of feab42e450f21f823efd1444015f0ba84e0d7d6a has failed:
```
On branch mergify/bp/release/1.3/pr-1703
Your branch is ahead of 'origin/release/1.3' by 14 commits.
  (use "git push" to publish your local commits)

You are currently cherry-picking commit feab42e4.
  (fix conflicts and run "git cherry-pick --continue")
  (use "git cherry-pick --skip" to skip this patch)
  (use "git cherry-pick --abort" to cancel the cherry-pick operation)

Changes to be committed:
	modified:   contracts/cyberpunk/src/msg.rs

Unmerged paths:
  (use "git add <file>..." to mark resolution)
	both modified:   contracts/cyberpunk/src/contract.rs

```


To fix up this pull request, you can check it out locally. See documentation: https://docs.github.com/en/github/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally

---


<details>
<summary>Mergify commands and options</summary>

<br />

More conditions and actions can be found in the [documentation](https://docs.mergify.com/).

You can also trigger Mergify actions by commenting on this pull request:

- `@Mergifyio refresh` will re-evaluate the rules
- `@Mergifyio rebase` will rebase this PR on its base branch
- `@Mergifyio update` will merge the base branch into this PR
- `@Mergifyio backport <destination>` will backport this PR on `<destination>` branch

Additionally, on Mergify [dashboard](https://dashboard.mergify.com) you can:

- look at your merge queues
- generate the Mergify configuration with the config editor.

Finally, you can contact us on https://mergify.com
</details>


Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 05d72ae19..ff5bb1708 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -355,15 +355,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
@@ -906,7 +906,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
@@ -983,7 +983,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 7cf92978d..19c04c761 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,9 +8,16 @@ 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-std: Add `FromStr` impl for `Coin`. ([#1684])
 - cosmwasm-std: Add `Coins` helper to handle multiple coins. ([#1687])
 
+[#1647]: https://github.com/CosmWasm/cosmwasm/pull/1647
 [#1684]: https://github.com/CosmWasm/cosmwasm/pull/1684
 [#1687]: https://github.com/CosmWasm/cosmwasm/pull/1687
 
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 32f43bf41..7bb0c3a51 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 1584ac900..b639402f2 100644
--- a/contracts/cyberpunk/schema/cyberpunk.json
+++ b/contracts/cyberpunk/schema/cyberpunk.json
@@ -185,12 +185,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 f5f1b9865..d5b0c9c2c 100644
--- a/contracts/cyberpunk/src/contract.rs
+++ b/contracts/cyberpunk/src/contract.rs
@@ -1,6 +1,6 @@
 use cosmwasm_std::{
-    entry_point, to_binary, Deps, DepsMut, Empty, Env, MessageInfo, QueryResponse, Response,
-    StdError, StdResult, WasmMsg,
+    entry_point, to_binary, DenomMetadata, Deps, DepsMut, Empty, Env, MessageInfo, PageRequest,
+    QueryResponse, Response, StdError, StdResult, WasmMsg,
 };
 
 use crate::errors::ContractError;
@@ -151,14 +151,109 @@ fn execute_mirror_env(env: Env) -> 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)?),
     }
 }
 
 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::{from_binary, DenomMetadata, DenomUnit, OwnedDeps};
+
+    fn setup() -> OwnedDeps<MockStorage, MockApi, MockQuerier> {
+        let mut deps = mock_dependencies();
+        let msg = Empty {};
+        let info = mock_info("creator", &[]);
+        let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
+        assert_eq!(0, res.messages.len());
+        deps
+    }
+
+    #[test]
+    fn instantiate_works() {
+        setup();
+    }
+
+    #[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 33d73a51b..e3a9366d7 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 {
@@ -36,4 +37,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/USING_COSMWASM_STD.md b/docs/USING_COSMWASM_STD.md
index 45841e908..2519b9b8a 100644
--- a/docs/USING_COSMWASM_STD.md
+++ b/docs/USING_COSMWASM_STD.md
@@ -44,6 +44,7 @@ The libarary comes with the following features:
 | baktraces    |                    | 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 c1f7da178..f3efc4355 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 = App::new("Contract checking")
diff --git a/packages/std/Cargo.toml b/packages/std/Cargo.toml
index d5929f0ee..af772d64e 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/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 98e8e45f9..28dfcc9ec 100644
--- a/packages/std/src/lib.rs
+++ b/packages/std/src/lib.rs
@@ -18,7 +18,9 @@ mod import_helpers;
 #[cfg(feature = "iterator")]
 mod iterator;
 mod math;
+mod metadata;
 mod never;
+mod pagination;
 mod panic;
 mod query;
 mod results;
@@ -53,7 +55,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/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