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::{ALLOWANCES, ALLOWANCES_SPENDER, 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 let update_fn = |allow: Option<AllowanceResponse>| -> Result<_, _> {
24 let mut val = allow.unwrap_or_default();
25 if let Some(exp) = expires {
26 if exp.is_expired(&env.block) {
27 return Err(ContractError::InvalidExpiration {});
28 }
29 val.expires = exp;
30 }
31 val.allowance += amount;
32 Ok(val)
33 };
34 ALLOWANCES.update(deps.storage, (&info.sender, &spender_addr), update_fn)?;
35 ALLOWANCES_SPENDER.update(deps.storage, (&spender_addr, &info.sender), update_fn)?;
36
37 let res = Response::new().add_attributes(vec![
38 attr("action", "increase_allowance"),
39 attr("owner", info.sender),
40 attr("spender", spender),
41 attr("amount", amount),
42 ]);
43 Ok(res)
44}
45
46pub fn execute_decrease_allowance(
47 deps: DepsMut,
48 env: Env,
49 info: MessageInfo,
50 spender: String,
51 amount: Uint128,
52 expires: Option<Expiration>,
53) -> Result<Response, ContractError> {
54 let spender_addr = deps.api.addr_validate(&spender)?;
55 if spender_addr == info.sender {
56 return Err(ContractError::CannotSetOwnAccount {});
57 }
58
59 let key = (&info.sender, &spender_addr);
60
61 fn reverse<'a>(t: (&'a Addr, &'a Addr)) -> (&'a Addr, &'a Addr) {
62 (t.1, t.0)
63 }
64
65 let mut allowance = ALLOWANCES.load(deps.storage, key)?;
67 if amount < allowance.allowance {
68 allowance.allowance = allowance
70 .allowance
71 .checked_sub(amount)
72 .map_err(StdError::overflow)?;
73 if let Some(exp) = expires {
74 if exp.is_expired(&env.block) {
75 return Err(ContractError::InvalidExpiration {});
76 }
77 allowance.expires = exp;
78 }
79 ALLOWANCES.save(deps.storage, key, &allowance)?;
80 ALLOWANCES_SPENDER.save(deps.storage, reverse(key), &allowance)?;
81 } else {
82 ALLOWANCES.remove(deps.storage, key);
83 ALLOWANCES_SPENDER.remove(deps.storage, reverse(key));
84 }
85
86 let res = Response::new().add_attributes(vec![
87 attr("action", "decrease_allowance"),
88 attr("owner", info.sender),
89 attr("spender", spender),
90 attr("amount", amount),
91 ]);
92 Ok(res)
93}
94
95pub fn deduct_allowance(
97 storage: &mut dyn Storage,
98 owner: &Addr,
99 spender: &Addr,
100 block: &BlockInfo,
101 amount: Uint128,
102) -> Result<AllowanceResponse, ContractError> {
103 let update_fn = |current: Option<AllowanceResponse>| -> _ {
104 match current {
105 Some(mut a) => {
106 if a.expires.is_expired(block) {
107 Err(ContractError::Expired {})
108 } else {
109 a.allowance = a
111 .allowance
112 .checked_sub(amount)
113 .map_err(StdError::overflow)?;
114 Ok(a)
115 }
116 }
117 None => Err(ContractError::NoAllowance {}),
118 }
119 };
120 ALLOWANCES.update(storage, (owner, spender), update_fn)?;
121 ALLOWANCES_SPENDER.update(storage, (spender, owner), update_fn)
122}
123
124pub fn execute_transfer_from(
125 deps: DepsMut,
126 env: Env,
127 info: MessageInfo,
128 owner: String,
129 recipient: String,
130 amount: Uint128,
131) -> Result<Response, ContractError> {
132 let rcpt_addr = deps.api.addr_validate(&recipient)?;
133 let owner_addr = deps.api.addr_validate(&owner)?;
134
135 deduct_allowance(deps.storage, &owner_addr, &info.sender, &env.block, amount)?;
137
138 BALANCES.update(
139 deps.storage,
140 &owner_addr,
141 |balance: Option<Uint128>| -> StdResult<_> {
142 Ok(balance.unwrap_or_default().checked_sub(amount)?)
143 },
144 )?;
145 BALANCES.update(
146 deps.storage,
147 &rcpt_addr,
148 |balance: Option<Uint128>| -> StdResult<_> { Ok(balance.unwrap_or_default() + amount) },
149 )?;
150
151 let res = Response::new().add_attributes(vec![
152 attr("action", "transfer_from"),
153 attr("from", owner),
154 attr("to", recipient),
155 attr("by", info.sender),
156 attr("amount", amount),
157 ]);
158 Ok(res)
159}
160
161pub fn execute_burn_from(
162 deps: DepsMut,
163
164 env: Env,
165 info: MessageInfo,
166 owner: String,
167 amount: Uint128,
168) -> Result<Response, ContractError> {
169 let owner_addr = deps.api.addr_validate(&owner)?;
170
171 deduct_allowance(deps.storage, &owner_addr, &info.sender, &env.block, amount)?;
173
174 BALANCES.update(
176 deps.storage,
177 &owner_addr,
178 |balance: Option<Uint128>| -> StdResult<_> {
179 Ok(balance.unwrap_or_default().checked_sub(amount)?)
180 },
181 )?;
182 TOKEN_INFO.update(deps.storage, |mut meta| -> StdResult<_> {
184 meta.total_supply = meta.total_supply.checked_sub(amount)?;
185 Ok(meta)
186 })?;
187
188 let res = Response::new().add_attributes(vec![
189 attr("action", "burn_from"),
190 attr("from", owner),
191 attr("by", info.sender),
192 attr("amount", amount),
193 ]);
194 Ok(res)
195}
196
197pub fn execute_send_from(
198 deps: DepsMut,
199 env: Env,
200 info: MessageInfo,
201 owner: String,
202 contract: String,
203 amount: Uint128,
204 msg: Binary,
205) -> Result<Response, ContractError> {
206 let rcpt_addr = deps.api.addr_validate(&contract)?;
207 let owner_addr = deps.api.addr_validate(&owner)?;
208
209 deduct_allowance(deps.storage, &owner_addr, &info.sender, &env.block, amount)?;
211
212 BALANCES.update(
214 deps.storage,
215 &owner_addr,
216 |balance: Option<Uint128>| -> StdResult<_> {
217 Ok(balance.unwrap_or_default().checked_sub(amount)?)
218 },
219 )?;
220 BALANCES.update(
221 deps.storage,
222 &rcpt_addr,
223 |balance: Option<Uint128>| -> StdResult<_> { Ok(balance.unwrap_or_default() + amount) },
224 )?;
225
226 let attrs = vec![
227 attr("action", "send_from"),
228 attr("from", &owner),
229 attr("to", &contract),
230 attr("by", &info.sender),
231 attr("amount", amount),
232 ];
233
234 let msg = Cw20ReceiveMsg {
236 sender: info.sender.into(),
237 amount,
238 msg,
239 }
240 .into_cosmos_msg(contract)?;
241
242 let res = Response::new().add_message(msg).add_attributes(attrs);
243 Ok(res)
244}
245
246pub fn query_allowance(deps: Deps, owner: String, spender: String) -> StdResult<AllowanceResponse> {
247 let owner_addr = deps.api.addr_validate(&owner)?;
248 let spender_addr = deps.api.addr_validate(&spender)?;
249 let allowance = ALLOWANCES
250 .may_load(deps.storage, (&owner_addr, &spender_addr))?
251 .unwrap_or_default();
252 Ok(allowance)
253}
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258
259 use cosmwasm_std::testing::{mock_dependencies_with_balance, mock_env, mock_info};
260 use cosmwasm_std::{coins, CosmosMsg, SubMsg, Timestamp, WasmMsg};
261 use cw20::{Cw20Coin, TokenInfoResponse};
262
263 use crate::contract::{execute, instantiate, query_balance, query_token_info};
264 use crate::msg::{ExecuteMsg, InstantiateMsg};
265
266 fn get_balance<T: Into<String>>(deps: Deps, address: T) -> Uint128 {
267 query_balance(deps, address.into()).unwrap().balance
268 }
269
270 fn do_instantiate<T: Into<String>>(
272 mut deps: DepsMut,
273 addr: T,
274 amount: Uint128,
275 ) -> TokenInfoResponse {
276 let instantiate_msg = InstantiateMsg {
277 name: "Auto Gen".to_string(),
278 symbol: "AUTO".to_string(),
279 decimals: 3,
280 initial_balances: vec![Cw20Coin {
281 address: addr.into(),
282 amount,
283 }],
284 mint: None,
285 marketing: None,
286 };
287 let info = mock_info("creator", &[]);
288 let env = mock_env();
289 instantiate(deps.branch(), env, info, instantiate_msg).unwrap();
290 query_token_info(deps.as_ref()).unwrap()
291 }
292
293 #[test]
294 fn increase_decrease_allowances() {
295 let mut deps = mock_dependencies_with_balance(&coins(2, "token"));
296
297 let owner = deps.api.addr_make("addr0001").to_string();
298 let spender = deps.api.addr_make("addr0002").to_string();
299 let info = mock_info(owner.as_ref(), &[]);
300 let env = mock_env();
301 do_instantiate(deps.as_mut(), owner.clone(), Uint128::new(12340000));
302
303 let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap();
305 assert_eq!(allowance, AllowanceResponse::default());
306
307 let allow1 = Uint128::new(7777);
309 let expires = Expiration::AtHeight(123_456);
310 let msg = ExecuteMsg::IncreaseAllowance {
311 spender: spender.clone(),
312 amount: allow1,
313 expires: Some(expires),
314 };
315 execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap();
316
317 let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap();
319 assert_eq!(
320 allowance,
321 AllowanceResponse {
322 allowance: allow1,
323 expires
324 }
325 );
326
327 let lower = Uint128::new(4444);
329 let allow2 = allow1.checked_sub(lower).unwrap();
330 let msg = ExecuteMsg::DecreaseAllowance {
331 spender: spender.clone(),
332 amount: lower,
333 expires: None,
334 };
335 execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap();
336 let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap();
337 assert_eq!(
338 allowance,
339 AllowanceResponse {
340 allowance: allow2,
341 expires
342 }
343 );
344
345 let raise = Uint128::new(87654);
347 let allow3 = allow2 + raise;
348 let new_expire = Expiration::AtTime(Timestamp::from_seconds(8888888888));
349 let msg = ExecuteMsg::IncreaseAllowance {
350 spender: spender.clone(),
351 amount: raise,
352 expires: Some(new_expire),
353 };
354 execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap();
355 let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap();
356 assert_eq!(
357 allowance,
358 AllowanceResponse {
359 allowance: allow3,
360 expires: new_expire
361 }
362 );
363
364 let msg = ExecuteMsg::DecreaseAllowance {
366 spender: spender.clone(),
367 amount: Uint128::new(99988647623876347),
368 expires: None,
369 };
370 execute(deps.as_mut(), env, info, msg).unwrap();
371 let allowance = query_allowance(deps.as_ref(), owner, spender).unwrap();
372 assert_eq!(allowance, AllowanceResponse::default());
373 }
374
375 #[test]
376 fn allowances_independent() {
377 let mut deps = mock_dependencies_with_balance(&coins(2, "token"));
378
379 let owner = deps.api.addr_make("addr0001").to_string();
380 let spender = deps.api.addr_make("addr0002").to_string();
381 let spender2 = deps.api.addr_make("addr0003").to_string();
382 let info = mock_info(owner.as_ref(), &[]);
383 let env = mock_env();
384 do_instantiate(deps.as_mut(), &owner, Uint128::new(12340000));
385
386 assert_eq!(
388 query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(),
389 AllowanceResponse::default()
390 );
391 assert_eq!(
392 query_allowance(deps.as_ref(), owner.clone(), spender2.clone()).unwrap(),
393 AllowanceResponse::default()
394 );
395 assert_eq!(
396 query_allowance(deps.as_ref(), spender.clone(), spender2.clone()).unwrap(),
397 AllowanceResponse::default()
398 );
399
400 let allow1 = Uint128::new(7777);
402 let expires = Expiration::AtHeight(123_456);
403 let msg = ExecuteMsg::IncreaseAllowance {
404 spender: spender.clone(),
405 amount: allow1,
406 expires: Some(expires),
407 };
408 execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap();
409
410 let allow2 = Uint128::new(87654);
412 let msg = ExecuteMsg::IncreaseAllowance {
413 spender: spender2.clone(),
414 amount: allow2,
415 expires: None,
416 };
417 execute(deps.as_mut(), env, info, msg).unwrap();
418
419 let expect_one = AllowanceResponse {
421 allowance: allow1,
422 expires,
423 };
424 let expect_two = AllowanceResponse {
425 allowance: allow2,
426 expires: Expiration::Never {},
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.clone(), spender2.clone()).unwrap(),
434 expect_two
435 );
436 assert_eq!(
437 query_allowance(deps.as_ref(), spender.clone(), spender2.clone()).unwrap(),
438 AllowanceResponse::default()
439 );
440
441 let info = mock_info(spender.as_ref(), &[]);
443 let env = mock_env();
444 let allow3 = Uint128::new(1821);
445 let expires3 = Expiration::AtTime(Timestamp::from_seconds(3767626296));
446 let msg = ExecuteMsg::IncreaseAllowance {
447 spender: spender2.clone(),
448 amount: allow3,
449 expires: Some(expires3),
450 };
451 execute(deps.as_mut(), env, info, msg).unwrap();
452 let expect_three = AllowanceResponse {
453 allowance: allow3,
454 expires: expires3,
455 };
456 assert_eq!(
457 query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap(),
458 expect_one
459 );
460 assert_eq!(
461 query_allowance(deps.as_ref(), owner, spender2.clone()).unwrap(),
462 expect_two
463 );
464 assert_eq!(
465 query_allowance(deps.as_ref(), spender, spender2).unwrap(),
466 expect_three
467 );
468 }
469
470 #[test]
471 fn no_self_allowance() {
472 let mut deps = mock_dependencies_with_balance(&coins(2, "token"));
473
474 let owner = deps.api.addr_make("addr0001").to_string();
475 let info = mock_info(owner.as_ref(), &[]);
476 let env = mock_env();
477 do_instantiate(deps.as_mut(), &owner, Uint128::new(12340000));
478
479 let msg = ExecuteMsg::IncreaseAllowance {
481 spender: owner.clone(),
482 amount: Uint128::new(7777),
483 expires: None,
484 };
485 let err = execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap_err();
486 assert_eq!(err, ContractError::CannotSetOwnAccount {});
487
488 let msg = ExecuteMsg::DecreaseAllowance {
490 spender: owner,
491 amount: Uint128::new(7777),
492 expires: None,
493 };
494 let err = execute(deps.as_mut(), env, info, msg).unwrap_err();
495 assert_eq!(err, ContractError::CannotSetOwnAccount {});
496 }
497
498 #[test]
499 fn transfer_from_respects_limits() {
500 let mut deps = mock_dependencies_with_balance(&[]);
501 let owner = deps.api.addr_make("addr0001").to_string();
502 let spender = deps.api.addr_make("addr0002").to_string();
503 let rcpt = deps.api.addr_make("addr0003").to_string();
504
505 let start = Uint128::new(999999);
506 do_instantiate(deps.as_mut(), &owner, start);
507
508 let allow1 = Uint128::new(77777);
510 let msg = ExecuteMsg::IncreaseAllowance {
511 spender: spender.clone(),
512 amount: allow1,
513 expires: None,
514 };
515 let info = mock_info(owner.as_ref(), &[]);
516 let env = mock_env();
517 execute(deps.as_mut(), env, info, msg).unwrap();
518
519 let transfer = Uint128::new(44444);
521 let msg = ExecuteMsg::TransferFrom {
522 owner: owner.clone(),
523 recipient: rcpt.clone(),
524 amount: transfer,
525 };
526 let info = mock_info(spender.as_ref(), &[]);
527 let env = mock_env();
528 let res = execute(deps.as_mut(), env, info, msg).unwrap();
529 assert_eq!(res.attributes[0], attr("action", "transfer_from"));
530
531 assert_eq!(
533 get_balance(deps.as_ref(), owner.clone()),
534 start.checked_sub(transfer).unwrap()
535 );
536 assert_eq!(get_balance(deps.as_ref(), rcpt.clone()), transfer);
537
538 let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap();
540 let expect = AllowanceResponse {
541 allowance: allow1.checked_sub(transfer).unwrap(),
542 expires: Expiration::Never {},
543 };
544 assert_eq!(expect, allowance);
545
546 let msg = ExecuteMsg::TransferFrom {
548 owner: owner.clone(),
549 recipient: rcpt.clone(),
550 amount: Uint128::new(33443),
551 };
552 let info = mock_info(spender.as_ref(), &[]);
553 let env = mock_env();
554 let err = execute(deps.as_mut(), env, info, msg).unwrap_err();
555 assert!(matches!(err, ContractError::Std(StdError::Overflow { .. })));
556
557 let info = mock_info(owner.as_ref(), &[]);
559 let mut env = mock_env();
560 let msg = ExecuteMsg::IncreaseAllowance {
561 spender: spender.clone(),
562 amount: Uint128::new(1000),
563 expires: Some(Expiration::AtHeight(env.block.height + 1)),
564 };
565 execute(deps.as_mut(), env.clone(), info, msg).unwrap();
566
567 env.block.height += 1;
568
569 let msg = ExecuteMsg::TransferFrom {
571 owner,
572 recipient: rcpt,
573 amount: Uint128::new(33443),
574 };
575 let info = mock_info(spender.as_ref(), &[]);
576 let err = execute(deps.as_mut(), env, info, msg).unwrap_err();
577 assert_eq!(err, ContractError::Expired {});
578 }
579
580 #[test]
581 fn burn_from_respects_limits() {
582 let mut deps = mock_dependencies_with_balance(&[]);
583 let owner = deps.api.addr_make("addr0001").to_string();
584 let spender = deps.api.addr_make("addr0002").to_string();
585
586 let start = Uint128::new(999999);
587 do_instantiate(deps.as_mut(), &owner, start);
588
589 let allow1 = Uint128::new(77777);
591 let msg = ExecuteMsg::IncreaseAllowance {
592 spender: spender.clone(),
593 amount: allow1,
594 expires: None,
595 };
596 let info = mock_info(owner.as_ref(), &[]);
597 let env = mock_env();
598 execute(deps.as_mut(), env, info, msg).unwrap();
599
600 let transfer = Uint128::new(44444);
602 let msg = ExecuteMsg::BurnFrom {
603 owner: owner.clone(),
604 amount: transfer,
605 };
606 let info = mock_info(spender.as_ref(), &[]);
607 let env = mock_env();
608 let res = execute(deps.as_mut(), env, info, msg).unwrap();
609 assert_eq!(res.attributes[0], attr("action", "burn_from"));
610
611 assert_eq!(
613 get_balance(deps.as_ref(), owner.clone()),
614 start.checked_sub(transfer).unwrap()
615 );
616
617 let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap();
619 let expect = AllowanceResponse {
620 allowance: allow1.checked_sub(transfer).unwrap(),
621 expires: Expiration::Never {},
622 };
623 assert_eq!(expect, allowance);
624
625 let msg = ExecuteMsg::BurnFrom {
627 owner: owner.clone(),
628 amount: Uint128::new(33443),
629 };
630 let info = mock_info(spender.as_ref(), &[]);
631 let env = mock_env();
632 let err = execute(deps.as_mut(), env, info, msg).unwrap_err();
633 assert!(matches!(err, ContractError::Std(StdError::Overflow { .. })));
634
635 let info = mock_info(owner.as_ref(), &[]);
637 let mut env = mock_env();
638 let msg = ExecuteMsg::IncreaseAllowance {
639 spender: spender.clone(),
640 amount: Uint128::new(1000),
641 expires: Some(Expiration::AtHeight(env.block.height + 1)),
642 };
643 execute(deps.as_mut(), env.clone(), info, msg).unwrap();
644
645 env.block.height += 1;
647
648 let msg = ExecuteMsg::BurnFrom {
650 owner,
651 amount: Uint128::new(33443),
652 };
653 let info = mock_info(spender.as_ref(), &[]);
654 let err = execute(deps.as_mut(), env, info, msg).unwrap_err();
655 assert_eq!(err, ContractError::Expired {});
656 }
657
658 #[test]
659 fn send_from_respects_limits() {
660 let mut deps = mock_dependencies_with_balance(&[]);
661 let owner = deps.api.addr_make("addr0001").to_string();
662 let spender = deps.api.addr_make("addr0002").to_string();
663 let contract = deps.api.addr_make("addr0003").to_string();
664 let send_msg = Binary::from(r#"{"some":123}"#.as_bytes());
665
666 let start = Uint128::new(999999);
667 do_instantiate(deps.as_mut(), &owner, start);
668
669 let allow1 = Uint128::new(77777);
671 let msg = ExecuteMsg::IncreaseAllowance {
672 spender: spender.clone(),
673 amount: allow1,
674 expires: None,
675 };
676 let info = mock_info(owner.as_ref(), &[]);
677 let env = mock_env();
678 execute(deps.as_mut(), env, info, msg).unwrap();
679
680 let transfer = Uint128::new(44444);
682 let msg = ExecuteMsg::SendFrom {
683 owner: owner.clone(),
684 amount: transfer,
685 contract: contract.clone(),
686 msg: send_msg.clone(),
687 };
688 let info = mock_info(spender.as_ref(), &[]);
689 let env = mock_env();
690 let res = execute(deps.as_mut(), env, info, msg).unwrap();
691 assert_eq!(res.attributes[0], attr("action", "send_from"));
692 assert_eq!(1, res.messages.len());
693
694 let binary_msg = Cw20ReceiveMsg {
696 sender: spender.clone(),
697 amount: transfer,
698 msg: send_msg.clone(),
699 }
700 .into_json_binary()
701 .unwrap();
702 assert_eq!(
703 res.messages[0],
704 SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute {
705 contract_addr: contract.clone(),
706 msg: binary_msg,
707 funds: vec![],
708 }))
709 );
710
711 assert_eq!(
713 get_balance(deps.as_ref(), owner.clone()),
714 start.checked_sub(transfer).unwrap()
715 );
716 assert_eq!(get_balance(deps.as_ref(), contract.clone()), transfer);
717
718 let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap();
720 let expect = AllowanceResponse {
721 allowance: allow1.checked_sub(transfer).unwrap(),
722 expires: Expiration::Never {},
723 };
724 assert_eq!(expect, allowance);
725
726 let msg = ExecuteMsg::SendFrom {
728 owner: owner.clone(),
729 amount: Uint128::new(33443),
730 contract: contract.clone(),
731 msg: send_msg.clone(),
732 };
733 let info = mock_info(spender.as_ref(), &[]);
734 let env = mock_env();
735 let err = execute(deps.as_mut(), env, info, msg).unwrap_err();
736 assert!(matches!(err, ContractError::Std(StdError::Overflow { .. })));
737
738 let info = mock_info(owner.as_ref(), &[]);
740 let mut env = mock_env();
741 let msg = ExecuteMsg::IncreaseAllowance {
742 spender: spender.clone(),
743 amount: Uint128::new(1000),
744 expires: Some(Expiration::AtHeight(env.block.height + 1)),
745 };
746 execute(deps.as_mut(), env.clone(), info, msg).unwrap();
747
748 env.block.height += 1;
750
751 let msg = ExecuteMsg::SendFrom {
753 owner,
754 amount: Uint128::new(33443),
755 contract,
756 msg: send_msg,
757 };
758 let info = mock_info(spender.as_ref(), &[]);
759 let err = execute(deps.as_mut(), env, info, msg).unwrap_err();
760 assert_eq!(err, ContractError::Expired {});
761 }
762
763 #[test]
764 fn no_past_expiration() {
765 let mut deps = mock_dependencies_with_balance(&coins(2, "token"));
766
767 let owner = deps.api.addr_make("addr0001").to_string();
768 let spender = deps.api.addr_make("addr0002").to_string();
769 let info = mock_info(owner.as_ref(), &[]);
770 let env = mock_env();
771 do_instantiate(deps.as_mut(), owner.clone(), Uint128::new(12340000));
772
773 let expires = Expiration::AtHeight(env.block.height);
775 let msg = ExecuteMsg::IncreaseAllowance {
776 spender: spender.clone(),
777 amount: Uint128::new(7777),
778 expires: Some(expires),
779 };
780
781 assert_eq!(
783 Err(ContractError::InvalidExpiration {}),
784 execute(deps.as_mut(), env.clone(), info.clone(), msg)
785 );
786
787 let expires = Expiration::AtTime(env.block.time.minus_seconds(1));
789 let msg = ExecuteMsg::IncreaseAllowance {
790 spender: spender.clone(),
791 amount: Uint128::new(7777),
792 expires: Some(expires),
793 };
794
795 assert_eq!(
797 Err(ContractError::InvalidExpiration {}),
798 execute(deps.as_mut(), env.clone(), info.clone(), msg)
799 );
800
801 let expires = Expiration::AtHeight(env.block.height + 1);
803 let allow = Uint128::new(7777);
804 let msg = ExecuteMsg::IncreaseAllowance {
805 spender: spender.clone(),
806 amount: allow,
807 expires: Some(expires),
808 };
809
810 execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap();
811
812 let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap();
814 assert_eq!(
815 allowance,
816 AllowanceResponse {
817 allowance: allow,
818 expires
819 }
820 );
821
822 let expires = Expiration::AtTime(env.block.time.plus_seconds(10));
824 let allow = Uint128::new(7777);
825 let msg = ExecuteMsg::IncreaseAllowance {
826 spender: spender.clone(),
827 amount: allow,
828 expires: Some(expires),
829 };
830
831 execute(deps.as_mut(), env.clone(), info.clone(), msg).unwrap();
832
833 let allowance = query_allowance(deps.as_ref(), owner.clone(), spender.clone()).unwrap();
835 assert_eq!(
836 allowance,
837 AllowanceResponse {
838 allowance: allow + allow, expires
840 }
841 );
842
843 let expires = Expiration::AtHeight(env.block.height);
845 let allow = Uint128::new(7777);
846 let msg = ExecuteMsg::IncreaseAllowance {
847 spender: spender.clone(),
848 amount: allow,
849 expires: Some(expires),
850 };
851
852 assert_eq!(
854 Err(ContractError::InvalidExpiration {}),
855 execute(deps.as_mut(), env.clone(), info.clone(), msg)
856 );
857
858 let expires = Expiration::AtHeight(env.block.height + 1);
860 let allow = Uint128::new(7777);
861 let msg = ExecuteMsg::DecreaseAllowance {
862 spender: spender.clone(),
863 amount: allow,
864 expires: Some(expires),
865 };
866
867 execute(deps.as_mut(), env, info, msg).unwrap();
868
869 let allowance = query_allowance(deps.as_ref(), owner, spender).unwrap();
871 assert_eq!(
872 allowance,
873 AllowanceResponse {
874 allowance: allow,
875 expires
876 }
877 );
878 }
879}