Skip to main content

something_for_tests/
allowances.rs

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