cw20_vesting/
allowances.rs

1use cosmwasm_std::{
2    attr, Addr, Binary, BlockInfo, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult,
3    Storage, Uint128,
4};
5use cw20::{AllowanceResponse, Cw20ReceiveMsg, Expiration};
6
7use crate::error::ContractError;
8use crate::state::{deduct_coins, ALLOWANCES, BALANCES, TOKEN_INFO};
9
10pub fn execute_increase_allowance(
11    deps: DepsMut,
12    _env: Env,
13    info: MessageInfo,
14    spender: String,
15    amount: Uint128,
16    expires: Option<Expiration>,
17) -> Result<Response, ContractError> {
18    let spender_addr = deps.api.addr_validate(&spender)?;
19    if spender_addr == info.sender {
20        return Err(ContractError::CannotSetOwnAccount {});
21    }
22
23    ALLOWANCES.update(
24        deps.storage,
25        (&info.sender, &spender_addr),
26        |allow| -> StdResult<_> {
27            let mut val = allow.unwrap_or_default();
28            if let Some(exp) = expires {
29                val.expires = exp;
30            }
31            val.allowance += amount;
32            Ok(val)
33        },
34    )?;
35
36    let res = Response::new().add_attributes(vec![
37        attr("action", "increase_allowance"),
38        attr("owner", info.sender),
39        attr("spender", spender),
40        attr("amount", amount),
41    ]);
42    Ok(res)
43}
44
45pub fn execute_decrease_allowance(
46    deps: DepsMut,
47    _env: Env,
48    info: MessageInfo,
49    spender: String,
50    amount: Uint128,
51    expires: Option<Expiration>,
52) -> Result<Response, ContractError> {
53    let spender_addr = deps.api.addr_validate(&spender)?;
54    if spender_addr == info.sender {
55        return Err(ContractError::CannotSetOwnAccount {});
56    }
57
58    let key = (&info.sender, &spender_addr);
59    // load value and delete if it hits 0, or update otherwise
60    let mut allowance = ALLOWANCES.load(deps.storage, key)?;
61    if amount < allowance.allowance {
62        // update the new amount
63        allowance.allowance = allowance
64            .allowance
65            .checked_sub(amount)
66            .map_err(StdError::overflow)?;
67        if let Some(exp) = expires {
68            allowance.expires = exp;
69        }
70        ALLOWANCES.save(deps.storage, key, &allowance)?;
71    } else {
72        ALLOWANCES.remove(deps.storage, key);
73    }
74
75    let res = Response::new().add_attributes(vec![
76        attr("action", "decrease_allowance"),
77        attr("owner", info.sender),
78        attr("spender", spender),
79        attr("amount", amount),
80    ]);
81    Ok(res)
82}
83
84// this can be used to update a lower allowance - call bucket.update with proper keys
85pub fn deduct_allowance(
86    storage: &mut dyn Storage,
87    owner: &Addr,
88    spender: &Addr,
89    block: &BlockInfo,
90    amount: Uint128,
91) -> Result<AllowanceResponse, ContractError> {
92    ALLOWANCES.update(storage, (owner, spender), |current| {
93        match current {
94            Some(mut a) => {
95                if a.expires.is_expired(block) {
96                    Err(ContractError::Expired {})
97                } else {
98                    // deduct the allowance if enough
99                    a.allowance = a
100                        .allowance
101                        .checked_sub(amount)
102                        .map_err(StdError::overflow)?;
103                    Ok(a)
104                }
105            }
106            None => Err(ContractError::NoAllowance {}),
107        }
108    })
109}
110
111pub fn execute_transfer_from(
112    deps: DepsMut,
113    env: Env,
114    info: MessageInfo,
115    owner: String,
116    recipient: String,
117    amount: Uint128,
118) -> Result<Response, ContractError> {
119    let rcpt_addr = deps.api.addr_validate(&recipient)?;
120    let owner_addr = deps.api.addr_validate(&owner)?;
121
122    // deduct allowance before doing anything else have enough allowance
123    deduct_allowance(deps.storage, &owner_addr, &info.sender, &env.block, amount)?;
124
125    // this will handle vesting checks as well
126    deduct_coins(deps.storage, &env, &owner_addr, amount)?;
127    BALANCES.update(
128        deps.storage,
129        &rcpt_addr,
130        |balance: Option<Uint128>| -> StdResult<_> { Ok(balance.unwrap_or_default() + amount) },
131    )?;
132
133    let res = Response::new().add_attributes(vec![
134        attr("action", "transfer_from"),
135        attr("from", owner),
136        attr("to", recipient),
137        attr("by", info.sender),
138        attr("amount", amount),
139    ]);
140    Ok(res)
141}
142
143pub fn execute_burn_from(
144    deps: DepsMut,
145
146    env: Env,
147    info: MessageInfo,
148    owner: String,
149    amount: Uint128,
150) -> Result<Response, ContractError> {
151    let owner_addr = deps.api.addr_validate(&owner)?;
152
153    // deduct allowance before doing anything else have enough allowance
154    deduct_allowance(deps.storage, &owner_addr, &info.sender, &env.block, amount)?;
155
156    // this will handle vesting checks as well
157    deduct_coins(deps.storage, &env, &owner_addr, amount)?;
158    // reduce total_supply
159    TOKEN_INFO.update(deps.storage, |mut meta| -> StdResult<_> {
160        meta.total_supply = meta.total_supply.checked_sub(amount)?;
161        Ok(meta)
162    })?;
163
164    let res = Response::new().add_attributes(vec![
165        attr("action", "burn_from"),
166        attr("from", owner),
167        attr("by", info.sender),
168        attr("amount", amount),
169    ]);
170    Ok(res)
171}
172
173pub fn execute_send_from(
174    deps: DepsMut,
175    env: Env,
176    info: MessageInfo,
177    owner: String,
178    contract: String,
179    amount: Uint128,
180    msg: Binary,
181) -> Result<Response, ContractError> {
182    let rcpt_addr = deps.api.addr_validate(&contract)?;
183    let owner_addr = deps.api.addr_validate(&owner)?;
184
185    // deduct allowance before doing anything else have enough allowance
186    deduct_allowance(deps.storage, &owner_addr, &info.sender, &env.block, amount)?;
187
188    // this will handle vesting checks as well
189    deduct_coins(deps.storage, &env, &owner_addr, amount)?;
190    BALANCES.update(
191        deps.storage,
192        &rcpt_addr,
193        |balance: Option<Uint128>| -> StdResult<_> { Ok(balance.unwrap_or_default() + amount) },
194    )?;
195
196    let attrs = vec![
197        attr("action", "send_from"),
198        attr("from", &owner),
199        attr("to", &contract),
200        attr("by", &info.sender),
201        attr("amount", amount),
202    ];
203
204    // create a send message
205    let msg = Cw20ReceiveMsg {
206        sender: info.sender.into(),
207        amount,
208        msg,
209    }
210    .into_cosmos_msg(contract)?;
211
212    let res = Response::new().add_message(msg).add_attributes(attrs);
213    Ok(res)
214}
215
216pub fn query_allowance(deps: Deps, owner: String, spender: String) -> StdResult<AllowanceResponse> {
217    let owner_addr = deps.api.addr_validate(&owner)?;
218    let spender_addr = deps.api.addr_validate(&spender)?;
219    let allowance = ALLOWANCES
220        .may_load(deps.storage, (&owner_addr, &spender_addr))?
221        .unwrap_or_default();
222    Ok(allowance)
223}
224
225#[cfg(test)]
226mod tests {
227    use super::*;
228
229    use cosmwasm_std::testing::{mock_dependencies_with_balance, mock_env, mock_info};
230    use cosmwasm_std::{coins, CosmosMsg, SubMsg, Timestamp, WasmMsg};
231    use cw20::TokenInfoResponse;
232
233    use crate::contract::{execute, instantiate, query_balance, query_token_info};
234    use crate::msg::{ExecuteMsg, InitBalance, InstantiateMsg};
235
236    fn get_balance<T: Into<String>>(deps: Deps, address: T) -> Uint128 {
237        query_balance(deps, address.into()).unwrap().balance
238    }
239
240    // this will set up the instantiation for other tests
241    fn do_instantiate<T: Into<String>>(
242        mut deps: DepsMut,
243        addr: T,
244        amount: Uint128,
245    ) -> TokenInfoResponse {
246        let instantiate_msg = InstantiateMsg {
247            name: "Auto Gen".to_string(),
248            symbol: "AUTO".to_string(),
249            decimals: 3,
250            initial_balances: vec![InitBalance {
251                address: addr.into(),
252                amount,
253                vesting: None,
254            }],
255            mint: None,
256            marketing: None,
257            allowed_vesters: None,
258        };
259        let info = mock_info("creator", &[]);
260        let env = mock_env();
261        instantiate(deps.branch(), env, info, instantiate_msg).unwrap();
262        query_token_info(deps.as_ref()).unwrap()
263    }
264
265    #[test]
266    fn increase_decrease_allowances() {
267        let mut deps = mock_dependencies_with_balance(&coins(2, "token"));
268
269        let owner = String::from("addr0001");
270        let spender = String::from("addr0002");
271        let info = mock_info(owner.as_ref(), &[]);
272        let env = mock_env();
273        do_instantiate(deps.as_mut(), owner.clone(), Uint128::new(12340000));
274
275        // no allowance to start
276        let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap();
277        assert_eq!(allowance, AllowanceResponse::default());
278
279        // set allowance with height expiration
280        let allow1 = Uint128::new(7777);
281        let expires = Expiration::AtHeight(5432);
282        let msg = ExecuteMsg::IncreaseAllowance {
283            spender: spender.clone(),
284            amount: allow1,
285            expires: Some(expires),
286        };
287        execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap();
288
289        // ensure it looks good
290        let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap();
291        assert_eq!(
292            allowance,
293            AllowanceResponse {
294                allowance: allow1,
295                expires
296            }
297        );
298
299        // decrease it a bit with no expire set - stays the same
300        let lower = Uint128::new(4444);
301        let allow2 = allow1.checked_sub(lower).unwrap();
302        let msg = ExecuteMsg::DecreaseAllowance {
303            spender: spender.clone(),
304            amount: lower,
305            expires: None,
306        };
307        execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap();
308        let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap();
309        assert_eq!(
310            allowance,
311            AllowanceResponse {
312                allowance: allow2,
313                expires
314            }
315        );
316
317        // increase it some more and override the expires
318        let raise = Uint128::new(87654);
319        let allow3 = allow2 + raise;
320        let new_expire = Expiration::AtTime(Timestamp::from_seconds(8888888888));
321        let msg = ExecuteMsg::IncreaseAllowance {
322            spender: spender.clone(),
323            amount: raise,
324            expires: Some(new_expire),
325        };
326        execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap();
327        let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap();
328        assert_eq!(
329            allowance,
330            AllowanceResponse {
331                allowance: allow3,
332                expires: new_expire
333            }
334        );
335
336        // decrease it below 0
337        let msg = ExecuteMsg::DecreaseAllowance {
338            spender: spender.clone(),
339            amount: Uint128::new(99988647623876347),
340            expires: None,
341        };
342        execute(deps.as_mut(), env, info, msg).unwrap();
343        let allowance = query_allowance(deps.as_ref(), owner, spender).unwrap();
344        assert_eq!(allowance, AllowanceResponse::default());
345    }
346
347    #[test]
348    fn allowances_independent() {
349        let mut deps = mock_dependencies_with_balance(&coins(2, "token"));
350
351        let owner = String::from("addr0001");
352        let spender = String::from("addr0002");
353        let spender2 = String::from("addr0003");
354        let info = mock_info(owner.as_ref(), &[]);
355        let env = mock_env();
356        do_instantiate(deps.as_mut(), &owner, Uint128::new(12340000));
357
358        // no allowance to start
359        assert_eq!(
360            query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(),
361            AllowanceResponse::default()
362        );
363        assert_eq!(
364            query_allowance(deps.as_ref(), owner.clone(), spender2.clone()).unwrap(),
365            AllowanceResponse::default()
366        );
367        assert_eq!(
368            query_allowance(deps.as_ref(), spender.clone(), spender2.clone()).unwrap(),
369            AllowanceResponse::default()
370        );
371
372        // set allowance with height expiration
373        let allow1 = Uint128::new(7777);
374        let expires = Expiration::AtHeight(5432);
375        let msg = ExecuteMsg::IncreaseAllowance {
376            spender: spender.clone(),
377            amount: allow1,
378            expires: Some(expires),
379        };
380        execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap();
381
382        // set other allowance with no expiration
383        let allow2 = Uint128::new(87654);
384        let msg = ExecuteMsg::IncreaseAllowance {
385            spender: spender2.clone(),
386            amount: allow2,
387            expires: None,
388        };
389        execute(deps.as_mut(), env, info, msg).unwrap();
390
391        // check they are proper
392        let expect_one = AllowanceResponse {
393            allowance: allow1,
394            expires,
395        };
396        let expect_two = AllowanceResponse {
397            allowance: allow2,
398            expires: Expiration::Never {},
399        };
400        assert_eq!(
401            query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(),
402            expect_one
403        );
404        assert_eq!(
405            query_allowance(deps.as_ref(), owner.clone(), spender2.clone()).unwrap(),
406            expect_two
407        );
408        assert_eq!(
409            query_allowance(deps.as_ref(), spender.clone(), spender2.clone()).unwrap(),
410            AllowanceResponse::default()
411        );
412
413        // also allow spender -> spender2 with no interference
414        let info = mock_info(spender.as_ref(), &[]);
415        let env = mock_env();
416        let allow3 = Uint128::new(1821);
417        let expires3 = Expiration::AtTime(Timestamp::from_seconds(3767626296));
418        let msg = ExecuteMsg::IncreaseAllowance {
419            spender: spender2.clone(),
420            amount: allow3,
421            expires: Some(expires3),
422        };
423        execute(deps.as_mut(), env, info, msg).unwrap();
424        let expect_three = AllowanceResponse {
425            allowance: allow3,
426            expires: expires3,
427        };
428        assert_eq!(
429            query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(),
430            expect_one
431        );
432        assert_eq!(
433            query_allowance(deps.as_ref(), owner, spender2.clone()).unwrap(),
434            expect_two
435        );
436        assert_eq!(
437            query_allowance(deps.as_ref(), spender, spender2).unwrap(),
438            expect_three
439        );
440    }
441
442    #[test]
443    fn no_self_allowance() {
444        let mut deps = mock_dependencies_with_balance(&coins(2, "token"));
445
446        let owner = String::from("addr0001");
447        let info = mock_info(owner.as_ref(), &[]);
448        let env = mock_env();
449        do_instantiate(deps.as_mut(), &owner, Uint128::new(12340000));
450
451        // self-allowance
452        let msg = ExecuteMsg::IncreaseAllowance {
453            spender: owner.clone(),
454            amount: Uint128::new(7777),
455            expires: None,
456        };
457        let err = execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap_err();
458        assert_eq!(err, ContractError::CannotSetOwnAccount {});
459
460        // decrease self-allowance
461        let msg = ExecuteMsg::DecreaseAllowance {
462            spender: owner,
463            amount: Uint128::new(7777),
464            expires: None,
465        };
466        let err = execute(deps.as_mut(), env, info, msg).unwrap_err();
467        assert_eq!(err, ContractError::CannotSetOwnAccount {});
468    }
469
470    #[test]
471    fn transfer_from_respects_limits() {
472        let mut deps = mock_dependencies_with_balance(&[]);
473        let owner = String::from("addr0001");
474        let spender = String::from("addr0002");
475        let rcpt = String::from("addr0003");
476
477        let start = Uint128::new(999999);
478        do_instantiate(deps.as_mut(), &owner, start);
479
480        // provide an allowance
481        let allow1 = Uint128::new(77777);
482        let msg = ExecuteMsg::IncreaseAllowance {
483            spender: spender.clone(),
484            amount: allow1,
485            expires: None,
486        };
487        let info = mock_info(owner.as_ref(), &[]);
488        let env = mock_env();
489        execute(deps.as_mut(), env, info, msg).unwrap();
490
491        // valid transfer of part of the allowance
492        let transfer = Uint128::new(44444);
493        let msg = ExecuteMsg::TransferFrom {
494            owner: owner.clone(),
495            recipient: rcpt.clone(),
496            amount: transfer,
497        };
498        let info = mock_info(spender.as_ref(), &[]);
499        let env = mock_env();
500        let res = execute(deps.as_mut(), env, info, msg).unwrap();
501        assert_eq!(res.attributes[0], attr("action", "transfer_from"));
502
503        // make sure money arrived
504        assert_eq!(
505            get_balance(deps.as_ref(), owner.clone()),
506            start.checked_sub(transfer).unwrap()
507        );
508        assert_eq!(get_balance(deps.as_ref(), rcpt.clone()), transfer);
509
510        // ensure it looks good
511        let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap();
512        let expect = AllowanceResponse {
513            allowance: allow1.checked_sub(transfer).unwrap(),
514            expires: Expiration::Never {},
515        };
516        assert_eq!(expect, allowance);
517
518        // cannot send more than the allowance
519        let msg = ExecuteMsg::TransferFrom {
520            owner: owner.clone(),
521            recipient: rcpt.clone(),
522            amount: Uint128::new(33443),
523        };
524        let info = mock_info(spender.as_ref(), &[]);
525        let env = mock_env();
526        let err = execute(deps.as_mut(), env, info, msg).unwrap_err();
527        assert!(matches!(err, ContractError::Std(StdError::Overflow { .. })));
528
529        // let us increase limit, but set the expiration (default env height is 12_345)
530        let info = mock_info(owner.as_ref(), &[]);
531        let env = mock_env();
532        let msg = ExecuteMsg::IncreaseAllowance {
533            spender: spender.clone(),
534            amount: Uint128::new(1000),
535            expires: Some(Expiration::AtHeight(env.block.height)),
536        };
537        execute(deps.as_mut(), env, info, msg).unwrap();
538
539        // we should now get the expiration error
540        let msg = ExecuteMsg::TransferFrom {
541            owner,
542            recipient: rcpt,
543            amount: Uint128::new(33443),
544        };
545        let info = mock_info(spender.as_ref(), &[]);
546        let env = mock_env();
547        let err = execute(deps.as_mut(), env, info, msg).unwrap_err();
548        assert_eq!(err, ContractError::Expired {});
549    }
550
551    #[test]
552    fn burn_from_respects_limits() {
553        let mut deps = mock_dependencies_with_balance(&[]);
554        let owner = String::from("addr0001");
555        let spender = String::from("addr0002");
556
557        let start = Uint128::new(999999);
558        do_instantiate(deps.as_mut(), &owner, start);
559
560        // provide an allowance
561        let allow1 = Uint128::new(77777);
562        let msg = ExecuteMsg::IncreaseAllowance {
563            spender: spender.clone(),
564            amount: allow1,
565            expires: None,
566        };
567        let info = mock_info(owner.as_ref(), &[]);
568        let env = mock_env();
569        execute(deps.as_mut(), env, info, msg).unwrap();
570
571        // valid burn of part of the allowance
572        let transfer = Uint128::new(44444);
573        let msg = ExecuteMsg::BurnFrom {
574            owner: owner.clone(),
575            amount: transfer,
576        };
577        let info = mock_info(spender.as_ref(), &[]);
578        let env = mock_env();
579        let res = execute(deps.as_mut(), env, info, msg).unwrap();
580        assert_eq!(res.attributes[0], attr("action", "burn_from"));
581
582        // make sure money burnt
583        assert_eq!(
584            get_balance(deps.as_ref(), owner.clone()),
585            start.checked_sub(transfer).unwrap()
586        );
587
588        // ensure it looks good
589        let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap();
590        let expect = AllowanceResponse {
591            allowance: allow1.checked_sub(transfer).unwrap(),
592            expires: Expiration::Never {},
593        };
594        assert_eq!(expect, allowance);
595
596        // cannot burn more than the allowance
597        let msg = ExecuteMsg::BurnFrom {
598            owner: owner.clone(),
599            amount: Uint128::new(33443),
600        };
601        let info = mock_info(spender.as_ref(), &[]);
602        let env = mock_env();
603        let err = execute(deps.as_mut(), env, info, msg).unwrap_err();
604        assert!(matches!(err, ContractError::Std(StdError::Overflow { .. })));
605
606        // let us increase limit, but set the expiration (default env height is 12_345)
607        let info = mock_info(owner.as_ref(), &[]);
608        let env = mock_env();
609        let msg = ExecuteMsg::IncreaseAllowance {
610            spender: spender.clone(),
611            amount: Uint128::new(1000),
612            expires: Some(Expiration::AtHeight(env.block.height)),
613        };
614        execute(deps.as_mut(), env, info, msg).unwrap();
615
616        // we should now get the expiration error
617        let msg = ExecuteMsg::BurnFrom {
618            owner,
619            amount: Uint128::new(33443),
620        };
621        let info = mock_info(spender.as_ref(), &[]);
622        let env = mock_env();
623        let err = execute(deps.as_mut(), env, info, msg).unwrap_err();
624        assert_eq!(err, ContractError::Expired {});
625    }
626
627    #[test]
628    fn send_from_respects_limits() {
629        let mut deps = mock_dependencies_with_balance(&[]);
630        let owner = String::from("addr0001");
631        let spender = String::from("addr0002");
632        let contract = String::from("cool-dex");
633        let send_msg = Binary::from(r#"{"some":123}"#.as_bytes());
634
635        let start = Uint128::new(999999);
636        do_instantiate(deps.as_mut(), &owner, start);
637
638        // provide an allowance
639        let allow1 = Uint128::new(77777);
640        let msg = ExecuteMsg::IncreaseAllowance {
641            spender: spender.clone(),
642            amount: allow1,
643            expires: None,
644        };
645        let info = mock_info(owner.as_ref(), &[]);
646        let env = mock_env();
647        execute(deps.as_mut(), env, info, msg).unwrap();
648
649        // valid send of part of the allowance
650        let transfer = Uint128::new(44444);
651        let msg = ExecuteMsg::SendFrom {
652            owner: owner.clone(),
653            amount: transfer,
654            contract: contract.clone(),
655            msg: send_msg.clone(),
656        };
657        let info = mock_info(spender.as_ref(), &[]);
658        let env = mock_env();
659        let res = execute(deps.as_mut(), env, info, msg).unwrap();
660        assert_eq!(res.attributes[0], attr("action", "send_from"));
661        assert_eq!(1, res.messages.len());
662
663        // we record this as sent by the one who requested, not the one who was paying
664        let binary_msg = Cw20ReceiveMsg {
665            sender: spender.clone(),
666            amount: transfer,
667            msg: send_msg.clone(),
668        }
669        .into_binary()
670        .unwrap();
671        assert_eq!(
672            res.messages[0],
673            SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute {
674                contract_addr: contract.clone(),
675                msg: binary_msg,
676                funds: vec![],
677            }))
678        );
679
680        // make sure money sent
681        assert_eq!(
682            get_balance(deps.as_ref(), owner.clone()),
683            start.checked_sub(transfer).unwrap()
684        );
685        assert_eq!(get_balance(deps.as_ref(), contract.clone()), transfer);
686
687        // ensure it looks good
688        let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap();
689        let expect = AllowanceResponse {
690            allowance: allow1.checked_sub(transfer).unwrap(),
691            expires: Expiration::Never {},
692        };
693        assert_eq!(expect, allowance);
694
695        // cannot send more than the allowance
696        let msg = ExecuteMsg::SendFrom {
697            owner: owner.clone(),
698            amount: Uint128::new(33443),
699            contract: contract.clone(),
700            msg: send_msg.clone(),
701        };
702        let info = mock_info(spender.as_ref(), &[]);
703        let env = mock_env();
704        let err = execute(deps.as_mut(), env, info, msg).unwrap_err();
705        assert!(matches!(err, ContractError::Std(StdError::Overflow { .. })));
706
707        // let us increase limit, but set the expiration to current block (expired)
708        let info = mock_info(owner.as_ref(), &[]);
709        let env = mock_env();
710        let msg = ExecuteMsg::IncreaseAllowance {
711            spender: spender.clone(),
712            amount: Uint128::new(1000),
713            expires: Some(Expiration::AtHeight(env.block.height)),
714        };
715        execute(deps.as_mut(), env, info, msg).unwrap();
716
717        // we should now get the expiration error
718        let msg = ExecuteMsg::SendFrom {
719            owner,
720            amount: Uint128::new(33443),
721            contract,
722            msg: send_msg,
723        };
724        let info = mock_info(spender.as_ref(), &[]);
725        let env = mock_env();
726        let err = execute(deps.as_mut(), env, info, msg).unwrap_err();
727        assert_eq!(err, ContractError::Expired {});
728    }
729}