Skip to content

Commit

Permalink
Merge pull request #370 from CosmWasm/cw20-logo-spec
Browse files Browse the repository at this point in the history
Cw20 logo spec
  • Loading branch information
ethanfrey committed Aug 3, 2021
2 parents 61ef8e7 + 02a0cd6 commit ec18b57
Show file tree
Hide file tree
Showing 10 changed files with 347 additions and 3 deletions.
30 changes: 30 additions & 0 deletions packages/cw20/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,36 @@ the minter is a smart contract.
This should be enabled with all blockchains that have iterator support.
It allows us to get lists of results with pagination.

## Marketing

This allows us to attach more metadata on the token to help with displaying the token in
wallets. When you see a token's website, then see it in a wallet, you know what it is.
However, if you see it in a wallet or a DEX trading pair, there is no clear way to find out
any more info about it.

This extension allows us to attach more "Marketing" metadata, which has no effect on the
on-chain functionality of the token, but is very useful in providing a better client-side
experience. Note, that we add a new role `marketing`, which can update such info, but not
affect on-chain logic.

### Messages

`UploadLogo{url | embedded}` - If the `env.sender` is the allowed marketing account,
this will either set a new URL reference where the logo is served, or allow them to upload
a small (less than 5KB) SVG or PNG logo onto the blockchain to be served.

`UpdateMarketing{project, description, marketing}` - If the `env.sender` is the allowed marketing
account, this will update some marketing-related metadata on the contract.

### Queries

`MarketingInfo{}` - Returns marketing-related metadata. Return type is
`MarketingInfoResponse {project, description, logo, marketing}`.

`DownloadLogo{}` - If the token's logo was previously uploaded to the blockchain
(see `UploadLogo` message), then it returns the raw data to be displayed in a browser.
Return type is `DownloadLogoResponse{ mime_type, data }`.

### Queries

`AllAllowances{owner, start_after, limit}` - Returns the list of all non-expired allowances
Expand Down
5 changes: 4 additions & 1 deletion packages/cw20/examples/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use cosmwasm_schema::{export_schema, remove_schemas, schema_for};

use cw20::{
AllAccountsResponse, AllAllowancesResponse, AllowanceResponse, BalanceResponse, Cw20ExecuteMsg,
Cw20QueryMsg, Cw20ReceiveMsg, MinterResponse, TokenInfoResponse,
Cw20QueryMsg, Cw20ReceiveMsg, DownloadLogoResponse, MarketingInfoResponse, MinterResponse,
TokenInfoResponse,
};

