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

Renamed "atom wars" contract to "hydro" #48

Merged
merged 1 commit into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
members = ["contracts/atom_wars", "contracts/tribute"]
members = ["contracts/hydro", "contracts/tribute"]

[profile.release]
opt-level = 3
Expand Down
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
# Atom Wars technical spec
# Hydro technical spec

This is an overview of **the first version of** Atom Wars from a technical and user perspective. The code implementing most of the voting system has already been written, but there is still a substantial amount of work to do to allow the contract to handle protocol owned liquidity. This integration will be done by Timewave.
This is an overview of **the first version of** Hydro from a technical and user perspective. The code implementing most of the voting system has already been written, but there is still a substantial amount of work to do to allow the contract to handle protocol owned liquidity. This integration will be done by Timewave.

Much of this has already been covered in the original Atom Wars post, but there are some nuances and differences here since this describes the functioning of the actual contract.
Much of this has already been covered in the original Hydro post, but there are some nuances and differences here since this describes the functioning of the actual contract.

# Protocol Owned Liquidity

Atom Wars is a system where Atom stakers can lock up their staked Atoms in exchange for voting power, which they can use to vote on proposals to deploy Atoms owned by the community pool into liquidity (aka market-making) positions on various decentralized exchanges.
Hydro is a system where Atom stakers can lock up their staked Atoms in exchange for voting power, which they can use to vote on proposals to deploy Atoms owned by the community pool into liquidity (aka market-making) positions on various decentralized exchanges.

Like other “___ Wars” competitions, Atom Wars uses the concept of “gauges”- deployed liquidity is split between proposals proportional to the number of votes that they receive, instead of a winner-takes-all approach.
Like other “___ Wars” competitions, Hydro uses the concept of “gauges”- deployed liquidity is split between proposals proportional to the number of votes that they receive, instead of a winner-takes-all approach.

Atom is commonly used to enter and exit positions of other Cosmos tokens. Supplying more liquidity will help to cement Atom’s role as interchain money. The competition in Atom Wars to secure these PoL spots will generate excitement around Atom, and incentivize holders to lock it up.
Atom is commonly used to enter and exit positions of other Cosmos tokens. Supplying more liquidity will help to cement Atom’s role as interchain money. The competition in Hydro to secure these PoL spots will generate excitement around Atom, and incentivize holders to lock it up.

Projects using the Cosmos Hub’s Interchain Security platform will receive a gauge multiplier. For a given percentage of the vote, they will receive a higher percentage of the deployed PoL.

# Locking

## Collateral

The first version of Atom Wars will take stAtom as collateral. There has been some opposition to this since there is still debate over whether liquid staking is safe, and Stride (the issuer of stAtom) charges a 10% fee on staking rewards. However, Stride is a consumer chain, and a portion of this fee ultimately goes back to the Hub.
The first version of Hydro will take stAtom as collateral. There has been some opposition to this since there is still debate over whether liquid staking is safe, and Stride (the issuer of stAtom) charges a 10% fee on staking rewards. However, Stride is a consumer chain, and a portion of this fee ultimately goes back to the Hub.

Accepting multiple liquid staking tokens would be complicated and require using an price oracle, and/or querying multiple LST providers before most actions to check how many Atoms each LST represents.

A future version of Atom Wars will let people lock up their Atoms without using a liquid staking token or unstaking from their current validator by using a technology called “LSM shares”, which effectively create a separate denomination for every delegator’s stake on every Hub validator. This is exciting, but non-trivial, since it will require the Atom Wars contract to be able to handle a potentially unlimited number of locked denominations, and it will require Atom Wars to query the Cosmos Hub in several places to validate these denominations whenever a user takes an action.
A future version of Hydro will let people lock up their Atoms without using a liquid staking token or unstaking from their current validator by using a technology called “LSM shares”, which effectively create a separate denomination for every delegator’s stake on every Hub validator. This is exciting, but non-trivial, since it will require the Hydro contract to be able to handle a potentially unlimited number of locked denominations, and it will require Hydro to query the Cosmos Hub in several places to validate these denominations whenever a user takes an action.

Until this technology is ready, Atom Wars will institute a cap on the number of stAtoms that can be locked in the contract. This cap will be a fraction of a percent of the total Atom supply, alleviating any security concerns.
Until this technology is ready, Hydro will institute a cap on the number of stAtoms that can be locked in the contract. This cap will be a fraction of a percent of the total Atom supply, alleviating any security concerns.

## Lock lengths

Expand Down Expand Up @@ -53,6 +53,6 @@ Once the round is over, proposals are deployed using Timewave, a product which a

# Tribute

