Skip to content

Commit 884e17b

Browse files
committed
add supported interface
1 parent 3610110 commit 884e17b

File tree

4 files changed

+213
-0
lines changed

4 files changed

+213
-0
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[alias]
2+
wasm = "build --release --target wasm32-unknown-unknown"
3+
wasm-debug = "build --target wasm32-unknown-unknown"
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[package]
2+
name = "cw2-supported-interface"
3+
version = "1.0.0"
4+
authors = ["ThienLK1@aura-nw"]
5+
edition = "2021"
6+
description = "Definition and types for the CosmWasm-2 interface"
7+
license = "Apache-2.0"
8+
repository = "https://github.com/CosmWasm/cw-plus"
9+
homepage = "https://cosmwasm.com"
10+
11+
[dependencies]
12+
cosmwasm-schema = "1.0.0"
13+
cosmwasm-std = { version = "1.0.0", default-features = false }
14+
cw-storage-plus = "0.16.0"
15+
schemars = "0.8.1"
16+
serde = { version = "1.0.0", default-features = false, features = ["derive"] }
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# CW2 Spec: Contract Info
2+
3+
Most of the CW* specs are focused on the *public interfaces*
4+
of the contract. The APIs used for `ExecuteMsg` or `QueryMsg`.
5+
However, when we wish to migrate or inspect smart contract info,
6+
we need some form of smart contract information embedded on state.
7+
8+
This is where CW2 comes in. It specifies a special Item to
9+
be stored on disk by all contracts on `instantiate`.
10+
11+
`ContractInfo` must be stored under the `"contract_info"` key which translates
12+
to `"636F6E74726163745F696E666F"` in hex format.
13+
Since the state is well defined, we do not need to support any "smart queries".
14+
We do provide a helper to construct a "raw query" to read the ContractInfo
15+
of any CW2-compliant contract.
16+
17+
You can query using:
18+
```shell
19+
wasmd query wasm contract-state raw [contract_addr] 636F6E74726163745F696E666F --node $RPC
20+
```
21+
22+
When the `migrate` function is called, then the new contract
23+
can read that data andsee if this is an expected contract we can
24+
migrate from. And also contain extra version information if we
25+
support multiple migrate paths.
26+
27+
### Data structures
28+
29+
**Required**
30+
31+
All CW2-compliant contracts must store the following data:
32+
33+
* key: `contract_info`
34+
* data: Json-serialized `ContractVersion`
35+
36+
```rust
37+
pub struct ContractVersion {
38+
/// contract is a globally unique identifier for the contract.
39+
/// it should build off standard namespacing for whichever language it is in,
40+
/// and prefix it with the registry we use.
41+
/// For rust we prefix with `crates.io:`, to give us eg. `crates.io:cw20-base`
42+
pub contract: String,
43+
/// version is any string that this implementation knows. It may be simple counter "1", "2".
44+
/// or semantic version on release tags "v0.7.0", or some custom feature flag list.
45+
/// the only code that needs to understand the version parsing is code that knows how to
46+
/// migrate from the given contract (and is tied to it's implementation somehow)
47+
pub version: String,
48+
}
49+
```
50+
51+
Thus, an serialized example may looks like:
52+
53+
```json
54+
{
55+
"contract": "crates.io:cw20-base",
56+
"version": "v0.1.0"
57+
}
58+
```
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/*!
2+
Most of the CW* specs are focused on the *public interfaces*
3+
of the contract. The APIs used for `ExecuteMsg` or `QueryMsg`.
4+
However, when we wish to migrate or inspect smart contract info,
5+
we need some form of smart contract information embedded on state.
6+
7+
This is where CW2 comes in. It specifies a special Item to
8+
be stored on disk by all contracts on `instantiate`.
9+
10+
`ContractInfo` must be stored under the `"contract_info"` key which translates
11+
to `"636F6E74726163745F696E666F"` in hex format.
12+
Since the state is well defined, we do not need to support any "smart queries".
13+
We do provide a helper to construct a "raw query" to read the ContractInfo
14+
of any CW2-compliant contract.
15+
16+
For more information on this specification, please check out the
17+
[README](https://github.com/CosmWasm/cw-plus/blob/main/packages/cw2/README.md).
18+
*/
19+
20+
use cosmwasm_schema::cw_serde;
21+
use cosmwasm_std::{CustomQuery, QuerierWrapper, QueryRequest, StdResult, Storage, WasmQuery};
22+
use cw_storage_plus::Item;
23+
24+
pub const CONTRACT: Item<ContractVersion> = Item::new("contract_info");
25+
26+
#[cw_serde]
27+
pub struct ContractVersion {
28+
/// contract is the crate name of the implementing contract, eg. `crate:cw20-base`
29+
/// we will use other prefixes for other languages, and their standard global namespacing
30+
pub contract: String,
31+
/// version is any string that this implementation knows. It may be simple counter "1", "2".
32+
/// or semantic version on release tags "v0.7.0", or some custom feature flag list.
33+
/// the only code that needs to understand the version parsing is code that knows how to
34+
/// migrate from the given contract (and is tied to it's implementation somehow)
35+
pub version: String,
36+
/// supported_interface is an optional parameter returning a vector of string represents interfaces
37+
/// that the contract support The string value is the interface crate names in Rust crate Registry.
38+
/// This parameter is inspired by the EIP-165 from Ethereum.
39+
/// Each string value should follow a common standard such as <Registry Domain>:<Crate Name>
40+
/// e.g ["crates.io:cw721","crates.io:cw2"]
41+
/// NOTE: this is just a hint for the caller to adapt on how to interact with this contract.
42+
/// There is no guarantee that the contract actually implement these interfaces.
43+
pub supported_interface: Option<Vec<String>>,
44+
}
45+
46+
/// get_contract_version can be use in migrate to read the previous version of this contract
47+
pub fn get_contract_version(store: &dyn Storage) -> StdResult<ContractVersion> {
48+
CONTRACT.load(store)
49+
}
50+
51+
/// set_contract_version should be used in instantiate to store the original version, and after a successful
52+
/// migrate to update it
53+
pub fn set_contract_version<T: Into<String>, U: Into<String>>(
54+
store: &mut dyn Storage,
55+
name: T,
56+
version: U,
57+
supported_interface: Option<Vec<String>>,
58+
) -> StdResult<()> {
59+
let val = ContractVersion {
60+
contract: name.into(),
61+
version: version.into(),
62+
supported_interface: supported_interface.into(),
63+
};
64+
CONTRACT.save(store, &val)
65+
}
66+
67+
/// This will make a raw_query to another contract to determine the current version it
68+
/// claims to be. This should not be trusted, but could be used as a quick filter
69+
/// if the other contract exists and claims to be a cw20-base contract for example.
70+
/// (Note: you usually want to require *interfaces* not *implementations* of the
71+
/// contracts you compose with, so be careful of overuse)
72+
pub fn query_contract_info<T, CQ>(
73+
querier: &QuerierWrapper<CQ>,
74+
contract_addr: T,
75+
) -> StdResult<ContractVersion>
76+
where
77+
T: Into<String>,
78+
CQ: CustomQuery,
79+
{
80+
let req = QueryRequest::Wasm(WasmQuery::Raw {
81+
contract_addr: contract_addr.into(),
82+
key: CONTRACT.as_slice().into(),
83+
});
84+
querier.query(&req)
85+
}
86+
87+
#[cfg(test)]
88+
mod tests {
89+
use super::*;
90+
use cosmwasm_std::testing::MockStorage;
91+
use std::vec::Vec;
92+
93+
#[test]
94+
fn get_and_set_work() {
95+
let mut store = MockStorage::new();
96+
97+
// error if not set
98+
assert!(get_contract_version(&store).is_err());
99+
100+
// set and get
101+
let contract_name = "crate:cw20-base";
102+
let contract_version = "0.2.0";
103+
set_contract_version(&mut store, contract_name, contract_version, None).unwrap();
104+
105+
let loaded = get_contract_version(&store).unwrap();
106+
let expected = ContractVersion {
107+
contract: contract_name.to_string(),
108+
version: contract_version.to_string(),
109+
supported_interface: None,
110+
};
111+
assert_eq!(expected, loaded);
112+
113+
// set and get with supported_interface
114+
let contract_name = "crate:cw20-base";
115+
let contract_version = "0.2.0";
116+
let supported_interface = Some(Vec::from([
117+
"crates.io:cw2".to_string(),
118+
"crates.io:cw721".to_string(),
119+
]));
120+
set_contract_version(
121+
&mut store,
122+
contract_name,
123+
contract_version,
124+
supported_interface.clone(),
125+
)
126+
.unwrap();
127+
128+
let loaded = get_contract_version(&store).unwrap();
129+
let expected = ContractVersion {
130+
contract: contract_name.to_string(),
131+
version: contract_version.to_string(),
132+
supported_interface: supported_interface.clone(),
133+
};
134+
assert_eq!(expected, loaded);
135+
}
136+
}

0 commit comments

Comments
 (0)