Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Audit Fixes and Improvements #742

Merged
merged 59 commits into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from 57 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
4b1398c
Add missing hooks to dao-voting-native-staked, start adding tests
JakeHartnell Sep 1, 2023
de50a62
Fix group contract attribute key emits "address"
JakeHartnell Sep 1, 2023
fbad6e7
Fix absoluteCount can be configured to be greater than the total
Sep 2, 2023
2a50a0b
Fix absoluteCount threshold for a new token is not validated
Sep 2, 2023
cdb8da2
Fix issuer contract can be blacklisted
Sep 2, 2023
74420d1
Fix Stargaze collection info bug, and absolute count validation
Sep 2, 2023
583db0c
Fix incorrect events are emitted for whitelist
Sep 2, 2023
c82fdd3
Fix lack of denom validation
Sep 2, 2023
d3ad487
Fix BEFORE_SEND_HOOK_FEATURES_ENABLED is not exposed through smart qu…
Sep 2, 2023
c6cf16d
Fix misleading from attribute when burning funds
Sep 2, 2023
c02a52a
Fix broken tests from denom validation
Sep 2, 2023
56159d2
Fix inconsistent attribute namings and orderings
Sep 2, 2023
27c1602
Fix INITITIAL_NFTS spelling XD
Sep 2, 2023
89cf32b
Fix counterintuitive variable namings
Sep 2, 2023
8bcae64
Better API for cw-tokenfactory-issuer
Sep 2, 2023
3b75780
Appease clippy gods
Sep 2, 2023
ce4e4fc
Reduce unneeded gas consumption by setting admin directly
JakeHartnell Sep 2, 2023
df1591b
Fix unneeded reply_always when instantiating new token
JakeHartnell Sep 2, 2023
bfff543
Remove unused function
JakeHartnell Sep 2, 2023
360297a
Improve comments
JakeHartnell Sep 2, 2023
527a600
Use “Migrate only if newer” pattern
JakeHartnell Sep 2, 2023
b4256b1
Clean up and fix issuer test-tube tests
JakeHartnell Sep 2, 2023
2781cec
Combine all dao-related hooks into a single package
JakeHartnell Sep 2, 2023
f96e6e4
Improve code resuse by consilidating stake hooks
JakeHartnell Sep 2, 2023
17928bc
Clean up types
JakeHartnell Sep 3, 2023
51c79c0
Remove need for unused TokenFactoryQuery, use shared stake hooks
JakeHartnell Sep 3, 2023
f20b1cb
Fix stake hook tests, update schema
JakeHartnell Sep 3, 2023
dc48b30
Incorporate NFT staking hooks into dao-hooks package
JakeHartnell Sep 3, 2023
2b04dbc
Add cw4::MemberChangedHookMsg and clean up dao-hooks pkg
JakeHartnell Sep 3, 2023
e48211f
Make clippy happy, use only migrate newer across all voting contracts
JakeHartnell Sep 3, 2023
426d139
cargo fmt
JakeHartnell Sep 3, 2023
c2c230d
clippy on latest rust version
JakeHartnell Sep 3, 2023
318cea5
Improve unstaking_duration validation reuse
Sep 5, 2023
8c1b31f
Allowlist should apply to transfers from *or to* an address
JakeHartnell Sep 5, 2023
8883b75
Add info on renouncing Token Factory Admin
JakeHartnell Sep 5, 2023
a8683f4
Remove outdated comments
JakeHartnell Sep 5, 2023
2cb2d59
Remove commented out metadata test
JakeHartnell Sep 5, 2023
f2d4ac3
More informative error to address BlockBeforeSend hook executing on m…
JakeHartnell Sep 5, 2023
5953169
Use tagged versions or upstream git repos for deps
JakeHartnell Sep 5, 2023
dd93317
Better code documentation for cw-tokenfactory-issuer
JakeHartnell Sep 5, 2023
7633b3e
Make it possible to unset BeforeSendHook, or set to a different contract
JakeHartnell Sep 5, 2023
0115fe7
Default to BeforeSendHook features being disabled
JakeHartnell Sep 5, 2023
dc7af3c
BeforeSendHook refactor and tests
JakeHartnell Sep 6, 2023
508d81c
Cleanup TODO, verify correct error
JakeHartnell Sep 6, 2023
8c54bf2
Accurate comment
JakeHartnell Sep 9, 2023
fa1a74a
Improve active threshold validation reuse
JakeHartnell Sep 9, 2023
71137e8
Don't create issuer for existing tokens
JakeHartnell Sep 9, 2023
b6f9c04
Reorg so we can have only one native token voting contract
JakeHartnell Sep 9, 2023
c46597e
No need to have an issuer for existing tokens
JakeHartnell Sep 9, 2023
cc764d0
Update Schema
JakeHartnell Sep 9, 2023
87aaf6d
Fix up integration tests
JakeHartnell Sep 11, 2023
21e91ee
Remove owner from dao-voting-cw721-staked
JakeHartnell Sep 11, 2023
2d40452
Improve TF docs
JakeHartnell Sep 11, 2023
4778813
Implement two-step ownership transfer for cw_tokenfactory_issue
JakeHartnell Sep 12, 2023
c232173
Tests for renouncing ownership
JakeHartnell Sep 12, 2023
be1ae92
Fix package name.
JakeHartnell Sep 12, 2023
1c51285
Fix integration tests
JakeHartnell Sep 12, 2023
26e24dd
set_before_update_hook -> set_before_send_hook
JakeHartnell Sep 12, 2023
67e8b83
Address remaining TODOs
JakeHartnell Sep 12, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
637 changes: 352 additions & 285 deletions Cargo.lock