The Atom Wars forum post mentions “tribute”- funds that proposal creators can attach to proposals which is paid out to the winning proposal. This is not implemented within the main Atom Wars contract, but it is possible for tribute to be awarded with pluggable tribute contracts that read from the Atom Wars contract. These can be switched out permissionlessly and even customized or reinvented by proposal authors.
The Hydro forum post mentions “tribute”- funds that proposal creators can attach to proposals which is paid out to the winning proposal. This is not implemented within the main Hydro contract, but it is possible for tribute to be awarded with pluggable tribute contracts that read from the Hydro contract. These can be switched out permissionlessly and even customized or reinvented by proposal authors.

We will deploy an example default tribute contract which pays out tribute to anyone who voted for a proposal- but only if that proposal wins. This can be used as is by proposal authors, or used as a starting point for custom tribute contracts.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "atom-wars"
name = "hydro"
version = "1.0.0"
authors = ["Jehan Tremback", "Philip Offtermatt", "Dusan Maksimovic"]
edition = "2018"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use std::fs::create_dir_all;

use cosmwasm_schema::{export_schema, remove_schemas, schema_for};

use atom_wars::msg::{ExecuteMsg, InstantiateMsg};
use atom_wars::query::QueryMsg;
use hydro::msg::{ExecuteMsg, InstantiateMsg};
use hydro::query::QueryMsg;

