Skip to content

Commit 8e3de80

Browse files
authored
feat(axelarnet-gateway): move CallContractWithToken from the nexus gateway to axelarnet gateway (#624)
1 parent 52ba749 commit 8e3de80

30 files changed

+661
-261
lines changed

Cargo.lock

+4-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ assert_ok = "1.0"
1919
axelar-wasm-std = { version = "^1.0.0", path = "packages/axelar-wasm-std" }
2020
axelar-wasm-std-derive = { version = "^1.0.0", path = "packages/axelar-wasm-std-derive" }
2121
axelarnet-gateway = { version = "^1.0.0", path = "contracts/axelarnet-gateway" }
22+
nexus-gateway = { version = "^1.0.0", path = "contracts/nexus-gateway" }
2223
bcs = "0.1.5"
2324
client = { version = "^1.0.0", path = "packages/client" }
2425
coordinator = { version = "^1.0.0", path = "contracts/coordinator" }

contracts/axelarnet-gateway/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ cw2 = { workspace = true }
4141
error-stack = { workspace = true }
4242
itertools = { workspace = true }
4343
msgs-derive = { workspace = true }
44+
nexus-gateway = { workspace = true, features = ["library"] }
4445
report = { workspace = true }
4546
router-api = { workspace = true }
4647
serde = { workspace = true }

contracts/axelarnet-gateway/src/clients/gateway.rs

+1
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ mod test {
154154
let msg = InstantiateMsg {
155155
chain_name: "source-chain".parse().unwrap(),
156156
router_address: "router".to_string(),
157+
nexus_gateway: "nexus-gateway".to_string(),
157158
};
158159

159160
instantiate(deps, env, info, msg.clone()).unwrap();

contracts/axelarnet-gateway/src/contract.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ pub fn instantiate(
5555
let config = Config {
5656
chain_name: msg.chain_name,
5757
router: address::validate_cosmwasm_address(deps.api, &msg.router_address)?,
58+
nexus_gateway: address::validate_cosmwasm_address(deps.api, &msg.nexus_gateway)?,
5859
};
5960

6061
state::save_config(deps.storage, &config)?;
@@ -76,7 +77,7 @@ pub fn execute(
7677
} => execute::call_contract(
7778
deps.storage,
7879
deps.querier,
79-
info.sender,
80+
info,
8081
execute::CallContractData {
8182
destination_chain,
8283
destination_address,

contracts/axelarnet-gateway/src/contract/execute.rs

+36-15
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ use std::str::FromStr;
22

33
use axelar_core_std::nexus;
44
use axelar_wasm_std::msg_id::HexTxHashAndEventIndex;
5+
use axelar_wasm_std::token::GetToken;
56
use axelar_wasm_std::{address, FnExt, IntoContractError};
67
use cosmwasm_schema::cw_serde;
7-
use cosmwasm_std::{Addr, DepsMut, HexBinary, QuerierWrapper, Response, Storage};
8+
use cosmwasm_std::{
9+
to_json_binary, Addr, DepsMut, HexBinary, MessageInfo, QuerierWrapper, Response, Storage,
10+
WasmMsg,
11+
};
812
use error_stack::{bail, ensure, report, Result, ResultExt};
913
use itertools::Itertools;
1014
use router_api::client::Router;
@@ -47,6 +51,8 @@ pub enum Error {
4751
Nexus,
4852
#[error("nonce from the nexus module overflowed u32")]
4953
NonceOverflow,
54+
#[error("invalid token received")]
55+
InvalidToken,
5056
}
5157

5258
#[cw_serde]
@@ -71,10 +77,14 @@ impl CallContractData {
7177
pub fn call_contract(
7278
storage: &mut dyn Storage,
7379
querier: QuerierWrapper,
74-
sender: Addr,
80+
info: MessageInfo,
7581
call_contract: CallContractData,
7682
) -> Result<Response, Error> {
77-
let Config { router, chain_name } = state::load_config(storage);
83+
let Config {
84+
router,
85+
chain_name,
86+
nexus_gateway,
87+
} = state::load_config(storage);
7888

7989
let client: nexus::Client = client::CosmosClient::new(querier).into();
8090
let nexus::query::TxHashAndNonceResponse { tx_hash, nonce } =
@@ -88,31 +98,42 @@ pub fn call_contract(
8898
),
8999
)
90100
.change_context(Error::InvalidCrossChainId)?;
91-
let source_address = Address::from_str(sender.as_str())
92-
.change_context(Error::InvalidSourceAddress(sender.clone()))?;
101+
let source_address = Address::from_str(info.sender.as_str())
102+
.change_context(Error::InvalidSourceAddress(info.sender.clone()))?;
93103
let msg = call_contract.to_message(id, source_address);
94104

95105
state::save_unique_routable_msg(storage, &msg.cc_id, &msg)
96106
.inspect_err(|err| panic_if_already_exists(err, &msg.cc_id))
97107
.change_context(Error::SaveRoutableMessage)?;
98108

99-
Ok(
100-
route_to_router(storage, &Router::new(router), vec![msg.clone()])?.add_event(
101-
AxelarnetGatewayEvent::ContractCalled {
102-
msg,
103-
payload: call_contract.payload,
104-
}
105-
.into(),
106-
),
107-
)
109+
let token = info.single_token().change_context(Error::InvalidToken)?;
110+
let event = AxelarnetGatewayEvent::ContractCalled {
111+
msg: msg.clone(),
112+
payload: call_contract.payload,
113+
token: token.clone(),
114+
};
115+
let res = match token {
116+
None => route_to_router(storage, &Router::new(router), vec![msg])?,
117+
Some(token) => Response::new().add_message(WasmMsg::Execute {
118+
contract_addr: nexus_gateway.to_string(),
119+
msg: to_json_binary(&nexus_gateway::msg::ExecuteMsg::RouteMessageWithToken(msg))
120+
.expect("failed to serialize route message with token"),
121+
funds: vec![token],
122+
}),
123+
}
124+
.add_event(event.into());
125+
126+
Ok(res)
108127
}
109128

110129
pub fn route_messages(
111130
storage: &mut dyn Storage,
112131
sender: Addr,
113132
msgs: Vec<Message>,
114133
) -> Result<Response, Error> {
115-
let Config { chain_name, router } = state::load_config(storage);
134+
let Config {
135+
chain_name, router, ..
136+
} = state::load_config(storage);
116137
let router = Router::new(router);
117138

118139
if sender == router.address {

contracts/axelarnet-gateway/src/events.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
use cosmwasm_std::{Attribute, Event, HexBinary};
1+
use axelar_wasm_std::event::EventExt;
2+
use cosmwasm_std::{Attribute, Coin, Event, HexBinary};
23
use router_api::Message;
34

45
pub enum AxelarnetGatewayEvent {
56
ContractCalled {
67
msg: Message,
78
payload: HexBinary,
9+
token: Option<Coin>,
810
},
911
/// Uses the same event name as `GatewayEvent` for consistency
1012
Routing {
@@ -18,10 +20,13 @@ pub enum AxelarnetGatewayEvent {
1820
impl From<AxelarnetGatewayEvent> for Event {
1921
fn from(other: AxelarnetGatewayEvent) -> Self {
2022
match other {
21-
AxelarnetGatewayEvent::ContractCalled { msg, payload } => {
22-
make_message_event("contract_called", msg)
23-
.add_attributes(vec![("payload", payload.to_string())])
24-
}
23+
AxelarnetGatewayEvent::ContractCalled {
24+
msg,
25+
payload,
26+
token,
27+
} => make_message_event("contract_called", msg)
28+
.add_attribute("payload", payload.to_string())
29+
.add_attribute_if_some("token", token.map(|token| token.to_string())),
2530
AxelarnetGatewayEvent::Routing { msg } => make_message_event("routing", msg),
2631
AxelarnetGatewayEvent::MessageExecuted { msg } => {
2732
make_message_event("message_executed", msg)

contracts/axelarnet-gateway/src/msg.rs

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ pub struct InstantiateMsg {
1111
pub chain_name: ChainName,
1212
/// Address of the router contract on axelar.
1313
pub router_address: String,
14+
/// Address of the nexus gateway contract on axelar.
15+
pub nexus_gateway: String,
1416
}
1517

1618
#[cw_serde]

contracts/axelarnet-gateway/src/state.rs

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ pub enum Error {
3333
pub struct Config {
3434
pub chain_name: ChainName,
3535
pub router: Addr,
36+
pub nexus_gateway: Addr,
3637
}
3738

3839
#[cw_serde]

contracts/axelarnet-gateway/tests/execute.rs

+36-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use axelarnet_gateway::StateError;
88
use cosmwasm_std::testing::{
99
mock_dependencies, mock_env, mock_info, MockQuerierCustomHandlerResult,
1010
};
11-
use cosmwasm_std::{ContractResult, DepsMut, HexBinary, Response, SystemResult};
11+
use cosmwasm_std::{Coin, ContractResult, DepsMut, HexBinary, Response, SystemResult};
1212
use rand::RngCore;
1313
use router_api::msg::ExecuteMsg as RouterExecuteMsg;
1414
use router_api::{CrossChainId, Message};
@@ -183,6 +183,7 @@ fn route_to_router_after_contract_call_with_tempered_data_fails() {
183183
utils::instantiate_contract(deps.as_mut()).unwrap();
184184
let response = utils::call_contract(
185185
deps.as_mut(),
186+
mock_info("sender", &[]),
186187
destination_chain,
187188
destination_address,
188189
payload,
@@ -222,6 +223,7 @@ fn route_to_router_after_contract_call_succeeds_multiple_times() {
222223
utils::instantiate_contract(deps.as_mut()).unwrap();
223224
let response = utils::call_contract(
224225
deps.as_mut(),
226+
mock_info("sender", &[]),
225227
destination_chain,
226228
destination_address,
227229
payload,
@@ -260,6 +262,7 @@ fn route_to_router_after_contract_call_ignores_duplicates() {
260262
utils::instantiate_contract(deps.as_mut()).unwrap();
261263
let response = utils::call_contract(
262264
deps.as_mut(),
265+
mock_info("sender", &[]),
263266
destination_chain,
264267
destination_address,
265268
payload,
@@ -301,6 +304,7 @@ fn contract_call_returns_correct_message() {
301304

302305
let response = assert_ok!(utils::call_contract(
303306
deps.as_mut(),
307+
mock_info("sender", &[]),
304308
destination_chain,
305309
destination_address,
306310
payload,
@@ -309,6 +313,36 @@ fn contract_call_returns_correct_message() {
309313
goldie::assert_json!(msg)
310314
}
311315

316+
#[test]
317+
fn contract_call_with_token_returns_correct_message() {
318+
let tx_hash: [u8; 32] = [2; 32];
319+
let nonce = 99;
320+
let token = Coin::new(10, "axelar");
321+
322+
let mut deps = mock_dependencies();
323+
deps.querier = deps
324+
.querier
325+
.with_custom_handler(reply_with_tx_hash_and_nonce(tx_hash, nonce));
326+
327+
let destination_chain = "destination-chain".parse().unwrap();
328+
let destination_address = "destination-address".parse().unwrap();
329+
let payload = vec![1, 2, 3].into();
330+
331+
utils::instantiate_contract(deps.as_mut()).unwrap();
332+
333+
let response = assert_ok!(utils::call_contract(
334+
deps.as_mut(),
335+
mock_info("sender", &[token]),
336+
destination_chain,
337+
destination_address,
338+
payload,
339+
));
340+
let msg = assert_ok!(inspect_response_msg::<nexus_gateway::msg::ExecuteMsg>(
341+
response
342+
));
343+
goldie::assert_json!(msg)
344+
}
345+
312346
#[test]
313347
fn contract_call_returns_correct_events() {
314348
let tx_hash: [u8; 32] =
@@ -330,6 +364,7 @@ fn contract_call_returns_correct_events() {
330364
utils::instantiate_contract(deps.as_mut()).unwrap();
331365
let response = assert_ok!(utils::call_contract(
332366
deps.as_mut(),
367+
mock_info("sender", &[]),
333368
destination_chain,
334369
destination_address,
335370
payload,

contracts/axelarnet-gateway/tests/instantiate.rs

+18
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,24 @@ fn invalid_router_address() {
2121
let msg = InstantiateMsg {
2222
chain_name: params::AXELARNET.parse().unwrap(),
2323
router_address: "".to_string(),
24+
nexus_gateway: params::NEXUS_GATEWAY.parse().unwrap(),
25+
};
26+
27+
assert_err_contains!(
28+
contract::instantiate(deps.as_mut(), mock_env(), mock_info("sender", &[]), msg),
29+
axelar_wasm_std::address::Error,
30+
axelar_wasm_std::address::Error::InvalidAddress(..),
31+
);
32+
}
33+
34+
#[test]
35+
fn invalid_nexus_gateway_address() {
36+
let mut deps = mock_dependencies();
37+
38+
let msg = InstantiateMsg {
39+
chain_name: params::AXELARNET.parse().unwrap(),
40+
router_address: params::ROUTER.parse().unwrap(),
41+
nexus_gateway: "".to_string(),
2442
};
2543

2644
assert_err_contains!(

contracts/axelarnet-gateway/tests/query.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use axelar_wasm_std::response::inspect_response_msg;
33
use axelarnet_gateway::msg::QueryMsg;
44
use axelarnet_gateway::{contract, ExecutableMessage};
55
use cosmwasm_std::testing::{
6-
mock_dependencies, mock_env, MockApi, MockQuerier, MockQuerierCustomHandlerResult, MockStorage,
6+
mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockQuerierCustomHandlerResult,
7+
MockStorage,
78
};
89
use cosmwasm_std::{from_json, ContractResult, Deps, OwnedDeps, SystemResult};
910
use rand::RngCore;
@@ -90,6 +91,7 @@ fn populate_routable_messages(
9091
.map(|i| {
9192
let response = utils::call_contract(
9293
deps.as_mut(),
94+
mock_info("sender", &[]),
9395
format!("destination-chain-{}", i).parse().unwrap(),
9496
format!("destination-address-{}", i).parse().unwrap(),
9597
vec![i].into(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"route_message_with_token": {
3+
"cc_id": {
4+
"source_chain": "axelarnet",
5+
"message_id": "0x0202020202020202020202020202020202020202020202020202020202020202-99"
6+
},
7+
"source_address": "sender",
8+
"destination_chain": "destination-chain",
9+
"destination_address": "destination-address",
10+
"payload_hash": "f1885eda54b7a053318cd41e2093220dab15d65381b1157a3633a83bfd5c9239"
11+
}
12+
}

contracts/axelarnet-gateway/tests/utils/execute.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use axelarnet_gateway::msg::ExecuteMsg as GatewayExecuteMsg;
33
use axelarnet_gateway::{contract, AxelarExecutableMsg};
44
use cosmwasm_schema::cw_serde;
55
use cosmwasm_std::testing::{mock_env, mock_info};
6-
use cosmwasm_std::{DepsMut, HexBinary, Response};
6+
use cosmwasm_std::{DepsMut, HexBinary, MessageInfo, Response};
77
use router_api::{Address, ChainName, CrossChainId, Message};
88

99
use crate::utils::params;
@@ -16,14 +16,15 @@ pub enum ExecuteMsg {
1616

1717
pub fn call_contract(
1818
deps: DepsMut,
19+
info: MessageInfo,
1920
destination_chain: ChainName,
2021
destination_address: Address,
2122
payload: HexBinary,
2223
) -> Result<Response, ContractError> {
2324
contract::execute(
2425
deps,
2526
mock_env(),
26-
mock_info("sender", &[]),
27+
info,
2728
GatewayExecuteMsg::CallContract {
2829
destination_chain,
2930
destination_address,

contracts/axelarnet-gateway/tests/utils/instantiate.rs

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub fn instantiate_contract(deps: DepsMut) -> Result<Response, ContractError> {
1414
InstantiateMsg {
1515
chain_name: params::AXELARNET.parse().unwrap(),
1616
router_address: params::ROUTER.parse().unwrap(),
17+
nexus_gateway: params::NEXUS_GATEWAY.parse().unwrap(),
1718
},
1819
)
1920
}

0 commit comments

Comments
 (0)