Large diffs are not rendered by default.

21 changes: 9 additions & 12 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ cw721 = "0.18"
cw721-base = "0.18"
env_logger = "0.10"
once_cell = "1.18"
osmosis-std = { git = "https://github.com/osmosis-labs/osmosis-rust", branch = "autobuild-v17.0.0-rc0" }
osmosis-test-tube = "17.0.0-rc0"
osmosis-std = "0.19.0"
osmosis-test-tube = "19.0.0"
proc-macro2 = "1.0"
prost = "0.11"
quote = "1.0"
Expand All @@ -71,9 +71,7 @@ sg-multi-test = "3.1.0"
syn = { version = "1.0", features = ["derive"] }
test-context = "0.1"
thiserror = { version = "1.0" }
# TODO use upstream when PR merged and new release tagged: https://github.com/CosmWasm/cw-multi-test/pull/51
token-bindings = { git = "https://github.com/CosmosContracts/token-bindings.git", rev = "0cd084b68172ffc9af29eb37fb915392ce351954" }
token-bindings-test = { git = "https://github.com/CosmosContracts/token-bindings.git", rev = "0cd084b68172ffc9af29eb37fb915392ce351954" }
token-bindings = "0.11.0"
wynd-utils = "0.4"

# One commit ahead of version 0.3.0. Allows initialization with an
Expand All @@ -94,28 +92,27 @@ cw721-controllers = { path = "./packages/cw721-controllers", version = "2.2.0" }
cw721-roles = { path = "./contracts/external/cw721-roles", version = "*" }
dao-cw721-extensions = { path = "./packages/dao-cw721-extensions", version = "*" }
dao-dao-core = { path = "./contracts/dao-dao-core", version = "2.2.0" }
dao-interface = { path = "./packages/dao-interface", version = "2.2.0" }
dao-dao-macros = { path = "./packages/dao-dao-macros", version = "2.2.0" }
dao-hooks = { path = "./packages/dao-hooks", version = "*" }
dao-interface = { path = "./packages/dao-interface", version = "2.2.0" }
dao-pre-propose-approval-single = { path = "./contracts/pre-propose/dao-pre-propose-approval-single", version = "2.2.0" }
dao-pre-propose-approver = { path = "./contracts/pre-propose/dao-pre-propose-approver", version = "2.2.0" }
dao-pre-propose-base = { path = "./packages/dao-pre-propose-base", version = "2.2.0" }
dao-pre-propose-multiple = { path = "./contracts/pre-propose/dao-pre-propose-multiple", version = "2.2.0" }
dao-pre-propose-single = { path = "./contracts/pre-propose/dao-pre-propose-single", version = "2.2.0" }
dao-proposal-condorcet = { path = "./contracts/proposal/dao-proposal-condorcet", version = "2.2.0" }
dao-proposal-hooks = { path = "./packages/dao-proposal-hooks", version = "2.2.0" }
dao-proposal-multiple = { path = "./contracts/proposal/dao-proposal-multiple", version = "2.2.0" }
dao-proposal-single = { path = "./contracts/proposal/dao-proposal-single", version = "2.2.0" }
dao-proposal-sudo = { path = "./test-contracts/dao-proposal-sudo", version = "2.2.0" }
dao-proposal-hook-counter = { path = "./test-contracts/dao-proposal-hook-counter", version = "2.2.0" }
dao-testing = { path = "./packages/dao-testing", version = "2.2.0" }
dao-vote-hooks = { path = "./packages/dao-vote-hooks", version = "2.2.0" }
dao-voting = { path = "./packages/dao-voting", version = "2.2.0" }
dao-voting-cw20-balance = { path = "./test-contracts/dao-voting-cw20-balance", version = "2.2.0" }
dao-voting-cw20-staked = { path = "./contracts/voting/dao-voting-cw20-staked", version = "2.2.0" }
dao-voting-cw4 = { path = "./contracts/voting/dao-voting-cw4", version = "2.2.0" }
dao-voting-cw721-roles = { path = "./contracts/voting/dao-voting-cw721-roles", version = "*" }
dao-voting-cw721-staked = { path = "./contracts/voting/dao-voting-cw721-staked", version = "2.2.0" }
dao-voting-native-staked = { path = "./contracts/voting/dao-voting-native-staked", version = "2.2.0" }
dao-voting-token-factory-staked = { path = "./contracts/voting/dao-voting-token-factory-staked", version = "2.2.0" }
dao-voting-token-staked = { path = "./contracts/voting/dao-voting-token-staked", version = "2.2.0" }

