Skip to content

Commit

Permalink
add supported interface
Browse files Browse the repository at this point in the history
  • Loading branch information
ThienLK1 committed Nov 8, 2022
1 parent 3610110 commit 884e17b
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 0 deletions.
3 changes: 3 additions & 0 deletions packages/cw2-supported-interface/.cargo/config
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"
16 changes: 16 additions & 0 deletions packages/cw2-supported-interface/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "cw2-supported-interface"
version = "1.0.0"
authors = ["ThienLK1@aura-nw"]
edition = "2021"
description = "Definition and types for the CosmWasm-2 interface"
license = "Apache-2.0"
repository = "https://github.com/CosmWasm/cw-plus"
homepage = "https://cosmwasm.com"

[dependencies]
cosmwasm-schema = "1.0.0"
cosmwasm-std = { version = "1.0.0", default-features = false }
cw-storage-plus = "0.16.0"
schemars = "0.8.1"
serde = { version = "1.0.0", default-features = false, features = ["derive"] }
58 changes: 58 additions & 0 deletions packages/cw2-supported-interface/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# CW2 Spec: Contract Info

Most of the CW* specs are focused on the *public interfaces*
of the contract. The APIs used for `ExecuteMsg` or `QueryMsg`.
However, when we wish to migrate or inspect smart contract info,
we need some form of smart contract information embedded on state.

This is where CW2 comes in. It specifies a special Item to
be stored on disk by all contracts on `instantiate`.

`ContractInfo` must be stored under the `"contract_info"` key which translates
to `"636F6E74726163745F696E666F"` in hex format.
Since the state is well defined, we do not need to support any "smart queries".
We do provide a helper to construct a "raw query" to read the ContractInfo
of any CW2-compliant contract.

You can query using:
```shell
wasmd query wasm contract-state raw [contract_addr] 636F6E74726163745F696E666F --node $RPC
```

When the `migrate` function is called, then the new contract
can read that data andsee if this is an expected contract we can
migrate from. And also contain extra version information if we
support multiple migrate paths.

### Data structures

**Required**

All CW2-compliant contracts must store the following data:

* key: `contract_info`
* data: Json-serialized `ContractVersion`

```rust
pub struct ContractVersion {
/// contract is a globally unique identifier for the contract.
/// it should build off standard namespacing for whichever language it is in,
/// and prefix it with the registry we use.
/// For rust we prefix with `crates.io:`, to give us eg. `crates.io:cw20-base`
pub contract: String,
/// version is any string that this implementation knows. It may be simple counter "1", "2".
/// or semantic version on release tags "v0.7.0", or some custom feature flag list.
/// the only code that needs to understand the version parsing is code that knows how to
/// migrate from the given contract (and is tied to it's implementation somehow)
pub version: String,
}
```

Thus, an serialized example may looks like:

```json
{
"contract": "crates.io:cw20-base",
"version": "v0.1.0"
}
```
136 changes: 136 additions & 0 deletions packages/cw2-supported-interface/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*!
Most of the CW* specs are focused on the *public interfaces*
of the contract. The APIs used for `ExecuteMsg` or `QueryMsg`.
However, when we wish to migrate or inspect smart contract info,
we need some form of smart contract information embedded on state.
This is where CW2 comes in. It specifies a special Item to
be stored on disk by all contracts on `instantiate`.
`ContractInfo` must be stored under the `"contract_info"` key which translates
to `"636F6E74726163745F696E666F"` in hex format.
Since the state is well defined, we do not need to support any "smart queries".
We do provide a helper to construct a "raw query" to read the ContractInfo
of any CW2-compliant contract.
For more information on this specification, please check out the
[README](https://github.com/CosmWasm/cw-plus/blob/main/packages/cw2/README.md).
*/

use cosmwasm_schema::cw_serde;
use cosmwasm_std::{CustomQuery, QuerierWrapper, QueryRequest, StdResult, Storage, WasmQuery};
use cw_storage_plus::Item;

pub const CONTRACT: Item<ContractVersion> = Item::new("contract_info");

#[cw_serde]
pub struct ContractVersion {
/// contract is the crate name of the implementing contract, eg. `crate:cw20-base`
/// we will use other prefixes for other languages, and their standard global namespacing
pub contract: String,
/// version is any string that this implementation knows. It may be simple counter "1", "2".
/// or semantic version on release tags "v0.7.0", or some custom feature flag list.
/// the only code that needs to understand the version parsing is code that knows how to
/// migrate from the given contract (and is tied to it's implementation somehow)
pub version: String,
/// 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:cw721","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: Option<Vec<String>>,
}

/// get_contract_version can be use in migrate to read the previous version of this contract
pub fn get_contract_version(store: &dyn Storage) -> StdResult<ContractVersion> {
CONTRACT.load(store)
}

/// set_contract_version should be used in instantiate to store the original version, and after a successful
/// migrate to update it
pub fn set_contract_version<T: Into<String>, U: Into<String>>(
store: &mut dyn Storage,
name: T,
version: U,
supported_interface: Option<Vec<String>>,
) -> StdResult<()> {
let val = ContractVersion {
contract: name.into(),
version: version.into(),
supported_interface: supported_interface.into(),
};
CONTRACT.save(store, &val)
}

/// This will make a raw_query to another contract to determine the current version it
/// claims to be. This should not be trusted, but could be used as a quick filter
/// if the other contract exists and claims to be a cw20-base contract for example.
/// (Note: you usually want to require *interfaces* not *implementations* of the
/// contracts you compose with, so be careful of overuse)
pub fn query_contract_info<T, CQ>(
querier: &QuerierWrapper<CQ>,
contract_addr: T,
) -> StdResult<ContractVersion>
where
T: Into<String>,
CQ: CustomQuery,
{
let req = QueryRequest::Wasm(WasmQuery::Raw {
contract_addr: contract_addr.into(),
key: CONTRACT.as_slice().into(),
});
querier.query(&req)
}

#[cfg(test)]
mod tests {
use super::*;
use cosmwasm_std::testing::MockStorage;
use std::vec::Vec;

#[test]
fn get_and_set_work() {
let mut store = MockStorage::new();

// error if not set
assert!(get_contract_version(&store).is_err());

// set and get
let contract_name = "crate:cw20-base";
let contract_version = "0.2.0";
set_contract_version(&mut store, contract_name, contract_version, None).unwrap();

let loaded = get_contract_version(&store).unwrap();
let expected = ContractVersion {
contract: contract_name.to_string(),
version: contract_version.to_string(),
supported_interface: None,
};
assert_eq!(expected, loaded);

// set and get with supported_interface
let contract_name = "crate:cw20-base";
let contract_version = "0.2.0";
let supported_interface = Some(Vec::from([
"crates.io:cw2".to_string(),
"crates.io:cw721".to_string(),
]));
set_contract_version(
&mut store,
contract_name,
contract_version,
supported_interface.clone(),
)
.unwrap();

let loaded = get_contract_version(&store).unwrap();
let expected = ContractVersion {
contract: contract_name.to_string(),
version: contract_version.to_string(),
supported_interface: supported_interface.clone(),
};
assert_eq!(expected, loaded);
}
}

0 comments on commit 884e17b

Please sign in to comment.