From 9f7847865b7312774c35f9f0f39d6c343ecebffe Mon Sep 17 00:00:00 2001 From: Jake Hartnell Date: Sat, 23 Dec 2023 14:45:14 -0800 Subject: [PATCH] dao-abc-factory contract Creates new cw-abc DAOs. --- Cargo.lock | 20 + Cargo.toml | 1 + contracts/external/cw-abc/README.md | 2 +- contracts/external/cw-abc/src/queries.rs | 8 +- .../external/dao-abc-factory/.cargo/config | 4 + contracts/external/dao-abc-factory/Cargo.toml | 35 ++ contracts/external/dao-abc-factory/README | 4 + .../dao-abc-factory/examples/schema.rs | 10 + .../schema/dao-abc-factory.json | 547 ++++++++++++++++++ .../external/dao-abc-factory/src/contract.rs | 146 +++++ .../external/dao-abc-factory/src/error.rs | 28 + contracts/external/dao-abc-factory/src/lib.rs | 5 + contracts/external/dao-abc-factory/src/msg.rs | 23 + 13 files changed, 828 insertions(+), 5 deletions(-) create mode 100644 contracts/external/dao-abc-factory/.cargo/config create mode 100644 contracts/external/dao-abc-factory/Cargo.toml create mode 100644 contracts/external/dao-abc-factory/README create mode 100644 contracts/external/dao-abc-factory/examples/schema.rs create mode 100644 contracts/external/dao-abc-factory/schema/dao-abc-factory.json create mode 100644 contracts/external/dao-abc-factory/src/contract.rs create mode 100644 contracts/external/dao-abc-factory/src/error.rs create mode 100644 contracts/external/dao-abc-factory/src/lib.rs create mode 100644 contracts/external/dao-abc-factory/src/msg.rs diff --git a/Cargo.lock b/Cargo.lock index 085798875..fc27a421e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1692,6 +1692,26 @@ dependencies = [ "thiserror", ] +[[package]] +name = "dao-abc-factory" +version = "2.4.0" +dependencies = [ + "cosmwasm-schema", + "cosmwasm-std", + "cosmwasm-storage", + "cw-abc", + "cw-multi-test", + "cw-ownable", + "cw-storage-plus 1.2.0", + "cw-tokenfactory-issuer", + "cw-utils 1.0.3", + "cw2 1.1.2", + "dao-dao-macros", + "dao-interface", + "dao-voting 2.4.0", + "thiserror", +] + [[package]] name = "dao-cw721-extensions" version = "2.4.0" diff --git a/Cargo.toml b/Cargo.toml index 079d8f0e9..181d1d566 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -101,6 +101,7 @@ cw-wormhole = { path = "./packages/cw-wormhole", version = "2.4.0" } cw20-stake = { path = "./contracts/staking/cw20-stake", version = "2.4.0" } cw721-controllers = { path = "./packages/cw721-controllers", version = "2.4.0" } cw721-roles = { path = "./contracts/external/cw721-roles", version = "2.4.0" } +dao-abc-factory = { path = "/contracts/external/dao-abc-factory", version = "*" } dao-cw721-extensions = { path = "./packages/dao-cw721-extensions", version = "2.4.0" } dao-dao-core = { path = "./contracts/dao-dao-core", version = "2.4.0" } dao-dao-macros = { path = "./packages/dao-dao-macros", version = "2.4.0" } diff --git a/contracts/external/cw-abc/README.md b/contracts/external/cw-abc/README.md index 64342e095..44ab82194 100644 --- a/contracts/external/cw-abc/README.md +++ b/contracts/external/cw-abc/README.md @@ -4,7 +4,7 @@ Implments an [Augmented Bonding Curve](https://medium.com/commonsstack/deep-dive Forked from and heavily inspired by the work on [cw20-bonding](https://github.com/cosmwasm/cw-tokens/tree/main/contracts/cw20-bonding). This contract uses native and token factory tokens instead. -NOTE: this contract is unaudited and experimental. NOT RECOMMENDED FOR PRODUCTION USE. +NOTE: this contract is NOT AUDITED and experimental. NOT RECOMMENDED FOR PRODUCTION USE. Use at your own risk. ## What are Augmented Bonding Curves? Before we get to the *Augmented* part, we must first describe bonding curves themselves. diff --git a/contracts/external/cw-abc/src/queries.rs b/contracts/external/cw-abc/src/queries.rs index d80c4272c..8bc9138a6 100644 --- a/contracts/external/cw-abc/src/queries.rs +++ b/contracts/external/cw-abc/src/queries.rs @@ -40,7 +40,7 @@ pub fn get_denom(deps: Deps) -> StdResult { pub fn query_donations( deps: Deps, - start_aftor: Option, + start_after: Option, limit: Option, ) -> StdResult { let donations = cw_paginate_storage::paginate_map( @@ -50,7 +50,7 @@ pub fn query_donations( querier: QuerierWrapper::new(deps.querier.deref()), }, &DONATIONS, - start_aftor + start_after .map(|addr| deps.api.addr_validate(&addr)) .transpose()? .as_ref(), @@ -64,7 +64,7 @@ pub fn query_donations( /// Query hatchers who contributed during the hatch phase pub fn query_hatchers( deps: Deps, - start_aftor: Option, + start_after: Option, limit: Option, ) -> StdResult { let hatchers = cw_paginate_storage::paginate_map( @@ -74,7 +74,7 @@ pub fn query_hatchers( querier: QuerierWrapper::new(deps.querier.deref()), }, &HATCHERS, - start_aftor + start_after .map(|addr| deps.api.addr_validate(&addr)) .transpose()? .as_ref(), diff --git a/contracts/external/dao-abc-factory/.cargo/config b/contracts/external/dao-abc-factory/.cargo/config new file mode 100644 index 000000000..336b618a1 --- /dev/null +++ b/contracts/external/dao-abc-factory/.cargo/config @@ -0,0 +1,4 @@ +[alias] +wasm = "build --release --target wasm32-unknown-unknown" +unit-test = "test --lib" +schema = "run --example schema" diff --git a/contracts/external/dao-abc-factory/Cargo.toml b/contracts/external/dao-abc-factory/Cargo.toml new file mode 100644 index 000000000..bfaaf4c1a --- /dev/null +++ b/contracts/external/dao-abc-factory/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "dao-abc-factory" +authors = ["Jake Hartnell"] +description = "A factory contract for cw-abc, intended for use with DAO DAO." +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-std = { workspace = true } +cosmwasm-schema = { workspace = true } +cosmwasm-storage = { workspace = true } +cw-abc = { workspace = true } +cw2 = { workspace = true } +cw-ownable = { workspace = true } +cw-storage-plus = { workspace = true } +cw-utils = { workspace = true } +thiserror = { workspace = true } +dao-dao-macros = { workspace = true } +dao-interface = { workspace = true } +dao-voting = { workspace = true } +cw-tokenfactory-issuer = { workspace = true, features = ["library"] } + +[dev-dependencies] +cw-multi-test = { workspace = true } diff --git a/contracts/external/dao-abc-factory/README b/contracts/external/dao-abc-factory/README new file mode 100644 index 000000000..0322b0f4d --- /dev/null +++ b/contracts/external/dao-abc-factory/README @@ -0,0 +1,4 @@ +# DAO ABC Factory +Used for creating DAOs with `dao-voting-token-staked` and `cw-abc`. + +NOTE: this contract is NOT AUDITED, use at your own risk. diff --git a/contracts/external/dao-abc-factory/examples/schema.rs b/contracts/external/dao-abc-factory/examples/schema.rs new file mode 100644 index 000000000..1c48cc544 --- /dev/null +++ b/contracts/external/dao-abc-factory/examples/schema.rs @@ -0,0 +1,10 @@ +use cosmwasm_schema::write_api; +use dao_abc_factory::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; + +fn main() { + write_api! { + instantiate: InstantiateMsg, + query: QueryMsg, + execute: ExecuteMsg, + } +} diff --git a/contracts/external/dao-abc-factory/schema/dao-abc-factory.json b/contracts/external/dao-abc-factory/schema/dao-abc-factory.json new file mode 100644 index 000000000..9c341ce9e --- /dev/null +++ b/contracts/external/dao-abc-factory/schema/dao-abc-factory.json @@ -0,0 +1,547 @@ +{ + "contract_name": "dao-abc-factory", + "contract_version": "2.4.0", + "idl_version": "1.0.0", + "instantiate": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InstantiateMsg", + "type": "object", + "additionalProperties": false + }, + "execute": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecuteMsg", + "oneOf": [ + { + "description": "Example Factory Implementation", + "type": "object", + "required": [ + "abc_factory" + ], + "properties": { + "abc_factory": { + "$ref": "#/definitions/InstantiateMsg" + } + }, + "additionalProperties": false + } + ], + "definitions": { + "ClosedConfig": { + "type": "object", + "additionalProperties": false + }, + "CommonsPhaseConfig": { + "type": "object", + "required": [ + "closed", + "hatch", + "open" + ], + "properties": { + "closed": { + "description": "The Closed phase where the Commons is closed to new members.", + "allOf": [ + { + "$ref": "#/definitions/ClosedConfig" + } + ] + }, + "hatch": { + "description": "The Hatch phase where initial contributors (Hatchers) participate in a hatch sale.", + "allOf": [ + { + "$ref": "#/definitions/HatchConfig" + } + ] + }, + "open": { + "description": "TODO Vest tokens after hatch phase The Vesting phase where tokens minted during the Hatch phase are locked (burning is disabled) to combat early speculation/arbitrage. pub vesting: VestingConfig, The Open phase where anyone can mint tokens by contributing the reserve token into the curve and becoming members of the Commons.", + "allOf": [ + { + "$ref": "#/definitions/OpenConfig" + } + ] + } + }, + "additionalProperties": false + }, + "CurveType": { + "oneOf": [ + { + "description": "Constant always returns `value * 10^-scale` as spot price", + "type": "object", + "required": [ + "constant" + ], + "properties": { + "constant": { + "type": "object", + "required": [ + "scale", + "value" + ], + "properties": { + "scale": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "value": { + "$ref": "#/definitions/Uint128" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "Linear returns `slope * 10^-scale * supply` as spot price", + "type": "object", + "required": [ + "linear" + ], + "properties": { + "linear": { + "type": "object", + "required": [ + "scale", + "slope" + ], + "properties": { + "scale": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "slope": { + "$ref": "#/definitions/Uint128" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "description": "SquareRoot returns `slope * 10^-scale * supply^0.5` as spot price", + "type": "object", + "required": [ + "square_root" + ], + "properties": { + "square_root": { + "type": "object", + "required": [ + "scale", + "slope" + ], + "properties": { + "scale": { + "type": "integer", + "format": "uint32", + "minimum": 0.0 + }, + "slope": { + "$ref": "#/definitions/Uint128" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "Decimal": { + "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", + "type": "string" + }, + "DenomUnit": { + "description": "DenomUnit represents a struct that describes a given denomination unit of the basic token.", + "type": "object", + "required": [ + "aliases", + "denom", + "exponent" + ], + "properties": { + "aliases": { + "description": "aliases is a list of string aliases for the given denom", + "type": "array", + "items": { + "type": "string" + } + }, + "denom": { + "description": "denom represents the string name of the given denom unit (e.g uatom).", + "type": "string" + }, + "exponent": { + "description": "exponent represents power of 10 exponent that one must raise the base_denom to in order to equal the given DenomUnit's denom 1 denom = 1^exponent base_denom (e.g. with a base_denom of uatom, one can create a DenomUnit of 'atom' with exponent = 6, thus: 1 atom = 10^6 uatom).", + "type": "integer", + "format": "uint32", + "minimum": 0.0 + } + } + }, + "HatchConfig": { + "type": "object", + "required": [ + "contribution_limits", + "entry_fee", + "exit_fee", + "initial_raise" + ], + "properties": { + "contribution_limits": { + "description": "The minimum and maximum contribution amounts (min, max) in the reserve token", + "allOf": [ + { + "$ref": "#/definitions/MinMax" + } + ] + }, + "entry_fee": { + "description": "The initial allocation (θ), percentage of the initial raise allocated to the Funding Pool", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "exit_fee": { + "description": "Exit tax for the hatch phase", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "initial_raise": { + "description": "The initial raise range (min, max) in the reserve token", + "allOf": [ + { + "$ref": "#/definitions/MinMax" + } + ] + } + }, + "additionalProperties": false + }, + "InstantiateMsg": { + "type": "object", + "required": [ + "curve_type", + "fees_recipient", + "phase_config", + "reserve", + "supply", + "token_issuer_code_id" + ], + "properties": { + "curve_type": { + "description": "Curve type for this contract", + "allOf": [ + { + "$ref": "#/definitions/CurveType" + } + ] + }, + "fees_recipient": { + "description": "The recipient for any fees collected from bonding curve operation", + "type": "string" + }, + "hatcher_allowlist": { + "description": "TODO different ways of doing this, for example DAO members? Using a whitelist contract? Merkle tree? Hatcher allowlist", + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "phase_config": { + "description": "Hatch configuration information", + "allOf": [ + { + "$ref": "#/definitions/CommonsPhaseConfig" + } + ] + }, + "reserve": { + "description": "Reserve token information", + "allOf": [ + { + "$ref": "#/definitions/ReserveToken" + } + ] + }, + "supply": { + "description": "Supply token information", + "allOf": [ + { + "$ref": "#/definitions/SupplyToken" + } + ] + }, + "token_issuer_code_id": { + "description": "The code id of the cw-tokenfactory-issuer contract", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + }, + "additionalProperties": false + }, + "MinMax": { + "description": "Struct for minimium and maximum values", + "type": "object", + "required": [ + "max", + "min" + ], + "properties": { + "max": { + "$ref": "#/definitions/Uint128" + }, + "min": { + "$ref": "#/definitions/Uint128" + } + }, + "additionalProperties": false + }, + "NewDenomMetadata": { + "type": "object", + "required": [ + "description", + "display", + "name", + "symbol" + ], + "properties": { + "additional_denom_units": { + "description": "Used define additional units of the token (e.g. \"tiger\") These must have an exponent larger than 0.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/DenomUnit" + } + }, + "description": { + "description": "The description of the token", + "type": "string" + }, + "display": { + "description": "The unit commonly used in communication (e.g. \"cat\")", + "type": "string" + }, + "name": { + "description": "The name of the token (e.g. \"Cat Coin\")", + "type": "string" + }, + "symbol": { + "description": "The ticker symbol of the token (e.g. \"CAT\")", + "type": "string" + } + }, + "additionalProperties": false + }, + "OpenConfig": { + "type": "object", + "required": [ + "entry_fee", + "exit_fee" + ], + "properties": { + "entry_fee": { + "description": "Percentage of capital put into the Reserve Pool during the Open phase when buying from the curve.", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + }, + "exit_fee": { + "description": "Exit taxation ratio", + "allOf": [ + { + "$ref": "#/definitions/Decimal" + } + ] + } + }, + "additionalProperties": false + }, + "ReserveToken": { + "type": "object", + "required": [ + "decimals", + "denom" + ], + "properties": { + "decimals": { + "description": "Number of decimal places for the reserve token, needed for proper curve math. Same format as decimals above, eg. if it is uatom, where 1 unit is 10^-6 ATOM, use 6 here", + "type": "integer", + "format": "uint8", + "minimum": 0.0 + }, + "denom": { + "description": "Reserve token denom (only support native for now)", + "type": "string" + } + }, + "additionalProperties": false + }, + "SupplyToken": { + "type": "object", + "required": [ + "decimals", + "subdenom" + ], + "properties": { + "decimals": { + "description": "Number of decimal places for the supply token, needed for proper curve math. Default for token factory is 6", + "type": "integer", + "format": "uint8", + "minimum": 0.0 + }, + "max_supply": { + "anyOf": [ + { + "$ref": "#/definitions/Uint128" + }, + { + "type": "null" + } + ] + }, + "metadata": { + "description": "Metadata for the supply token to create", + "anyOf": [ + { + "$ref": "#/definitions/NewDenomMetadata" + }, + { + "type": "null" + } + ] + }, + "subdenom": { + "description": "The denom to create for the supply token", + "type": "string" + } + }, + "additionalProperties": false + }, + "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" + } + } + }, + "query": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "QueryMsg", + "oneOf": [ + { + "type": "object", + "required": [ + "info" + ], + "properties": { + "info": { + "type": "object", + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "daos" + ], + "properties": { + "daos": { + "type": "object", + "properties": { + "limit": { + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "start_after": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + }, + "migrate": null, + "sudo": null, + "responses": { + "daos": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Array_of_Addr", + "type": "array", + "items": { + "$ref": "#/definitions/Addr" + }, + "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" + } + } + }, + "info": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InfoResponse", + "type": "object", + "required": [ + "info" + ], + "properties": { + "info": { + "$ref": "#/definitions/ContractVersion" + } + }, + "additionalProperties": false, + "definitions": { + "ContractVersion": { + "type": "object", + "required": [ + "contract", + "version" + ], + "properties": { + "contract": { + "description": "contract is the crate name of the implementing contract, eg. `crate:cw20-base` we will use other prefixes for other languages, and their standard global namespacing", + "type": "string" + }, + "version": { + "description": "version is any string that this implementation knows. It may be simple counter \"1\", \"2\". or semantic version on release tags \"v0.7.0\", or some custom feature flag list. the only code that needs to understand the version parsing is code that knows how to migrate from the given contract (and is tied to it's implementation somehow)", + "type": "string" + } + }, + "additionalProperties": false + } + } + } + } +} diff --git a/contracts/external/dao-abc-factory/src/contract.rs b/contracts/external/dao-abc-factory/src/contract.rs new file mode 100644 index 000000000..ecd909e22 --- /dev/null +++ b/contracts/external/dao-abc-factory/src/contract.rs @@ -0,0 +1,146 @@ +#[cfg(not(feature = "library"))] +use cosmwasm_std::entry_point; +use cosmwasm_std::{ + to_json_binary, Addr, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Order, Reply, Response, + StdResult, SubMsg, WasmMsg, +}; +use cw2::set_contract_version; +use cw_abc::msg::{InstantiateMsg as AbcInstantiateMsg, QueryMsg as AbcQueryMsg}; +use cw_storage_plus::{Bound, Item, Map}; +use cw_utils::parse_reply_instantiate_data; +use dao_interface::{token::TokenFactoryCallback, voting::Query as VotingModuleQueryMsg}; + +use crate::{ + error::ContractError, + msg::{ExecuteMsg, InstantiateMsg, QueryMsg}, +}; + +const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); +const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); + +const INSTANTIATE_ABC_REPLY_ID: u64 = 1; + +const DAOS: Map = Map::new("daos"); +const VOTING_MODULE: Item = Item::new("voting_module"); + +#[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)?; + + Ok(Response::new().add_attribute("method", "instantiate")) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: ExecuteMsg, +) -> Result { + match msg { + ExecuteMsg::AbcFactory(msg) => execute_token_factory_factory(deps, env, info, msg), + } +} + +pub fn execute_token_factory_factory( + deps: DepsMut, + _env: Env, + info: MessageInfo, + msg: AbcInstantiateMsg, +) -> Result { + // Save voting module address + VOTING_MODULE.save(deps.storage, &info.sender)?; + + // Query for DAO + let dao: Addr = deps + .querier + .query_wasm_smart(info.sender, &VotingModuleQueryMsg::Dao {})?; + + DAOS.save(deps.storage, dao, &Empty {})?; + + // Instantiate new contract, further setup is handled in the + // SubMsg reply. + let msg = SubMsg::reply_on_success( + WasmMsg::Instantiate { + // No admin as we want the bonding curve contract to be immutable + admin: None, + code_id: msg.token_issuer_code_id, + msg: to_json_binary(&msg)?, + funds: vec![], + label: "cw_abc".to_string(), + }, + INSTANTIATE_ABC_REPLY_ID, + ); + + Ok(Response::new().add_submessage(msg)) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { + match msg { + QueryMsg::Info {} => query_info(deps), + QueryMsg::Daos { start_after, limit } => query_daos(deps, start_after, limit), + } +} + +pub fn query_info(deps: Deps) -> StdResult { + let info = cw2::get_contract_version(deps.storage)?; + to_json_binary(&dao_interface::voting::InfoResponse { info }) +} + +pub fn query_daos( + deps: Deps, + start_after: Option, + limit: Option, +) -> StdResult { + to_json_binary( + &DAOS + .keys( + deps.storage, + None, + start_after + .map(|s| deps.api.addr_validate(&s)) + .transpose()? + .map(Bound::exclusive), + Order::Descending, + ) + .take(limit.unwrap_or(25) as usize) + .collect::>>()?, + ) +} + +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result { + match msg.id { + INSTANTIATE_ABC_REPLY_ID => { + // Parse issuer address from instantiate reply + let abc_addr = parse_reply_instantiate_data(msg)?.contract_address; + + // Query for denom + let denom = deps + .querier + .query_wasm_smart(abc_addr.clone(), &AbcQueryMsg::Denom {})?; + + // Query for token contract + let token_contract: Addr = deps + .querier + .query_wasm_smart(abc_addr.clone(), &AbcQueryMsg::TokenContract {})?; + + // Responses for `dao-voting-token-staked` MUST include a + // TokenFactoryCallback. + Ok( + Response::new().set_data(to_json_binary(&TokenFactoryCallback { + denom, + token_contract: Some(token_contract.to_string()), + module_instantiate_callback: None, + })?), + ) + } + _ => Err(ContractError::UnknownReplyId { id: msg.id }), + } +} diff --git a/contracts/external/dao-abc-factory/src/error.rs b/contracts/external/dao-abc-factory/src/error.rs new file mode 100644 index 000000000..4a29782c7 --- /dev/null +++ b/contracts/external/dao-abc-factory/src/error.rs @@ -0,0 +1,28 @@ +use cosmwasm_std::StdError; +use cw_utils::{ParseReplyError, PaymentError}; +use dao_voting::threshold::ActiveThresholdError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum ContractError { + #[error("{0}")] + Std(#[from] StdError), + + #[error(transparent)] + ActiveThresholdError(#[from] ActiveThresholdError), + + #[error(transparent)] + ParseReplyError(#[from] ParseReplyError), + + #[error(transparent)] + PaymentError(#[from] PaymentError), + + #[error("Unauthorized")] + Unauthorized {}, + + #[error("Got a submessage reply with unknown id: {id}")] + UnknownReplyId { id: u64 }, + + #[error("Factory message must serialize to WasmMsg::Execute")] + UnsupportedFactoryMsg {}, +} diff --git a/contracts/external/dao-abc-factory/src/lib.rs b/contracts/external/dao-abc-factory/src/lib.rs new file mode 100644 index 000000000..3915b791e --- /dev/null +++ b/contracts/external/dao-abc-factory/src/lib.rs @@ -0,0 +1,5 @@ +pub mod contract; +mod error; +pub mod msg; + +pub use crate::error::ContractError; diff --git a/contracts/external/dao-abc-factory/src/msg.rs b/contracts/external/dao-abc-factory/src/msg.rs new file mode 100644 index 000000000..6d6cdf52a --- /dev/null +++ b/contracts/external/dao-abc-factory/src/msg.rs @@ -0,0 +1,23 @@ +use cosmwasm_schema::{cw_serde, QueryResponses}; +use cw_abc::msg::InstantiateMsg as AbcInstantiateMsg; + +#[cw_serde] +pub struct InstantiateMsg {} + +#[cw_serde] +pub enum ExecuteMsg { + /// Example Factory Implementation + AbcFactory(AbcInstantiateMsg), +} + +#[cw_serde] +#[derive(QueryResponses)] +pub enum QueryMsg { + #[returns(dao_interface::voting::InfoResponse)] + Info {}, + #[returns(Vec)] + Daos { + start_after: Option, + limit: Option, + }, +}