From a84fbad7f615ca9e39ddeefe9c4185bedf97e18d Mon Sep 17 00:00:00 2001 From: Noah Saso Date: Fri, 26 May 2023 15:06:35 -0700 Subject: [PATCH] Added first draft DAO registry contract. --- Cargo.lock | 22 + contracts/external/dao-registry/.cargo/config | 6 + contracts/external/dao-registry/Cargo.toml | 36 + contracts/external/dao-registry/README.md | 5 + .../external/dao-registry/examples/schema.rs | 10 + .../dao-registry/schema/dao-registry.json | 1589 +++++++++++++++++ .../dao-registry/schema/raw/execute.json | 398 +++++ .../dao-registry/schema/raw/instantiate.json | 91 + .../dao-registry/schema/raw/query.json | 126 ++ .../schema/raw/response_to_config.json | 90 + .../response_to_most_recent_registration.json | 192 ++ .../schema/raw/response_to_ownership.json | 107 ++ .../raw/response_to_pending_registration.json | 192 ++ .../schema/raw/response_to_registration.json | 192 ++ .../schema/raw/response_to_resolve.json | 192 ++ .../external/dao-registry/src/contract.rs | 506 ++++++ contracts/external/dao-registry/src/error.rs | 50 + contracts/external/dao-registry/src/lib.rs | 16 + contracts/external/dao-registry/src/msg.rs | 131 ++ .../external/dao-registry/src/registration.rs | 186 ++ contracts/external/dao-registry/src/state.rs | 30 + contracts/external/dao-registry/src/tests.rs | 736 ++++++++ 22 files changed, 4903 insertions(+) create mode 100644 contracts/external/dao-registry/.cargo/config create mode 100644 contracts/external/dao-registry/Cargo.toml create mode 100644 contracts/external/dao-registry/README.md create mode 100644 contracts/external/dao-registry/examples/schema.rs create mode 100644 contracts/external/dao-registry/schema/dao-registry.json create mode 100644 contracts/external/dao-registry/schema/raw/execute.json create mode 100644 contracts/external/dao-registry/schema/raw/instantiate.json create mode 100644 contracts/external/dao-registry/schema/raw/query.json create mode 100644 contracts/external/dao-registry/schema/raw/response_to_config.json create mode 100644 contracts/external/dao-registry/schema/raw/response_to_most_recent_registration.json create mode 100644 contracts/external/dao-registry/schema/raw/response_to_ownership.json create mode 100644 contracts/external/dao-registry/schema/raw/response_to_pending_registration.json create mode 100644 contracts/external/dao-registry/schema/raw/response_to_registration.json create mode 100644 contracts/external/dao-registry/schema/raw/response_to_resolve.json create mode 100644 contracts/external/dao-registry/src/contract.rs create mode 100644 contracts/external/dao-registry/src/error.rs create mode 100644 contracts/external/dao-registry/src/lib.rs create mode 100644 contracts/external/dao-registry/src/msg.rs create mode 100644 contracts/external/dao-registry/src/registration.rs create mode 100644 contracts/external/dao-registry/src/state.rs create mode 100644 contracts/external/dao-registry/src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index 98eff573b..936e893d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1682,6 +1682,28 @@ dependencies = [ "thiserror", ] +[[package]] +name = "dao-registry" +version = "2.1.0" +dependencies = [ + "anyhow", + "cosmwasm-schema", + "cosmwasm-std", + "cw-denom", + "cw-multi-test", + "cw-ownable", + "cw-paginate 2.1.0", + "cw-storage-plus 1.0.1 (git+https://github.com/CosmWasm/cw-storage-plus?rev=6db957ce730a95141a3ab4dc5ab76fb38e8c0c42)", + "cw-utils 0.16.0", + "cw2 0.16.0", + "cw20 0.16.0", + "cw20-base 0.16.0", + "cw721 0.16.0", + "dao-testing", + "serde", + "thiserror", +] + [[package]] name = "dao-testing" version = "2.1.0" diff --git a/contracts/external/dao-registry/.cargo/config b/contracts/external/dao-registry/.cargo/config new file mode 100644 index 000000000..8d4bc738b --- /dev/null +++ b/contracts/external/dao-registry/.cargo/config @@ -0,0 +1,6 @@ +[alias] +wasm = "build --release --target wasm32-unknown-unknown" +wasm-debug = "build --target wasm32-unknown-unknown" +unit-test = "test --lib" +integration-test = "test --test integration" +schema = "run --example schema" diff --git a/contracts/external/dao-registry/Cargo.toml b/contracts/external/dao-registry/Cargo.toml new file mode 100644 index 000000000..058720eff --- /dev/null +++ b/contracts/external/dao-registry/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "dao-registry" +authors = ["Noah Saso"] +edition = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +version = { workspace = true } + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +# for more explicit tests, cargo test --features=backtraces +backtraces = ["cosmwasm-std/backtraces"] +# use library feature to disable all instantiate/execute/query exports +library = [] + +[dependencies] +cosmwasm-schema = { workspace = true } +cosmwasm-std = { workspace = true } +cw-denom = { workspace = true } +cw-ownable = { workspace = true } +cw-paginate = { workspace = true } +cw-storage-plus = { workspace = true } +cw-utils = { workspace = true } +cw2 = { workspace = true } +cw20 = { workspace = true } +cw721 = { workspace = true } +serde = { workspace = true } +thiserror = { workspace = true } + +[dev-dependencies] +anyhow = { workspace = true } +cw-multi-test = { workspace = true } +cw20-base = { workspace = true } +dao-testing = { workspace = true } diff --git a/contracts/external/dao-registry/README.md b/contracts/external/dao-registry/README.md new file mode 100644 index 000000000..4833d5945 --- /dev/null +++ b/contracts/external/dao-registry/README.md @@ -0,0 +1,5 @@ +# dao-registry + +This contract enables the registration of DAOs and associated unique names. Like +domain names, a fee is charged on a given interval for registration, and +registrations can be renewed at most one registration period in advance. diff --git a/contracts/external/dao-registry/examples/schema.rs b/contracts/external/dao-registry/examples/schema.rs new file mode 100644 index 000000000..7b3ae8242 --- /dev/null +++ b/contracts/external/dao-registry/examples/schema.rs @@ -0,0 +1,10 @@ +use cosmwasm_schema::write_api; +use dao_registry::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + +fn main() { + write_api! { + instantiate: InstantiateMsg, + query: QueryMsg, + execute: ExecuteMsg, + } +} diff --git a/contracts/external/dao-registry/schema/dao-registry.json b/contracts/external/dao-registry/schema/dao-registry.json new file mode 100644 index 000000000..9a6fe676b --- /dev/null +++ b/contracts/external/dao-registry/schema/dao-registry.json @@ -0,0 +1,1589 @@ +{ + "contract_name": "dao-registry", + "contract_version": "2.1.0", + "idl_version": "1.0.0", + "instantiate": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "fee_amount", + "fee_denom", + "owner", + "registration_period" + ], + "properties": { + "fee_amount": { + "description": "The fee amount to register a DAO.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "fee_denom": { + "description": "The fee denom to register a DAO.", + "allOf": [ + { + "$ref": "#/definitions/UncheckedDenom" + } + ] + }, + "owner": { + "description": "The owner of the DAO registry. The owner is responsible for approving and rejecting registrations and receives the fees paid. They also have admin-level privileges.", + "type": "string" + }, + "registration_period": { + "description": "How long a registration lasts.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "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": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + }, + "UncheckedDenom": { + "description": "A denom that has not been checked to confirm it points to a valid asset.", + "oneOf": [ + { + "description": "A native (bank module) asset.", + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A cw20 asset.", + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + }, + "execute": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "description": "Register or renew with a cw20 token.", + "type": "object", + "required": [ + "receive" + ], + "properties": { + "receive": { + "$ref": "#/definitions/Cw20ReceiveMsg" + } + }, + "additionalProperties": false + }, + { + "description": "Register with a native token or auto-register by the owner.", + "type": "object", + "required": [ + "register" + ], + "properties": { + "register": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "address": { + "description": "The DAO to register. This can be used by the owner to assign a registration without a fee or approval step.", + "type": [ + "string", + "null" + ] + }, + "name": { + "description": "The name of the registration.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Renew with a native token. Renewal can only occur if the remaining time is less than the length of a registration. This means you can only renew once every registration period, to prevent squatting more than one registration period in advance.", + "type": "object", + "required": [ + "renew" + ], + "properties": { + "renew": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Approve a pending registration. Only the owner can do this.", + "type": "object", + "required": [ + "approve" + ], + "properties": { + "approve": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "The address of the DAO with a pending registration. This is used instead of the name since several pending registrations may be trying to register the same name.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Reject a pending registration. Only the owner can do this. The registration fee is returned to the DAO.", + "type": "object", + "required": [ + "reject" + ], + "properties": { + "reject": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "The address of the DAO with a pending registration. This is used instead of the name since several pending registrations may be trying to register the same name.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Revoke a registration. Only the owner can do this. The registration fee is NOT returned to the DAO.", + "type": "object", + "required": [ + "revoke" + ], + "properties": { + "revoke": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "description": "The name of the registration.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Update the expiration of a registration. Only the owner can do this.", + "type": "object", + "required": [ + "update_expiration" + ], + "properties": { + "update_expiration": { + "type": "object", + "required": [ + "expiration", + "name" + ], + "properties": { + "expiration": { + "description": "The new expiration.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + }, + "name": { + "description": "The name of the registration.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Update the config of the registry. Only the owner can do this.", + "type": "object", + "required": [ + "update_config" + ], + "properties": { + "update_config": { + "type": "object", + "properties": { + "fee_amount": { + "description": "The new fee amount to register a DAO.", + "anyOf": [ + { + "$ref": "#/definitions/Uint128" + }, + { + "type": "null" + } + ] + }, + "fee_denom": { + "description": "The new fee denom to register a DAO.", + "anyOf": [ + { + "$ref": "#/definitions/UncheckedDenom" + }, + { + "type": "null" + } + ] + }, + "registration_period": { + "description": "The new registration period.", + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Update the contract's ownership. The `action` to be provided can be either to propose transferring ownership to an account, accept a pending ownership transfer, or renounce the ownership permanently.", + "type": "object", + "required": [ + "update_ownership" + ], + "properties": { + "update_ownership": { + "$ref": "#/definitions/Action" + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Action": { + "description": "Actions that can be taken to alter the contract's ownership", + "oneOf": [ + { + "description": "Propose to transfer the contract's ownership to another account, optionally with an expiry time.\n\nCan only be called by the contract's current owner.\n\nAny existing pending ownership transfer is overwritten.", + "type": "object", + "required": [ + "transfer_ownership" + ], + "properties": { + "transfer_ownership": { + "type": "object", + "required": [ + "new_owner" + ], + "properties": { + "expiry": { + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "new_owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Accept the pending ownership transfer.\n\nCan only be called by the pending owner.", + "type": "string", + "enum": [ + "accept_ownership" + ] + }, + { + "description": "Give up the contract's ownership and the possibility of appointing a new owner.\n\nCan only be invoked by the contract's current owner.\n\nAny existing pending ownership transfer is canceled.", + "type": "string", + "enum": [ + "renounce_ownership" + ] + } + ] + }, + "Binary": { + "description": "Binary is a wrapper around Vec 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. See also .", + "type": "string" + }, + "Cw20ReceiveMsg": { + "description": "Cw20ReceiveMsg should be de/serialized under `Receive()` variant in a ExecuteMsg", + "type": "object", + "required": [ + "amount", + "msg", + "sender" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "msg": { + "$ref": "#/definitions/Binary" + }, + "sender": { + "type": "string" + } + }, + "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)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "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": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + }, + "UncheckedDenom": { + "description": "A denom that has not been checked to confirm it points to a valid asset.", + "oneOf": [ + { + "description": "A native (bank module) asset.", + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A cw20 asset.", + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + }, + "query": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ + { + "description": "Get the current ownership.", + "type": "object", + "required": [ + "ownership" + ], + "properties": { + "ownership": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the configuration of the DAO registry.", + "type": "object", + "required": [ + "config" + ], + "properties": { + "config": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the active registration for the given address or None if no active registration exists.", + "type": "object", + "required": [ + "registration" + ], + "properties": { + "registration": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "The address with a registration.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the active registration for the given name or None if no active registration exists.", + "type": "object", + "required": [ + "resolve" + ], + "properties": { + "resolve": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "description": "The name of a registration.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the pending registration for the given address or None if no pending registration exists.", + "type": "object", + "required": [ + "pending_registration" + ], + "properties": { + "pending_registration": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "The address with a pending registration.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the most recent registration for the given address or None if the address has never been registered.", + "type": "object", + "required": [ + "most_recent_registration" + ], + "properties": { + "most_recent_registration": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "The address with a registration.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "migrate": null, + "sudo": null, + "responses": { + "config": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Config", + "type": "object", + "required": [ + "fee_amount", + "fee_denom", + "registration_period" + ], + "properties": { + "fee_amount": { + "description": "The fee amount to register a DAO.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "fee_denom": { + "description": "The fee denom to register a DAO.", + "allOf": [ + { + "$ref": "#/definitions/CheckedDenom" + } + ] + }, + "registration_period": { + "description": "How long a registration lasts. For a new registration, this is the first expiration. For a renewal, this is the amount of time added to the current expiration.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "CheckedDenom": { + "description": "A denom that has been checked to point to a valid asset. This enum should never be constructed literally and should always be built by calling `into_checked` on an `UncheckedDenom` instance.", + "oneOf": [ + { + "description": "A native (bank module) asset.", + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A cw20 asset.", + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "$ref": "#/definitions/Addr" + } + }, + "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": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } + }, + "most_recent_registration": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Nullable_Registration", + "anyOf": [ + { + "$ref": "#/definitions/Registration" + }, + { + "type": "null" + } + ], + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "CheckedDenom": { + "description": "A denom that has been checked to point to a valid asset. This enum should never be constructed literally and should always be built by calling `into_checked` on an `UncheckedDenom` instance.", + "oneOf": [ + { + "description": "A native (bank module) asset.", + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A cw20 asset.", + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "$ref": "#/definitions/Addr" + } + }, + "additionalProperties": false + } + ] + }, + "Config": { + "type": "object", + "required": [ + "fee_amount", + "fee_denom", + "registration_period" + ], + "properties": { + "fee_amount": { + "description": "The fee amount to register a DAO.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "fee_denom": { + "description": "The fee denom to register a DAO.", + "allOf": [ + { + "$ref": "#/definitions/CheckedDenom" + } + ] + }, + "registration_period": { + "description": "How long a registration lasts. For a new registration, this is the first expiration. For a renewal, this is the amount of time added to the current expiration.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + } + }, + "additionalProperties": false + }, + "Registration": { + "type": "object", + "required": [ + "address", + "expiration", + "name", + "status" + ], + "properties": { + "address": { + "description": "The address of the DAO.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "expiration": { + "description": "When the registration expires.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + }, + "name": { + "description": "The unique name of the DAO.", + "type": "string" + }, + "status": { + "description": "The status of the registration.", + "allOf": [ + { + "$ref": "#/definitions/RegistrationStatus" + } + ] + } + }, + "additionalProperties": false + }, + "RegistrationStatus": { + "oneOf": [ + { + "description": "The registration is pending approval.", + "type": "object", + "required": [ + "pending" + ], + "properties": { + "pending": { + "type": "object", + "required": [ + "config" + ], + "properties": { + "config": { + "description": "The config that was used to register. This is necessary in case the fee or registration period change after a request is submitted.", + "allOf": [ + { + "$ref": "#/definitions/Config" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "The registration was approved.", + "type": "string", + "enum": [ + "approved" + ] + }, + { + "description": "The registration is rejected.", + "type": "string", + "enum": [ + "rejected" + ] + }, + { + "description": "The registration is revoked.", + "type": "string", + "enum": [ + "revoked" + ] + } + ] + }, + "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": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } + }, + "ownership": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Ownership_for_Addr", + "description": "The contract's ownership info", + "type": "object", + "properties": { + "owner": { + "description": "The contract's current owner. `None` if the ownership has been renounced.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + }, + "pending_expiry": { + "description": "The deadline for the pending owner to accept the ownership. `None` if there isn't a pending ownership transfer, or if a transfer exists and it doesn't have a deadline.", + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "pending_owner": { + "description": "The account who has been proposed to take over the ownership. `None` if there isn't a pending ownership transfer.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "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)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "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": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } + }, + "pending_registration": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Nullable_Registration", + "anyOf": [ + { + "$ref": "#/definitions/Registration" + }, + { + "type": "null" + } + ], + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "CheckedDenom": { + "description": "A denom that has been checked to point to a valid asset. This enum should never be constructed literally and should always be built by calling `into_checked` on an `UncheckedDenom` instance.", + "oneOf": [ + { + "description": "A native (bank module) asset.", + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A cw20 asset.", + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "$ref": "#/definitions/Addr" + } + }, + "additionalProperties": false + } + ] + }, + "Config": { + "type": "object", + "required": [ + "fee_amount", + "fee_denom", + "registration_period" + ], + "properties": { + "fee_amount": { + "description": "The fee amount to register a DAO.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "fee_denom": { + "description": "The fee denom to register a DAO.", + "allOf": [ + { + "$ref": "#/definitions/CheckedDenom" + } + ] + }, + "registration_period": { + "description": "How long a registration lasts. For a new registration, this is the first expiration. For a renewal, this is the amount of time added to the current expiration.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + } + }, + "additionalProperties": false + }, + "Registration": { + "type": "object", + "required": [ + "address", + "expiration", + "name", + "status" + ], + "properties": { + "address": { + "description": "The address of the DAO.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "expiration": { + "description": "When the registration expires.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + }, + "name": { + "description": "The unique name of the DAO.", + "type": "string" + }, + "status": { + "description": "The status of the registration.", + "allOf": [ + { + "$ref": "#/definitions/RegistrationStatus" + } + ] + } + }, + "additionalProperties": false + }, + "RegistrationStatus": { + "oneOf": [ + { + "description": "The registration is pending approval.", + "type": "object", + "required": [ + "pending" + ], + "properties": { + "pending": { + "type": "object", + "required": [ + "config" + ], + "properties": { + "config": { + "description": "The config that was used to register. This is necessary in case the fee or registration period change after a request is submitted.", + "allOf": [ + { + "$ref": "#/definitions/Config" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "The registration was approved.", + "type": "string", + "enum": [ + "approved" + ] + }, + { + "description": "The registration is rejected.", + "type": "string", + "enum": [ + "rejected" + ] + }, + { + "description": "The registration is revoked.", + "type": "string", + "enum": [ + "revoked" + ] + } + ] + }, + "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": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } + }, + "registration": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Nullable_Registration", + "anyOf": [ + { + "$ref": "#/definitions/Registration" + }, + { + "type": "null" + } + ], + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "CheckedDenom": { + "description": "A denom that has been checked to point to a valid asset. This enum should never be constructed literally and should always be built by calling `into_checked` on an `UncheckedDenom` instance.", + "oneOf": [ + { + "description": "A native (bank module) asset.", + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A cw20 asset.", + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "$ref": "#/definitions/Addr" + } + }, + "additionalProperties": false + } + ] + }, + "Config": { + "type": "object", + "required": [ + "fee_amount", + "fee_denom", + "registration_period" + ], + "properties": { + "fee_amount": { + "description": "The fee amount to register a DAO.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "fee_denom": { + "description": "The fee denom to register a DAO.", + "allOf": [ + { + "$ref": "#/definitions/CheckedDenom" + } + ] + }, + "registration_period": { + "description": "How long a registration lasts. For a new registration, this is the first expiration. For a renewal, this is the amount of time added to the current expiration.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + } + }, + "additionalProperties": false + }, + "Registration": { + "type": "object", + "required": [ + "address", + "expiration", + "name", + "status" + ], + "properties": { + "address": { + "description": "The address of the DAO.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "expiration": { + "description": "When the registration expires.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + }, + "name": { + "description": "The unique name of the DAO.", + "type": "string" + }, + "status": { + "description": "The status of the registration.", + "allOf": [ + { + "$ref": "#/definitions/RegistrationStatus" + } + ] + } + }, + "additionalProperties": false + }, + "RegistrationStatus": { + "oneOf": [ + { + "description": "The registration is pending approval.", + "type": "object", + "required": [ + "pending" + ], + "properties": { + "pending": { + "type": "object", + "required": [ + "config" + ], + "properties": { + "config": { + "description": "The config that was used to register. This is necessary in case the fee or registration period change after a request is submitted.", + "allOf": [ + { + "$ref": "#/definitions/Config" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "The registration was approved.", + "type": "string", + "enum": [ + "approved" + ] + }, + { + "description": "The registration is rejected.", + "type": "string", + "enum": [ + "rejected" + ] + }, + { + "description": "The registration is revoked.", + "type": "string", + "enum": [ + "revoked" + ] + } + ] + }, + "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": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } + }, + "resolve": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Nullable_Registration", + "anyOf": [ + { + "$ref": "#/definitions/Registration" + }, + { + "type": "null" + } + ], + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "CheckedDenom": { + "description": "A denom that has been checked to point to a valid asset. This enum should never be constructed literally and should always be built by calling `into_checked` on an `UncheckedDenom` instance.", + "oneOf": [ + { + "description": "A native (bank module) asset.", + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A cw20 asset.", + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "$ref": "#/definitions/Addr" + } + }, + "additionalProperties": false + } + ] + }, + "Config": { + "type": "object", + "required": [ + "fee_amount", + "fee_denom", + "registration_period" + ], + "properties": { + "fee_amount": { + "description": "The fee amount to register a DAO.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "fee_denom": { + "description": "The fee denom to register a DAO.", + "allOf": [ + { + "$ref": "#/definitions/CheckedDenom" + } + ] + }, + "registration_period": { + "description": "How long a registration lasts. For a new registration, this is the first expiration. For a renewal, this is the amount of time added to the current expiration.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + } + }, + "additionalProperties": false + }, + "Registration": { + "type": "object", + "required": [ + "address", + "expiration", + "name", + "status" + ], + "properties": { + "address": { + "description": "The address of the DAO.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "expiration": { + "description": "When the registration expires.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + }, + "name": { + "description": "The unique name of the DAO.", + "type": "string" + }, + "status": { + "description": "The status of the registration.", + "allOf": [ + { + "$ref": "#/definitions/RegistrationStatus" + } + ] + } + }, + "additionalProperties": false + }, + "RegistrationStatus": { + "oneOf": [ + { + "description": "The registration is pending approval.", + "type": "object", + "required": [ + "pending" + ], + "properties": { + "pending": { + "type": "object", + "required": [ + "config" + ], + "properties": { + "config": { + "description": "The config that was used to register. This is necessary in case the fee or registration period change after a request is submitted.", + "allOf": [ + { + "$ref": "#/definitions/Config" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "The registration was approved.", + "type": "string", + "enum": [ + "approved" + ] + }, + { + "description": "The registration is rejected.", + "type": "string", + "enum": [ + "rejected" + ] + }, + { + "description": "The registration is revoked.", + "type": "string", + "enum": [ + "revoked" + ] + } + ] + }, + "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": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } + } + } +} diff --git a/contracts/external/dao-registry/schema/raw/execute.json b/contracts/external/dao-registry/schema/raw/execute.json new file mode 100644 index 000000000..d2e0edfa4 --- /dev/null +++ b/contracts/external/dao-registry/schema/raw/execute.json @@ -0,0 +1,398 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "description": "Register or renew with a cw20 token.", + "type": "object", + "required": [ + "receive" + ], + "properties": { + "receive": { + "$ref": "#/definitions/Cw20ReceiveMsg" + } + }, + "additionalProperties": false + }, + { + "description": "Register with a native token or auto-register by the owner.", + "type": "object", + "required": [ + "register" + ], + "properties": { + "register": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "address": { + "description": "The DAO to register. This can be used by the owner to assign a registration without a fee or approval step.", + "type": [ + "string", + "null" + ] + }, + "name": { + "description": "The name of the registration.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Renew with a native token. Renewal can only occur if the remaining time is less than the length of a registration. This means you can only renew once every registration period, to prevent squatting more than one registration period in advance.", + "type": "object", + "required": [ + "renew" + ], + "properties": { + "renew": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Approve a pending registration. Only the owner can do this.", + "type": "object", + "required": [ + "approve" + ], + "properties": { + "approve": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "The address of the DAO with a pending registration. This is used instead of the name since several pending registrations may be trying to register the same name.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Reject a pending registration. Only the owner can do this. The registration fee is returned to the DAO.", + "type": "object", + "required": [ + "reject" + ], + "properties": { + "reject": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "The address of the DAO with a pending registration. This is used instead of the name since several pending registrations may be trying to register the same name.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Revoke a registration. Only the owner can do this. The registration fee is NOT returned to the DAO.", + "type": "object", + "required": [ + "revoke" + ], + "properties": { + "revoke": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "description": "The name of the registration.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Update the expiration of a registration. Only the owner can do this.", + "type": "object", + "required": [ + "update_expiration" + ], + "properties": { + "update_expiration": { + "type": "object", + "required": [ + "expiration", + "name" + ], + "properties": { + "expiration": { + "description": "The new expiration.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + }, + "name": { + "description": "The name of the registration.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Update the config of the registry. Only the owner can do this.", + "type": "object", + "required": [ + "update_config" + ], + "properties": { + "update_config": { + "type": "object", + "properties": { + "fee_amount": { + "description": "The new fee amount to register a DAO.", + "anyOf": [ + { + "$ref": "#/definitions/Uint128" + }, + { + "type": "null" + } + ] + }, + "fee_denom": { + "description": "The new fee denom to register a DAO.", + "anyOf": [ + { + "$ref": "#/definitions/UncheckedDenom" + }, + { + "type": "null" + } + ] + }, + "registration_period": { + "description": "The new registration period.", + "anyOf": [ + { + "$ref": "#/definitions/Timestamp" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Update the contract's ownership. The `action` to be provided can be either to propose transferring ownership to an account, accept a pending ownership transfer, or renounce the ownership permanently.", + "type": "object", + "required": [ + "update_ownership" + ], + "properties": { + "update_ownership": { + "$ref": "#/definitions/Action" + } + }, + "additionalProperties": false + } + ], + "definitions": { + "Action": { + "description": "Actions that can be taken to alter the contract's ownership", + "oneOf": [ + { + "description": "Propose to transfer the contract's ownership to another account, optionally with an expiry time.\n\nCan only be called by the contract's current owner.\n\nAny existing pending ownership transfer is overwritten.", + "type": "object", + "required": [ + "transfer_ownership" + ], + "properties": { + "transfer_ownership": { + "type": "object", + "required": [ + "new_owner" + ], + "properties": { + "expiry": { + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "new_owner": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Accept the pending ownership transfer.\n\nCan only be called by the pending owner.", + "type": "string", + "enum": [ + "accept_ownership" + ] + }, + { + "description": "Give up the contract's ownership and the possibility of appointing a new owner.\n\nCan only be invoked by the contract's current owner.\n\nAny existing pending ownership transfer is canceled.", + "type": "string", + "enum": [ + "renounce_ownership" + ] + } + ] + }, + "Binary": { + "description": "Binary is a wrapper around Vec 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. See also .", + "type": "string" + }, + "Cw20ReceiveMsg": { + "description": "Cw20ReceiveMsg should be de/serialized under `Receive()` variant in a ExecuteMsg", + "type": "object", + "required": [ + "amount", + "msg", + "sender" + ], + "properties": { + "amount": { + "$ref": "#/definitions/Uint128" + }, + "msg": { + "$ref": "#/definitions/Binary" + }, + "sender": { + "type": "string" + } + }, + "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)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "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": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + }, + "UncheckedDenom": { + "description": "A denom that has not been checked to confirm it points to a valid asset.", + "oneOf": [ + { + "description": "A native (bank module) asset.", + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A cw20 asset.", + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } +} diff --git a/contracts/external/dao-registry/schema/raw/instantiate.json b/contracts/external/dao-registry/schema/raw/instantiate.json new file mode 100644 index 000000000..f166b98d2 --- /dev/null +++ b/contracts/external/dao-registry/schema/raw/instantiate.json @@ -0,0 +1,91 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "required": [ + "fee_amount", + "fee_denom", + "owner", + "registration_period" + ], + "properties": { + "fee_amount": { + "description": "The fee amount to register a DAO.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "fee_denom": { + "description": "The fee denom to register a DAO.", + "allOf": [ + { + "$ref": "#/definitions/UncheckedDenom" + } + ] + }, + "owner": { + "description": "The owner of the DAO registry. The owner is responsible for approving and rejecting registrations and receives the fees paid. They also have admin-level privileges.", + "type": "string" + }, + "registration_period": { + "description": "How long a registration lasts.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "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": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + }, + "UncheckedDenom": { + "description": "A denom that has not been checked to confirm it points to a valid asset.", + "oneOf": [ + { + "description": "A native (bank module) asset.", + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A cw20 asset.", + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } +} diff --git a/contracts/external/dao-registry/schema/raw/query.json b/contracts/external/dao-registry/schema/raw/query.json new file mode 100644 index 000000000..0622af72f --- /dev/null +++ b/contracts/external/dao-registry/schema/raw/query.json @@ -0,0 +1,126 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ + { + "description": "Get the current ownership.", + "type": "object", + "required": [ + "ownership" + ], + "properties": { + "ownership": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the configuration of the DAO registry.", + "type": "object", + "required": [ + "config" + ], + "properties": { + "config": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the active registration for the given address or None if no active registration exists.", + "type": "object", + "required": [ + "registration" + ], + "properties": { + "registration": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "The address with a registration.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the active registration for the given name or None if no active registration exists.", + "type": "object", + "required": [ + "resolve" + ], + "properties": { + "resolve": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "description": "The name of a registration.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the pending registration for the given address or None if no pending registration exists.", + "type": "object", + "required": [ + "pending_registration" + ], + "properties": { + "pending_registration": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "The address with a pending registration.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Returns the most recent registration for the given address or None if the address has never been registered.", + "type": "object", + "required": [ + "most_recent_registration" + ], + "properties": { + "most_recent_registration": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "description": "The address with a registration.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] +} diff --git a/contracts/external/dao-registry/schema/raw/response_to_config.json b/contracts/external/dao-registry/schema/raw/response_to_config.json new file mode 100644 index 000000000..42223c465 --- /dev/null +++ b/contracts/external/dao-registry/schema/raw/response_to_config.json @@ -0,0 +1,90 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Config", + "type": "object", + "required": [ + "fee_amount", + "fee_denom", + "registration_period" + ], + "properties": { + "fee_amount": { + "description": "The fee amount to register a DAO.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "fee_denom": { + "description": "The fee denom to register a DAO.", + "allOf": [ + { + "$ref": "#/definitions/CheckedDenom" + } + ] + }, + "registration_period": { + "description": "How long a registration lasts. For a new registration, this is the first expiration. For a renewal, this is the amount of time added to the current expiration.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "CheckedDenom": { + "description": "A denom that has been checked to point to a valid asset. This enum should never be constructed literally and should always be built by calling `into_checked` on an `UncheckedDenom` instance.", + "oneOf": [ + { + "description": "A native (bank module) asset.", + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A cw20 asset.", + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "$ref": "#/definitions/Addr" + } + }, + "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": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/external/dao-registry/schema/raw/response_to_most_recent_registration.json b/contracts/external/dao-registry/schema/raw/response_to_most_recent_registration.json new file mode 100644 index 000000000..e28967852 --- /dev/null +++ b/contracts/external/dao-registry/schema/raw/response_to_most_recent_registration.json @@ -0,0 +1,192 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Nullable_Registration", + "anyOf": [ + { + "$ref": "#/definitions/Registration" + }, + { + "type": "null" + } + ], + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "CheckedDenom": { + "description": "A denom that has been checked to point to a valid asset. This enum should never be constructed literally and should always be built by calling `into_checked` on an `UncheckedDenom` instance.", + "oneOf": [ + { + "description": "A native (bank module) asset.", + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A cw20 asset.", + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "$ref": "#/definitions/Addr" + } + }, + "additionalProperties": false + } + ] + }, + "Config": { + "type": "object", + "required": [ + "fee_amount", + "fee_denom", + "registration_period" + ], + "properties": { + "fee_amount": { + "description": "The fee amount to register a DAO.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "fee_denom": { + "description": "The fee denom to register a DAO.", + "allOf": [ + { + "$ref": "#/definitions/CheckedDenom" + } + ] + }, + "registration_period": { + "description": "How long a registration lasts. For a new registration, this is the first expiration. For a renewal, this is the amount of time added to the current expiration.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + } + }, + "additionalProperties": false + }, + "Registration": { + "type": "object", + "required": [ + "address", + "expiration", + "name", + "status" + ], + "properties": { + "address": { + "description": "The address of the DAO.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "expiration": { + "description": "When the registration expires.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + }, + "name": { + "description": "The unique name of the DAO.", + "type": "string" + }, + "status": { + "description": "The status of the registration.", + "allOf": [ + { + "$ref": "#/definitions/RegistrationStatus" + } + ] + } + }, + "additionalProperties": false + }, + "RegistrationStatus": { + "oneOf": [ + { + "description": "The registration is pending approval.", + "type": "object", + "required": [ + "pending" + ], + "properties": { + "pending": { + "type": "object", + "required": [ + "config" + ], + "properties": { + "config": { + "description": "The config that was used to register. This is necessary in case the fee or registration period change after a request is submitted.", + "allOf": [ + { + "$ref": "#/definitions/Config" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "The registration was approved.", + "type": "string", + "enum": [ + "approved" + ] + }, + { + "description": "The registration is rejected.", + "type": "string", + "enum": [ + "rejected" + ] + }, + { + "description": "The registration is revoked.", + "type": "string", + "enum": [ + "revoked" + ] + } + ] + }, + "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": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/external/dao-registry/schema/raw/response_to_ownership.json b/contracts/external/dao-registry/schema/raw/response_to_ownership.json new file mode 100644 index 000000000..f0b5195d9 --- /dev/null +++ b/contracts/external/dao-registry/schema/raw/response_to_ownership.json @@ -0,0 +1,107 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Ownership_for_Addr", + "description": "The contract's ownership info", + "type": "object", + "properties": { + "owner": { + "description": "The contract's current owner. `None` if the ownership has been renounced.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + }, + "pending_expiry": { + "description": "The deadline for the pending owner to accept the ownership. `None` if there isn't a pending ownership transfer, or if a transfer exists and it doesn't have a deadline.", + "anyOf": [ + { + "$ref": "#/definitions/Expiration" + }, + { + "type": "null" + } + ] + }, + "pending_owner": { + "description": "The account who has been proposed to take over the ownership. `None` if there isn't a pending ownership transfer.", + "anyOf": [ + { + "$ref": "#/definitions/Addr" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "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)", + "oneOf": [ + { + "description": "AtHeight will expire when `env.block.height` >= height", + "type": "object", + "required": [ + "at_height" + ], + "properties": { + "at_height": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + { + "description": "AtTime will expire when `env.block.time` >= time", + "type": "object", + "required": [ + "at_time" + ], + "properties": { + "at_time": { + "$ref": "#/definitions/Timestamp" + } + }, + "additionalProperties": false + }, + { + "description": "Never will never expire. Used to express the empty variant", + "type": "object", + "required": [ + "never" + ], + "properties": { + "never": { + "type": "object", + "additionalProperties": false + } + }, + "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": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/external/dao-registry/schema/raw/response_to_pending_registration.json b/contracts/external/dao-registry/schema/raw/response_to_pending_registration.json new file mode 100644 index 000000000..e28967852 --- /dev/null +++ b/contracts/external/dao-registry/schema/raw/response_to_pending_registration.json @@ -0,0 +1,192 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Nullable_Registration", + "anyOf": [ + { + "$ref": "#/definitions/Registration" + }, + { + "type": "null" + } + ], + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "CheckedDenom": { + "description": "A denom that has been checked to point to a valid asset. This enum should never be constructed literally and should always be built by calling `into_checked` on an `UncheckedDenom` instance.", + "oneOf": [ + { + "description": "A native (bank module) asset.", + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A cw20 asset.", + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "$ref": "#/definitions/Addr" + } + }, + "additionalProperties": false + } + ] + }, + "Config": { + "type": "object", + "required": [ + "fee_amount", + "fee_denom", + "registration_period" + ], + "properties": { + "fee_amount": { + "description": "The fee amount to register a DAO.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "fee_denom": { + "description": "The fee denom to register a DAO.", + "allOf": [ + { + "$ref": "#/definitions/CheckedDenom" + } + ] + }, + "registration_period": { + "description": "How long a registration lasts. For a new registration, this is the first expiration. For a renewal, this is the amount of time added to the current expiration.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + } + }, + "additionalProperties": false + }, + "Registration": { + "type": "object", + "required": [ + "address", + "expiration", + "name", + "status" + ], + "properties": { + "address": { + "description": "The address of the DAO.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "expiration": { + "description": "When the registration expires.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + }, + "name": { + "description": "The unique name of the DAO.", + "type": "string" + }, + "status": { + "description": "The status of the registration.", + "allOf": [ + { + "$ref": "#/definitions/RegistrationStatus" + } + ] + } + }, + "additionalProperties": false + }, + "RegistrationStatus": { + "oneOf": [ + { + "description": "The registration is pending approval.", + "type": "object", + "required": [ + "pending" + ], + "properties": { + "pending": { + "type": "object", + "required": [ + "config" + ], + "properties": { + "config": { + "description": "The config that was used to register. This is necessary in case the fee or registration period change after a request is submitted.", + "allOf": [ + { + "$ref": "#/definitions/Config" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "The registration was approved.", + "type": "string", + "enum": [ + "approved" + ] + }, + { + "description": "The registration is rejected.", + "type": "string", + "enum": [ + "rejected" + ] + }, + { + "description": "The registration is revoked.", + "type": "string", + "enum": [ + "revoked" + ] + } + ] + }, + "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": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/external/dao-registry/schema/raw/response_to_registration.json b/contracts/external/dao-registry/schema/raw/response_to_registration.json new file mode 100644 index 000000000..e28967852 --- /dev/null +++ b/contracts/external/dao-registry/schema/raw/response_to_registration.json @@ -0,0 +1,192 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Nullable_Registration", + "anyOf": [ + { + "$ref": "#/definitions/Registration" + }, + { + "type": "null" + } + ], + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "CheckedDenom": { + "description": "A denom that has been checked to point to a valid asset. This enum should never be constructed literally and should always be built by calling `into_checked` on an `UncheckedDenom` instance.", + "oneOf": [ + { + "description": "A native (bank module) asset.", + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A cw20 asset.", + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "$ref": "#/definitions/Addr" + } + }, + "additionalProperties": false + } + ] + }, + "Config": { + "type": "object", + "required": [ + "fee_amount", + "fee_denom", + "registration_period" + ], + "properties": { + "fee_amount": { + "description": "The fee amount to register a DAO.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "fee_denom": { + "description": "The fee denom to register a DAO.", + "allOf": [ + { + "$ref": "#/definitions/CheckedDenom" + } + ] + }, + "registration_period": { + "description": "How long a registration lasts. For a new registration, this is the first expiration. For a renewal, this is the amount of time added to the current expiration.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + } + }, + "additionalProperties": false + }, + "Registration": { + "type": "object", + "required": [ + "address", + "expiration", + "name", + "status" + ], + "properties": { + "address": { + "description": "The address of the DAO.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "expiration": { + "description": "When the registration expires.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + }, + "name": { + "description": "The unique name of the DAO.", + "type": "string" + }, + "status": { + "description": "The status of the registration.", + "allOf": [ + { + "$ref": "#/definitions/RegistrationStatus" + } + ] + } + }, + "additionalProperties": false + }, + "RegistrationStatus": { + "oneOf": [ + { + "description": "The registration is pending approval.", + "type": "object", + "required": [ + "pending" + ], + "properties": { + "pending": { + "type": "object", + "required": [ + "config" + ], + "properties": { + "config": { + "description": "The config that was used to register. This is necessary in case the fee or registration period change after a request is submitted.", + "allOf": [ + { + "$ref": "#/definitions/Config" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "The registration was approved.", + "type": "string", + "enum": [ + "approved" + ] + }, + { + "description": "The registration is rejected.", + "type": "string", + "enum": [ + "rejected" + ] + }, + { + "description": "The registration is revoked.", + "type": "string", + "enum": [ + "revoked" + ] + } + ] + }, + "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": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/external/dao-registry/schema/raw/response_to_resolve.json b/contracts/external/dao-registry/schema/raw/response_to_resolve.json new file mode 100644 index 000000000..e28967852 --- /dev/null +++ b/contracts/external/dao-registry/schema/raw/response_to_resolve.json @@ -0,0 +1,192 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Nullable_Registration", + "anyOf": [ + { + "$ref": "#/definitions/Registration" + }, + { + "type": "null" + } + ], + "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, + "CheckedDenom": { + "description": "A denom that has been checked to point to a valid asset. This enum should never be constructed literally and should always be built by calling `into_checked` on an `UncheckedDenom` instance.", + "oneOf": [ + { + "description": "A native (bank module) asset.", + "type": "object", + "required": [ + "native" + ], + "properties": { + "native": { + "type": "string" + } + }, + "additionalProperties": false + }, + { + "description": "A cw20 asset.", + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "$ref": "#/definitions/Addr" + } + }, + "additionalProperties": false + } + ] + }, + "Config": { + "type": "object", + "required": [ + "fee_amount", + "fee_denom", + "registration_period" + ], + "properties": { + "fee_amount": { + "description": "The fee amount to register a DAO.", + "allOf": [ + { + "$ref": "#/definitions/Uint128" + } + ] + }, + "fee_denom": { + "description": "The fee denom to register a DAO.", + "allOf": [ + { + "$ref": "#/definitions/CheckedDenom" + } + ] + }, + "registration_period": { + "description": "How long a registration lasts. For a new registration, this is the first expiration. For a renewal, this is the amount of time added to the current expiration.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + } + }, + "additionalProperties": false + }, + "Registration": { + "type": "object", + "required": [ + "address", + "expiration", + "name", + "status" + ], + "properties": { + "address": { + "description": "The address of the DAO.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "expiration": { + "description": "When the registration expires.", + "allOf": [ + { + "$ref": "#/definitions/Timestamp" + } + ] + }, + "name": { + "description": "The unique name of the DAO.", + "type": "string" + }, + "status": { + "description": "The status of the registration.", + "allOf": [ + { + "$ref": "#/definitions/RegistrationStatus" + } + ] + } + }, + "additionalProperties": false + }, + "RegistrationStatus": { + "oneOf": [ + { + "description": "The registration is pending approval.", + "type": "object", + "required": [ + "pending" + ], + "properties": { + "pending": { + "type": "object", + "required": [ + "config" + ], + "properties": { + "config": { + "description": "The config that was used to register. This is necessary in case the fee or registration period change after a request is submitted.", + "allOf": [ + { + "$ref": "#/definitions/Config" + } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "The registration was approved.", + "type": "string", + "enum": [ + "approved" + ] + }, + { + "description": "The registration is rejected.", + "type": "string", + "enum": [ + "rejected" + ] + }, + { + "description": "The registration is revoked.", + "type": "string", + "enum": [ + "revoked" + ] + } + ] + }, + "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": [ + { + "$ref": "#/definitions/Uint64" + } + ] + }, + "Uint128": { + "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", + "type": "string" + }, + "Uint64": { + "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", + "type": "string" + } + } +} diff --git a/contracts/external/dao-registry/src/contract.rs b/contracts/external/dao-registry/src/contract.rs new file mode 100644 index 000000000..841ce8664 --- /dev/null +++ b/contracts/external/dao-registry/src/contract.rs @@ -0,0 +1,506 @@ +#[cfg(not(feature = "library"))] +use cosmwasm_std::entry_point; +use cosmwasm_std::{ + from_binary, to_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult, + Timestamp, Uint128, +}; +use cw2::set_contract_version; +use cw20::Cw20ReceiveMsg; +use cw_denom::{CheckedDenom, UncheckedDenom}; +use cw_utils::{must_pay, nonpayable}; + +use crate::error::ContractError; +use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg, ReceiveMsg}; +use crate::registration::Registration; +use crate::state::{Config, CONFIG, NAMES, PENDING_REGISTRATIONS, REGISTRATIONS}; + +const CONTRACT_NAME: &str = "crates.io:dao-registry"; +const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn instantiate( + deps: DepsMut, + _env: Env, + _info: MessageInfo, + msg: InstantiateMsg, +) -> Result { + set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + cw_ownable::initialize_owner(deps.storage, deps.api, Some(&msg.owner))?; + + let fee_denom = msg.fee_denom.into_checked(deps.as_ref())?; + CONFIG.save( + deps.storage, + &Config { + fee_amount: msg.fee_amount, + fee_denom, + registration_period: msg.registration_period, + }, + )?; + + Ok(Response::new() + .add_attribute("method", "instantiate") + .add_attribute("owner", msg.owner)) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: ExecuteMsg, +) -> Result { + match msg { + ExecuteMsg::UpdateOwnership(action) => execute_update_owner(deps, info, env, action), + ExecuteMsg::Receive(msg) => execute_receive_cw20(env, deps, info, msg), + ExecuteMsg::Register { name, address } => execute_register(env, deps, info, name, address), + ExecuteMsg::Renew {} => execute_renew(env, deps, info), + ExecuteMsg::Approve { address } => execute_approve(env, deps, info, address), + ExecuteMsg::Reject { address } => execute_reject(env, deps, info, address), + ExecuteMsg::Revoke { name } => execute_revoke(env, deps, info, name), + ExecuteMsg::UpdateExpiration { name, expiration } => { + execute_update_expiration(deps, info, name, expiration) + } + ExecuteMsg::UpdateConfig { + fee_amount, + fee_denom, + registration_period, + } => execute_update_config(deps, info, fee_amount, fee_denom, registration_period), + } +} + +pub fn execute_update_owner( + deps: DepsMut, + info: MessageInfo, + env: Env, + action: cw_ownable::Action, +) -> Result { + match action { + // There must be an owner. + cw_ownable::Action::RenounceOwnership {} => Err(ContractError::CannotRenounceOwnership), + // Allow transfering and accepting ownership. + _ => { + let ownership = cw_ownable::update_ownership(deps, &env.block, &info.sender, action)?; + Ok(Response::default().add_attributes(ownership.into_attributes())) + } + } +} + +pub fn execute_receive_cw20( + env: Env, + deps: DepsMut, + info: MessageInfo, + receive_msg: Cw20ReceiveMsg, +) -> Result { + // Only accepts cw20 tokens. + nonpayable(&info)?; + + let msg: ReceiveMsg = from_binary(&receive_msg.msg)?; + + // Validate amount and denom. + let config = CONFIG.load(deps.storage)?; + if !config.fee_denom.is_cw20(&info.sender) { + return Err(ContractError::WrongDenom); + } + if receive_msg.amount != config.fee_amount { + return Err(ContractError::WrongAmount); + } + + match msg { + ReceiveMsg::Register { name } => try_register(env, deps, info.sender, name, false), + ReceiveMsg::Renew {} => try_renew(env, deps, info.sender), + } +} + +pub fn execute_register( + env: Env, + deps: DepsMut, + info: MessageInfo, + name: String, + address: Option, +) -> Result { + // If explicit address passed, validate sender is the owner. This will + // bypass the fee and approval step. Otherwise, require fee payment. + let owner_override = address.is_some(); + if owner_override { + cw_ownable::assert_owner(deps.storage, &info.sender)?; + // Ensure owner cannot pay any fee. + nonpayable(&info)?; + } else { + let config = CONFIG.load(deps.storage)?; + // Validate amount and denom. Only accepts native tokens. + match config.fee_denom { + CheckedDenom::Native(ref denom) => { + let sent = must_pay(&info, denom)?; + if config.fee_amount != sent { + return Err(ContractError::WrongAmount {}); + } + } + CheckedDenom::Cw20(_) => { + // Cw20 registration happens via ExecuteMsg::Receive. + return Err(ContractError::WrongDenom {}); + } + }; + } + + // If address passed in (owner override), validate it. Otherwise use the + // sender. + let registration_address = if let Some(address) = address { + deps.api.addr_validate(&address)? + } else { + info.sender + }; + + try_register(env, deps, registration_address, name, owner_override) +} + +pub fn execute_renew( + env: Env, + deps: DepsMut, + info: MessageInfo, +) -> Result { + let config = CONFIG.load(deps.storage)?; + // Validate amount and denom. Only accepts native tokens. + match config.fee_denom { + CheckedDenom::Native(ref denom) => { + let sent = must_pay(&info, denom)?; + if config.fee_amount != sent { + return Err(ContractError::WrongAmount {}); + } + } + CheckedDenom::Cw20(_) => { + // Cw20 registration happens via ExecuteMsg::Receive. + nonpayable(&info)?; + } + }; + + try_renew(env, deps, info.sender) +} + +pub fn execute_approve( + env: Env, + deps: DepsMut, + info: MessageInfo, + address: String, +) -> Result { + // Validate address. + let address = deps.api.addr_validate(&address)?; + + // Only owner can approve. + cw_ownable::assert_owner(deps.storage, &info.sender)?; + + // Get pending registration. + let mut registration = + pending_or_active_registration_for_address(&env, &deps.as_ref(), &address)? + .ok_or(ContractError::NoPendingRegistrationFound)?; + if !registration.is_pending() { + return Err(ContractError::NoPendingRegistrationFound); + } + + // Forward the fee to the owner. + let owner = get_owner(&deps.as_ref())?; + let transfer_fee_msg = registration.get_transfer_msg(&owner, None)?; + + // Approve the registration. + registration.approve(&env, deps)?; + + Ok(Response::new() + .add_message(transfer_fee_msg) + .add_attribute("method", "approve") + .add_attribute("address", address.to_string()) + .add_attribute("name", registration.name)) +} + +pub fn execute_reject( + env: Env, + deps: DepsMut, + info: MessageInfo, + address: String, +) -> Result { + // Validate address. + let address = deps.api.addr_validate(&address)?; + + // Only owner can reject. + cw_ownable::assert_owner(deps.storage, &info.sender)?; + + // Get pending registration. + let mut registration = + pending_or_active_registration_for_address(&env, &deps.as_ref(), &address)? + .ok_or(ContractError::NoPendingRegistrationFound)?; + if !registration.is_pending() { + return Err(ContractError::NoPendingRegistrationFound); + } + + // Send the fee back to the address. + let transfer_fee_msg = registration.get_transfer_msg(®istration.address, None)?; + + // Reject the registration. + registration.reject(&env, deps)?; + + Ok(Response::new() + .add_message(transfer_fee_msg) + .add_attribute("method", "reject") + .add_attribute("address", address.to_string()) + .add_attribute("name", registration.name)) +} + +pub fn execute_revoke( + env: Env, + deps: DepsMut, + info: MessageInfo, + name: String, +) -> Result { + // Only owner can revoke. + cw_ownable::assert_owner(deps.storage, &info.sender)?; + + // Get active registration. + let mut registration = active_registration_for_name(&env, &deps.as_ref(), &name)? + .ok_or(ContractError::NoRegistrationFound)?; + + // Reject the registration. + registration.revoke(&env, deps)?; + + Ok(Response::new() + .add_attribute("method", "revoke") + .add_attribute("address", registration.address.to_string()) + .add_attribute("name", registration.name)) +} + +pub fn execute_update_expiration( + deps: DepsMut, + info: MessageInfo, + name: String, + expiration: Timestamp, +) -> Result { + // Only owner can update expiration. + cw_ownable::assert_owner(deps.storage, &info.sender)?; + + // Get registration. + let mut registration = + registration_for_name(&deps.as_ref(), &name)?.ok_or(ContractError::NoRegistrationFound)?; + + // Update the expiration. + registration.expiration = expiration; + REGISTRATIONS.save(deps.storage, registration.address.clone(), ®istration)?; + + Ok(Response::new() + .add_attribute("method", "update_expiration") + .add_attribute("address", registration.address.to_string()) + .add_attribute("name", registration.name)) +} + +pub fn execute_update_config( + deps: DepsMut, + info: MessageInfo, + fee_amount: Option, + fee_denom: Option, + registration_period: Option, +) -> Result { + // Only owner can update config. + cw_ownable::assert_owner(deps.storage, &info.sender)?; + + let checked_denom = fee_denom + .map(|unchecked| unchecked.into_checked(deps.as_ref())) + .transpose()?; + + // Update config. + let config = CONFIG.update(deps.storage, |config| { + StdResult::::Ok(Config { + fee_amount: fee_amount.unwrap_or(config.fee_amount), + fee_denom: checked_denom.unwrap_or(config.fee_denom), + registration_period: registration_period.unwrap_or(config.registration_period), + }) + })?; + + Ok(Response::new() + .add_attribute("method", "update_config") + .add_attribute("fee_amount", config.fee_amount.to_string()) + .add_attribute("fee_denom", config.fee_denom.to_string()) + .add_attribute( + "registration_period", + config.registration_period.to_string(), + )) +} + +pub fn try_register( + env: Env, + deps: DepsMut, + address: Addr, + name: String, + // If true, the owner is registering on behalf of the address. Don't + // transfer any fee in this case. + owner_override: bool, +) -> Result { + // Validate DAO not already pending registration or registered. + if let Some(registration) = + pending_or_active_registration_for_address(&env, &deps.as_ref(), &address)? + { + if registration.is_pending() { + return Err(ContractError::RegistrationPending); + } else { + return Err(ContractError::AlreadyRegistered); + } + } + + // Validate name not already registered. + if active_registration_for_name(&env, &deps.as_ref(), &name)?.is_some() { + return Err(ContractError::NameAlreadyRegistered); + } + + // TODO: Validate name. + if name.len() < 3 || name.len() > 32 { + return Err(ContractError::InvalidName); + } + + // Register the DAO. + let config = CONFIG.load(deps.storage)?; + let mut registration = Registration::new(address.clone(), name.clone(), config); + REGISTRATIONS.save(deps.storage, address.clone(), ®istration)?; + PENDING_REGISTRATIONS.save(deps.storage, address.clone(), ®istration)?; + + // If owner is registering on behalf of the address, registration is + // approved immediately and there is no fee to send anywhere. + if owner_override { + registration.approve(&env, deps)?; + } + + Ok(Response::new() + .add_attribute("method", "register") + .add_attribute("address", address.to_string()) + .add_attribute("name", name) + .add_attribute("expiration", registration.expiration.nanos().to_string())) +} + +pub fn try_renew(env: Env, deps: DepsMut, address: Addr) -> Result { + // Validate DAO is registered. + let mut registration = + match pending_or_active_registration_for_address(&env, &deps.as_ref(), &address)? { + Some(registration) => registration, + None => return Err(ContractError::NoRegistrationFound), + }; + + // Validate registration is still active. Otherwise it is pending. + if !registration.is_active(&env) { + return Err(ContractError::NoRegistrationFound); + } + + // Validate registration can be renewed. + if !registration.is_renewable(&env, &deps)? { + return Err(ContractError::RegistrationAlreadyRenewed); + } + + // Renew the registration. + let config = CONFIG.load(deps.storage)?; + registration.expiration = registration + .expiration + .plus_nanos(config.registration_period.nanos()); + REGISTRATIONS.save(deps.storage, address.clone(), ®istration)?; + + // Pass the fee through to the owner. + let owner = get_owner(&deps.as_ref())?; + let transfer_fee_msg = registration.get_transfer_msg(&owner, Some(config))?; + + Ok(Response::new() + .add_message(transfer_fee_msg) + .add_attribute("method", "renew") + .add_attribute("address", address.to_string()) + .add_attribute("name", registration.name) + .add_attribute("expiration", registration.expiration.nanos().to_string())) +} + +// Get the pending or active registration for the address. This will return None +// if the address is not registered or if the most recent registration has +// expired. +fn pending_or_active_registration_for_address( + env: &Env, + deps: &Deps, + address: &Addr, +) -> StdResult> { + let registration = REGISTRATIONS + .may_load(deps.storage, address.clone())? + .and_then(|registration| { + if registration.is_pending() || registration.is_active(env) { + Some(registration) + } else { + None + } + }); + Ok(registration) +} + +// Get the registration for the given name. This will return None if the name +// has never been registered or if the most recent registerer registered a +// different name since registering this one. +fn registration_for_name(deps: &Deps, name: &String) -> StdResult> { + let registration = NAMES + .may_load(deps.storage, name.to_string())? + .map(|addr| REGISTRATIONS.load(deps.storage, addr)) + .transpose()? + .and_then(|registration| { + if registration.name == *name { + Some(registration) + } else { + None + } + }); + Ok(registration) +} + +// Get the active registration for the given name. This will return None if the +// name is not registered or if the most recent registration has expired. +fn active_registration_for_name( + env: &Env, + deps: &Deps, + name: &String, +) -> StdResult> { + let registration = registration_for_name(deps, name)?.and_then(|registration| { + if registration.is_active(env) { + Some(registration) + } else { + None + } + }); + Ok(registration) +} + +pub fn get_owner(deps: &Deps) -> StdResult { + Ok(cw_ownable::get_ownership(deps.storage) + .map(|ownership: cw_ownable::Ownership| ownership.owner)? + .unwrap()) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { + match msg { + QueryMsg::Ownership {} => to_binary(&cw_ownable::get_ownership(deps.storage)?), + QueryMsg::Config {} => to_binary(&CONFIG.load(deps.storage)?), + QueryMsg::Registration { address } => to_binary( + ®ISTRATIONS + .may_load(deps.storage, deps.api.addr_validate(&address)?)? + .and_then(|registration| { + if registration.is_active(&env) { + Some(registration) + } else { + None + } + }), + ), + QueryMsg::Resolve { name } => to_binary(&active_registration_for_name(&env, &deps, &name)?), + QueryMsg::PendingRegistration { address } => to_binary( + &pending_or_active_registration_for_address( + &env, + &deps, + &deps.api.addr_validate(&address)?, + )? + .and_then(|registration| { + if registration.is_pending() { + Some(registration) + } else { + None + } + }), + ), + QueryMsg::MostRecentRegistration { address } => { + to_binary(®ISTRATIONS.may_load(deps.storage, deps.api.addr_validate(&address)?)?) + } + } +} diff --git a/contracts/external/dao-registry/src/error.rs b/contracts/external/dao-registry/src/error.rs new file mode 100644 index 000000000..8e59a3233 --- /dev/null +++ b/contracts/external/dao-registry/src/error.rs @@ -0,0 +1,50 @@ +use cosmwasm_std::StdError; +use cw_denom::DenomError; +use cw_ownable::OwnershipError; +use cw_utils::PaymentError; +use thiserror::Error; + +#[derive(Error, Debug, PartialEq)] +pub enum ContractError { + #[error(transparent)] + Std(#[from] StdError), + + #[error(transparent)] + Denom(#[from] DenomError), + + #[error(transparent)] + Ownable(#[from] OwnershipError), + + #[error("{0}")] + PaymentError(#[from] PaymentError), + + #[error("Cannot renounce ownership")] + CannotRenounceOwnership, + + #[error("Wrong denom")] + WrongDenom, + + #[error("Wrong amount")] + WrongAmount, + + #[error("Name already registered")] + NameAlreadyRegistered, + + #[error("Already registered")] + AlreadyRegistered, + + #[error("Registration pending")] + RegistrationPending, + + #[error("Name must be between 3 and 32 characters")] + InvalidName, + + #[error("No registration found")] + NoRegistrationFound, + + #[error("Registration already renewed")] + RegistrationAlreadyRenewed, + + #[error("No pending registration found")] + NoPendingRegistrationFound, +} diff --git a/contracts/external/dao-registry/src/lib.rs b/contracts/external/dao-registry/src/lib.rs new file mode 100644 index 000000000..bcc17820a --- /dev/null +++ b/contracts/external/dao-registry/src/lib.rs @@ -0,0 +1,16 @@ +#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))] + +pub mod contract; +pub mod error; +pub mod msg; +pub mod registration; +pub mod state; + +pub use crate::error::ContractError; + +// Consumers don't need a cw_ownable dependency to use this contract's queries. +pub use cw_denom::{CheckedDenom, UncheckedDenom}; +pub use cw_ownable::Ownership; + +#[cfg(test)] +mod tests; diff --git a/contracts/external/dao-registry/src/msg.rs b/contracts/external/dao-registry/src/msg.rs new file mode 100644 index 000000000..59f2fc782 --- /dev/null +++ b/contracts/external/dao-registry/src/msg.rs @@ -0,0 +1,131 @@ +use cosmwasm_schema::{cw_serde, QueryResponses}; +use cosmwasm_std::{Timestamp, Uint128}; +use cw20::Cw20ReceiveMsg; +use cw_denom::UncheckedDenom; +use cw_ownable::cw_ownable_execute; + +use crate::{registration::Registration, state::Config}; + +#[cw_serde] +pub struct InstantiateMsg { + /// The owner of the DAO registry. The owner is responsible for approving + /// and rejecting registrations and receives the fees paid. They also have + /// admin-level privileges. + pub owner: String, + /// The fee amount to register a DAO. + pub fee_amount: Uint128, + /// The fee denom to register a DAO. + pub fee_denom: UncheckedDenom, + /// How long a registration lasts. + pub registration_period: Timestamp, +} + +#[cw_ownable_execute] +#[cw_serde] +pub enum ExecuteMsg { + /// Register or renew with a cw20 token. + Receive(Cw20ReceiveMsg), + /// Register with a native token or auto-register by the owner. + Register { + /// The name of the registration. + name: String, + /// The DAO to register. This can be used by the owner to assign a + /// registration without a fee or approval step. + address: Option, + }, + /// Renew with a native token. Renewal can only occur if the remaining time + /// is less than the length of a registration. This means you can only renew + /// once every registration period, to prevent squatting more than one + /// registration period in advance. + Renew {}, + /// Approve a pending registration. Only the owner can do this. + Approve { + /// The address of the DAO with a pending registration. This is used + /// instead of the name since several pending registrations may be + /// trying to register the same name. + address: String, + }, + /// Reject a pending registration. Only the owner can do this. The + /// registration fee is returned to the DAO. + Reject { + /// The address of the DAO with a pending registration. This is used + /// instead of the name since several pending registrations may be + /// trying to register the same name. + address: String, + }, + /// Revoke a registration. Only the owner can do this. The registration fee + /// is NOT returned to the DAO. + Revoke { + /// The name of the registration. + name: String, + }, + /// Update the expiration of a registration. Only the owner can do this. + UpdateExpiration { + /// The name of the registration. + name: String, + /// The new expiration. + expiration: Timestamp, + }, + /// Update the config of the registry. Only the owner can do this. + UpdateConfig { + /// The new fee amount to register a DAO. + fee_amount: Option, + /// The new fee denom to register a DAO. + fee_denom: Option, + /// The new registration period. + registration_period: Option, + }, +} + +#[cw_serde] +pub enum ReceiveMsg { + /// Register or renew with a cw20 token. + Register { + /// The name of the registration. + name: String, + }, + /// Renew with a cw20 token. Renewal can only occur if the remaining time is + /// less than the length of a registration. This means you can only renew + /// once every registration period, to prevent squatting more than one + /// registration period in advance. + Renew {}, +} + +#[cw_serde] +#[derive(QueryResponses)] +pub enum QueryMsg { + /// Get the current ownership. + #[returns(::cw_ownable::Ownership<::cosmwasm_std::Addr>)] + Ownership {}, + /// Returns the configuration of the DAO registry. + #[returns(Config)] + Config {}, + /// Returns the active registration for the given address or None if no + /// active registration exists. + #[returns(Option)] + Registration { + /// The address with a registration. + address: String, + }, + /// Returns the active registration for the given name or None if no active + /// registration exists. + #[returns(Option)] + Resolve { + /// The name of a registration. + name: String, + }, + /// Returns the pending registration for the given address or None if no + /// pending registration exists. + #[returns(Option)] + PendingRegistration { + /// The address with a pending registration. + address: String, + }, + /// Returns the most recent registration for the given address or None if + /// the address has never been registered. + #[returns(Option)] + MostRecentRegistration { + /// The address with a registration. + address: String, + }, +} diff --git a/contracts/external/dao-registry/src/registration.rs b/contracts/external/dao-registry/src/registration.rs new file mode 100644 index 000000000..abc771aaa --- /dev/null +++ b/contracts/external/dao-registry/src/registration.rs @@ -0,0 +1,186 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{ + to_binary, Addr, BankMsg, Coin, CosmosMsg, DepsMut, Env, StdResult, Timestamp, WasmMsg, +}; +use cw_denom::CheckedDenom; + +use crate::{ + state::{Config, CONFIG, NAMES, PENDING_REGISTRATIONS, REGISTRATIONS}, + ContractError, +}; + +#[cw_serde] +pub enum RegistrationStatus { + /// The registration is pending approval. + Pending { + /// The config that was used to register. This is necessary in case the + /// fee or registration period change after a request is submitted. + config: Config, + }, + /// The registration was approved. + Approved, + /// The registration is rejected. + Rejected, + /// The registration is revoked. + Revoked, +} + +#[cw_serde] +pub struct Registration { + /// The status of the registration. + pub status: RegistrationStatus, + /// The address of the DAO. + pub address: Addr, + /// The unique name of the DAO. + pub name: String, + /// When the registration expires. + pub expiration: Timestamp, +} + +impl Registration { + pub fn new(address: Addr, name: String, config: Config) -> Self { + Self { + status: RegistrationStatus::Pending { config }, + address, + name, + expiration: Timestamp::from_nanos(0), + } + } + + // Approve pending registration. + pub fn approve(&mut self, env: &Env, deps: DepsMut) -> Result { + if !self.is_pending() { + return Err(ContractError::NoPendingRegistrationFound); + } + + PENDING_REGISTRATIONS.remove(deps.storage, self.address.clone()); + NAMES.save(deps.storage, self.name.clone(), &self.address)?; + + let config = CONFIG.load(deps.storage)?; + let registration = + REGISTRATIONS.update(deps.storage, self.address.clone(), |registration| { + if let Some(mut registration) = registration { + registration.status = RegistrationStatus::Approved; + registration.expiration = env + .block + .time + .plus_nanos(config.registration_period.nanos()); + + Ok(registration) + } else { + Err(ContractError::NoPendingRegistrationFound) + } + })?; + + Ok(registration) + } + + // Reject pending registration. + pub fn reject(&mut self, env: &Env, deps: DepsMut) -> Result { + if !self.is_pending() { + return Err(ContractError::NoPendingRegistrationFound); + } + + PENDING_REGISTRATIONS.remove(deps.storage, self.address.clone()); + + let registration = + REGISTRATIONS.update(deps.storage, self.address.clone(), |registration| { + if let Some(mut registration) = registration { + registration.status = RegistrationStatus::Rejected; + registration.expiration = env.block.time; + + Ok(registration) + } else { + Err(ContractError::NoPendingRegistrationFound) + } + })?; + + Ok(registration) + } + + // Revoke active registration. + pub fn revoke(&mut self, env: &Env, deps: DepsMut) -> Result { + if !self.is_active(env) { + return Err(ContractError::NoRegistrationFound); + } + + let registration = + REGISTRATIONS.update(deps.storage, self.address.clone(), |registration| { + if let Some(mut registration) = registration { + registration.status = RegistrationStatus::Revoked; + registration.expiration = env.block.time; + + Ok(registration) + } else { + Err(ContractError::NoPendingRegistrationFound) + } + })?; + + Ok(registration) + } + + pub fn is_pending(&self) -> bool { + matches!(self.status, RegistrationStatus::Pending { .. }) + } + + pub fn is_active(&self, env: &Env) -> bool { + match self.status { + // If approved, active if not yet expired. + RegistrationStatus::Approved => !self.is_expired(env), + _ => false, + } + } + + pub fn is_expired(&self, env: &Env) -> bool { + self.expiration <= env.block.time + } + + // Renewable if active and within one registration period of expiration. If + // not, it has already been renewed for the next period. + pub fn is_renewable(&self, env: &Env, deps: &DepsMut) -> Result { + let config = CONFIG.load(deps.storage)?; + Ok(self.is_active(env) + && self.expiration + <= env + .block + .time + .plus_nanos(config.registration_period.nanos())) + } + + // Get the cosmos msg to transfer the fee. When pending, the stored config + // is used. When renewing after approved, the current config is used. + pub fn get_transfer_msg( + &self, + to: &Addr, + config: Option, + ) -> Result { + match &self.status { + RegistrationStatus::Pending { config } => Ok(get_transfer_msg(to, config)?), + RegistrationStatus::Approved => Ok(get_transfer_msg(to, &config.unwrap())?), + _ => Err(ContractError::NoPendingRegistrationFound), + } + } +} + +// Get the cosmos msg to transfer the fee based on the config. +fn get_transfer_msg(to: &Addr, config: &Config) -> StdResult { + Ok(match &config.fee_denom { + CheckedDenom::Native(denom) => BankMsg::Send { + to_address: to.to_string(), + amount: vec![Coin { + denom: denom.to_string(), + amount: config.fee_amount, + }], + } + .into(), + CheckedDenom::Cw20(address) => WasmMsg::Execute { + contract_addr: address.to_string(), + msg: to_binary(&cw20::Cw20ExecuteMsg::Transfer { + recipient: to.to_string(), + amount: config.fee_amount, + })?, + funds: vec![], + } + .into(), + }) +} diff --git a/contracts/external/dao-registry/src/state.rs b/contracts/external/dao-registry/src/state.rs new file mode 100644 index 000000000..0011ae108 --- /dev/null +++ b/contracts/external/dao-registry/src/state.rs @@ -0,0 +1,30 @@ +use cosmwasm_schema::cw_serde; +use cosmwasm_std::{Addr, Timestamp, Uint128}; +use cw_denom::CheckedDenom; +use cw_storage_plus::{Item, Map}; + +use crate::registration::Registration; + +/// The configuration of the DAO registry. +pub const CONFIG: Item = Item::new("config"); + +/// Map address to its pending registration. +pub const PENDING_REGISTRATIONS: Map = Map::new("pending_registrations"); + +/// Map address to its most recent registration. +pub const REGISTRATIONS: Map = Map::new("registrations"); + +/// Map name to its most recent registered address. +pub const NAMES: Map = Map::new("names"); + +#[cw_serde] +pub struct Config { + /// The fee amount to register a DAO. + pub fee_amount: Uint128, + /// The fee denom to register a DAO. + pub fee_denom: CheckedDenom, + /// How long a registration lasts. For a new registration, this is the first + /// expiration. For a renewal, this is the amount of time added to the + /// current expiration. + pub registration_period: Timestamp, +} diff --git a/contracts/external/dao-registry/src/tests.rs b/contracts/external/dao-registry/src/tests.rs new file mode 100644 index 000000000..1b87a0c70 --- /dev/null +++ b/contracts/external/dao-registry/src/tests.rs @@ -0,0 +1,736 @@ +#![cfg(test)] + +use cosmwasm_std::{coins, Addr, Empty, Timestamp, Uint128}; +use cw_denom::{CheckedDenom, UncheckedDenom}; +use cw_multi_test::{App, BankSudo, Contract, ContractWrapper, Executor, SudoMsg}; + +use crate::{ + msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, + registration::{Registration, RegistrationStatus}, + state::Config, + ContractError, +}; + +const OWNER: &str = "owner"; +const NOT_OWNER: &str = "not_owner"; +const DAO1: &str = "dao1"; +const DAO2: &str = "dao2"; +const NAME1: &str = "name1"; +const NAME2: &str = "name2"; + +const FEE_AMOUNT: u128 = 100; +const FEE_DENOM: &str = "denom"; +const REGISTRATION_PERIOD_NANOS: u64 = 1_000_000_000; + +const INITIAL_BALANCE: u128 = FEE_AMOUNT * 5; + +fn setup_contract() -> Box> { + let contract = ContractWrapper::new( + crate::contract::execute, + crate::contract::instantiate, + crate::contract::query, + ); + Box::new(contract) +} + +fn instantiate() -> (App, Addr) { + let mut app = App::default(); + + // Mint DAOs tokens to register. + app.sudo(SudoMsg::Bank({ + BankSudo::Mint { + to_address: DAO1.to_string(), + amount: coins(INITIAL_BALANCE, FEE_DENOM), + } + })) + .unwrap(); + app.sudo(SudoMsg::Bank({ + BankSudo::Mint { + to_address: DAO2.to_string(), + amount: coins(INITIAL_BALANCE, FEE_DENOM), + } + })) + .unwrap(); + + // Instantiate contract. + let code_id = app.store_code(setup_contract()); + let addr = app + .instantiate_contract( + code_id, + Addr::unchecked(OWNER), + &InstantiateMsg { + owner: OWNER.to_string(), + fee_amount: Uint128::new(FEE_AMOUNT), + fee_denom: UncheckedDenom::Native(FEE_DENOM.to_string()), + registration_period: Timestamp::from_nanos(REGISTRATION_PERIOD_NANOS), + }, + &[], + "registry", + None, + ) + .unwrap(); + + (app, addr) +} + +#[test] +pub fn test_instantiate() { + instantiate(); +} + +#[test] +pub fn test_updatable_owner() { + let (mut app, addr) = instantiate(); + + // Ensure owner is set. + let res: cw_ownable::Ownership = app + .wrap() + .query_wasm_smart(addr.clone(), &QueryMsg::Ownership {}) + .unwrap(); + assert_eq!(res.owner, Some(OWNER.to_string())); + + // Update owner. + let new_owner = "new_owner"; + app.execute_contract( + Addr::unchecked(OWNER), + addr.clone(), + &ExecuteMsg::UpdateOwnership(cw_ownable::Action::TransferOwnership { + new_owner: new_owner.to_string(), + expiry: None, + }), + &[], + ) + .unwrap(); + + // Accept ownership transfer. + app.execute_contract( + Addr::unchecked(new_owner), + addr.clone(), + &ExecuteMsg::UpdateOwnership(cw_ownable::Action::AcceptOwnership), + &[], + ) + .unwrap(); + + // Ensure owner is updated to new owner. + let res: cw_ownable::Ownership = app + .wrap() + .query_wasm_smart(addr.clone(), &QueryMsg::Ownership {}) + .unwrap(); + assert_eq!(res.owner, Some(new_owner.to_string())); + + // Ensure old owner can no longer update. + let err: ContractError = app + .execute_contract( + Addr::unchecked(OWNER), + addr.clone(), + &ExecuteMsg::UpdateOwnership(cw_ownable::Action::TransferOwnership { + new_owner: "new_new_owner".to_string(), + expiry: None, + }), + &[], + ) + .unwrap_err() + .downcast() + .unwrap(); + assert_eq!( + err, + ContractError::Ownable(cw_ownable::OwnershipError::NotOwner) + ); + + // Disallow renouncing ownership. + let err: ContractError = app + .execute_contract( + Addr::unchecked(new_owner), + addr, + &ExecuteMsg::UpdateOwnership(cw_ownable::Action::RenounceOwnership), + &[], + ) + .unwrap_err() + .downcast() + .unwrap(); + assert_eq!(err, ContractError::CannotRenounceOwnership); +} + +#[test] +pub fn test_update_config() { + let (mut app, addr) = instantiate(); + + // Ensure config is set. + let config: Config = app + .wrap() + .query_wasm_smart(addr.clone(), &QueryMsg::Config {}) + .unwrap(); + assert_eq!( + config, + Config { + fee_amount: Uint128::new(FEE_AMOUNT), + fee_denom: CheckedDenom::Native(FEE_DENOM.to_string()), + registration_period: Timestamp::from_nanos(REGISTRATION_PERIOD_NANOS), + } + ); + + // Update config. + app.execute_contract( + Addr::unchecked(OWNER), + addr.clone(), + &ExecuteMsg::UpdateConfig { + fee_amount: Some(Uint128::new(2 * FEE_AMOUNT)), + fee_denom: Some(UncheckedDenom::Native("new_denom".to_string())), + registration_period: Some(Timestamp::from_nanos(2 * REGISTRATION_PERIOD_NANOS)), + }, + &[], + ) + .unwrap(); + + // Ensure config is updated. + let new_config: Config = app + .wrap() + .query_wasm_smart(addr.clone(), &QueryMsg::Config {}) + .unwrap(); + assert_eq!( + new_config, + Config { + fee_amount: Uint128::new(2 * FEE_AMOUNT), + fee_denom: CheckedDenom::Native("new_denom".to_string()), + registration_period: Timestamp::from_nanos(2 * REGISTRATION_PERIOD_NANOS), + } + ); + + // Ensure non-owner cannot update. + let err: ContractError = app + .execute_contract( + Addr::unchecked(NOT_OWNER), + addr, + &ExecuteMsg::UpdateConfig { + fee_amount: Some(Uint128::new(2 * FEE_AMOUNT)), + fee_denom: Some(UncheckedDenom::Native("new_denom".to_string())), + registration_period: Some(Timestamp::from_nanos(2 * REGISTRATION_PERIOD_NANOS)), + }, + &[], + ) + .unwrap_err() + .downcast() + .unwrap(); + assert_eq!( + err, + ContractError::Ownable(cw_ownable::OwnershipError::NotOwner) + ); +} + +#[test] +pub fn test_register_approve() { + let (mut app, addr) = instantiate(); + + // Register with no funds. + let err: ContractError = app + .execute_contract( + Addr::unchecked(DAO1), + addr.clone(), + &ExecuteMsg::Register { + name: NAME1.to_string(), + address: None, + }, + &[], + ) + .unwrap_err() + .downcast() + .unwrap(); + assert_eq!( + err, + ContractError::PaymentError(cw_utils::PaymentError::NoFunds {}) + ); + + // Register with insufficient funds. + let err: ContractError = app + .execute_contract( + Addr::unchecked(DAO1), + addr.clone(), + &ExecuteMsg::Register { + name: NAME1.to_string(), + address: None, + }, + &coins(FEE_AMOUNT / 2, FEE_DENOM), + ) + .unwrap_err() + .downcast() + .unwrap(); + assert_eq!(err, ContractError::WrongAmount); + + // Register with correct funds. + app.execute_contract( + Addr::unchecked(DAO1), + addr.clone(), + &ExecuteMsg::Register { + name: NAME1.to_string(), + address: None, + }, + &coins(FEE_AMOUNT, FEE_DENOM), + ) + .unwrap(); + + // Ensure fee is stored in the contract. + let balance = app + .wrap() + .query_balance(addr.clone(), FEE_DENOM) + .unwrap() + .amount; + assert_eq!(balance, Uint128::new(FEE_AMOUNT)); + + // Ensure pending registration is created. + let pending_registration = app + .wrap() + .query_wasm_smart::>( + addr.clone(), + &QueryMsg::PendingRegistration { + address: DAO1.to_string(), + }, + ) + .unwrap() + .unwrap(); + let most_recent_registration = app + .wrap() + .query_wasm_smart::>( + addr.clone(), + &QueryMsg::MostRecentRegistration { + address: DAO1.to_string(), + }, + ) + .unwrap() + .unwrap(); + assert_eq!(pending_registration, most_recent_registration); + assert!(pending_registration.is_pending()); + assert_eq!( + pending_registration.address, + Addr::unchecked(DAO1.to_string()) + ); + assert_eq!(pending_registration.name, NAME1.to_string()); + assert_eq!(pending_registration.expiration, Timestamp::from_nanos(0)); + + // Ensure DAO not registered. + let registration = app + .wrap() + .query_wasm_smart::>( + addr.clone(), + &QueryMsg::Registration { + address: DAO1.to_string(), + }, + ) + .unwrap(); + let resolved_registration = app + .wrap() + .query_wasm_smart::>( + addr.clone(), + &QueryMsg::Resolve { + name: NAME1.to_string(), + }, + ) + .unwrap(); + assert!(registration.is_none()); + assert!(resolved_registration.is_none()); + + // Approve registration by owner. + app.execute_contract( + Addr::unchecked(OWNER), + addr.clone(), + &ExecuteMsg::Approve { + address: DAO1.to_string(), + }, + &[], + ) + .unwrap(); + + // Ensure fee was transferred to the owner. + let balance = app + .wrap() + .query_balance(addr.clone(), FEE_DENOM) + .unwrap() + .amount; + assert_eq!(balance, Uint128::zero()); + let balance = app.wrap().query_balance(OWNER, FEE_DENOM).unwrap().amount; + assert_eq!(balance, Uint128::new(FEE_AMOUNT)); + + // Ensure DAO registered. + let pending_registration = app + .wrap() + .query_wasm_smart::>( + addr.clone(), + &QueryMsg::PendingRegistration { + address: DAO1.to_string(), + }, + ) + .unwrap(); + assert!(pending_registration.is_none()); + + let registration = app + .wrap() + .query_wasm_smart::>( + addr.clone(), + &QueryMsg::Registration { + address: DAO1.to_string(), + }, + ) + .unwrap() + .unwrap(); + let most_recent_registration = app + .wrap() + .query_wasm_smart::>( + addr.clone(), + &QueryMsg::MostRecentRegistration { + address: DAO1.to_string(), + }, + ) + .unwrap() + .unwrap(); + let resolved_registration = app + .wrap() + .query_wasm_smart::>( + addr.clone(), + &QueryMsg::Resolve { + name: NAME1.to_string(), + }, + ) + .unwrap() + .unwrap(); + assert_eq!(registration, most_recent_registration); + assert_eq!(registration, resolved_registration); + assert_eq!(registration.address, Addr::unchecked(DAO1.to_string())); + assert_eq!(registration.name, NAME1.to_string()); + assert_eq!( + registration.expiration, + app.block_info().time.plus_nanos(REGISTRATION_PERIOD_NANOS) + ); + + // Ensure DAO cannot register again. + let err: ContractError = app + .execute_contract( + Addr::unchecked(DAO1), + addr.clone(), + &ExecuteMsg::Register { + name: NAME2.to_string(), + address: None, + }, + &coins(FEE_AMOUNT, FEE_DENOM), + ) + .unwrap_err() + .downcast() + .unwrap(); + assert_eq!(err, ContractError::AlreadyRegistered); + + // Ensure another DAO cannot register the same name. + let err: ContractError = app + .execute_contract( + Addr::unchecked(DAO2), + addr, + &ExecuteMsg::Register { + name: NAME1.to_string(), + address: None, + }, + &coins(FEE_AMOUNT, FEE_DENOM), + ) + .unwrap_err() + .downcast() + .unwrap(); + assert_eq!(err, ContractError::NameAlreadyRegistered); +} + +#[test] +pub fn test_register_reject() { + let (mut app, addr) = instantiate(); + + // Register. + app.execute_contract( + Addr::unchecked(DAO2), + addr.clone(), + &ExecuteMsg::Register { + name: NAME2.to_string(), + address: None, + }, + &coins(FEE_AMOUNT, FEE_DENOM), + ) + .unwrap(); + + // Ensure fee is stored in the contract. + let balance = app + .wrap() + .query_balance(addr.clone(), FEE_DENOM) + .unwrap() + .amount; + assert_eq!(balance, Uint128::new(FEE_AMOUNT)); + + // Ensure pending registration is created. + let pending_registration = app + .wrap() + .query_wasm_smart::>( + addr.clone(), + &QueryMsg::PendingRegistration { + address: DAO2.to_string(), + }, + ) + .unwrap() + .unwrap(); + let most_recent_registration = app + .wrap() + .query_wasm_smart::>( + addr.clone(), + &QueryMsg::MostRecentRegistration { + address: DAO2.to_string(), + }, + ) + .unwrap() + .unwrap(); + assert_eq!(pending_registration, most_recent_registration); + assert!(pending_registration.is_pending()); + assert_eq!( + pending_registration.address, + Addr::unchecked(DAO2.to_string()) + ); + assert_eq!(pending_registration.name, NAME2.to_string()); + assert_eq!(pending_registration.expiration, Timestamp::from_nanos(0)); + + // Ensure DAO not registered. + let registration = app + .wrap() + .query_wasm_smart::>( + addr.clone(), + &QueryMsg::Registration { + address: DAO2.to_string(), + }, + ) + .unwrap(); + let resolved_registration = app + .wrap() + .query_wasm_smart::>( + addr.clone(), + &QueryMsg::Resolve { + name: NAME2.to_string(), + }, + ) + .unwrap(); + assert!(registration.is_none()); + assert!(resolved_registration.is_none()); + + // Reject registration by owner. + app.execute_contract( + Addr::unchecked(OWNER), + addr.clone(), + &ExecuteMsg::Reject { + address: DAO2.to_string(), + }, + &[], + ) + .unwrap(); + + // Ensure fee was transferred back to the DAO. + let balance = app + .wrap() + .query_balance(addr.clone(), FEE_DENOM) + .unwrap() + .amount; + assert_eq!(balance, Uint128::zero()); + let balance = app.wrap().query_balance(OWNER, FEE_DENOM).unwrap().amount; + assert_eq!(balance, Uint128::zero()); + let balance = app.wrap().query_balance(DAO2, FEE_DENOM).unwrap().amount; + assert_eq!(balance, Uint128::new(INITIAL_BALANCE)); + + // Ensure DAO not registered. + let pending_registration = app + .wrap() + .query_wasm_smart::>( + addr.clone(), + &QueryMsg::PendingRegistration { + address: DAO2.to_string(), + }, + ) + .unwrap(); + let registration = app + .wrap() + .query_wasm_smart::>( + addr.clone(), + &QueryMsg::Registration { + address: DAO2.to_string(), + }, + ) + .unwrap(); + let resolved_registration = app + .wrap() + .query_wasm_smart::>( + addr.clone(), + &QueryMsg::Resolve { + name: NAME2.to_string(), + }, + ) + .unwrap(); + let most_recent_registration = app + .wrap() + .query_wasm_smart::>( + addr.clone(), + &QueryMsg::MostRecentRegistration { + address: DAO2.to_string(), + }, + ) + .unwrap() + .unwrap(); + assert!(pending_registration.is_none()); + assert!(registration.is_none()); + assert!(resolved_registration.is_none()); + assert_eq!( + most_recent_registration.status, + RegistrationStatus::Rejected + ); + assert_eq!( + most_recent_registration.address, + Addr::unchecked(DAO2.to_string()) + ); + assert_eq!(most_recent_registration.name, NAME2.to_string()); + + // Ensure DAO can register again. + app.execute_contract( + Addr::unchecked(DAO2), + addr.clone(), + &ExecuteMsg::Register { + name: NAME2.to_string(), + address: None, + }, + &coins(FEE_AMOUNT, FEE_DENOM), + ) + .unwrap(); + + // Ensure another DAO can register the same name. + app.execute_contract( + Addr::unchecked(DAO1), + addr, + &ExecuteMsg::Register { + name: NAME2.to_string(), + address: None, + }, + &coins(FEE_AMOUNT, FEE_DENOM), + ) + .unwrap(); +} + +#[test] +pub fn test_owner_privileges() { + let (mut app, addr) = instantiate(); + + // Register by owner. + app.execute_contract( + Addr::unchecked(OWNER), + addr.clone(), + &ExecuteMsg::Register { + name: NAME1.to_string(), + address: Some(DAO1.to_string()), + }, + &[], + ) + .unwrap(); + + // Ensure DAO registered. + let pending_registration = app + .wrap() + .query_wasm_smart::>( + addr.clone(), + &QueryMsg::PendingRegistration { + address: DAO1.to_string(), + }, + ) + .unwrap(); + assert!(pending_registration.is_none()); + + let registration = app + .wrap() + .query_wasm_smart::>( + addr.clone(), + &QueryMsg::Registration { + address: DAO1.to_string(), + }, + ) + .unwrap() + .unwrap(); + let most_recent_registration = app + .wrap() + .query_wasm_smart::>( + addr.clone(), + &QueryMsg::MostRecentRegistration { + address: DAO1.to_string(), + }, + ) + .unwrap() + .unwrap(); + let resolved_registration = app + .wrap() + .query_wasm_smart::>( + addr.clone(), + &QueryMsg::Resolve { + name: NAME1.to_string(), + }, + ) + .unwrap() + .unwrap(); + assert_eq!(registration, most_recent_registration); + assert_eq!(registration, resolved_registration); + assert_eq!(registration.address, Addr::unchecked(DAO1.to_string())); + assert_eq!(registration.name, NAME1.to_string()); + assert_eq!( + registration.expiration, + app.block_info().time.plus_nanos(REGISTRATION_PERIOD_NANOS) + ); + + // Update expiration by owner. + let new_expiration = registration.expiration.plus_nanos(100); + app.execute_contract( + Addr::unchecked(OWNER), + addr.clone(), + &ExecuteMsg::UpdateExpiration { + name: NAME1.to_string(), + expiration: new_expiration, + }, + &[], + ) + .unwrap(); + + // Ensure DAO registration updated. + let resolved_registration = app + .wrap() + .query_wasm_smart::>( + addr.clone(), + &QueryMsg::Resolve { + name: NAME1.to_string(), + }, + ) + .unwrap() + .unwrap(); + assert_eq!(resolved_registration.expiration, new_expiration); + + // Revoke registration by owner. + app.execute_contract( + Addr::unchecked(OWNER), + addr.clone(), + &ExecuteMsg::Revoke { + name: NAME1.to_string(), + }, + &[], + ) + .unwrap(); + + // Ensure DAO not registered. + let registration = app + .wrap() + .query_wasm_smart::>( + addr.clone(), + &QueryMsg::Registration { + address: DAO1.to_string(), + }, + ) + .unwrap(); + let resolved_registration = app + .wrap() + .query_wasm_smart::>( + addr, + &QueryMsg::Resolve { + name: NAME1.to_string(), + }, + ) + .unwrap(); + assert!(registration.is_none()); + assert!(resolved_registration.is_none()); +}