-
Notifications
You must be signed in to change notification settings - Fork 355
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cw22 add supported interface #844
base: main
Are you sure you want to change the base?
Changes from 21 commits
ca3b764
cce9ca8
8ba749c
55d2aca
3d0f227
5b4275d
a868481
719ff72
c774fb8
80697a1
c3f5a41
e42a428
c072201
af031e3
67a913c
99ee321
a07ac76
df1f0c6
c9e3508
43db6a0
22c6aa6
a6c6106
1353e5b
fb37be3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[alias] | ||
wasm = "build --release --target wasm32-unknown-unknown" | ||
wasm-debug = "build --target wasm32-unknown-unknown" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
[package] | ||
name = "cw22" | ||
version = "0.1.0" | ||
authors = ["ThienLK1@aura-nw"] | ||
edition = "2021" | ||
description = "Definition and types for the CosmWasm-22 interface" | ||
license = "Apache-2.0" | ||
repository = "https://github.com/CosmWasm/cw-plus" | ||
homepage = "https://cosmwasm.com" | ||
|
||
[dependencies] | ||
cosmwasm-schema = "1.1.0" | ||
cosmwasm-std = { version = "1.1.0", default-features = false } | ||
cw-storage-plus = "1.0.1" | ||
schemars = "0.8.1" | ||
serde = { version = "1.0.103", default-features = false, features = ["derive"] } | ||
semver = "1.0.14" |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,54 @@ | ||||||||||
# CW22 Spec: Contract Info | ||||||||||
|
||||||||||
The standard used to declare which interface contract implements. This standard is inspired by the EIP-165 from | ||||||||||
Ethereum. | ||||||||||
|
||||||||||
For more information on this specification, please check out the | ||||||||||
[README](https://github.com/CosmWasm/cw-plus/blob/main/packages/cw22/README.md). | ||||||||||
|
||||||||||
### Data structures | ||||||||||
|
||||||||||
**Required** | ||||||||||
|
||||||||||
All CW22-compliant contracts must store the following data: | ||||||||||
|
||||||||||
* key: `contract_supported_interface` | ||||||||||
* data: Json-serialized `ContractSupportedInterface` | ||||||||||
|
||||||||||
```rust | ||||||||||
pub struct ContractSupportedInterface { | ||||||||||
/// supported_interface is an optional parameter returning a vector of string represents interfaces | ||||||||||
/// that the contract support The string value is the interface crate names in Rust crate Registry. | ||||||||||
/// This parameter is inspired by the EIP-165 from Ethereum. | ||||||||||
/// Each string value should follow a common standard such as <Registry Domain>:<Crate Name> | ||||||||||
/// e.g "crates.io:cw2" | ||||||||||
/// NOTE: this is just a hint for the caller to adapt on how to interact with this contract. | ||||||||||
/// There is no guarantee that the contract actually implement these interfaces. | ||||||||||
pub supported_interface: String, | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: You can avoid it with a Cow type. Just add a lifetime generic on top ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, even though it is not exactly the same as in implementation, I would leave it this way - not sure if you forgot to update or left it on purpose, but it is probably easier to think about this way and it is semantically equivalent. |
||||||||||
/// semantic version on release tags of the interface package following SemVer guideline. | ||||||||||
/// e.g "0.16.0" | ||||||||||
pub version: String, | ||||||||||
} | ||||||||||
``` | ||||||||||
|
||||||||||
Below is an example used in cw20 contract, where we declare to implement cw20 interface with version 0.16.0 at | ||||||||||
instantiate: | ||||||||||
|
||||||||||
```rust | ||||||||||
use cw22::{set_contract_supported_interface, ContractSupportedInterface}; | ||||||||||
|
||||||||||
pub fn instantiate( | ||||||||||
mut deps: DepsMut, | ||||||||||
_env: Env, | ||||||||||
_info: MessageInfo, | ||||||||||
msg: InstantiateMsg, | ||||||||||
) -> Result<Response, ContractError> { | ||||||||||
///... | ||||||||||
let supported_interface = ContractSupportedInterface { | ||||||||||
supported_interface: String::from("crates.io:cw20"), | ||||||||||
version: String::from("0.16.0"), | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yea, I may be a little bit biased but seeing this and then sending the whole structure by reference line later my whole brain screams "cost". There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. However, those should be updated - as supported_interface: From::from("crates.io:cw20"),
version: From::from("0.16.0"), or supported_interface: "crates.io:cw20".into(),
version: "0.16.0".into(), Both of those work for Sorry for the annoying nitpicking here. We struggle with documentation quality, and we don't want to introduce any more dept in this area. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
}; | ||||||||||
set_contract_supported_interface(deps.storage, &[supported_interface])?; | ||||||||||
///... | ||||||||||
} | ||||||||||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
/*! | ||
CW22 defines a way for a contract to declare which interfaces do the contract implement | ||
This standard is inspired by the EIP-165 from Ethereum. Originally it was proposed to | ||
be merged into CW2: Contract Info, then it is splitted to a separated cargo to keep CW2 | ||
being backward compatible. | ||
|
||
Each supported interface contains a string value pointing to the corresponding cargo package | ||
and a specific release of the package. There is also a function to check whether the contract | ||
support a specific version of an interface or not. | ||
|
||
The version string for each interface follows Semantic Versioning standard. More info is in: | ||
https://docs.rs/semver/latest/semver/ | ||
*/ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit/consistency: we use |
||
use cosmwasm_schema::cw_serde; | ||
use cosmwasm_std::{StdError, StdResult, Storage}; | ||
use cw_storage_plus::Map; | ||
use semver::{Version, VersionReq}; | ||
|
||
pub const SUPPORTED_INTERFACES: Map<&str, String> = Map::new("supported_interfaces"); | ||
|
||
#[cw_serde] | ||
pub struct ContractSupportedInterface { | ||
/// supported_interface is an optional parameter returning a vector of string represents interfaces | ||
/// that the contract support The string value is the interface crate names in Rust crate Registry. | ||
/// This parameter is inspired by the EIP-165 from Ethereum. | ||
/// Each string value should follow a common standard such as <Registry Domain>:<Crate Name> | ||
/// e.g "crates.io:cw2" | ||
/// NOTE: this is just a hint for the caller to adapt on how to interact with this contract. | ||
/// There is no guarantee that the contract actually implement these interfaces. | ||
pub supported_interface: String, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is where the comment about |
||
/// semantic version on release tags of the interface package following SemVer guideline. | ||
/// e.g "0.16.0" | ||
pub version: String, | ||
} | ||
|
||
/// set_contract_supported_interface should be used in instantiate to store the original version | ||
/// of supported interfaces. It should also be used after every migration. | ||
pub fn set_contract_supported_interface( | ||
store: &mut dyn Storage, | ||
supported_interfaces: &[ContractSupportedInterface], | ||
) -> StdResult<()> { | ||
for item in supported_interfaces { | ||
let ver = Version::parse(&item.version); | ||
match ver { | ||
Ok(_) => { | ||
SUPPORTED_INTERFACES.save(store, &item.supported_interface, &item.version)?; | ||
} | ||
Err(_) => { | ||
return Err(StdError::generic_err("Version's format is invalid")); | ||
} | ||
} | ||
} | ||
Ok(()) | ||
} | ||
|
||
/// query_supported_interface_version show the version of an interface supported by the contract | ||
pub fn query_supported_interface_version( | ||
store: &dyn Storage, | ||
interface: &str, | ||
) -> StdResult<Option<String>> { | ||
let version = SUPPORTED_INTERFACES.may_load(store, interface)?; | ||
Ok(version) | ||
} | ||
|
||
pub fn minimum_version(version: &str, required: &str) -> bool { | ||
if let Ok(ver) = Version::parse(version) { | ||
if let Ok(req) = VersionReq::parse(format!(">={}", required).as_str()) { | ||
return req.matches(&ver); | ||
} | ||
} | ||
false | ||
} | ||
|
||
/// query_supported_interface show if contract supports an interface with version following SemVer query | ||
/// query example">=1.2.3, <1.8.0" | ||
pub fn require_version(version: &str, request: &str) -> bool { | ||
if let Ok(ver) = Version::parse(version) { | ||
if let Ok(req) = VersionReq::parse(request) { | ||
return req.matches(&ver); | ||
} | ||
} | ||
false | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use cosmwasm_std::testing::MockStorage; | ||
|
||
#[test] | ||
fn get_and_set_work() { | ||
let mut store = MockStorage::new(); | ||
|
||
let interface2 = "crates.io:cw2"; | ||
let interface22 = "crates.io:cw22"; | ||
let interface721 = "crates.io:cw721"; | ||
let contract_interface2 = ContractSupportedInterface { | ||
supported_interface: String::from(interface2), | ||
version: String::from("0.16.0"), | ||
}; | ||
let contract_interface22 = ContractSupportedInterface { | ||
supported_interface: String::from(interface22), | ||
version: String::from("0.1.0"), | ||
}; | ||
let contract_interface721 = ContractSupportedInterface { | ||
supported_interface: String::from(interface22), | ||
version: String::from("v0.1.0"), | ||
}; | ||
|
||
// set supported_interface error | ||
let supported_interface = &[contract_interface721]; | ||
|
||
let rs_error = | ||
set_contract_supported_interface(&mut store, supported_interface).unwrap_err(); | ||
let expected = StdError::generic_err("Version's format is invalid"); | ||
assert_eq!(expected, rs_error); | ||
|
||
// set supported_interface | ||
let supported_interface = &[contract_interface2, contract_interface22]; | ||
|
||
set_contract_supported_interface(&mut store, supported_interface).unwrap(); | ||
// get version of not supported interface | ||
let loaded = query_supported_interface_version(&store, interface721).unwrap(); | ||
assert_eq!(None, loaded); | ||
|
||
// get version of supported interface | ||
let loaded = query_supported_interface_version(&store, interface2).unwrap(); | ||
let expected = String::from("0.16.0"); | ||
assert_eq!(Some(expected), loaded); | ||
} | ||
|
||
#[test] | ||
fn test_require_version() { | ||
let version_req = ">=0.1.0"; | ||
let result = require_version("0.16.0", version_req); | ||
assert!(result); | ||
|
||
let version_req = ">=0.16.0"; | ||
let result = require_version("0.1.0", version_req); | ||
assert!(!result); | ||
|
||
let version_req = ">=1.2.3, <1.8.0"; | ||
let result = require_version("0.16.0", version_req); | ||
assert!(!result); | ||
|
||
let version_req = ">=0.2.3"; | ||
let result = require_version("v0.16.0", version_req); | ||
assert!(!result); | ||
|
||
let version_req = "!=0.2.3"; | ||
let result = require_version("0.16.0", version_req); | ||
assert!(!result); | ||
} | ||
|
||
#[test] | ||
fn test_minimum_version() { | ||
let result = minimum_version("0.16.0", "0.2.3"); | ||
assert!(result); | ||
|
||
let result = minimum_version("0.2.0", "0.2.3"); | ||
assert!(!result); | ||
|
||
let result = minimum_version("v0.16.0", "0.2.3"); | ||
assert!(!result); | ||
|
||
let result = minimum_version("0.16.0", "v0.2.3"); | ||
assert!(!result); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: this is different than implementation, please update