fn main() {
let mut out_dir = current_dir().unwrap();
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion contracts/tribute/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ thiserror = { workspace = true }
cw-storage-plus = { workspace = true }
cw2 = { workspace = true }
cosmwasm-schema = { workspace = true }
atom-wars = { path = "../atom_wars" }
hydro = { path = "../hydro" }

[dev-dependencies]
cosmwasm-schema = { workspace = true }
38 changes: 19 additions & 19 deletions contracts/tribute/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use crate::error::ContractError;
use crate::msg::{ExecuteMsg, InstantiateMsg};
use crate::query::QueryMsg;
use crate::state::{Config, Tribute, CONFIG, TRIBUTE_CLAIMS, TRIBUTE_ID, TRIBUTE_MAP};
use atom_wars::query::QueryMsg as AtomWarsQueryMsg;
use atom_wars::state::{Proposal, Vote};
use hydro::query::QueryMsg as HydroQueryMsg;
use hydro::state::{Proposal, Vote};

/// Contract name that is used for migration.
const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME");
Expand All @@ -27,7 +27,7 @@ pub fn instantiate(
) -> Result<Response, ContractError> {
set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
let config = Config {
atom_wars_contract: deps.api.addr_validate(&msg.atom_wars_contract)?,
hydro_contract: deps.api.addr_validate(&msg.hydro_contract)?,
top_n_props_count: msg.top_n_props_count,
};

Expand Down Expand Up @@ -72,13 +72,13 @@ fn add_tribute(
tranche_id: u64,
proposal_id: u64,
) -> Result<Response, ContractError> {
let atom_wars_contract = CONFIG.load(deps.storage)?.atom_wars_contract;
let current_round_id = query_current_round_id(&deps, &atom_wars_contract)?;
let hydro_contract = CONFIG.load(deps.storage)?.hydro_contract;
let current_round_id = query_current_round_id(&deps, &hydro_contract)?;

// Check that the proposal exists
query_proposal(
&deps,
&atom_wars_contract,
&hydro_contract,
current_round_id,
tranche_id,
proposal_id,
Expand Down Expand Up @@ -152,7 +152,7 @@ fn claim_tribute(

// Check that the round is ended
let config = CONFIG.load(deps.storage)?;
let current_round_id = query_current_round_id(&deps, &config.atom_wars_contract)?;
let current_round_id = query_current_round_id(&deps, &config.hydro_contract)?;

if round_id >= current_round_id {
return Err(ContractError::Std(StdError::generic_err(
Expand All @@ -163,7 +163,7 @@ fn claim_tribute(
// Look up voter's vote for the round, error if it cannot be found
let vote = query_user_vote(
&deps,
&config.atom_wars_contract,
&config.hydro_contract,
round_id,
tranche_id,
voter.clone().to_string(),
Expand Down Expand Up @@ -232,7 +232,7 @@ fn refund_tribute(
let config = CONFIG.load(deps.storage)?;

// Check that the round is ended by checking that the round_id is less than the current round
let current_round_id = query_current_round_id(&deps, &config.atom_wars_contract)?;
let current_round_id = query_current_round_id(&deps, &config.hydro_contract)?;
if round_id >= current_round_id {
return Err(ContractError::Std(StdError::generic_err(
"Round has not ended yet",
Expand Down Expand Up @@ -320,24 +320,24 @@ pub fn query_proposal_tributes(
.collect()
}

fn query_current_round_id(deps: &DepsMut, atom_wars_contract: &Addr) -> Result<u64, ContractError> {
fn query_current_round_id(deps: &DepsMut, hydro_contract: &Addr) -> Result<u64, ContractError> {
let current_round_id: u64 = deps
.querier
.query_wasm_smart(atom_wars_contract, &AtomWarsQueryMsg::CurrentRound {})?;
.query_wasm_smart(hydro_contract, &HydroQueryMsg::CurrentRound {})?;

Ok(current_round_id)
}

fn query_proposal(
deps: &DepsMut,
atom_wars_contract: &Addr,
hydro_contract: &Addr,
round_id: u64,
tranche_id: u64,
proposal_id: u64,
) -> Result<Proposal, ContractError> {
let proposal: Proposal = deps.querier.query_wasm_smart(
atom_wars_contract,
&AtomWarsQueryMsg::Proposal {
hydro_contract,
&HydroQueryMsg::Proposal {
round_id,
tranche_id,
proposal_id,
Expand All @@ -349,14 +349,14 @@ fn query_proposal(

fn query_user_vote(
deps: &DepsMut,
atom_wars_contract: &Addr,
hydro_contract: &Addr,
round_id: u64,
tranche_id: u64,
address: String,
) -> Result<Vote, ContractError> {
Ok(deps.querier.query_wasm_smart(
atom_wars_contract,
&AtomWarsQueryMsg::UserVote {
hydro_contract,
&HydroQueryMsg::UserVote {
round_id,
tranche_id,
address,
Expand All @@ -372,8 +372,8 @@ fn get_top_n_proposal(
proposal_id: u64,
) -> Result<Option<Proposal>, ContractError> {
let proposals: Vec<Proposal> = deps.querier.query_wasm_smart(
&config.atom_wars_contract,
&AtomWarsQueryMsg::TopNProposals {
&config.hydro_contract,
&HydroQueryMsg::TopNProposals {
round_id,
tranche_id,
number_of_proposals: config.top_n_props_count as usize,
Expand Down
2 changes: 1 addition & 1 deletion contracts/tribute/src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct InstantiateMsg {
pub atom_wars_contract: String,
pub hydro_contract: String,
pub top_n_props_count: u64,
}

Expand Down
2 changes: 1 addition & 1 deletion contracts/tribute/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub const CONFIG: Item<Config> = Item::new("config");

#[cw_serde]
pub struct Config {
pub atom_wars_contract: Addr,
pub hydro_contract: Addr,
pub top_n_props_count: u64,
}

Expand Down
42 changes: 21 additions & 21 deletions contracts/tribute/src/testing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,30 @@ use crate::{
contract::{execute, instantiate, query_proposal_tributes},
msg::{ExecuteMsg, InstantiateMsg},
};
use atom_wars::query::QueryMsg as AtomWarsQueryMsg;
use atom_wars::state::{CovenantParams, Proposal, Vote};
use cosmwasm_std::{
from_json,
testing::{mock_dependencies, mock_env, mock_info},
to_json_binary, Binary, ContractResult, QuerierResult, Response, SystemError, SystemResult,
Uint128, WasmQuery,
};
use cosmwasm_std::{BankMsg, Coin, CosmosMsg};
use hydro::query::QueryMsg as HydroQueryMsg;
use hydro::state::{CovenantParams, Proposal, Vote};

pub fn get_instantiate_msg(atom_wars_contract: String) -> InstantiateMsg {
pub fn get_instantiate_msg(hydro_contract: String) -> InstantiateMsg {
InstantiateMsg {
atom_wars_contract,
hydro_contract,
top_n_props_count: 10,
}
}

const DEFAULT_DENOM: &str = "uatom";
const ATOM_WARS_CONTRACT_ADDRESS: &str = "addr0000";
const HYDRO_CONTRACT_ADDRESS: &str = "addr0000";
const USER_ADDRESS_1: &str = "addr0001";
const USER_ADDRESS_2: &str = "addr0002";

pub struct MockWasmQuerier {
atom_wars_contract: String,
hydro_contract: String,
current_round: u64,
proposal: Option<Proposal>,
user_vote: Option<(u64, u64, String, Vote)>,
Expand All @@ -34,14 +34,14 @@ pub struct MockWasmQuerier {

impl MockWasmQuerier {
fn new(
atom_wars_contract: String,
hydro_contract: String,
current_round: u64,
proposal: Option<Proposal>,
user_vote: Option<(u64, u64, String, Vote)>,
top_n_proposals: Vec<Proposal>,
) -> Self {
Self {
atom_wars_contract,
hydro_contract,
current_round,
proposal,
user_vote,
Expand All @@ -52,15 +52,15 @@ impl MockWasmQuerier {
fn handler(&self, query: &WasmQuery) -> QuerierResult {
match query {
WasmQuery::Smart { contract_addr, msg } => {
if *contract_addr != self.atom_wars_contract {
if *contract_addr != self.hydro_contract {
return SystemResult::Err(SystemError::NoSuchContract {
addr: contract_addr.to_string(),
});
}

let response = match from_json(msg).unwrap() {
AtomWarsQueryMsg::CurrentRound {} => to_json_binary(&self.current_round),
AtomWarsQueryMsg::Proposal {
HydroQueryMsg::CurrentRound {} => to_json_binary(&self.current_round),
HydroQueryMsg::Proposal {
round_id,
tranche_id,
proposal_id,
Expand All @@ -84,7 +84,7 @@ impl MockWasmQuerier {
_ => return err,
}
}
AtomWarsQueryMsg::UserVote {
HydroQueryMsg::UserVote {
round_id,
tranche_id,
address,
Expand All @@ -105,7 +105,7 @@ impl MockWasmQuerier {
_ => return err,
}
}
AtomWarsQueryMsg::TopNProposals {
HydroQueryMsg::TopNProposals {
round_id: _,
tranche_id: _,
number_of_proposals: _,
Expand Down Expand Up @@ -235,15 +235,15 @@ fn add_tribute_test() {
);

let mock_querier = MockWasmQuerier::new(
ATOM_WARS_CONTRACT_ADDRESS.to_string(),
HYDRO_CONTRACT_ADDRESS.to_string(),
test.mock_data.0,
test.mock_data.1,
None,
vec![],
);
deps.querier.update_wasm(move |q| mock_querier.handler(q));

let msg = get_instantiate_msg(ATOM_WARS_CONTRACT_ADDRESS.to_string());
let msg = get_instantiate_msg(HYDRO_CONTRACT_ADDRESS.to_string());
let res = instantiate(deps.as_mut(), env.clone(), info.clone(), msg.clone());
assert!(res.is_ok());

Expand Down Expand Up @@ -438,15 +438,15 @@ fn claim_tribute_test() {
);

let mock_querier = MockWasmQuerier::new(
ATOM_WARS_CONTRACT_ADDRESS.to_string(),
HYDRO_CONTRACT_ADDRESS.to_string(),
test.mock_data.0,
test.mock_data.2.clone(),
None,
vec![],
);
deps.querier.update_wasm(move |q| mock_querier.handler(q));

let msg = get_instantiate_msg(ATOM_WARS_CONTRACT_ADDRESS.to_string());
let msg = get_instantiate_msg(HYDRO_CONTRACT_ADDRESS.to_string());
let res = instantiate(deps.as_mut(), env.clone(), info.clone(), msg.clone());
assert!(res.is_ok());

Expand All @@ -462,7 +462,7 @@ fn claim_tribute_test() {

// Update the expected round so that the tribute can be claimed
let mock_querier = MockWasmQuerier::new(
ATOM_WARS_CONTRACT_ADDRESS.to_string(),
HYDRO_CONTRACT_ADDRESS.to_string(),
test.mock_data.1,
test.mock_data.2.clone(),
test.mock_data.3.clone(),
Expand Down Expand Up @@ -628,15 +628,15 @@ fn refund_tribute_test() {
);

let mock_querier = MockWasmQuerier::new(
ATOM_WARS_CONTRACT_ADDRESS.to_string(),
HYDRO_CONTRACT_ADDRESS.to_string(),
test.mock_data.0,
test.mock_data.2.clone(),
None,
vec![],
);
deps.querier.update_wasm(move |q| mock_querier.handler(q));

let msg = get_instantiate_msg(ATOM_WARS_CONTRACT_ADDRESS.to_string());
let msg = get_instantiate_msg(HYDRO_CONTRACT_ADDRESS.to_string());
let res = instantiate(deps.as_mut(), env.clone(), info.clone(), msg.clone());
assert!(res.is_ok());

Expand All @@ -652,7 +652,7 @@ fn refund_tribute_test() {

// Update the expected round so that the tribute can be refunded
let mock_querier = MockWasmQuerier::new(
ATOM_WARS_CONTRACT_ADDRESS.to_string(),
HYDRO_CONTRACT_ADDRESS.to_string(),
test.mock_data.1,
test.mock_data.2.clone(),
None,
Expand Down
2 changes: 1 addition & 1 deletion docs/spec.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Hydro: Technical Specification
This document describes the technical specification of the Hydro protocol.

The state that Hydro keeps can be found in [`../contracts/atom_wars/src/state.rs`](../contracts/atom_wars/src/state.rs).
The state that Hydro keeps can be found in [`../contracts/hydro/src/state.rs`](../contracts/hydro/src/state.rs).
and [`../contracts/tribute/state.rs`](../contracts/tribute/state.rs).

Let us first give a high-level overview of the functionality.
Expand Down
Loading