Skip to content

Commit 2ddabc4

Browse files
authored
Merge pull request #685 from CosmWasm/add-default-gas-limit
Add default gas limit to cw20-ics20
2 parents a45d3a1 + 1ab59a4 commit 2ddabc4

File tree

5 files changed

+106
-19
lines changed

5 files changed

+106
-19
lines changed

contracts/cw20-ics20/src/contract.rs

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ pub fn instantiate(
3838
set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
3939
let cfg = Config {
4040
default_timeout: msg.default_timeout,
41+
default_gas_limit: msg.default_gas_limit,
4142
};
4243
CONFIG.save(deps.storage, &cfg)?;
4344

@@ -107,19 +108,23 @@ pub fn execute_transfer(
107108
if !CHANNEL_INFO.has(deps.storage, &msg.channel) {
108109
return Err(ContractError::NoSuchChannel { id: msg.channel });
109110
}
111+
let config = CONFIG.load(deps.storage)?;
110112

111-
// if cw20 token, ensure it is whitelisted
113+
// if cw20 token, validate and ensure it is whitelisted, or we set default gas limit
112114
if let Amount::Cw20(coin) = &amount {
113115
let addr = deps.api.addr_validate(&coin.address)?;
114-
ALLOW_LIST
115-
.may_load(deps.storage, &addr)?
116-
.ok_or(ContractError::NotOnAllowList)?;
116+
// if limit is set, then we always allow cw20
117+
if config.default_gas_limit.is_none() {
118+
ALLOW_LIST
119+
.may_load(deps.storage, &addr)?
120+
.ok_or(ContractError::NotOnAllowList)?;
121+
}
117122
};
118123

119124
// delta from user is in seconds
120125
let timeout_delta = match msg.timeout {
121126
Some(t) => t,
122-
None => CONFIG.load(deps.storage)?.default_timeout,
127+
None => config.default_timeout,
123128
};
124129
// timeout is in nanoseconds
125130
let timeout = env.block.time.plus_seconds(timeout_delta);
@@ -202,7 +207,7 @@ const MIGRATE_VERSION_2: &str = "0.12.0-alpha1";
202207
const MIGRATE_VERSION_3: &str = "0.13.1";
203208

204209
#[cfg_attr(not(feature = "library"), entry_point)]
205-
pub fn migrate(mut deps: DepsMut, env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
210+
pub fn migrate(mut deps: DepsMut, env: Env, msg: MigrateMsg) -> Result<Response, ContractError> {
206211
let version: Version = CONTRACT_VERSION.parse().map_err(from_semver)?;
207212
let stored = get_contract_version(deps.storage)?;
208213
let storage_version: Version = stored.version.parse().map_err(from_semver)?;
@@ -233,6 +238,7 @@ pub fn migrate(mut deps: DepsMut, env: Env, _msg: MigrateMsg) -> Result<Response
233238
ADMIN.set(deps.branch(), Some(old_config.gov_contract))?;
234239
let config = Config {
235240
default_timeout: old_config.default_timeout,
241+
default_gas_limit: None,
236242
};
237243
CONFIG.save(deps.storage, &config)?;
238244
}
@@ -242,6 +248,15 @@ pub fn migrate(mut deps: DepsMut, env: Env, _msg: MigrateMsg) -> Result<Response
242248
}
243249
// otherwise no migration (yet) - add them here
244250

251+
// always allow setting the default gas limit via MigrateMsg, even if same version
252+
// (Note this doesn't allow unsetting it now)
253+
if msg.default_gas_limit.is_some() {
254+
CONFIG.update(deps.storage, |mut old| -> StdResult<_> {
255+
old.default_gas_limit = msg.default_gas_limit;
256+
Ok(old)
257+
})?;
258+
}
259+
245260
// we don't need to save anything if migrating from the same version
246261
if storage_version < version {
247262
set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
@@ -313,6 +328,7 @@ fn query_config(deps: Deps) -> StdResult<ConfigResponse> {
313328
let admin = ADMIN.get(deps)?.unwrap_or_else(|| Addr::unchecked(""));
314329
let res = ConfigResponse {
315330
default_timeout: cfg.default_timeout,
331+
default_gas_limit: cfg.default_gas_limit,
316332
gov_contract: admin.into(),
317333
};
318334
Ok(res)
@@ -512,9 +528,9 @@ mod test {
512528
}
513529

514530
#[test]
515-
fn execute_cw20_fails_if_not_whitelisted() {
531+
fn execute_cw20_fails_if_not_whitelisted_unless_default_gas_limit() {
516532
let send_channel = "channel-15";
517-
let mut deps = setup(&["channel-3", send_channel], &[]);
533+
let mut deps = setup(&[send_channel], &[]);
518534

519535
let cw20_addr = "my-token";
520536
let transfer = TransferMsg {
@@ -528,10 +544,23 @@ mod test {
528544
msg: to_binary(&transfer).unwrap(),
529545
});
530546

531-
// works with proper funds
547+
// rejected as not on allow list
532548
let info = mock_info(cw20_addr, &[]);
533-
let err = execute(deps.as_mut(), mock_env(), info, msg).unwrap_err();
549+
let err = execute(deps.as_mut(), mock_env(), info.clone(), msg.clone()).unwrap_err();
534550
assert_eq!(err, ContractError::NotOnAllowList);
551+
552+
// add a default gas limit
553+
migrate(
554+
deps.as_mut(),
555+
mock_env(),
556+
MigrateMsg {
557+
default_gas_limit: Some(123456),
558+
},
559+
)
560+
.unwrap();
561+
562+
// try again
563+
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
535564
}
536565

537566
#[test]
@@ -558,11 +587,22 @@ mod test {
558587
.unwrap();
559588

560589
// run migration
561-
migrate(deps.as_mut(), mock_env(), MigrateMsg {}).unwrap();
590+
migrate(
591+
deps.as_mut(),
592+
mock_env(),
593+
MigrateMsg {
594+
default_gas_limit: Some(123456),
595+
},
596+
)
597+
.unwrap();
562598

563599
// check new channel state
564600
let chan = query_channel(deps.as_ref(), send_channel.into()).unwrap();
565601
assert_eq!(chan.balances, vec![Amount::native(50000, native)]);
566602
assert_eq!(chan.total_sent, vec![Amount::native(114000, native)]);
603+
604+
// check config updates
605+
let config = query_config(deps.as_ref()).unwrap();
606+
assert_eq!(config.default_gas_limit, Some(123456));
567607
}
568608
}

contracts/cw20-ics20/src/ibc.rs

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::amount::Amount;
1212
use crate::error::{ContractError, Never};
1313
use crate::state::{
1414
reduce_channel_balance, undo_reduce_channel_balance, ChannelInfo, ReplyArgs, ALLOW_LIST,
15-
CHANNEL_INFO, REPLY_ARGS,
15+
CHANNEL_INFO, CONFIG, REPLY_ARGS,
1616
};
1717
use cw20::Cw20ExecuteMsg;
1818

@@ -273,10 +273,14 @@ fn check_gas_limit(deps: Deps, amount: &Amount) -> Result<Option<u64>, ContractE
273273
Amount::Cw20(coin) => {
274274
// if cw20 token, use the registered gas limit, or error if not whitelisted
275275
let addr = deps.api.addr_validate(&coin.address)?;
276-
Ok(ALLOW_LIST
277-
.may_load(deps.storage, &addr)?
278-
.ok_or(ContractError::NotOnAllowList)?
279-
.gas_limit)
276+
let allowed = ALLOW_LIST.may_load(deps.storage, &addr)?;
277+
match allowed {
278+
Some(allow) => Ok(allow.gas_limit),
279+
None => match CONFIG.load(deps.storage)?.default_gas_limit {
280+
Some(base) => Ok(Some(base)),
281+
None => Err(ContractError::NotOnAllowList),
282+
},
283+
}
280284
}
281285
_ => Ok(None),
282286
}
@@ -386,8 +390,8 @@ mod test {
386390
use super::*;
387391
use crate::test_helpers::*;
388392

389-
use crate::contract::{execute, query_channel};
390-
use crate::msg::{ExecuteMsg, TransferMsg};
393+
use crate::contract::{execute, migrate, query_channel};
394+
use crate::msg::{ExecuteMsg, MigrateMsg, TransferMsg};
391395
use cosmwasm_std::testing::{mock_env, mock_info};
392396
use cosmwasm_std::{coins, to_vec, IbcEndpoint, IbcMsg, IbcTimeout, Timestamp};
393397
use cw20::Cw20ReceiveMsg;
@@ -621,4 +625,39 @@ mod test {
621625
assert_eq!(state.balances, vec![Amount::native(111111111, denom)]);
622626
assert_eq!(state.total_sent, vec![Amount::native(987654321, denom)]);
623627
}
628+
629+
#[test]
630+
fn check_gas_limit_handles_all_cases() {
631+
let send_channel = "channel-9";
632+
let allowed = "foobar";
633+
let allowed_gas = 777666;
634+
let mut deps = setup(&[send_channel], &[(allowed, allowed_gas)]);
635+
636+
// allow list will get proper gas
637+
let limit = check_gas_limit(deps.as_ref(), &Amount::cw20(500, allowed)).unwrap();
638+
assert_eq!(limit, Some(allowed_gas));
639+
640+
// non-allow list will error
641+
let random = "tokenz";
642+
check_gas_limit(deps.as_ref(), &Amount::cw20(500, random)).unwrap_err();
643+
644+
// add default_gas_limit
645+
let def_limit = 54321;
646+
migrate(
647+
deps.as_mut(),
648+
mock_env(),
649+
MigrateMsg {
650+
default_gas_limit: Some(def_limit),
651+
},
652+
)
653+
.unwrap();
654+
655+
// allow list still gets proper gas
656+
let limit = check_gas_limit(deps.as_ref(), &Amount::cw20(500, allowed)).unwrap();
657+
assert_eq!(limit, Some(allowed_gas));
658+
659+
// non-allow list will now get default
660+
let limit = check_gas_limit(deps.as_ref(), &Amount::cw20(500, random)).unwrap();
661+
assert_eq!(limit, Some(def_limit));
662+
}
624663
}

contracts/cw20-ics20/src/msg.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ pub struct InitMsg {
1414
pub gov_contract: String,
1515
/// initial allowlist - all cw20 tokens we will send must be previously allowed by governance
1616
pub allowlist: Vec<AllowMsg>,
17+
/// If set, contracts off the allowlist will run with this gas limit.
18+
/// If unset, will refuse to accept any contract off the allow list.
19+
pub default_gas_limit: Option<u64>,
1720
}
1821

1922
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
@@ -23,7 +26,9 @@ pub struct AllowMsg {
2326
}
2427

2528
#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)]
26-
pub struct MigrateMsg {}
29+
pub struct MigrateMsg {
30+
pub default_gas_limit: Option<u64>,
31+
}
2732

2833
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
2934
#[serde(rename_all = "snake_case")]
@@ -98,6 +103,7 @@ pub struct PortResponse {
98103
#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
99104
pub struct ConfigResponse {
100105
pub default_timeout: u64,
106+
pub default_gas_limit: Option<u64>,
101107
pub gov_contract: String,
102108
}
103109

contracts/cw20-ics20/src/state.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub struct ChannelState {
3232
#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
3333
pub struct Config {
3434
pub default_timeout: u64,
35+
pub default_gas_limit: Option<u64>,
3536
}
3637

3738
#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]

contracts/cw20-ics20/src/test_helpers.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ pub fn setup(
7070

7171
// instantiate an empty contract
7272
let instantiate_msg = InitMsg {
73+
default_gas_limit: None,
7374
default_timeout: DEFAULT_TIMEOUT,
7475
gov_contract: "gov".to_string(),
7576
allowlist,

0 commit comments

Comments
 (0)