Skip to content

Commit

Permalink
feat(nexus-gateway)!: call contract with token (#599)
Browse files Browse the repository at this point in the history
  • Loading branch information
fish-sammy authored Aug 28, 2024
1 parent 490f6f2 commit 8308c8d
Show file tree
Hide file tree
Showing 13 changed files with 249 additions and 35 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 38 additions & 4 deletions contracts/axelarnet-gateway/src/client.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
use axelar_wasm_std::vec::VecExt;
use cosmwasm_std::{HexBinary, WasmMsg};
use cosmwasm_std::{Addr, HexBinary, WasmMsg};
use error_stack::{Result, ResultExt};
use router_api::{Address, ChainName, CrossChainId, Message};

use crate::msg::{ExecuteMsg, QueryMsg};

#[derive(thiserror::Error, Debug, PartialEq)]
pub enum Error {
#[error("failed to query the chain name at gateway contract {0}")]
QueryChainName(Addr),
}

impl<'a> From<client::Client<'a, ExecuteMsg, QueryMsg>> for Client<'a> {
fn from(client: client::Client<'a, ExecuteMsg, QueryMsg>) -> Self {
Client { client }
Expand Down Expand Up @@ -36,17 +43,37 @@ impl<'a> Client<'a> {
msgs.to_none_if_empty()
.map(|messages| self.client.execute(&ExecuteMsg::RouteMessages(messages)))
}

pub fn chain_name(&self) -> Result<ChainName, Error> {
self.client
.query(&QueryMsg::ChainName)
.change_context_lazy(|| Error::QueryChainName(self.client.address.clone()))
}
}

#[cfg(test)]
mod test {
use std::str::FromStr;

use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockQuerier};
use cosmwasm_std::{to_json_binary, Addr, DepsMut, QuerierWrapper};
use cosmwasm_std::{from_json, to_json_binary, Addr, DepsMut, QuerierWrapper, WasmQuery};

use super::*;
use crate::contract::instantiate;
use crate::contract::{instantiate, query};
use crate::msg::InstantiateMsg;

#[test]
fn chain_name() {
let (querier, _, addr) = setup();
let client: Client =
client::Client::new(QuerierWrapper::new(&querier), addr.clone()).into();

assert_eq!(
client.chain_name().unwrap(),
ChainName::from_str("source-chain").unwrap()
);
}

#[test]
fn call_contract() {
let (querier, _, addr) = setup();
Expand Down Expand Up @@ -104,7 +131,14 @@ mod test {
let mut deps = mock_dependencies();
let instantiate_msg = instantiate_contract(deps.as_mut());

let querier = MockQuerier::default();
let mut querier = MockQuerier::default();
querier.update_wasm(move |msg| match msg {
WasmQuery::Smart { contract_addr, msg } if contract_addr == addr => {
let msg = from_json::<QueryMsg>(msg).unwrap();
Ok(query(deps.as_ref(), mock_env(), msg).into()).into()
}
_ => panic!("unexpected query: {:?}", msg),
});

(querier, instantiate_msg, Addr::unchecked(addr))
}
Expand Down
1 change: 1 addition & 0 deletions contracts/axelarnet-gateway/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ pub fn query(
QueryMsg::ReceivedMessages { cc_ids } => {
to_json_binary(&query::received_messages(deps, cc_ids)?)
}
QueryMsg::ChainName => to_json_binary(&state::load_config(deps.storage)?.chain_name),
}
.map_err(axelar_wasm_std::error::ContractError::from)
}
Expand Down
2 changes: 1 addition & 1 deletion contracts/axelarnet-gateway/src/contract/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub fn call_contract(

let msg = Message {
cc_id: cc_id.clone(),
source_address: Address::try_from(sender.clone().into_string())
source_address: Address::try_from(sender.into_string())
.expect("failed to convert sender address"),
destination_chain,
destination_address,
Expand Down
2 changes: 1 addition & 1 deletion contracts/axelarnet-gateway/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pub mod msg;
mod state;

mod client;
pub use client::Client;
pub use client::{Client, Error};

mod executable;
pub use executable::AxelarExecutableMsg;
4 changes: 4 additions & 0 deletions contracts/axelarnet-gateway/src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,8 @@ pub enum QueryMsg {
/// Returns the received messages with their status for the given cross-chain ids.
#[returns(Vec<MessageWithStatus>)]
ReceivedMessages { cc_ids: Vec<CrossChainId> },

/// Returns the chain name for this gateway.
#[returns(ChainName)]
ChainName,
}
3 changes: 3 additions & 0 deletions contracts/nexus-gateway/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ library = []

[dependencies]
axelar-wasm-std = { workspace = true, features = ["derive"] }
axelarnet-gateway = { workspace = true, features = ["library"] }
client = { workspace = true }
cosmwasm-schema = { workspace = true }
cosmwasm-std = { workspace = true }
cw-storage-plus = { workspace = true }
Expand All @@ -29,6 +31,7 @@ report = { workspace = true }
router-api = { workspace = true }
schemars = "0.8.15"
serde = { version = "1.0.188", features = ["derive"] }
sha3 = { workspace = true }
thiserror = { workspace = true }
voting-verifier = { workspace = true, features = ["library"] }

Expand Down
122 changes: 110 additions & 12 deletions contracts/nexus-gateway/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ use axelar_wasm_std::address;
#[cfg(not(feature = "library"))]
use cosmwasm_std::entry_point;
use cosmwasm_std::{Addr, DepsMut, Empty, Env, MessageInfo, Response, Storage};
use error_stack::{Report, ResultExt};
use error_stack::Report;

use crate::contract::execute::{route_to_nexus, route_to_router};
use crate::contract::execute::{call_contract_with_token, route_to_nexus, route_to_router};
use crate::error::ContractError;
use crate::msg::{ExecuteMsg, InstantiateMsg};
use crate::state::Config;
Expand Down Expand Up @@ -39,26 +39,43 @@ pub fn instantiate(

let nexus = address::validate_cosmwasm_address(deps.api, &msg.nexus)?;
let router = address::validate_cosmwasm_address(deps.api, &msg.router)?;

state::save_config(deps.storage, Config { nexus, router }).expect("config must be saved");
let axelar_gateway = address::validate_cosmwasm_address(deps.api, &msg.axelar_gateway)?;

state::save_config(
deps.storage,
Config {
nexus,
router,
axelar_gateway,
},
)
.expect("config must be saved");

Ok(Response::default())
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
deps: DepsMut,
_env: Env,
_: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> Result<Response<nexus::Message>, axelar_wasm_std::error::ContractError> {
let res = match msg.ensure_permissions(deps.storage, &info.sender, match_router, match_nexus)? {
ExecuteMsg::RouteMessages(msgs) => {
route_to_nexus(deps.storage, msgs).change_context(ContractError::RouteToNexus)?
}
ExecuteMsg::RouteMessagesFromNexus(msgs) => {
route_to_router(deps.storage, msgs).change_context(ContractError::RouteToRouter)?
}
ExecuteMsg::CallContractWithToken {
destination_chain,
destination_address,
payload,
} => call_contract_with_token(
deps.storage,
deps.querier,
info,
destination_chain,
destination_address,
payload,
)?,
ExecuteMsg::RouteMessages(msgs) => route_to_nexus(deps.storage, msgs)?,
ExecuteMsg::RouteMessagesFromNexus(msgs) => route_to_router(deps.storage, msgs)?,
};

Ok(res)
Expand All @@ -77,14 +94,18 @@ mod tests {
use axelar_wasm_std::msg_id::HexTxHashAndEventIndex;
use axelar_wasm_std::{err_contains, permission_control};
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{from_json, Addr, CosmosMsg, WasmMsg};
use cosmwasm_std::{
from_json, to_json_binary, Addr, BankMsg, Coin, CosmosMsg, QuerierResult, SubMsg, WasmMsg,
WasmQuery,
};
use hex::decode;
use router_api::CrossChainId;

use super::*;

const NEXUS: &str = "nexus";
const ROUTER: &str = "router";
const AXELAR_GATEWAY: &str = "axelar_gateway";

#[test]
fn migrate_sets_contract_version() {
Expand All @@ -97,6 +118,80 @@ mod tests {
assert_eq!(contract_version.version, CONTRACT_VERSION);
}

#[test]
fn call_contract_with_token_no_token() {
let mut deps = mock_dependencies();
instantiate_contract(deps.as_mut());

let res = execute(
deps.as_mut(),
mock_env(),
mock_info(NEXUS, &[]),
ExecuteMsg::CallContractWithToken {
destination_chain: "destinationChain".parse().unwrap(),
destination_address: "0xD419".parse().unwrap(),
payload: decode("bb9b5566c2f4876863333e481f4698350154259ffe6226e283b16ce18a64bcf1")
.unwrap()
.into(),
},
);
assert!(res.is_err_and(|err| err_contains!(
err.report,
ContractError,
ContractError::InvalidToken { .. }
)));
}

#[test]
fn call_contract_with_token() {
let mut deps = mock_dependencies();
deps.querier.update_wasm(move |msg| match msg {
WasmQuery::Smart { contract_addr, msg } if contract_addr == AXELAR_GATEWAY => {
let msg = from_json::<axelarnet_gateway::msg::QueryMsg>(msg).unwrap();

match msg {
axelarnet_gateway::msg::QueryMsg::ChainName => {
QuerierResult::Ok(to_json_binary("axelarnet").into())
}
_ => panic!("unexpected query: {:?}", msg),
}
}
_ => panic!("unexpected query: {:?}", msg),
});
instantiate_contract(deps.as_mut());

let token = Coin {
denom: "denom".to_string(),
amount: 100u128.into(),
};
let res = execute(
deps.as_mut(),
mock_env(),
mock_info(NEXUS, &[token.clone()]),
ExecuteMsg::CallContractWithToken {
destination_chain: "destinationChain".parse().unwrap(),
destination_address: "0xD419".parse().unwrap(),
payload: decode("bb9b5566c2f4876863333e481f4698350154259ffe6226e283b16ce18a64bcf1")
.unwrap()
.into(),
},
);
assert!(res.is_ok_and(move |res| match res.messages.as_slice() {
[SubMsg {
msg: CosmosMsg::Bank(BankMsg::Send { to_address, amount }),
..
}, SubMsg {
msg:
CosmosMsg::Custom(nexus::Message {
token: Some(actual_token),
..
}),
..
}] => *actual_token == token && to_address == NEXUS && *amount == vec![token],
_ => false,
}));
}

#[test]
fn route_to_router_unauthorized() {
let mut deps = mock_dependencies();
Expand Down Expand Up @@ -252,6 +347,7 @@ mod tests {
source_tx_id: msg_ids[0].tx_hash.to_vec().try_into().unwrap(),
source_tx_index: msg_ids[0].event_index as u64,
id: msg_ids[0].to_string(),
token: None,
},
nexus::Message {
source_chain: "sourceChain".parse().unwrap(),
Expand All @@ -267,6 +363,7 @@ mod tests {
source_tx_id: msg_ids[1].tx_hash.to_vec().try_into().unwrap(),
source_tx_index: msg_ids[1].event_index as u64,
id: msg_ids[1].to_string(),
token: None,
},
];
msgs
Expand Down Expand Up @@ -316,6 +413,7 @@ mod tests {
InstantiateMsg {
nexus: NEXUS.to_string(),
router: ROUTER.to_string(),
axelar_gateway: AXELAR_GATEWAY.to_string(),
},
)
.unwrap();
Expand Down
Loading

0 comments on commit 8308c8d

Please sign in to comment.