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

Injective ts #83

Draft
wants to merge 35 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
2ed7425
Add pausable ism with tests
yorhodes Jan 9, 2024
a6c890a
Fix paused query error case
yorhodes Jan 9, 2024
ee01584
Run CI against all PRs
yorhodes Jan 9, 2024
476b777
Add pausable ISM to README
yorhodes Jan 9, 2024
c2c2804
Build wasm
nambrot Jan 9, 2024
ef9c6f3
Fix scripts
yorhodes Jan 9, 2024
8a9aa1b
Allow threshold == set size and add tests
nambrot Jan 9, 2024
63c62bb
Upload artifacts
nambrot Jan 9, 2024
ae2706d
Force
nambrot Jan 9, 2024
46d3e36
Move into makefile
nambrot Jan 10, 2024
f7af326
Install rename
nambrot Jan 11, 2024
280fb07
Rename properly
nambrot Jan 11, 2024
bf522be
Add cosmjs patch for injective deploy
yorhodes Jan 11, 2024
1e5d4f3
Add testnet deploy
yorhodes Jan 11, 2024
4817d7a
Use new set validators interface
yorhodes Jan 11, 2024
60c3807
Save injective testnet deploy
yorhodes Jan 11, 2024
7aec518
Deploy warp route
yorhodes Jan 11, 2024
95c599d
Add updated patched
yorhodes Jan 12, 2024
d5666cd
Update test.yaml
nambrot Jan 12, 2024
e7c890c
Deploy with patches
yorhodes Jan 12, 2024
3f77274
Send message to goerli
yorhodes Jan 12, 2024
7973533
Merge pull request #1 from hyperlane-xyz/pausable-ism
yorhodes Jan 16, 2024
26f040f
Merge pull request #3 from hyperlane-xyz/nambrot/fix-multisig-ism
yorhodes Jan 16, 2024
04468ba
Fix renaming
yorhodes Jan 16, 2024
ddb174b
Fix makefile indentation
yorhodes Jan 16, 2024
189ecf3
Force cargo install
yorhodes Jan 16, 2024
2cd2bfc
Merge pull request #2 from hyperlane-xyz/nambrot/ci-wasm-build
yorhodes Jan 16, 2024
05c34f7
simple fee hook (#6)
yorhodes Jan 19, 2024
94fed0f
Add mailbox unit tests for post dispatch (#7)
yorhodes Jan 19, 2024
ad51a0e
Merge branch 'hypmain' into injective-ts
yorhodes Jan 19, 2024
0cb0740
Upload new wasm
yorhodes Jan 19, 2024
0aee415
Remove owner from merkle instantiate
yorhodes Jan 19, 2024
aef5195
Deploy core to testnet
yorhodes Jan 19, 2024
97bf0f5
Support pausable ISM deploys
yorhodes Jan 22, 2024
ed6b3cc
Deploy core to injective mainnet
yorhodes Jan 24, 2024
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
25 changes: 18 additions & 7 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ name: test

on:
pull_request:
branches:
- "main"
push:
branches:
- "main"
Expand All @@ -30,12 +28,25 @@ jobs:
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}

- name: Install Rust
run: rustup update stable
run: rustup update 1.72

- name: Install target
run: rustup target add wasm32-unknown-unknown
- name: Install rename
run: sudo apt-get install -y rename

- run: cargo test --workspace --exclude hpl-tests
- name: Install rust deps
run: make install

- name: Run tests
run: cargo test --workspace --exclude hpl-tests

- name: Build wasm
run: make ci-build

- name: Upload wasm archive
uses: actions/upload-artifact@v2
with:
name: wasm_codes
path: wasm_codes.zip

coverage:
runs-on: ubuntu-latest
Expand All @@ -54,7 +65,7 @@ jobs:
uses: taiki-e/install-action@cargo-llvm-cov

- name: Generate code coverage
run: cargo llvm-cov --all-features --workspace --exclude hpl-tests --codecov --output-path codecov.json
run: cargo llvm-cov --workspace --exclude hpl-tests --codecov --output-path codecov.json

- name: Upload to codecov.io
uses: codecov/codecov-action@v3
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ hpl-mailbox = { path = "./contracts/core/mailbox" }
hpl-validator-announce = { path = "./contracts/core/va" }

hpl-hook-merkle = { path = "./contracts/hooks/merkle" }
hpl-hook-fee = { path = "./contracts/hooks/fee" }
hpl-hook-pausable = { path = "./contracts/hooks/pausable" }
hpl-hook-routing = { path = "./contracts/hooks/routing" }
hpl-hook-routing-custom = { path = "./contracts/hooks/routing-custom" }
Expand Down
14 changes: 10 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@

clean:
@cargo clean
@rm -rf ./artifacts

install:
cargo install --force cw-optimizoor cosmwasm-check beaker
rustup target add wasm32-unknown-unknown

schema:
ls ./contracts | xargs -n 1 -t beaker wasm ts-gen

build:
cargo build
cargo wasm

build-dev: clean
cargo cw-optimizoor
rename --force 's/(.*)-(.*)\.wasm/$$1\.wasm/d' artifacts/*

check: build-dev
check: build
ls -d ./artifacts/*.wasm | xargs -I x cosmwasm-check x

ci-build: check
zip -jr wasm_codes.zip artifacts
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ grcov . -s . --binary-path ./target/debug/ -t lcov --branch --ignore-not-existin

- [aggregate ism](./contracts/isms/aggregate)

- [pausable](./contracts/isms/pausable)

- For testing: [mock ism](./contracts/mocks/mock-ism)

5. Set deployed hooks and isms to Mailbox
Expand Down
8 changes: 7 additions & 1 deletion contracts/core/mailbox/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use cosmwasm_std::StdError;
use cosmwasm_std::{Coin, StdError};
use thiserror::Error;

#[derive(Error, Debug, PartialEq)]
Expand All @@ -9,6 +9,12 @@ pub enum ContractError {
#[error("{0}")]
Payment(#[from] cw_utils::PaymentError),

#[error("insufficient hook payment: wanted {wanted:?}, received {received:?}")]
HookPayment {
wanted: Vec<Coin>,
received: Vec<Coin>,
},

#[error("{0}")]
CoinsError(#[from] cosmwasm_std::CoinsError),

Expand Down
199 changes: 139 additions & 60 deletions contracts/core/mailbox/src/execute.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use cosmwasm_std::{
ensure, ensure_eq, to_json_binary, wasm_execute, BankMsg, Coins, DepsMut, Env, HexBinary,
MessageInfo, Response, StdResult,
ensure, ensure_eq, to_json_binary, wasm_execute, Coin, Coins, DepsMut, Env, HexBinary,
MessageInfo, Response
};
use hpl_interface::{
core::{
mailbox::{DispatchMsg, DispatchResponse},
HandleMsg,
},
hook::{self, post_dispatch},
hook::{post_dispatch, quote_dispatch},
ism,
types::Message,
};
Expand Down Expand Up @@ -107,74 +107,50 @@ pub fn dispatch(
}
);

// calculate gas
let default_hook = config.get_default_hook();
let required_hook = config.get_required_hook();

// build hyperlane message
let msg =
dispatch_msg
.clone()
.to_msg(MAILBOX_VERSION, nonce, config.local_domain, &info.sender)?;
let msg_id = msg.id();
let metadata = dispatch_msg.clone().metadata.unwrap_or_default();
let hook = dispatch_msg.get_hook_addr(deps.api, config.get_default_hook())?;

let base_fee = hook::quote_dispatch(
&deps.querier,
dispatch_msg.get_hook_addr(deps.api, default_hook)?,
dispatch_msg.metadata.clone().unwrap_or_default(),
msg.clone(),
)?
.fees;

let required_fee = hook::quote_dispatch(
&deps.querier,
&required_hook,
dispatch_msg.metadata.clone().unwrap_or_default(),
msg.clone(),
)?
.fees;

// assert gas received is satisfies required gas
let mut total_fee = required_fee.clone().into_iter().try_fold(
Coins::try_from(base_fee.clone())?,
|mut acc, fee| {
acc.add(fee)?;
StdResult::Ok(acc)
},
)?;
for fund in info.funds {
total_fee.sub(fund)?;
// assert gas received satisfies required gas
let required_hook = config.get_required_hook();
let required_hook_fees: Vec<Coin> =
quote_dispatch(&deps.querier, &required_hook, metadata.clone(), msg.clone())?.fees;

let mut funds = Coins::try_from(info.funds.clone())?;
for coin in required_hook_fees.iter() {
if let Err(_) = funds.sub(coin.clone()) {
return Err(ContractError::HookPayment {
wanted: required_hook_fees,
received: info.funds,
});
}
}

// interaction
let hook = dispatch_msg.get_hook_addr(deps.api, config.get_default_hook())?;
let hook_metadata = dispatch_msg.metadata.unwrap_or_default();

// effects
// commit to message
let msg_id = msg.id();
NONCE.save(deps.storage, &(nonce + 1))?;
LATEST_DISPATCHED_ID.save(deps.storage, &msg_id.to_vec())?;

// make message
// build post dispatch calls
let post_dispatch_msgs = vec![
post_dispatch(
required_hook,
hook_metadata.clone(),
metadata.clone(),
msg.clone(),
Some(required_fee),
Some(required_hook_fees),
)?,
post_dispatch(hook, hook_metadata, msg.clone(), Some(base_fee))?,
post_dispatch(hook, metadata, msg.clone(), Some(funds.to_vec()))?,
];

let refund_msg = BankMsg::Send {
to_address: info.sender.to_string(),
amount: total_fee.to_vec(),
};

Ok(Response::new()
.add_event(emit_dispatch_id(msg_id.clone()))
.add_event(emit_dispatch(msg))
.set_data(to_json_binary(&DispatchResponse { message_id: msg_id })?)
.add_messages(post_dispatch_msgs)
.add_message(refund_msg))
.add_messages(post_dispatch_msgs))
}

pub fn process(
Expand Down Expand Up @@ -251,15 +227,18 @@ pub fn process(

#[cfg(test)]
mod tests {
use std::collections::HashMap;

use cosmwasm_std::{
coin, from_json,
testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage},
to_json_binary, Addr, ContractResult, OwnedDeps, QuerierResult, SystemResult, WasmQuery,
to_json_binary, Addr, ContractResult, CosmosMsg, OwnedDeps, QuerierResult,
SystemResult, WasmMsg, WasmQuery,
};

use hpl_interface::{
core::mailbox::InstantiateMsg,
hook::{ExpectedHookQueryMsg, HookQueryMsg, QuoteDispatchResponse},
hook::{ExpectedHookQueryMsg, HookQueryMsg, PostDispatchMsg, QuoteDispatchResponse},
ism::IsmQueryMsg,
types::bech32_encode,
};
Expand All @@ -278,8 +257,11 @@ mod tests {

type TestDeps = OwnedDeps<MockStorage, MockApi, MockQuerier>;

fn mock_query_handler(req: &WasmQuery) -> QuerierResult {
let (req, _addr) = match req {
fn mock_query_handler(
req: &WasmQuery,
addr_fees: &Option<HashMap<String, Vec<Coin>>>,
) -> QuerierResult {
let (req, addr) = match req {
WasmQuery::Smart { msg, contract_addr } => (from_json(msg).unwrap(), contract_addr),
_ => unreachable!("wrong query type"),
};
Expand All @@ -289,17 +271,18 @@ mod tests {
_ => unreachable!("wrong query type"),
};

let mut fees = Coins::default();
let mut fees = match addr_fees {
Some(fees) => fees.get(addr).unwrap_or(&vec![]).clone(),
None => vec![],
};

if !req.metadata.is_empty() {
let parsed_fee = u32::from_be_bytes(req.metadata.as_slice().try_into().unwrap());

fees = Coins::from(coin(parsed_fee as u128, "utest"));
fees = vec![coin(parsed_fee as u128, "utest")];
}

let res = QuoteDispatchResponse {
fees: fees.into_vec(),
};
let res = QuoteDispatchResponse { fees };
let res = to_json_binary(&res).unwrap();

SystemResult::Ok(ContractResult::Ok(res))
Expand Down Expand Up @@ -422,7 +405,7 @@ mod tests {

let mut deps = mock_dependencies();

deps.querier.update_wasm(mock_query_handler);
deps.querier.update_wasm(|q| mock_query_handler(q, &None));

instantiate(
deps.as_mut(),
Expand Down Expand Up @@ -468,6 +451,102 @@ mod tests {
);
}

#[rstest]
#[case(vec![coin(100, "usd")], vec![coin(100, "usd")])]
#[should_panic]
#[case(vec![coin(100, "usd")], vec![coin(50, "usd")])]
#[should_panic]
#[case(vec![coin(100, "usdt")], vec![coin(100, "usd")])]
#[case(vec![coin(50, "usd")], vec![coin(100, "usd")])]
fn test_post_dispatch(#[case] required_hook_fees: Vec<Coin>, #[case] funds: Vec<Coin>) {
let mut deps = mock_dependencies();

let mut hook_fees = HashMap::new();
hook_fees.insert("required_hook".into(), required_hook_fees.clone());

// not enforced by mailbox
// hook_fees.insert("default_hook".into(), default_hook_fees);

let opt = Some(hook_fees);

deps.querier
.update_wasm(move |q| mock_query_handler(q, &opt));

let hrp = "osmo";

instantiate(
deps.as_mut(),
mock_env(),
mock_info(OWNER, &[]),
InstantiateMsg {
hrp: "osmo".to_string(),
owner: OWNER.to_string(),
domain: LOCAL_DOMAIN,
},
)
.unwrap();

set_default_hook(deps.as_mut(), mock_info(OWNER, &[]), "default_hook".into()).unwrap();
set_required_hook(deps.as_mut(), mock_info(OWNER, &[]), "required_hook".into()).unwrap();

let dispatch_msg = DispatchMsg::new(DEST_DOMAIN, gen_bz(32), gen_bz(123));

let sender = bech32_encode(hrp, gen_bz(32).as_slice()).unwrap();

let msg = dispatch_msg
.clone()
.to_msg(
MAILBOX_VERSION,
NONCE.load(deps.as_ref().storage).unwrap(),
LOCAL_DOMAIN,
&sender,
)
.unwrap();

let post_dispatch_msg = to_json_binary(
&PostDispatchMsg {
metadata: HexBinary::default(),
message: msg.into(),
}
.wrap(), // not sure why I need this
)
.unwrap();

let res = dispatch(
deps.as_mut(),
mock_info(sender.as_str(), &funds),
dispatch_msg.clone(),
)
.map_err(|e| e.to_string())
.unwrap();

let msgs: Vec<_> = res.messages.into_iter().map(|v| v.msg).collect();

assert_eq!(
msgs[0],
CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: "required_hook".to_string(),
msg: post_dispatch_msg.clone(),
funds: required_hook_fees.clone()
},)
);

// subtract required_hook_fees from funds
let mut remaining_funds = Coins::try_from(funds).unwrap();
for coin in required_hook_fees {
remaining_funds.sub(coin).unwrap();
}

assert_eq!(
msgs[1],
CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: "default_hook".to_string(),
msg: post_dispatch_msg,
funds: remaining_funds.into_vec() // forward all remaining funds
})
);
}

fn test_process_query_handler(query: &WasmQuery) -> QuerierResult {
match query {
WasmQuery::Smart { contract_addr, msg } => {
Expand Down
Loading