fn main() {
Expand All @@ -21,6 +22,8 @@ fn main() {
export_schema(&schema_for!(BalanceResponse), &out_dir);
export_schema(&schema_for!(TokenInfoResponse), &out_dir);
export_schema(&schema_for!(MinterResponse), &out_dir);
export_schema(&schema_for!(DownloadLogoResponse), &out_dir);
export_schema(&schema_for!(MarketingInfoResponse), &out_dir);
export_schema(&schema_for!(AllAllowancesResponse), &out_dir);
export_schema(&schema_for!(AllAccountsResponse), &out_dir);
}
111 changes: 111 additions & 0 deletions packages/cw20/schema/cw20_execute_msg.json
Original file line number Diff line number Diff line change
Expand Up @@ -258,13 +258,93 @@
}
},
"additionalProperties": false
},
{
"description": "Only with the \"marketing\" extension. If authorized, updates marketing metadata. Setting None/null for any of these will leave it unchanged. Setting Some(\"\") will clear this field on the contract storage",
"type": "object",
"required": [
"update_marketing"
],
"properties": {
"update_marketing": {
"type": "object",
"properties": {
"description": {
"description": "A longer description of the token and it's utility. Designed for tooltips or such",
"type": [
"string",
"null"
]
},
"marketing": {
"description": "The address (if any) who can update this data structure",
"type": [
"string",
"null"
]
},
"project": {
"description": "A URL pointing to the project behind this token.",
"type": [
"string",
"null"
]
}
}
}
},
"additionalProperties": false
},
{
"description": "If set as the \"marketing\" role on the contract, upload a new URL, SVG, or PNG for the token",
"type": "object",
"required": [
"upload_logo"
],
"properties": {
"upload_logo": {
"$ref": "#/definitions/Logo"
}
},
"additionalProperties": false
}
],
"definitions": {
"Binary": {
"description": "Binary is a wrapper around Vec<u8> to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec<u8>",
"type": "string"
},
"EmbeddedLogo": {
"description": "This is used to store the logo on the blockchain in an accepted format. Enforce maximum size of 5KB on all variants.",
"anyOf": [
{
"description": "Store the Logo as an SVG file. The content must conform to the spec at https://en.wikipedia.org/wiki/Scalable_Vector_Graphics (The contract should do some light-weight sanity-check validation)",
"type": "object",
"required": [
"svg"
],
"properties": {
"svg": {
"$ref": "#/definitions/Binary"
}
},
"additionalProperties": false
},
{
"description": "Store the Logo as a PNG file. This will likely only support up to 64x64 or so within the 5KB limit.",
"type": "object",
"required": [
"png"
],
"properties": {
"png": {
"$ref": "#/definitions/Binary"
}
},
"additionalProperties": false
}
]
},
"Expiration": {
"description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)",
"anyOf": [
Expand Down Expand Up @@ -311,6 +391,37 @@
}
]
},
"Logo": {
"description": "This is used for uploading logo data, or setting it in InstantiateData",
"anyOf": [
{
"description": "A reference to an externally hosted logo. Must be a valid HTTP or HTTPS URL.",
"type": "object",
"required": [
"url"
],
"properties": {
"url": {
"type": "string"
}
},
"additionalProperties": false
},
{
"description": "Logo content stored on the blockchain. Enforce maximum size of 5KB on all variants",
"type": "object",
"required": [
"embedded"
],
"properties": {
"embedded": {
"$ref": "#/definitions/EmbeddedLogo"
}
},
"additionalProperties": false
}
]
},
"Timestamp": {
"description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.\n\n## Examples\n\n``` # use cosmwasm_std::Timestamp; let ts = Timestamp::from_nanos(1_000_000_202); assert_eq!(ts.nanos(), 1_000_000_202); assert_eq!(ts.seconds(), 1); assert_eq!(ts.subsec_nanos(), 202);\n\nlet ts = ts.plus_seconds(2); assert_eq!(ts.nanos(), 3_000_000_202); assert_eq!(ts.seconds(), 3); assert_eq!(ts.subsec_nanos(), 202); ```",
"allOf": [
Expand Down
26 changes: 26 additions & 0 deletions packages/cw20/schema/cw20_query_msg.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,32 @@
},
"additionalProperties": false
},
{
"description": "Only with \"marketing\" extension Returns more metadata on the contract to display in the client: - description, logo, project url, etc. Return type: MarketingInfoResponse.",
"type": "object",
"required": [
"marketing_info"
],
"properties": {
"marketing_info": {
"type": "object"
}
},
"additionalProperties": false
},
{
"description": "Only with \"marketing\" extension Downloads the embedded logo data (if stored on chain). Errors if no logo data stored for this contract. Return type: DownloadLogoResponse.",
"type": "object",
"required": [
"download_logo"
],
"properties": {
"download_logo": {
"type": "object"
}
},
"additionalProperties": false
},
{
"description": "Only with \"enumerable\" extension (and \"allowances\") Returns all allowances this owner has approved. Supports pagination. Return type: AllAllowancesResponse.",
"type": "object",
Expand Down
24 changes: 24 additions & 0 deletions packages/cw20/schema/download_logo_response.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "DownloadLogoResponse",
"description": "When we download an embedded logo, we get this response type. We expect a SPA to be able to accept this info and display it.",
"type": "object",
"required": [
"data",
"mime_type"
],
"properties": {
"data": {
"$ref": "#/definitions/Binary"
},
"mime_type": {
"type": "string"
}
},
"definitions": {
"Binary": {
"description": "Binary is a wrapper around Vec<u8> to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec<u8>",
"type": "string"
}
}
}
65 changes: 65 additions & 0 deletions packages/cw20/schema/marketing_info_response.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "MarketingInfoResponse",
"type": "object",
"properties": {
"description": {
"description": "A longer description of the token and it's utility. Designed for tooltips or such",
"type": [
"string",
"null"
]
},
"logo": {
"description": "A link to the logo, or a comment there is an on-chain logo stored",
"anyOf": [
{
"$ref": "#/definitions/LogoInfo"
},
{
"type": "null"
}
]
},
"marketing": {
"description": "The address (if any) who can update this data structure",
"type": [
"string",
"null"
]
},
"project": {
"description": "A URL pointing to the project behind this token.",
"type": [
"string",
"null"
]
}
},
"definitions": {
"LogoInfo": {
"description": "This is used to display logo info, provide a link or inform there is one that can be downloaded from the blockchain itself",
"anyOf": [
{
"type": "string",
"enum": [
"embedded"
]
},
{
"description": "A reference to an externally hosted logo. Must be a valid HTTP or HTTPS URL.",
"type": "object",
"required": [
"url"
],
"properties": {
"url": {
"type": "string"
}
},
"additionalProperties": false
}
]
}
}
}
4 changes: 3 additions & 1 deletion packages/cw20/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@ pub use crate::balance::Balance;
pub use crate::coin::{Cw20Coin, Cw20CoinVerified};
pub use crate::denom::Denom;
pub use crate::helpers::Cw20Contract;
pub use crate::logo::{EmbeddedLogo, Logo, LogoInfo};
pub use crate::msg::Cw20ExecuteMsg;
pub use crate::query::{
AllAccountsResponse, AllAllowancesResponse, AllowanceInfo, AllowanceResponse, BalanceResponse,
Cw20QueryMsg, MinterResponse, TokenInfoResponse,
Cw20QueryMsg, DownloadLogoResponse, MarketingInfoResponse, MinterResponse, TokenInfoResponse,
};
pub use crate::receiver::Cw20ReceiveMsg;