# v1 dependencies. used for state migrations.
cw-core-v1 = { package = "cw-core", version = "0.1.0" }
Expand All @@ -129,6 +126,6 @@ cw4-voting-v1 = { package = "cw4-voting", version = "0.1.0" }
voting-v1 = { package = "dao-voting", version = "0.1.0" }
stake-cw20-v03 = { package = "stake-cw20", version = "0.2.6" }

# TODO remove when upstream PR merged and new release tagged: https://github.com/CosmWasm/cw-multi-test/pull/51
# TODO remove when new release is tagged upstream
[patch.crates-io]
cw-multi-test = { git = "https://github.com/JakeHartnell/cw-multi-test.git", branch = "bank-supply-support" }
cw-multi-test = { git = "https://github.com/CosmWasm/cw-multi-test.git", rev = "d38db7752b9f054c395d6108453f8b321e4cab02" }
17 changes: 9 additions & 8 deletions ci/integration-tests/src/helpers/helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,16 @@ pub fn create_dao(
.unwrap();

let ProposalCreationPolicy::Module { addr: pre_propose } = chain
.orc
.query(
"dao_proposal_single",
&dao_proposal_single::msg::QueryMsg::ProposalCreationPolicy {}
).unwrap()
.data()
.unwrap()
.orc
.query(
"dao_proposal_single",
&dao_proposal_single::msg::QueryMsg::ProposalCreationPolicy {},
)
.unwrap()
.data()
.unwrap()
else {
panic!("expected pre-propose module")
panic!("expected pre-propose module")
};
chain
.orc
Expand Down
14 changes: 3 additions & 11 deletions ci/integration-tests/src/tests/dao_voting_cw721_staked_test.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use cosm_orc::orchestrator::{ExecReq, SigningKey};
use cosmwasm_std::{Binary, Empty, Uint128};
use cw_utils::Duration;
use dao_interface::state::Admin;
use test_context::test_context;

use dao_voting_cw721_staked as module;
Expand Down Expand Up @@ -38,7 +37,6 @@ pub fn instantiate_cw721_base(chain: &mut Chain, key: &SigningKey, minter: &str)

fn setup_test(
chain: &mut Chain,
owner: Option<Admin>,
unstaking_duration: Option<Duration>,
key: &SigningKey,
minter: &str,
Expand All @@ -50,7 +48,6 @@ fn setup_test(
CONTRACT_NAME,
"instantiate_dao_voting_cw721_staked",
&module::msg::InstantiateMsg {
owner,
nft_contract: module::msg::NftContract::Existing {
address: cw721.clone(),
},
Expand Down Expand Up @@ -169,7 +166,7 @@ fn cw721_stake_tokens(chain: &mut Chain) {
let user_addr = chain.users["user1"].account.address.clone();
let user_key = chain.users["user1"].key.clone();

let CommonTest { module, .. } = setup_test(chain, None, None, &user_key, &user_addr);
let CommonTest { module, .. } = setup_test(chain, None, &user_key, &user_addr);

mint_and_stake_nft(chain, &user_key, &user_addr, &module, "a");

Expand Down Expand Up @@ -202,13 +199,8 @@ fn cw721_stake_max_claims_works(chain: &mut Chain) {
let user_addr = chain.users["user1"].account.address.clone();
let user_key = chain.users["user1"].key.clone();

let CommonTest { module, .. } = setup_test(
chain,
None,
Some(Duration::Height(1)),
&user_key,
&user_addr,
);
let CommonTest { module, .. } =
setup_test(chain, Some(Duration::Height(1)), &user_key, &user_addr);

// Create `MAX_CLAIMS` claims.

Expand Down
6 changes: 3 additions & 3 deletions contracts/dao-dao-core/schema/dao-dao-core.json
Original file line number Diff line number Diff line change
Expand Up @@ -1029,7 +1029,7 @@
]
},
"channel_id": {
"description": "exisiting channel to send the tokens over",
"description": "existing channel to send the tokens over",
"type": "string"
},
"timeout": {
Expand Down Expand Up @@ -1147,7 +1147,7 @@
"minimum": 0.0
},
"revision": {
"description": "the version that the client is currently on (eg. after reseting the chain this could increment 1 as height drops to 0)",
"description": "the version that the client is currently on (e.g. after resetting the chain this could increment 1 as height drops to 0)",
"type": "integer",
"format": "uint64",
"minimum": 0.0
Expand Down Expand Up @@ -1398,7 +1398,7 @@
}
},
"label": {
"description": "A human-readbale label for the contract",
"description": "A human-readable label for the contract.\n\nValid values should: - not be empty - not be bigger than 128 bytes (or some chain-specific limit) - not start / end with whitespace",
"type": "string"
},
"msg": {
Expand Down
3 changes: 2 additions & 1 deletion contracts/external/cw-tokenfactory-issuer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ test-tube = []
cosmwasm-schema = { workspace = true }
cosmwasm-std = { workspace = true }
cosmwasm-storage = { workspace = true }
cw-storage-plus = { workspace = true }
cw2 = { workspace = true }
cw-ownable = { workspace = true }
cw-storage-plus = { workspace = true }
osmosis-std = { workspace = true }
prost = { workspace = true }
schemars = { workspace = true }
Expand Down
71 changes: 63 additions & 8 deletions contracts/external/cw-tokenfactory-issuer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,28 @@ This repo contains a set of contracts that when used in conjunction with the x/t
- Creating a new Token Factory token or using an existing one
- Granting and revoking allowances for the minting and burning of tokens
- Updating token metadata
- Freezing and unfreezing transfers, with a whitelist to allow some address to continue to transfer
- Force transfering tokens via the contract owner
- Updating the contract owner or Token Factory admin
- And more! (see [Advanced Features](#advanced-features))

It is intended to work on multiple chains supporting Token Factory, and has been tested on Juno Network and Osmosis.

The contract has an owner (which can be removed or updated via `ExecuteMsg::ChangeContractOwner {}`), but it can delegate capabilities to other acccounts. For example, the owner of a contract can delegate minting allowance of 1000 tokens to a new address.
The contract has an owner (which can be removed or updated via `ExecuteMsg::UpdateOwnership {}`), but it can delegate capabilities to other acccounts. For example, the owner of a contract can delegate minting allowance of 1000 tokens to a new address.

The contract is also the admin of the newly created Token Factory denom. For minting and burning, users then interact with the contract using its own ExecuteMsgs which trigger the contract's access control logic, and the contract then dispatches tokenfactory sdk.Msgs from its own contract account.
Ownership functionality for this contract is implemented using the `cw-ownable` library.

NOTE: this contract contains a `SudoMsg::BlockBeforeSend` hook that allows for the blacklisting of specific accounts as well as the freezing of all transfers if necessary. This feature is not enabled on every chain using Token Factory, and so blacklisting and freezing features are disabled if `MsgBeforeSendHook` is not supported. DAOs wishing to leverage these features on chains after support is added can call `ExecuteMsg::SetBeforeSendHook {}`.
The `cw_tokenfactory_issuer` contract is also the admin of newly created Token Factory denoms. For minting and burning, users then interact with the contract using its own ExecuteMsgs which trigger the contract's access control logic, and the contract then dispatches tokenfactory sdk.Msgs from its own contract account.

## Instantiation
When instantiating `cw-tokenfactory-issuer`, you can either create a `new_token` or an `existing_token`.

When instantiating `cw-tokenfactory-issuer`, you can either create a `new` or an `existing`.

### Creating a new Token Factory token

To create a new Token Factory token, simply instantiate the contract with a `subdenom`, this will create a new contract as well as a token with a denom formatted as `factory/{contract_address}/{subdenom}`.

Example instantiate message:

``` json
```json
{
"new_token": {
"subdenom": "test"
Expand All @@ -37,13 +38,67 @@ Example instantiate message:
All other updates can be preformed afterwards via this contract's `ExecuteMsg` enum. See `src/msg.rs` for available methods.

### Using an Existing Token

You can also instantiate this contract with an existing token, however most features will not be available until the previous Token Factory admin transfers admin rights to the instantiated contract and optionally calls `ExecuteMsg::SetBeforeSendHook {}` to enable dependent features.

Example instantiate message:
``` json

```json
{
"existing_token": {
"denom": "factory/{contract_address}/{subdenom}"
}
}
```

## Renouncing Token Factory Admin
Some DAOs or protocols after the initial setup phase may wish to render their tokens immutable, permanently disabling features of this contract.

To do so, they must execute a `ExcuteMessage::UpdateTokenFactoryAdmin {}` method, setting the Admin to a null address or the bank module for your respective chain.

For example, on Juno this could be:

``` json
{
"update_token_factory_admin": {
"new_admin": "juno1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"
}
}
```

The Token Factory standard requires a Token Factory admin per token, by setting to a null address the Token is rendered immutable and the `cw-tokenfactory-issuer` will be unable to make future updates. This is secure as the cryptography that underlies the chain enforces that even with the largest super computers in the world it would take an astonomically large amount of time to compute the private key for this address.

### Advanced Features

This contract supports a number of advanced features which DAOs or token issuers may wist to leverage:
- Freezing and unfreezing transfers, with an allowlist to allow specified addresses to allow transfer to or from
- Denylist to prevent certain addresses from transferring
- Force transfering tokens via the contract owner

**By default, these features are disabled**, and must be explictly enabled by the contract owner (for example via a DAO governance prop).

Moreover, for these features to work, your chain must support the `MsgBeforeSendHook` bank module hook. This is not yet available on every chain using Token Factory, and so denylisting and freezing features are not available if `MsgBeforeSendHook` is not supported.

On chains where `MsgBeforeSendHook` is supported, DAOs or issuers wishing to leverage these features must set the before send hook with `ExecuteMsg::SetBeforeSendHook {}`.

This method takes a `cosmwasm_address`, which is the address of a contract implement a `SudoMsg::BlockBeforeSend` entrypoint. Normally this will be the address of the `cw_tokenfactory_issuer` contract itself, but it is possible to specify a custom contract. This contract contains a `SudoMsg::BlockBeforeSend` hook that allows for the denylisting of specific accounts as well as the freezing of all transfers if necessary.

Example message to set before send hook:
``` json
{
"set_before_send_hook": {
"cosmwasm_address": "<address of your cw_tokenfactory_issuer contract>"
}
}
```

DAOs or issuers wishing to leverage these features on chains without support can call `ExecuteMsg::SetBeforeSendHook {}` when support is added.

If a DAO or issuer wishes to disable and removed before send hook related functionality, they simply need to call `ExecuteMsg::SetBeforeSendHook {}` with an empty string for the `cosmwasm_address` like so:
``` json
{
"set_before_send_hook": {
"cosmwasm_address": ""
}
}
```
Loading
Loading