Skip to content

Commit 9e367bb

Browse files
authored
feat: allow the contract UpdateMappingPair (#33)
* feat: allow the contract UpdateMappingPair * feat: fund minimum fee to tokenFactory * chore: fix testcase * chore: fix send RegisterDenom to the current contract * feat: split denom and prefix * feat: update for demo * feat: encode address by base58
1 parent cdf397d commit 9e367bb

File tree

6 files changed

+134
-31
lines changed

6 files changed

+134
-31
lines changed

Cargo.lock

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

contracts/cw-ics20-latest/Cargo.toml

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "cw-ics20-latest"
3-
version = "1.0.8"
3+
version = "1.0.9"
44
authors = ["Ethan Frey <[email protected]>, Oraichain Labs"]
55
edition = "2021"
66
description = "IBC Enabled contracts that receives CW20 tokens and sends them over ICS20 to a remote chain"
@@ -32,7 +32,8 @@ sha256 = "=1.1.0"
3232
skip = { workspace = true }
3333
tokenfactory = { workspace = true }
3434
token-bindings = { workspace = true }
35-
35+
bs58 = "0.5.1"
36+
hex = "0.4.3"
3637
[dev-dependencies]
3738
cosmwasm-vm = { workspace = true }
3839
# osmosis-test-tube = { workspace = true }

contracts/cw-ics20-latest/src/contract.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use cw2::set_contract_version;
99
use cw20::{Cw20ExecuteMsg, Cw20ReceiveMsg};
1010
use cw20_ics20_msg::converter::ConverterController;
1111
use cw20_ics20_msg::helper::parse_ibc_wasm_port_id;
12+
use cw_controllers::AdminError;
1213
use cw_storage_plus::Bound;
1314
use oraiswap::asset::AssetInfo;
1415
use oraiswap::router::RouterController;
@@ -165,7 +166,7 @@ pub fn execute(
165166
orai_receiver,
166167
args,
167168
} => ibc_hooks_receive(deps, env, info, func, orai_receiver, args),
168-
ExecuteMsg::RegisterDenom(msg) => register_denom(deps, info, msg),
169+
ExecuteMsg::RegisterDenom(msg) => register_denom(deps, env, info, msg),
169170
ExecuteMsg::WithdrawAsset { coin, receiver } => {
170171
execute_withdraw_asset(deps, info, coin, receiver)
171172
}
@@ -201,10 +202,13 @@ fn execute_withdraw_asset(
201202

202203
pub fn register_denom(
203204
deps: DepsMut,
205+
env: Env,
204206
info: MessageInfo,
205207
msg: RegisterDenomMsg,
206208
) -> Result<Response, ContractError> {
207-
ADMIN.assert_admin(deps.as_ref(), &info.sender)?;
209+
if !ADMIN.is_admin(deps.as_ref(), &info.sender)? && info.sender != env.contract.address {
210+
return Err(ContractError::Admin(AdminError::NotAdmin {}));
211+
};
208212

209213
let config = CONFIG.load(deps.storage)?;
210214

@@ -840,7 +844,7 @@ pub fn execute_delete_mapping_pair(
840844
}
841845

842846
#[entry_point]
843-
pub fn migrate(deps: DepsMut, _env: Env, msg: MigrateMsg) -> Result<Response, ContractError> {
847+
pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
844848
// we don't need to save anything if migrating from the same version
845849
set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
846850

contracts/cw-ics20-latest/src/ibc.rs

+52-13
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,33 @@ use std::ops::Mul;
22

33
use cosmwasm_schema::cw_serde;
44
use cosmwasm_std::{
5-
attr, entry_point, from_json, to_json_binary, wasm_execute, Api, Binary, CosmosMsg, Decimal,
6-
Deps, DepsMut, Env, Ibc3ChannelOpenResponse, IbcBasicResponse, IbcChannel, IbcChannelCloseMsg,
7-
IbcChannelConnectMsg, IbcChannelOpenMsg, IbcEndpoint, IbcMsg, IbcOrder, IbcPacket,
8-
IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg, IbcReceiveResponse, IbcTimeout,
9-
Order, QuerierWrapper, Reply, Response, StdError, StdResult, Storage, SubMsg, SubMsgResult,
10-
Uint128,
5+
attr, entry_point, from_json, to_json_binary, wasm_execute, Api, Binary, Coin, CosmosMsg,
6+
Decimal, Deps, DepsMut, Env, Ibc3ChannelOpenResponse, IbcBasicResponse, IbcChannel,
7+
IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg, IbcEndpoint, IbcMsg, IbcOrder,
8+
IbcPacket, IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg, IbcReceiveResponse,
9+
IbcTimeout, Order, QuerierWrapper, Reply, Response, StdError, StdResult, Storage, SubMsg,
10+
SubMsgResult, Uint128,
1111
};
1212

1313
use cw20_ics20_msg::helper::{
14-
denom_to_asset_info, get_prefix_decode_bech32, parse_asset_info_denom,
14+
denom_to_asset_info, get_full_denom, get_prefix_decode_bech32, parse_asset_info_denom,
1515
};
1616
use cw_storage_plus::Map;
1717
use oraiswap::asset::AssetInfo;
1818
use oraiswap::router::{RouterController, SwapOperation};
1919
use skip::entry_point::ExecuteMsg as EntryPointExecuteMsg;
20+
use token_bindings::Metadata;
2021

2122
use crate::contract::build_mint_mapping_msg;
2223
use crate::error::{ContractError, Never};
23-
use crate::msg::ExecuteMsg;
24+
use crate::msg::{ExecuteMsg, RegisterDenomMsg};
2425
use crate::state::{
2526
get_key_ics20_ibc_denom, ics20_denoms, undo_reduce_channel_balance, ALLOW_LIST, CHANNEL_INFO,
2627
CONFIG, RELAYER_FEE, TOKEN_FEE,
2728
};
2829
use cw20_ics20_msg::amount::{convert_remote_to_local, Amount};
2930
use cw20_ics20_msg::msg::FeeData;
30-
use cw20_ics20_msg::state::{ChannelInfo, Ratio};
31+
use cw20_ics20_msg::state::{ChannelInfo, MappingMetadata, Ratio};
3132

3233
pub const ICS20_VERSION: &str = "ics20-1";
3334
pub const ICS20_ORDERING: IbcOrder = IbcOrder::Unordered;
@@ -329,7 +330,7 @@ fn handle_ibc_packet_receive_native_remote_chain(
329330
let config = CONFIG.load(storage)?;
330331
let mut cosmos_msgs: Vec<CosmosMsg> = vec![];
331332
let ibc_packet_amount = msg.amount.to_string();
332-
let attributes = vec![
333+
let attributes: Vec<(&str, &str)> = vec![
333334
("action", "receive_native"),
334335
("sender", &msg.sender),
335336
("receiver", &msg.receiver),
@@ -341,9 +342,47 @@ fn handle_ibc_packet_receive_native_remote_chain(
341342

342343
// key in form transfer/channel-0/foo
343344
let ibc_denom = get_key_ics20_ibc_denom(&packet.dest.port_id, &packet.dest.channel_id, denom);
344-
let pair_mapping = ics20_denoms()
345-
.load(storage, &ibc_denom)
346-
.map_err(|_| ContractError::NotOnMappingList {})?;
345+
let pair_mapping = match ics20_denoms().load(storage, &ibc_denom) {
346+
Ok(pair_mapping) => pair_mapping,
347+
Err(_) => {
348+
let (prefix, denom) =
349+
denom
350+
.split_once("0x")
351+
.ok_or(ContractError::Std(StdError::GenericErr {
352+
msg: String::from("Cannot parse denom"),
353+
}))?;
354+
355+
let bytes_address = hex::decode(denom).map_err(|_| {
356+
ContractError::Std(StdError::GenericErr {
357+
msg: String::from("Invalid hex address"),
358+
})
359+
})?;
360+
let base58_address = bs58::encode(bytes_address).into_string();
361+
let base58_denom = format!("{}0x{}", prefix, base58_address);
362+
// push a register denom msg to the contract
363+
cosmos_msgs.push(
364+
wasm_execute(
365+
env.contract.address.to_string(),
366+
&ExecuteMsg::RegisterDenom(RegisterDenomMsg {
367+
subdenom: base58_denom.clone(),
368+
metadata: None,
369+
}),
370+
vec![Coin::new(1, "orai")],
371+
)?
372+
.into(),
373+
);
374+
let new_metadata = MappingMetadata {
375+
asset_info: AssetInfo::NativeToken {
376+
denom: get_full_denom(config.token_factory_addr.to_string(), base58_denom),
377+
},
378+
remote_decimals: 1, // Since we don't know metadata of remote_chain token, we set it to 1 in both decimals
379+
asset_info_decimals: 1,
380+
is_mint_burn: true, // Always mint burn if we don't know the metadata
381+
};
382+
ics20_denoms().save(storage, &ibc_denom, &new_metadata)?;
383+
new_metadata
384+
}
385+
};
347386
let initial_receive_asset_info = pair_mapping.asset_info;
348387
let to_send = Amount::from_parts(
349388
parse_asset_info_denom(&initial_receive_asset_info),

contracts/cw-ics20-latest/src/testing/ibc_tests.rs

+56-12
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ use cosmwasm_std::{
77
use cosmwasm_testing_util::mock::MockContract;
88
use cosmwasm_vm::testing::MockInstanceOptions;
99
use cw20_ics20_msg::converter::ConverterController;
10+
use cw20_ics20_msg::helper::get_full_denom;
1011
use cw_controllers::AdminError;
1112
use oraiswap::asset::AssetInfo;
1213
use oraiswap::router::RouterController;
14+
use token_bindings::Metadata;
1315

1416
use crate::ibc::{
1517
convert_remote_denom_to_evm_prefix, deduct_fee, deduct_relayer_fee, deduct_token_fee,
@@ -28,8 +30,8 @@ use cosmwasm_std::{
2830

2931
use crate::error::ContractError;
3032
use crate::state::{
31-
get_key_ics20_ibc_denom, increase_channel_balance, reduce_channel_balance, Config, ADMIN,
32-
CHANNEL_REVERSE_STATE, CONFIG, RELAYER_FEE, REPLY_ARGS, TOKEN_FEE,
33+
get_key_ics20_ibc_denom, ics20_denoms, increase_channel_balance, reduce_channel_balance,
34+
Config, ADMIN, CHANNEL_REVERSE_STATE, CONFIG, RELAYER_FEE, REPLY_ARGS, TOKEN_FEE,
3335
};
3436
use cw20::{Cw20CoinVerified, Cw20ExecuteMsg, Cw20ReceiveMsg};
3537
use cw20_ics20_msg::amount::{convert_remote_to_local, Amount};
@@ -41,7 +43,7 @@ use crate::contract::{
4143
};
4244
use crate::msg::{
4345
AllowMsg, ChannelResponse, ConfigResponse, ExecuteMsg, InitMsg, ListChannelsResponse,
44-
ListMappingResponse, PairQuery, QueryMsg,
46+
ListMappingResponse, PairQuery, QueryMsg, RegisterDenomMsg,
4547
};
4648
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
4749
use cosmwasm_std::{coins, to_json_vec};
@@ -230,28 +232,70 @@ fn send_native_from_remote_mapping_not_found() {
230232
let send_channel = "channel-9";
231233
let cw20_addr = "token-addr";
232234
let custom_addr = "custom-addr";
233-
let cw20_denom = "cw20:token-addr";
235+
let cw20_denom = "oraib0x10407cEa4B614AB11bd05B326193d84ec20851f6";
234236
let gas_limit = 1234567;
235237
let mut deps = setup(
236238
&["channel-1", "channel-7", send_channel],
237239
&[(cw20_addr, gas_limit)],
238240
);
239-
241+
let config = CONFIG.load(deps.as_ref().storage).unwrap();
240242
// prepare some mock packets
241243
let recv_packet =
242244
mock_receive_packet_remote_to_local(send_channel, 876543210, cw20_denom, custom_addr, None);
243245

246+
let (prefix, denom) = cw20_denom.split_once("0x").unwrap();
247+
let bytes_address = hex::decode(denom)
248+
.map_err(|_| {
249+
ContractError::Std(StdError::GenericErr {
250+
msg: String::from("Invalid hex address"),
251+
})
252+
})
253+
.unwrap();
254+
let base58_address = bs58::encode(bytes_address).into_string();
255+
let base58_denom = format!("{}0x{}", prefix, base58_address);
244256
// we can receive this denom, channel balance should increase
245257
let msg = IbcPacketReceiveMsg::new(recv_packet.clone(), relayer);
246258
let res = ibc_packet_receive(deps.as_mut(), mock_env(), msg).unwrap();
247-
// assert_eq!(res, StdError)
259+
248260
assert_eq!(
249-
res.attributes
250-
.into_iter()
251-
.find(|attr| attr.key.eq("error"))
252-
.unwrap()
253-
.value,
254-
"You can only send native tokens that has a map to the corresponding asset info"
261+
res.messages[0].msg,
262+
wasm_execute(
263+
"cosmos2contract",
264+
&ExecuteMsg::RegisterDenom(RegisterDenomMsg {
265+
subdenom: String::from(base58_denom.clone()),
266+
metadata: None
267+
}),
268+
vec![Coin::new(1u128.into(), "orai")]
269+
)
270+
.unwrap()
271+
.into()
272+
);
273+
execute(
274+
deps.as_mut(),
275+
mock_env(),
276+
mock_info("cosmos2contract", &[]),
277+
ExecuteMsg::RegisterDenom(RegisterDenomMsg {
278+
subdenom: String::from(denom),
279+
metadata: None,
280+
}),
281+
)
282+
.unwrap();
283+
let pair_mapping = ics20_denoms()
284+
.load(
285+
deps.as_ref().storage,
286+
"wasm.cosmos2contract/channel-9/oraib0x10407cEa4B614AB11bd05B326193d84ec20851f6",
287+
)
288+
.unwrap();
289+
assert_eq!(
290+
pair_mapping,
291+
MappingMetadata {
292+
asset_info: AssetInfo::NativeToken {
293+
denom: get_full_denom(config.token_factory_addr.to_string(), base58_denom),
294+
},
295+
remote_decimals: 1,
296+
asset_info_decimals: 1,
297+
is_mint_burn: true
298+
}
255299
);
256300
}
257301

packages/cw20-ics20-msg/src/helper.rs

+4
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ pub fn to_orai_bridge_address(address: &str) -> StdResult<String> {
4949
})
5050
}
5151

52+
pub fn get_full_denom(factory_contract: String, subdenom: String) -> String {
53+
format!("factory/{}/{}", factory_contract, subdenom)
54+
}
55+
5256
#[cfg(test)]
5357
mod tests {
5458
use crate::helper::{get_prefix_decode_bech32, to_orai_bridge_address};

0 commit comments

Comments
 (0)