mod balance;
mod coin;
mod denom;
mod helpers;
mod logo;
mod msg;
mod query;
mod receiver;
Expand Down
38 changes: 38 additions & 0 deletions packages/cw20/src/logo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use cosmwasm_std::Binary;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

/// This is used for uploading logo data, or setting it in InstantiateData
#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
#[serde(rename_all = "snake_case")]
pub enum Logo {
/// A reference to an externally hosted logo. Must be a valid HTTP or HTTPS URL.
Url(String),
/// Logo content stored on the blockchain. Enforce maximum size of 5KB on all variants
Embedded(EmbeddedLogo),
}

/// This is used to store the logo on the blockchain in an accepted format.
/// Enforce maximum size of 5KB on all variants.
#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
#[serde(rename_all = "snake_case")]
pub enum EmbeddedLogo {
/// Store the Logo as an SVG file. The content must conform to the spec
/// at https://en.wikipedia.org/wiki/Scalable_Vector_Graphics
/// (The contract should do some light-weight sanity-check validation)
Svg(Binary),
/// Store the Logo as a PNG file. This will likely only support up to 64x64 or so
/// within the 5KB limit.
Png(Binary),
}

/// This is used to display logo info, provide a link or inform there is one
/// that can be downloaded from the blockchain itself
#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
#[serde(rename_all = "snake_case")]
pub enum LogoInfo {
/// A reference to an externally hosted logo. Must be a valid HTTP or HTTPS URL.
Url(String),
/// There is an embedded logo on the chain, make another call to download it.
Embedded,
}
14 changes: 14 additions & 0 deletions packages/cw20/src/msg.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::logo::Logo;
use cosmwasm_std::{Binary, Uint128};
use cw0::Expiration;

Expand Down Expand Up @@ -54,4 +55,17 @@ pub enum Cw20ExecuteMsg {
/// Only with the "mintable" extension. If authorized, creates amount new tokens
/// and adds to the recipient balance.
Mint { recipient: String, amount: Uint128 },
/// Only with the "marketing" extension. If authorized, updates marketing metadata.
/// Setting None/null for any of these will leave it unchanged.
/// Setting Some("") will clear this field on the contract storage
UpdateMarketing {
/// A URL pointing to the project behind this token.
project: Option<String>,
/// A longer description of the token and it's utility. Designed for tooltips or such
description: Option<String>,
/// The address (if any) who can update this data structure
marketing: Option<String>,
},
/// If set as the "marketing" role on the contract, upload a new URL, SVG, or PNG for the token
UploadLogo(Logo),
}

0 comments on commit ec18b57

Please sign in to comment.