Skip to content

Commit aa8ff79

Browse files
authored
Merge pull request #3 from aura-nw/cw2_add_supported_interface
add cw2-supported-interface
2 parents 27d05d3 + be5cef0 commit aa8ff79

File tree

6 files changed

+229
-43
lines changed

6 files changed

+229
-43
lines changed

Cargo.lock

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
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+
}

packages/cw2/src/lib.rs

Lines changed: 5 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,16 @@ Most of the CW* specs are focused on the *public interfaces*
33
of the contract. The APIs used for `ExecuteMsg` or `QueryMsg`.
44
However, when we wish to migrate or inspect smart contract info,
55
we need some form of smart contract information embedded on state.
6-
76
This is where CW2 comes in. It specifies a special Item to
87
be stored on disk by all contracts on `instantiate`.
9-
108
`ContractInfo` must be stored under the `"contract_info"` key which translates
119
to `"636F6E74726163745F696E666F"` in hex format.
1210
Since the state is well defined, we do not need to support any "smart queries".
1311
We do provide a helper to construct a "raw query" to read the ContractInfo
1412
of any CW2-compliant contract.
15-
1613
For more information on this specification, please check out the
1714
[README](https://github.com/CosmWasm/cw-plus/blob/main/packages/cw2/README.md).
18-
*/
15+
*/
1916

2017
use cosmwasm_schema::cw_serde;
2118
use cosmwasm_std::{CustomQuery, QuerierWrapper, QueryRequest, StdResult, Storage, WasmQuery};
@@ -33,14 +30,6 @@ pub struct ContractVersion {
3330
/// the only code that needs to understand the version parsing is code that knows how to
3431
/// migrate from the given contract (and is tied to it's implementation somehow)
3532
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>>,
4433
}
4534

4635
/// get_contract_version can be use in migrate to read the previous version of this contract
@@ -54,12 +43,10 @@ pub fn set_contract_version<T: Into<String>, U: Into<String>>(
5443
store: &mut dyn Storage,
5544
name: T,
5645
version: U,
57-
supported_interface: Option<Vec<String>>,
5846
) -> StdResult<()> {
5947
let val = ContractVersion {
6048
contract: name.into(),
6149
version: version.into(),
62-
supported_interface: supported_interface.into(),
6350
};
6451
CONTRACT.save(store, &val)
6552
}
@@ -73,9 +60,9 @@ pub fn query_contract_info<T, CQ>(
7360
querier: &QuerierWrapper<CQ>,
7461
contract_addr: T,
7562
) -> StdResult<ContractVersion>
76-
where
77-
T: Into<String>,
78-
CQ: CustomQuery,
63+
where
64+
T: Into<String>,
65+
CQ: CustomQuery,
7966
{
8067
let req = QueryRequest::Wasm(WasmQuery::Raw {
8168
contract_addr: contract_addr.into(),
@@ -88,7 +75,6 @@ where
8875
mod tests {
8976
use super::*;
9077
use cosmwasm_std::testing::MockStorage;
91-
use std::vec::Vec;
9278

9379
#[test]
9480
fn get_and_set_work() {
@@ -100,36 +86,12 @@ mod tests {
10086
// set and get
10187
let contract_name = "crate:cw20-base";
10288
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();
89+
set_contract_version(&mut store, contract_name, contract_version).unwrap();
12790

12891
let loaded = get_contract_version(&store).unwrap();
12992
let expected = ContractVersion {
13093
contract: contract_name.to_string(),
13194
version: contract_version.to_string(),
132-
supported_interface: supported_interface.clone(),
13395
};
13496
assert_eq!(expected, loaded);
13597
}

0 commit comments

Comments
 (0)