github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/dev/wasm/erc20/src/contract.rs (about) 1 use cosmwasm_std::{ 2 entry_point, to_binary, to_vec, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, 3 StdResult, Storage, Uint128,SubMsg,CosmosMsg 4 }; 5 use cosmwasm_storage::{PrefixedStorage, ReadonlyPrefixedStorage}; 6 use std::convert::TryInto; 7 8 use crate::error::ContractError; 9 use crate::msg::{AllowanceResponse, BalanceResponse, ExecuteMsg, InstantiateMsg, QueryMsg,SendToEvmMsg}; 10 use crate::state::Constants; 11 12 pub const PREFIX_CONFIG: &[u8] = b"config"; 13 pub const PREFIX_BALANCES: &[u8] = b"balances"; 14 pub const PREFIX_ALLOWANCES: &[u8] = b"allowances"; 15 16 pub const KEY_CONSTANTS: &[u8] = b"constants"; 17 pub const KEY_TOTAL_SUPPLY: &[u8] = b"total_supply"; 18 const EVM_CONTRACT_ADDR: &str = "ex19yaqyv090mjenkenw3d7lxlucvstg00p2r45pk"; 19 20 #[entry_point] 21 pub fn instantiate( 22 deps: DepsMut, 23 _env: Env, 24 _info: MessageInfo, 25 msg: InstantiateMsg, 26 ) -> Result<Response, ContractError> { 27 let mut total_supply: u128 = 0; 28 { 29 // Initial balances 30 let mut balances_store = PrefixedStorage::new(deps.storage, PREFIX_BALANCES); 31 for row in msg.initial_balances { 32 let amount_raw = row.amount.u128(); 33 balances_store.set(row.address.as_str().as_bytes(), &amount_raw.to_be_bytes()); 34 total_supply += amount_raw; 35 } 36 } 37 38 // Check name, symbol, decimals 39 if !is_valid_name(&msg.name) { 40 return Err(ContractError::NameWrongFormat {}); 41 } 42 if !is_valid_symbol(&msg.symbol) { 43 return Err(ContractError::TickerWrongSymbolFormat {}); 44 } 45 if msg.decimals > 18 { 46 return Err(ContractError::DecimalsExceeded {}); 47 } 48 49 let mut config_store = PrefixedStorage::new(deps.storage, PREFIX_CONFIG); 50 let constants = to_vec(&Constants { 51 name: msg.name, 52 symbol: msg.symbol, 53 decimals: msg.decimals, 54 })?; 55 config_store.set(KEY_CONSTANTS, &constants); 56 config_store.set(KEY_TOTAL_SUPPLY, &total_supply.to_be_bytes()); 57 58 Ok(Response::default()) 59 } 60 61 #[entry_point] 62 pub fn execute( 63 deps: DepsMut, 64 env: Env, 65 info: MessageInfo, 66 msg: ExecuteMsg, 67 ) -> Result<Response<SendToEvmMsg>, ContractError> { 68 match msg { 69 ExecuteMsg::Approve { spender, amount } => try_approve(deps, env, info, spender, &amount), 70 ExecuteMsg::Transfer { recipient, amount } => { 71 try_transfer(deps, env, info, recipient, &amount) 72 } 73 ExecuteMsg::TransferFrom { 74 owner, 75 recipient, 76 amount, 77 } => try_transfer_from(deps, env, info, owner, recipient, &amount), 78 ExecuteMsg::Burn { amount } => try_burn(deps, env, info, &amount), 79 ExecuteMsg::MintCW20 { 80 recipient, 81 amount, 82 } => try_mint_cw20(deps, env,info,recipient,amount), 83 84 ExecuteMsg::SendToEvm { 85 evmContract, 86 recipient, 87 amount, 88 } => try_send_to_erc20(deps, env,evmContract,recipient,amount,info), 89 } 90 } 91 92 #[entry_point] 93 pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result<Binary, ContractError> { 94 match msg { 95 QueryMsg::Balance { address } => { 96 let address_key = deps.api.addr_validate(&address)?; 97 let balance = read_balance(deps.storage, &address_key)?; 98 let out = to_binary(&BalanceResponse { 99 balance: Uint128::from(balance), 100 })?; 101 Ok(out) 102 } 103 QueryMsg::Allowance { owner, spender } => { 104 let owner_key = deps.api.addr_validate(&owner)?; 105 let spender_key = deps.api.addr_validate(&spender)?; 106 let allowance = read_allowance(deps.storage, &owner_key, &spender_key)?; 107 let out = to_binary(&AllowanceResponse { 108 allowance: Uint128::from(allowance), 109 })?; 110 Ok(out) 111 } 112 } 113 } 114 115 fn try_mint_cw20( 116 deps: DepsMut, 117 _env: Env, 118 info: MessageInfo, 119 recipient: String, 120 amount: Uint128, 121 ) -> Result<Response<SendToEvmMsg>, ContractError> { 122 if info.sender.to_string() != EVM_CONTRACT_ADDR.to_string() { 123 return Err(ContractError::ContractERC20Err { 124 addr:info.sender.to_string() 125 }); 126 } 127 let amount_raw = amount.u128(); 128 let recipient_address = deps.api.addr_validate(recipient.as_str())?; 129 let mut account_balance = read_balance(deps.storage, &recipient_address)?; 130 131 132 account_balance += amount_raw; 133 134 let mut balances_store = PrefixedStorage::new(deps.storage, PREFIX_BALANCES); 135 balances_store.set( 136 &recipient_address.as_str().as_bytes(), 137 &account_balance.to_be_bytes(), 138 ); 139 140 let mut config_store = PrefixedStorage::new(deps.storage, PREFIX_CONFIG); 141 let data = config_store 142 .get(KEY_TOTAL_SUPPLY) 143 .expect("no total supply data stored"); 144 let mut total_supply = bytes_to_u128(&data).unwrap(); 145 146 total_supply += amount_raw; 147 148 config_store.set(KEY_TOTAL_SUPPLY, &total_supply.to_be_bytes()); 149 150 Ok(Response::new() 151 .add_attribute("action", "MINT") 152 .add_attribute("account", recipient_address) 153 .add_attribute("sender", info.sender.to_string()) 154 .add_attribute("amount", amount.to_string())) 155 } 156 157 fn try_send_to_erc20( 158 deps: DepsMut, 159 _env: Env, 160 erc20: String, 161 recipient: String, 162 amount: Uint128, 163 info: MessageInfo, 164 ) -> Result<Response<SendToEvmMsg>, ContractError> { 165 let amount_raw = amount.u128(); 166 let to = info.sender; 167 168 let mut account_balance = read_balance(deps.storage, &to)?; 169 170 if account_balance < amount_raw { 171 return Err(ContractError::InsufficientFunds { 172 balance: account_balance, 173 required: amount_raw, 174 }); 175 } 176 account_balance -= amount_raw; 177 178 let mut balances_store = PrefixedStorage::new(deps.storage, PREFIX_BALANCES); 179 balances_store.set( 180 to.as_str().as_bytes(), 181 &account_balance.to_be_bytes(), 182 ); 183 184 let mut config_store = PrefixedStorage::new(deps.storage, PREFIX_CONFIG); 185 let data = config_store 186 .get(KEY_TOTAL_SUPPLY) 187 .expect("no total supply data stored"); 188 let mut total_supply = bytes_to_u128(&data).unwrap(); 189 190 total_supply -= amount_raw; 191 192 config_store.set(KEY_TOTAL_SUPPLY, &total_supply.to_be_bytes()); 193 194 // callevm(_env,erc20,recipient,amount); 195 // Ok(Response::new() 196 // .add_attribute("action", "try_send_to_erc20") 197 // .add_attribute("account", to.to_string()) 198 // .add_attribute("amount", amount.to_string())) 199 200 let hehe = SendToEvmMsg { 201 sender: _env.contract.address.to_string(), 202 contract: erc20.to_string(), 203 recipient: recipient, 204 amount: amount, 205 }; 206 207 Ok(Response::new() 208 .add_attribute("action", "call evm") 209 .add_attribute("amount", amount.to_string()) 210 .add_message(hehe) 211 .set_data(b"the result data")) 212 } 213 214 // fn callevm( 215 // _env: Env, 216 // erc20: String, 217 // recipient: String, 218 // amount: Uint128, 219 // ) -> Result<Response<crate::msg::SendToEvmMsg>, ContractError> { 220 // let message = crate::msg::SendToEvmMsg::SendToEvm { 221 // sender: _env.contract.address.to_string(), 222 // contract: erc20.to_string(), 223 // recipient: recipient, 224 // amount: amount, 225 // }; 226 // 227 // Ok(Response::new() 228 // .add_attribute("action", "call evm") 229 // .add_attribute("amount", amount.to_string()) 230 // .add_message(message)) 231 // } 232 fn try_transfer( 233 deps: DepsMut, 234 _env: Env, 235 info: MessageInfo, 236 recipient: String, 237 amount: &Uint128, 238 ) -> Result<Response<SendToEvmMsg>, ContractError> { 239 perform_transfer( 240 deps.storage, 241 &info.sender, 242 &deps.api.addr_validate(recipient.as_str())?, 243 amount.u128(), 244 )?; 245 Ok(Response::new() 246 .add_attribute("action", "transfer") 247 .add_attribute("sender", info.sender) 248 .add_attribute("recipient", recipient)) 249 } 250 251 fn try_transfer_from( 252 deps: DepsMut, 253 _env: Env, 254 info: MessageInfo, 255 owner: String, 256 recipient: String, 257 amount: &Uint128, 258 ) -> Result<Response<SendToEvmMsg>, ContractError> { 259 let owner_address = deps.api.addr_validate(owner.as_str())?; 260 let recipient_address = deps.api.addr_validate(recipient.as_str())?; 261 let amount_raw = amount.u128(); 262 263 let mut allowance = read_allowance(deps.storage, &owner_address, &info.sender)?; 264 if allowance < amount_raw { 265 return Err(ContractError::InsufficientAllowance { 266 allowance, 267 required: amount_raw, 268 }); 269 } 270 allowance -= amount_raw; 271 write_allowance(deps.storage, &owner_address, &info.sender, allowance)?; 272 perform_transfer(deps.storage, &owner_address, &recipient_address, amount_raw)?; 273 274 Ok(Response::new() 275 .add_attribute("action", "transfer_from") 276 .add_attribute("spender", &info.sender) 277 .add_attribute("sender", owner) 278 .add_attribute("recipient", recipient)) 279 } 280 281 fn try_approve( 282 deps: DepsMut, 283 _env: Env, 284 info: MessageInfo, 285 spender: String, 286 amount: &Uint128, 287 ) -> Result<Response<SendToEvmMsg>, ContractError> { 288 let spender_address = deps.api.addr_validate(spender.as_str())?; 289 write_allowance(deps.storage, &info.sender, &spender_address, amount.u128())?; 290 Ok(Response::new() 291 .add_attribute("action", "approve") 292 .add_attribute("owner", info.sender) 293 .add_attribute("spender", spender)) 294 } 295 296 /// Burn tokens 297 /// 298 /// Remove `amount` tokens from the system irreversibly, from signer account 299 /// 300 /// @param amount the amount of money to burn 301 fn try_burn( 302 deps: DepsMut, 303 _env: Env, 304 info: MessageInfo, 305 amount: &Uint128, 306 ) -> Result<Response<SendToEvmMsg>, ContractError> { 307 let amount_raw = amount.u128(); 308 309 let mut account_balance = read_balance(deps.storage, &info.sender)?; 310 311 if account_balance < amount_raw { 312 return Err(ContractError::InsufficientFunds { 313 balance: account_balance, 314 required: amount_raw, 315 }); 316 } 317 account_balance -= amount_raw; 318 319 let mut balances_store = PrefixedStorage::new(deps.storage, PREFIX_BALANCES); 320 balances_store.set( 321 info.sender.as_str().as_bytes(), 322 &account_balance.to_be_bytes(), 323 ); 324 325 let mut config_store = PrefixedStorage::new(deps.storage, PREFIX_CONFIG); 326 let data = config_store 327 .get(KEY_TOTAL_SUPPLY) 328 .expect("no total supply data stored"); 329 let mut total_supply = bytes_to_u128(&data).unwrap(); 330 331 total_supply -= amount_raw; 332 333 config_store.set(KEY_TOTAL_SUPPLY, &total_supply.to_be_bytes()); 334 335 Ok(Response::new() 336 .add_attribute("action", "burn") 337 .add_attribute("account", info.sender) 338 .add_attribute("amount", amount.to_string())) 339 } 340 341 fn perform_transfer( 342 store: &mut dyn Storage, 343 from: &Addr, 344 to: &Addr, 345 amount: u128, 346 ) -> Result<(), ContractError> { 347 let mut balances_store = PrefixedStorage::new(store, PREFIX_BALANCES); 348 349 let mut from_balance = match balances_store.get(from.as_str().as_bytes()) { 350 Some(data) => bytes_to_u128(&data), 351 None => Ok(0u128), 352 }?; 353 354 if from_balance < amount { 355 return Err(ContractError::InsufficientFunds { 356 balance: from_balance, 357 required: amount, 358 }); 359 } 360 from_balance -= amount; 361 balances_store.set(from.as_str().as_bytes(), &from_balance.to_be_bytes()); 362 363 let mut to_balance = match balances_store.get(to.as_str().as_bytes()) { 364 Some(data) => bytes_to_u128(&data), 365 None => Ok(0u128), 366 }?; 367 to_balance += amount; 368 balances_store.set(to.as_str().as_bytes(), &to_balance.to_be_bytes()); 369 370 Ok(()) 371 } 372 373 // Converts 16 bytes value into u128 374 // Errors if data found that is not 16 bytes 375 pub fn bytes_to_u128(data: &[u8]) -> Result<u128, ContractError> { 376 match data[0..16].try_into() { 377 Ok(bytes) => Ok(u128::from_be_bytes(bytes)), 378 Err(_) => Err(ContractError::CorruptedDataFound {}), 379 } 380 } 381 382 // Reads 16 byte storage value into u128 383 // Returns zero if key does not exist. Errors if data found that is not 16 bytes 384 pub fn read_u128(store: &ReadonlyPrefixedStorage, key: &Addr) -> Result<u128, ContractError> { 385 let result = store.get(key.as_str().as_bytes()); 386 match result { 387 Some(data) => bytes_to_u128(&data), 388 None => Ok(0u128), 389 } 390 } 391 392 fn read_balance(store: &dyn Storage, owner: &Addr) -> Result<u128, ContractError> { 393 let balance_store = ReadonlyPrefixedStorage::new(store, PREFIX_BALANCES); 394 read_u128(&balance_store, owner) 395 } 396 397 fn read_allowance( 398 store: &dyn Storage, 399 owner: &Addr, 400 spender: &Addr, 401 ) -> Result<u128, ContractError> { 402 let owner_store = 403 ReadonlyPrefixedStorage::multilevel(store, &[PREFIX_ALLOWANCES, owner.as_str().as_bytes()]); 404 read_u128(&owner_store, spender) 405 } 406 407 #[allow(clippy::unnecessary_wraps)] 408 fn write_allowance( 409 store: &mut dyn Storage, 410 owner: &Addr, 411 spender: &Addr, 412 amount: u128, 413 ) -> StdResult<()> { 414 let mut owner_store = 415 PrefixedStorage::multilevel(store, &[PREFIX_ALLOWANCES, owner.as_str().as_bytes()]); 416 owner_store.set(spender.as_str().as_bytes(), &amount.to_be_bytes()); 417 Ok(()) 418 } 419 420 fn is_valid_name(name: &str) -> bool { 421 let bytes = name.as_bytes(); 422 if bytes.len() < 3 || bytes.len() > 30 { 423 return false; 424 } 425 true 426 } 427 428 fn is_valid_symbol(symbol: &str) -> bool { 429 let bytes = symbol.as_bytes(); 430 if bytes.len() < 3 || bytes.len() > 6 { 431 return false; 432 } 433 for byte in bytes.iter() { 434 if *byte < 65 || *byte > 90 { 435 return false; 436 } 437 } 438 true 439 } 440 441 #[cfg(test)] 442 mod tests { 443 use super::*; 444 use crate::msg::InitialBalance; 445 use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; 446 use cosmwasm_std::{from_slice, Addr, Env, MessageInfo, Storage, Timestamp, Uint128}; 447 use cosmwasm_storage::ReadonlyPrefixedStorage; 448 449 fn mock_env_height(signer: &str, height: u64, time: u64) -> (Env, MessageInfo) { 450 let mut env = mock_env(); 451 let info = mock_info(signer, &[]); 452 env.block.height = height; 453 env.block.time = Timestamp::from_seconds(time); 454 (env, info) 455 } 456 457 fn get_constants(storage: &dyn Storage) -> Constants { 458 let config_storage = ReadonlyPrefixedStorage::new(storage, PREFIX_CONFIG); 459 let data = config_storage 460 .get(KEY_CONSTANTS) 461 .expect("no config data stored"); 462 from_slice(&data).expect("invalid data") 463 } 464 465 fn get_total_supply(storage: &dyn Storage) -> u128 { 466 let config_storage = ReadonlyPrefixedStorage::new(storage, PREFIX_CONFIG); 467 let data = config_storage 468 .get(KEY_TOTAL_SUPPLY) 469 .expect("no decimals data stored"); 470 return bytes_to_u128(&data).unwrap(); 471 } 472 473 fn get_balance(storage: &dyn Storage, address: &Addr) -> u128 { 474 let balances_storage = ReadonlyPrefixedStorage::new(storage, PREFIX_BALANCES); 475 return read_u128(&balances_storage, address).unwrap(); 476 } 477 478 fn get_allowance(storage: &dyn Storage, owner: &Addr, spender: &Addr) -> u128 { 479 let owner_storage = ReadonlyPrefixedStorage::multilevel( 480 storage, 481 &[PREFIX_ALLOWANCES, owner.as_str().as_bytes()], 482 ); 483 return read_u128(&owner_storage, spender).unwrap(); 484 } 485 486 mod instantiate { 487 use super::*; 488 use crate::error::ContractError; 489 490 #[test] 491 fn works() { 492 let mut deps = mock_dependencies(&[]); 493 let instantiate_msg = InstantiateMsg { 494 name: "Cash Token".to_string(), 495 symbol: "CASH".to_string(), 496 decimals: 9, 497 initial_balances: [InitialBalance { 498 address: "addr0000".to_string(), 499 amount: Uint128::from(11223344u128), 500 }] 501 .to_vec(), 502 }; 503 let (env, info) = mock_env_height("creator", 450, 550); 504 let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); 505 assert_eq!(0, res.messages.len()); 506 assert_eq!( 507 get_constants(&deps.storage), 508 Constants { 509 name: "Cash Token".to_string(), 510 symbol: "CASH".to_string(), 511 decimals: 9 512 } 513 ); 514 assert_eq!( 515 get_balance(&deps.storage, &Addr::unchecked("addr0000".to_string())), 516 11223344 517 ); 518 assert_eq!(get_total_supply(&deps.storage), 11223344); 519 } 520 521 #[test] 522 fn works_with_empty_balance() { 523 let mut deps = mock_dependencies(&[]); 524 let instantiate_msg = InstantiateMsg { 525 name: "Cash Token".to_string(), 526 symbol: "CASH".to_string(), 527 decimals: 9, 528 initial_balances: [].to_vec(), 529 }; 530 let (env, info) = mock_env_height("creator", 450, 550); 531 let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); 532 assert_eq!(0, res.messages.len()); 533 assert_eq!(get_total_supply(&deps.storage), 0); 534 } 535 536 #[test] 537 fn works_with_multiple_balances() { 538 let mut deps = mock_dependencies(&[]); 539 let instantiate_msg = InstantiateMsg { 540 name: "Cash Token".to_string(), 541 symbol: "CASH".to_string(), 542 decimals: 9, 543 initial_balances: [ 544 InitialBalance { 545 address: "addr0000".to_string(), 546 amount: Uint128::from(11u128), 547 }, 548 InitialBalance { 549 address: "addr1111".to_string(), 550 amount: Uint128::from(22u128), 551 }, 552 InitialBalance { 553 address: "addrbbbb".to_string(), 554 amount: Uint128::from(33u128), 555 }, 556 ] 557 .to_vec(), 558 }; 559 let (env, info) = mock_env_height("creator", 450, 550); 560 let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); 561 assert_eq!(0, res.messages.len()); 562 assert_eq!( 563 get_balance(&deps.storage, &Addr::unchecked("addr0000".to_string())), 564 11 565 ); 566 assert_eq!( 567 get_balance(&deps.storage, &Addr::unchecked("addr1111".to_string())), 568 22 569 ); 570 assert_eq!( 571 get_balance(&deps.storage, &Addr::unchecked("addrbbbb".to_string())), 572 33 573 ); 574 assert_eq!(get_total_supply(&deps.storage), 66); 575 } 576 577 #[test] 578 fn works_with_balance_larger_than_53_bit() { 579 let mut deps = mock_dependencies(&[]); 580 // This value cannot be represented precisely in JavaScript and jq. Both 581 // node -e "console.attr(9007199254740993)" 582 // echo '{ "value": 9007199254740993 }' | jq 583 // return 9007199254740992 584 let instantiate_msg = InstantiateMsg { 585 name: "Cash Token".to_string(), 586 symbol: "CASH".to_string(), 587 decimals: 9, 588 initial_balances: [InitialBalance { 589 address: "addr0000".to_string(), 590 amount: Uint128::from(9007199254740993u128), 591 }] 592 .to_vec(), 593 }; 594 let (env, info) = mock_env_height("creator", 450, 550); 595 let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); 596 assert_eq!(0, res.messages.len()); 597 assert_eq!( 598 get_balance(&deps.storage, &Addr::unchecked("addr0000".to_string())), 599 9007199254740993 600 ); 601 assert_eq!(get_total_supply(&deps.storage), 9007199254740993); 602 } 603 604 #[test] 605 // Typical supply like 100 million tokens with 18 decimals exceeds the 64 bit range 606 fn works_with_balance_larger_than_64_bit() { 607 let mut deps = mock_dependencies(&[]); 608 let instantiate_msg = InstantiateMsg { 609 name: "Cash Token".to_string(), 610 symbol: "CASH".to_string(), 611 decimals: 9, 612 initial_balances: [InitialBalance { 613 address: "addr0000".to_string(), 614 amount: Uint128::from(100000000000000000000000000u128), 615 }] 616 .to_vec(), 617 }; 618 let (env, info) = mock_env_height("creator", 450, 550); 619 let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); 620 assert_eq!(0, res.messages.len()); 621 assert_eq!( 622 get_balance(&deps.storage, &Addr::unchecked("addr0000".to_string())), 623 100000000000000000000000000 624 ); 625 assert_eq!(get_total_supply(&deps.storage), 100000000000000000000000000); 626 } 627 628 #[test] 629 fn fails_for_large_decimals() { 630 let mut deps = mock_dependencies(&[]); 631 let instantiate_msg = InstantiateMsg { 632 name: "Cash Token".to_string(), 633 symbol: "CASH".to_string(), 634 decimals: 42, 635 initial_balances: [].to_vec(), 636 }; 637 let (env, info) = mock_env_height("creator", 450, 550); 638 let result = instantiate(deps.as_mut(), env, info, instantiate_msg); 639 match result { 640 Ok(_) => panic!("expected error"), 641 Err(ContractError::DecimalsExceeded {}) => {} 642 Err(e) => panic!("unexpected error: {:?}", e), 643 } 644 } 645 646 #[test] 647 fn fails_for_name_too_short() { 648 let mut deps = mock_dependencies(&[]); 649 let instantiate_msg = InstantiateMsg { 650 name: "CC".to_string(), 651 symbol: "CASH".to_string(), 652 decimals: 9, 653 initial_balances: [].to_vec(), 654 }; 655 let (env, info) = mock_env_height("creator", 450, 550); 656 let result = instantiate(deps.as_mut(), env, info, instantiate_msg); 657 match result { 658 Ok(_) => panic!("expected error"), 659 Err(ContractError::NameWrongFormat {}) => {} 660 Err(e) => panic!("unexpected error: {:?}", e), 661 } 662 } 663 664 #[test] 665 fn fails_for_name_too_long() { 666 let mut deps = mock_dependencies(&[]); 667 let instantiate_msg = InstantiateMsg { 668 name: "Cash coin. Cash coin. Cash coin. Cash coin.".to_string(), 669 symbol: "CASH".to_string(), 670 decimals: 9, 671 initial_balances: [].to_vec(), 672 }; 673 let (env, info) = mock_env_height("creator", 450, 550); 674 let result = instantiate(deps.as_mut(), env, info, instantiate_msg); 675 match result { 676 Ok(_) => panic!("expected error"), 677 Err(ContractError::NameWrongFormat {}) => {} 678 Err(e) => panic!("unexpected error: {:?}", e), 679 } 680 } 681 682 #[test] 683 fn fails_for_symbol_too_short() { 684 let mut deps = mock_dependencies(&[]); 685 let instantiate_msg = InstantiateMsg { 686 name: "De De".to_string(), 687 symbol: "DD".to_string(), 688 decimals: 9, 689 initial_balances: [].to_vec(), 690 }; 691 let (env, info) = mock_env_height("creator", 450, 550); 692 let result = instantiate(deps.as_mut(), env, info, instantiate_msg); 693 match result { 694 Ok(_) => panic!("expected error"), 695 Err(ContractError::TickerWrongSymbolFormat {}) => {} 696 Err(e) => panic!("unexpected error: {:?}", e), 697 } 698 } 699 700 #[test] 701 fn fails_for_symbol_too_long() { 702 let mut deps = mock_dependencies(&[]); 703 let instantiate_msg = InstantiateMsg { 704 name: "Super Coin".to_string(), 705 symbol: "SUPERCOIN".to_string(), 706 decimals: 9, 707 initial_balances: [].to_vec(), 708 }; 709 let (env, info) = mock_env_height("creator", 450, 550); 710 let result = instantiate(deps.as_mut(), env, info, instantiate_msg); 711 match result { 712 Ok(_) => panic!("expected error"), 713 Err(ContractError::TickerWrongSymbolFormat {}) => {} 714 Err(e) => panic!("unexpected error: {:?}", e), 715 } 716 } 717 718 #[test] 719 fn fails_for_symbol_lowercase() { 720 let mut deps = mock_dependencies(&[]); 721 let instantiate_msg = InstantiateMsg { 722 name: "Cash Token".to_string(), 723 symbol: "CaSH".to_string(), 724 decimals: 9, 725 initial_balances: [].to_vec(), 726 }; 727 let (env, info) = mock_env_height("creator", 450, 550); 728 let result = instantiate(deps.as_mut(), env, info, instantiate_msg); 729 match result { 730 Ok(_) => panic!("expected error"), 731 Err(ContractError::TickerWrongSymbolFormat {}) => {} 732 Err(e) => panic!("unexpected error: {:?}", e), 733 } 734 } 735 } 736 737 mod transfer { 738 use super::*; 739 use crate::error::ContractError; 740 use cosmwasm_std::attr; 741 742 fn make_instantiate_msg() -> InstantiateMsg { 743 InstantiateMsg { 744 name: "Cash Token".to_string(), 745 symbol: "CASH".to_string(), 746 decimals: 9, 747 initial_balances: vec![ 748 InitialBalance { 749 address: "addr0000".to_string(), 750 amount: Uint128::from(11u128), 751 }, 752 InitialBalance { 753 address: "addr1111".to_string(), 754 amount: Uint128::from(22u128), 755 }, 756 InitialBalance { 757 address: "addrbbbb".to_string(), 758 amount: Uint128::from(33u128), 759 }, 760 ], 761 } 762 } 763 764 #[test] 765 fn can_send_to_existing_recipient() { 766 let mut deps = mock_dependencies(&[]); 767 let instantiate_msg = make_instantiate_msg(); 768 let (env, info) = mock_env_height("creator", 450, 550); 769 let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); 770 assert_eq!(0, res.messages.len()); 771 // Initial state 772 assert_eq!( 773 get_balance(&deps.storage, &Addr::unchecked("addr0000".to_string())), 774 11 775 ); 776 assert_eq!( 777 get_balance(&deps.storage, &Addr::unchecked("addr1111".to_string())), 778 22 779 ); 780 assert_eq!( 781 get_balance(&deps.storage, &Addr::unchecked("addrbbbb".to_string())), 782 33 783 ); 784 assert_eq!(get_total_supply(&deps.storage), 66); 785 // Transfer 786 let transfer_msg = ExecuteMsg::Transfer { 787 recipient: "addr1111".to_string(), 788 amount: Uint128::from(1u128), 789 }; 790 let (env, info) = mock_env_height("addr0000", 450, 550); 791 let transfer_result = execute(deps.as_mut(), env, info, transfer_msg).unwrap(); 792 assert_eq!(transfer_result.messages.len(), 0); 793 assert_eq!( 794 transfer_result.attributes, 795 vec![ 796 attr("action", "transfer"), 797 attr("sender", "addr0000"), 798 attr("recipient", "addr1111"), 799 ] 800 ); 801 // New state 802 assert_eq!( 803 get_balance(&deps.storage, &Addr::unchecked("addr0000".to_string())), 804 10 805 ); // -1 806 assert_eq!( 807 get_balance(&deps.storage, &Addr::unchecked("addr1111".to_string())), 808 23 809 ); // +1 810 assert_eq!( 811 get_balance(&deps.storage, &Addr::unchecked("addrbbbb".to_string())), 812 33 813 ); 814 assert_eq!(get_total_supply(&deps.storage), 66); 815 } 816 817 #[test] 818 fn can_send_to_non_existent_recipient() { 819 let mut deps = mock_dependencies(&[]); 820 let instantiate_msg = make_instantiate_msg(); 821 let (env, info) = mock_env_height("creator", 450, 550); 822 let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); 823 assert_eq!(0, res.messages.len()); 824 // Initial state 825 assert_eq!( 826 get_balance(&deps.storage, &Addr::unchecked("addr0000".to_string())), 827 11 828 ); 829 assert_eq!( 830 get_balance(&deps.storage, &Addr::unchecked("addr1111".to_string())), 831 22 832 ); 833 assert_eq!( 834 get_balance(&deps.storage, &Addr::unchecked("addrbbbb".to_string())), 835 33 836 ); 837 assert_eq!(get_total_supply(&deps.storage), 66); 838 // Transfer 839 let transfer_msg = ExecuteMsg::Transfer { 840 recipient: "addr2323".to_string(), 841 amount: Uint128::from(1u128), 842 }; 843 let (env, info) = mock_env_height("addr0000", 450, 550); 844 let transfer_result = execute(deps.as_mut(), env, info, transfer_msg).unwrap(); 845 assert_eq!(transfer_result.messages.len(), 0); 846 assert_eq!( 847 transfer_result.attributes, 848 vec![ 849 attr("action", "transfer"), 850 attr("sender", "addr0000"), 851 attr("recipient", "addr2323"), 852 ] 853 ); 854 // New state 855 assert_eq!( 856 get_balance(&deps.storage, &Addr::unchecked("addr0000".to_string())), 857 10 858 ); 859 assert_eq!( 860 get_balance(&deps.storage, &Addr::unchecked("addr1111".to_string())), 861 22 862 ); 863 assert_eq!( 864 get_balance(&deps.storage, &Addr::unchecked("addr2323".to_string())), 865 1 866 ); 867 assert_eq!( 868 get_balance(&deps.storage, &Addr::unchecked("addrbbbb".to_string())), 869 33 870 ); 871 assert_eq!(get_total_supply(&deps.storage), 66); 872 } 873 874 #[test] 875 fn can_send_zero_amount() { 876 let mut deps = mock_dependencies(&[]); 877 let instantiate_msg = make_instantiate_msg(); 878 let (env, info) = mock_env_height("creator", 450, 550); 879 let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); 880 assert_eq!(0, res.messages.len()); 881 // Initial state 882 assert_eq!( 883 get_balance(&deps.storage, &Addr::unchecked("addr0000".to_string())), 884 11 885 ); 886 assert_eq!( 887 get_balance(&deps.storage, &Addr::unchecked("addr1111".to_string())), 888 22 889 ); 890 assert_eq!( 891 get_balance(&deps.storage, &Addr::unchecked("addrbbbb".to_string())), 892 33 893 ); 894 assert_eq!(get_total_supply(&deps.storage), 66); 895 // Transfer 896 let transfer_msg = ExecuteMsg::Transfer { 897 recipient: "addr1111".to_string(), 898 amount: Uint128::from(0u128), 899 }; 900 let (env, info) = mock_env_height("addr0000", 450, 550); 901 let transfer_result = execute(deps.as_mut(), env, info, transfer_msg).unwrap(); 902 assert_eq!(transfer_result.messages.len(), 0); 903 assert_eq!( 904 transfer_result.attributes, 905 vec![ 906 attr("action", "transfer"), 907 attr("sender", "addr0000"), 908 attr("recipient", "addr1111"), 909 ] 910 ); 911 // New state (unchanged) 912 assert_eq!( 913 get_balance(&deps.storage, &Addr::unchecked("addr0000".to_string())), 914 11 915 ); 916 assert_eq!( 917 get_balance(&deps.storage, &Addr::unchecked("addr1111".to_string())), 918 22 919 ); 920 assert_eq!( 921 get_balance(&deps.storage, &Addr::unchecked("addrbbbb".to_string())), 922 33 923 ); 924 assert_eq!(get_total_supply(&deps.storage), 66); 925 } 926 927 #[test] 928 fn can_send_to_sender() { 929 let mut deps = mock_dependencies(&[]); 930 let instantiate_msg = make_instantiate_msg(); 931 let (env, info) = mock_env_height("creator", 450, 550); 932 let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); 933 assert_eq!(0, res.messages.len()); 934 let sender = "addr0000"; 935 // Initial state 936 assert_eq!(get_balance(&deps.storage, &Addr::unchecked(sender)), 11); 937 // Transfer 938 let transfer_msg = ExecuteMsg::Transfer { 939 recipient: sender.to_string(), 940 amount: Uint128::from(3u128), 941 }; 942 let (env, info) = mock_env_height(&sender, 450, 550); 943 let transfer_result = execute(deps.as_mut(), env, info, transfer_msg).unwrap(); 944 assert_eq!(transfer_result.messages.len(), 0); 945 assert_eq!( 946 transfer_result.attributes, 947 vec![ 948 attr("action", "transfer"), 949 attr("sender", "addr0000"), 950 attr("recipient", "addr0000"), 951 ] 952 ); 953 // New state 954 assert_eq!(get_balance(&deps.storage, &Addr::unchecked(sender)), 11); 955 } 956 957 #[test] 958 fn fails_on_insufficient_balance() { 959 let mut deps = mock_dependencies(&[]); 960 let instantiate_msg = make_instantiate_msg(); 961 let (env, info) = mock_env_height("creator", 450, 550); 962 let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); 963 assert_eq!(0, res.messages.len()); 964 // Initial state 965 assert_eq!( 966 get_balance(&deps.storage, &Addr::unchecked("addr0000".to_string())), 967 11 968 ); 969 assert_eq!( 970 get_balance(&deps.storage, &Addr::unchecked("addr1111".to_string())), 971 22 972 ); 973 assert_eq!( 974 get_balance(&deps.storage, &Addr::unchecked("addrbbbb".to_string())), 975 33 976 ); 977 assert_eq!(get_total_supply(&deps.storage), 66); 978 // Transfer 979 let transfer_msg = ExecuteMsg::Transfer { 980 recipient: "addr1111".to_string(), 981 amount: Uint128::from(12u128), 982 }; 983 let (env, info) = mock_env_height("addr0000", 450, 550); 984 let transfer_result = execute(deps.as_mut(), env, info, transfer_msg); 985 match transfer_result { 986 Ok(_) => panic!("expected error"), 987 Err(ContractError::InsufficientFunds { 988 balance: 11, 989 required: 12, 990 }) => {} 991 Err(e) => panic!("unexpected error: {:?}", e), 992 } 993 // New state (unchanged) 994 assert_eq!( 995 get_balance(&deps.storage, &Addr::unchecked("addr0000".to_string())), 996 11 997 ); 998 assert_eq!( 999 get_balance(&deps.storage, &Addr::unchecked("addr1111".to_string())), 1000 22 1001 ); 1002 assert_eq!( 1003 get_balance(&deps.storage, &Addr::unchecked("addrbbbb".to_string())), 1004 33 1005 ); 1006 assert_eq!(get_total_supply(&deps.storage), 66); 1007 } 1008 } 1009 1010 mod approve { 1011 use super::*; 1012 use cosmwasm_std::attr; 1013 1014 fn make_instantiate_msg() -> InstantiateMsg { 1015 InstantiateMsg { 1016 name: "Cash Token".to_string(), 1017 symbol: "CASH".to_string(), 1018 decimals: 9, 1019 initial_balances: vec![ 1020 InitialBalance { 1021 address: "addr0000".to_string(), 1022 amount: Uint128::from(11u128), 1023 }, 1024 InitialBalance { 1025 address: "addr1111".to_string(), 1026 amount: Uint128::from(22u128), 1027 }, 1028 InitialBalance { 1029 address: "addrbbbb".to_string(), 1030 amount: Uint128::from(33u128), 1031 }, 1032 ], 1033 } 1034 } 1035 1036 fn make_spender() -> Addr { 1037 Addr::unchecked("dadadadadadadada".to_string()) 1038 } 1039 1040 #[test] 1041 fn has_zero_allowance_by_default() { 1042 let mut deps = mock_dependencies(&[]); 1043 let instantiate_msg = make_instantiate_msg(); 1044 let (env, info) = mock_env_height("creator", 450, 550); 1045 let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); 1046 assert_eq!(0, res.messages.len()); 1047 // Existing owner 1048 assert_eq!( 1049 get_allowance(&deps.storage, &Addr::unchecked("addr0000"), &make_spender()), 1050 0 1051 ); 1052 // Non-existing owner 1053 assert_eq!( 1054 get_allowance( 1055 &deps.storage, 1056 &Addr::unchecked("addr4567".to_string()), 1057 &make_spender() 1058 ), 1059 0 1060 ); 1061 } 1062 1063 #[test] 1064 fn can_set_allowance() { 1065 let mut deps = mock_dependencies(&[]); 1066 let instantiate_msg = make_instantiate_msg(); 1067 let (env, info) = mock_env_height("creator", 450, 550); 1068 let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); 1069 assert_eq!(0, res.messages.len()); 1070 assert_eq!( 1071 get_allowance( 1072 &deps.storage, 1073 &Addr::unchecked("addr7654".to_string()), 1074 &make_spender() 1075 ), 1076 0 1077 ); 1078 // First approval 1079 let owner = Addr::unchecked("addr7654".to_string()); 1080 let spender = make_spender(); 1081 let approve_msg1 = ExecuteMsg::Approve { 1082 spender: spender.clone().to_string().to_string(), 1083 amount: Uint128::from(334422u128), 1084 }; 1085 let (env, info) = mock_env_height(&owner.as_str(), 450, 550); 1086 let approve_result1 = execute(deps.as_mut(), env, info, approve_msg1).unwrap(); 1087 assert_eq!(approve_result1.messages.len(), 0); 1088 assert_eq!( 1089 approve_result1.attributes, 1090 vec![ 1091 attr("action", "approve"), 1092 attr("owner", owner.clone().to_string()), 1093 attr("spender", spender.clone().to_string()), 1094 ] 1095 ); 1096 assert_eq!( 1097 get_allowance(&deps.storage, &owner, &make_spender()), 1098 334422 1099 ); 1100 // Updated approval 1101 let approve_msg = ExecuteMsg::Approve { 1102 spender: spender.clone().to_string().to_string(), 1103 amount: Uint128::from(777888u128), 1104 }; 1105 let (env, info) = mock_env_height(&owner.as_str(), 450, 550); 1106 let approve_result2 = execute(deps.as_mut(), env, info, approve_msg).unwrap(); 1107 assert_eq!(approve_result2.messages.len(), 0); 1108 assert_eq!( 1109 approve_result2.attributes, 1110 vec![ 1111 attr("action", "approve"), 1112 attr("owner", owner.as_str()), 1113 attr("spender", spender.as_str()), 1114 ] 1115 ); 1116 assert_eq!(get_allowance(&deps.storage, &owner, &spender), 777888); 1117 } 1118 } 1119 1120 mod transfer_from { 1121 use super::*; 1122 use crate::error::ContractError; 1123 use cosmwasm_std::{attr, Addr}; 1124 1125 fn make_instantiate_msg() -> InstantiateMsg { 1126 InstantiateMsg { 1127 name: "Cash Token".to_string(), 1128 symbol: "CASH".to_string(), 1129 decimals: 9, 1130 initial_balances: vec![ 1131 InitialBalance { 1132 address: "addr0000".to_string(), 1133 amount: Uint128::from(11u128), 1134 }, 1135 InitialBalance { 1136 address: "addr1111".to_string(), 1137 amount: Uint128::from(22u128), 1138 }, 1139 InitialBalance { 1140 address: "addrbbbb".to_string(), 1141 amount: Uint128::from(33u128), 1142 }, 1143 ], 1144 } 1145 } 1146 1147 fn make_spender() -> Addr { 1148 Addr::unchecked("dadadadadadadada".to_string()) 1149 } 1150 1151 #[test] 1152 fn works() { 1153 let mut deps = mock_dependencies(&[]); 1154 let instantiate_msg = make_instantiate_msg(); 1155 let (env, info) = mock_env_height("creator", 450, 550); 1156 let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); 1157 assert_eq!(0, res.messages.len()); 1158 let owner = "addr0000"; 1159 let spender = make_spender(); 1160 let recipient = Addr::unchecked("addr1212".to_string()); 1161 // Set approval 1162 let approve_msg = ExecuteMsg::Approve { 1163 spender: spender.clone().to_string().to_string(), 1164 amount: Uint128::from(4u128), 1165 }; 1166 let (env, info) = mock_env_height(&owner.clone(), 450, 550); 1167 let approve_result = execute(deps.as_mut(), env, info, approve_msg).unwrap(); 1168 assert_eq!(approve_result.messages.len(), 0); 1169 assert_eq!( 1170 approve_result.attributes, 1171 vec![ 1172 attr("action", "approve"), 1173 attr("owner", owner.clone().to_string()), 1174 attr("spender", spender.clone().to_string()), 1175 ] 1176 ); 1177 assert_eq!( 1178 get_balance(&deps.storage, &Addr::unchecked(owner.clone())), 1179 11 1180 ); 1181 assert_eq!( 1182 get_allowance(&deps.storage, &Addr::unchecked(owner.clone()), &spender), 1183 4 1184 ); 1185 // Transfer less than allowance but more than balance 1186 let transfer_from_msg = ExecuteMsg::TransferFrom { 1187 owner: owner.clone().to_string().to_string(), 1188 recipient: recipient.clone().to_string(), 1189 amount: Uint128::from(3u128), 1190 }; 1191 let (env, info) = mock_env_height(&spender.as_str(), 450, 550); 1192 let transfer_from_result = 1193 execute(deps.as_mut(), env, info, transfer_from_msg).unwrap(); 1194 assert_eq!(transfer_from_result.messages.len(), 0); 1195 assert_eq!( 1196 transfer_from_result.attributes, 1197 vec![ 1198 attr("action", "transfer_from"), 1199 attr("spender", spender.clone()), 1200 attr("sender", owner), 1201 attr("recipient", recipient), 1202 ] 1203 ); 1204 // State changed 1205 assert_eq!(get_balance(&deps.storage, &Addr::unchecked(owner)), 8); 1206 assert_eq!( 1207 get_allowance(&deps.storage, &Addr::unchecked(owner), &spender), 1208 1 1209 ); 1210 } 1211 1212 #[test] 1213 fn fails_when_allowance_too_low() { 1214 let mut deps = mock_dependencies(&[]); 1215 let instantiate_msg = make_instantiate_msg(); 1216 let (env, info) = mock_env_height("creator", 450, 550); 1217 let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); 1218 assert_eq!(0, res.messages.len()); 1219 let owner = "addr0000"; 1220 let spender = make_spender(); 1221 let recipient = Addr::unchecked("addr1212".to_string()); 1222 // Set approval 1223 let approve_msg = ExecuteMsg::Approve { 1224 spender: spender.clone().to_string(), 1225 amount: Uint128::from(2u128), 1226 }; 1227 let (env, info) = mock_env_height(&owner.clone(), 450, 550); 1228 let approve_result = execute(deps.as_mut(), env, info, approve_msg).unwrap(); 1229 assert_eq!(approve_result.messages.len(), 0); 1230 assert_eq!( 1231 approve_result.attributes, 1232 vec![ 1233 attr("action", "approve"), 1234 attr("owner", owner.clone().to_string()), 1235 attr("spender", spender.clone().to_string()), 1236 ] 1237 ); 1238 assert_eq!(get_balance(&deps.storage, &Addr::unchecked(owner)), 11); 1239 assert_eq!( 1240 get_allowance(&deps.storage, &Addr::unchecked(owner), &spender), 1241 2 1242 ); 1243 // Transfer less than allowance but more than balance 1244 let fransfer_from_msg = ExecuteMsg::TransferFrom { 1245 owner: owner.clone().to_string(), 1246 recipient: recipient.clone().to_string(), 1247 amount: Uint128::from(3u128), 1248 }; 1249 let (env, info) = mock_env_height(&spender.as_str(), 450, 550); 1250 let transfer_result = execute(deps.as_mut(), env, info, fransfer_from_msg); 1251 match transfer_result { 1252 Ok(_) => panic!("expected error"), 1253 Err(ContractError::InsufficientAllowance { 1254 allowance: 2, 1255 required: 3, 1256 }) => {} 1257 Err(e) => panic!("unexpected error: {:?}", e), 1258 } 1259 } 1260 1261 #[test] 1262 fn fails_when_allowance_is_set_but_balance_too_low() { 1263 let mut deps = mock_dependencies(&[]); 1264 let instantiate_msg = make_instantiate_msg(); 1265 let (env, info) = mock_env_height("creator", 450, 550); 1266 let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); 1267 assert_eq!(0, res.messages.len()); 1268 let owner = "addr0000"; 1269 let spender = make_spender(); 1270 let recipient = Addr::unchecked("addr1212".to_string()); 1271 // Set approval 1272 let approve_msg = ExecuteMsg::Approve { 1273 spender: spender.clone().to_string(), 1274 amount: Uint128::from(20u128), 1275 }; 1276 let (env, info) = mock_env_height(&owner.clone(), 450, 550); 1277 let approve_result = execute(deps.as_mut(), env, info, approve_msg).unwrap(); 1278 assert_eq!(approve_result.messages.len(), 0); 1279 assert_eq!( 1280 approve_result.attributes, 1281 vec![ 1282 attr("action", "approve"), 1283 attr("owner", owner.clone().to_string()), 1284 attr("spender", spender.clone().to_string()), 1285 ] 1286 ); 1287 assert_eq!(get_balance(&deps.storage, &Addr::unchecked(owner)), 11); 1288 assert_eq!( 1289 get_allowance(&deps.storage, &Addr::unchecked(owner), &spender), 1290 20 1291 ); 1292 // Transfer less than allowance but more than balance 1293 let fransfer_from_msg = ExecuteMsg::TransferFrom { 1294 owner: owner.clone().to_string(), 1295 recipient: recipient.clone().to_string(), 1296 amount: Uint128::from(15u128), 1297 }; 1298 let (env, info) = mock_env_height(&spender.as_str(), 450, 550); 1299 let transfer_result = execute(deps.as_mut(), env, info, fransfer_from_msg); 1300 match transfer_result { 1301 Ok(_) => panic!("expected error"), 1302 Err(ContractError::InsufficientFunds { 1303 balance: 11, 1304 required: 15, 1305 }) => {} 1306 Err(e) => panic!("unexpected error: {:?}", e), 1307 } 1308 } 1309 } 1310 1311 mod burn { 1312 use super::*; 1313 use crate::error::ContractError; 1314 use cosmwasm_std::{attr, Addr}; 1315 1316 fn make_instantiate_msg() -> InstantiateMsg { 1317 InstantiateMsg { 1318 name: "Cash Token".to_string(), 1319 symbol: "CASH".to_string(), 1320 decimals: 9, 1321 initial_balances: vec![ 1322 InitialBalance { 1323 address: "addr0000".to_string(), 1324 amount: Uint128::from(11u128), 1325 }, 1326 InitialBalance { 1327 address: "addr1111".to_string(), 1328 amount: Uint128::from(22u128), 1329 }, 1330 ], 1331 } 1332 } 1333 1334 #[test] 1335 fn can_burn_from_existing_account() { 1336 let mut deps = mock_dependencies(&[]); 1337 let instantiate_msg = make_instantiate_msg(); 1338 let (env, info) = mock_env_height("creator", 450, 550); 1339 let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); 1340 assert_eq!(0, res.messages.len()); 1341 // Initial state 1342 assert_eq!( 1343 get_balance(&deps.storage, &Addr::unchecked("addr0000".to_string())), 1344 11 1345 ); 1346 assert_eq!( 1347 get_balance(&deps.storage, &Addr::unchecked("addr1111".to_string())), 1348 22 1349 ); 1350 assert_eq!(get_total_supply(&deps.storage), 33); 1351 // Burn 1352 let burn_msg = ExecuteMsg::Burn { 1353 amount: Uint128::from(1u128), 1354 }; 1355 let (env, info) = mock_env_height("addr0000", 450, 550); 1356 let burn_result = execute(deps.as_mut(), env, info, burn_msg).unwrap(); 1357 assert_eq!(burn_result.messages.len(), 0); 1358 assert_eq!( 1359 burn_result.attributes, 1360 vec![ 1361 attr("action", "burn"), 1362 attr("account", "addr0000"), 1363 attr("amount", "1") 1364 ] 1365 ); 1366 // New state 1367 assert_eq!( 1368 get_balance(&deps.storage, &Addr::unchecked("addr0000".to_string())), 1369 10 1370 ); // -1 1371 assert_eq!( 1372 get_balance(&deps.storage, &Addr::unchecked("addr1111".to_string())), 1373 22 1374 ); 1375 assert_eq!(get_total_supply(&deps.storage), 32); 1376 } 1377 1378 #[test] 1379 fn can_burn_zero_amount() { 1380 let mut deps = mock_dependencies(&[]); 1381 let instantiate_msg = make_instantiate_msg(); 1382 let (env, info) = mock_env_height("creator", 450, 550); 1383 let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); 1384 assert_eq!(0, res.messages.len()); 1385 // Initial state 1386 assert_eq!( 1387 get_balance(&deps.storage, &Addr::unchecked("addr0000".to_string())), 1388 11 1389 ); 1390 assert_eq!( 1391 get_balance(&deps.storage, &Addr::unchecked("addr1111".to_string())), 1392 22 1393 ); 1394 assert_eq!(get_total_supply(&deps.storage), 33); 1395 // Burn 1396 let burn_msg = ExecuteMsg::Burn { 1397 amount: Uint128::from(0u128), 1398 }; 1399 let (env, info) = mock_env_height("addr0000", 450, 550); 1400 let burn_result = execute(deps.as_mut(), env, info, burn_msg).unwrap(); 1401 assert_eq!(burn_result.messages.len(), 0); 1402 assert_eq!( 1403 burn_result.attributes, 1404 vec![ 1405 attr("action", "burn"), 1406 attr("account", "addr0000"), 1407 attr("amount", "0"), 1408 ] 1409 ); 1410 // New state (unchanged) 1411 assert_eq!( 1412 get_balance(&deps.storage, &Addr::unchecked("addr0000".to_string())), 1413 11 1414 ); 1415 assert_eq!( 1416 get_balance(&deps.storage, &Addr::unchecked("addr1111".to_string())), 1417 22 1418 ); 1419 assert_eq!(get_total_supply(&deps.storage), 33); 1420 } 1421 1422 #[test] 1423 fn fails_on_insufficient_balance() { 1424 let mut deps = mock_dependencies(&[]); 1425 let instantiate_msg = make_instantiate_msg(); 1426 let (env, info) = mock_env_height("creator", 450, 550); 1427 let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); 1428 assert_eq!(0, res.messages.len()); 1429 // Initial state 1430 assert_eq!( 1431 get_balance(&deps.storage, &Addr::unchecked("addr0000".to_string())), 1432 11 1433 ); 1434 assert_eq!( 1435 get_balance(&deps.storage, &Addr::unchecked("addr1111".to_string())), 1436 22 1437 ); 1438 assert_eq!(get_total_supply(&deps.storage), 33); 1439 // Burn 1440 let burn_msg = ExecuteMsg::Burn { 1441 amount: Uint128::from(12u128), 1442 }; 1443 let (env, info) = mock_env_height("addr0000", 450, 550); 1444 let burn_result = execute(deps.as_mut(), env, info, burn_msg); 1445 match burn_result { 1446 Ok(_) => panic!("expected error"), 1447 Err(ContractError::InsufficientFunds { 1448 balance: 11, 1449 required: 12, 1450 }) => {} 1451 Err(e) => panic!("unexpected error: {:?}", e), 1452 } 1453 // New state (unchanged) 1454 assert_eq!( 1455 get_balance(&deps.storage, &Addr::unchecked("addr0000".to_string())), 1456 11 1457 ); 1458 assert_eq!( 1459 get_balance(&deps.storage, &Addr::unchecked("addr1111".to_string())), 1460 22 1461 ); 1462 assert_eq!(get_total_supply(&deps.storage), 33); 1463 } 1464 } 1465 1466 mod query { 1467 use super::*; 1468 use cosmwasm_std::{attr, Addr}; 1469 1470 fn address(index: u8) -> Addr { 1471 match index { 1472 0 => Addr::unchecked("addr0000".to_string()), // contract instantiateializer 1473 1 => Addr::unchecked("addr1111".to_string()), 1474 2 => Addr::unchecked("addr4321".to_string()), 1475 3 => Addr::unchecked("addr5432".to_string()), 1476 4 => Addr::unchecked("addr6543".to_string()), 1477 _ => panic!("Unsupported address index"), 1478 } 1479 } 1480 1481 fn make_instantiate_msg() -> InstantiateMsg { 1482 InstantiateMsg { 1483 name: "Cash Token".to_string(), 1484 symbol: "CASH".to_string(), 1485 decimals: 9, 1486 initial_balances: vec![ 1487 InitialBalance { 1488 address: address(1).to_string(), 1489 amount: Uint128::from(11u128), 1490 }, 1491 InitialBalance { 1492 address: address(2).to_string(), 1493 amount: Uint128::from(22u128), 1494 }, 1495 InitialBalance { 1496 address: address(3).to_string(), 1497 amount: Uint128::from(33u128), 1498 }, 1499 ], 1500 } 1501 } 1502 1503 #[test] 1504 fn can_query_balance_of_existing_address() { 1505 let mut deps = mock_dependencies(&[]); 1506 let instantiate_msg = make_instantiate_msg(); 1507 let (env, info) = mock_env_height(&address(0).as_str(), 450, 550); 1508 let res = instantiate(deps.as_mut(), env.clone(), info, instantiate_msg).unwrap(); 1509 assert_eq!(0, res.messages.len()); 1510 let query_msg = QueryMsg::Balance { 1511 address: address(1).to_string(), 1512 }; 1513 let query_result = query(deps.as_ref(), env, query_msg).unwrap(); 1514 assert_eq!(query_result.as_slice(), b"{\"balance\":\"11\"}"); 1515 } 1516 1517 #[test] 1518 fn can_query_balance_of_nonexisting_address() { 1519 let mut deps = mock_dependencies(&[]); 1520 let instantiate_msg = make_instantiate_msg(); 1521 let (env, info) = mock_env_height(&address(0).as_str(), 450, 550); 1522 let res = instantiate(deps.as_mut(), env.clone(), info, instantiate_msg).unwrap(); 1523 assert_eq!(0, res.messages.len()); 1524 let query_msg = QueryMsg::Balance { 1525 address: address(4).to_string(), // only indices 1, 2, 3 are instantiateialized 1526 }; 1527 let query_result = query(deps.as_ref(), env, query_msg).unwrap(); 1528 assert_eq!(query_result.as_slice(), b"{\"balance\":\"0\"}"); 1529 } 1530 1531 #[test] 1532 fn can_query_allowance_of_existing_addresses() { 1533 let mut deps = mock_dependencies(&[]); 1534 let instantiate_msg = make_instantiate_msg(); 1535 let (env, info) = mock_env_height(&address(0).as_str(), 450, 550); 1536 let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); 1537 assert_eq!(0, res.messages.len()); 1538 let owner = address(2); 1539 let spender = address(1); 1540 let approve_msg = ExecuteMsg::Approve { 1541 spender: spender.clone().to_string(), 1542 amount: Uint128::from(42u128), 1543 }; 1544 let (env, info) = mock_env_height(&owner.as_str(), 450, 550); 1545 let action_result = execute(deps.as_mut(), env.clone(), info, approve_msg).unwrap(); 1546 assert_eq!(action_result.messages.len(), 0); 1547 assert_eq!( 1548 action_result.attributes, 1549 vec![ 1550 attr("action", "approve"), 1551 attr("owner", owner.clone().to_string()), 1552 attr("spender", spender.clone().to_string()), 1553 ] 1554 ); 1555 let query_msg = QueryMsg::Allowance { 1556 owner: owner.clone().to_string(), 1557 spender: spender.clone().to_string(), 1558 }; 1559 let query_result = query(deps.as_ref(), env.clone(), query_msg).unwrap(); 1560 assert_eq!(query_result.as_slice(), b"{\"allowance\":\"42\"}"); 1561 } 1562 1563 #[test] 1564 fn can_query_allowance_of_nonexisting_owner() { 1565 let mut deps = mock_dependencies(&[]); 1566 let instantiate_msg = make_instantiate_msg(); 1567 let (env, info) = mock_env_height(&address(0).as_str(), 450, 550); 1568 let res = instantiate(deps.as_mut(), env, info, instantiate_msg).unwrap(); 1569 assert_eq!(0, res.messages.len()); 1570 let owner = address(2); 1571 let spender = address(1); 1572 let bob = address(3); 1573 let approve_msg = ExecuteMsg::Approve { 1574 spender: spender.clone().to_string(), 1575 amount: Uint128::from(42u128), 1576 }; 1577 let (env, info) = mock_env_height(&owner.as_str(), 450, 550); 1578 let approve_result = execute(deps.as_mut(), env.clone(), info, approve_msg).unwrap(); 1579 assert_eq!(approve_result.messages.len(), 0); 1580 assert_eq!( 1581 approve_result.attributes, 1582 vec![ 1583 attr("action", "approve"), 1584 attr("owner", owner.clone().to_string()), 1585 attr("spender", spender.clone().to_string()), 1586 ] 1587 ); 1588 // different spender 1589 let query_msg = QueryMsg::Allowance { 1590 owner: owner.clone().to_string(), 1591 spender: bob.clone().to_string(), 1592 }; 1593 let query_result = query(deps.as_ref(), env.clone(), query_msg).unwrap(); 1594 assert_eq!(query_result.as_slice(), b"{\"allowance\":\"0\"}"); 1595 // differnet owner 1596 let query_msg = QueryMsg::Allowance { 1597 owner: bob.clone().to_string(), 1598 spender: spender.clone().to_string(), 1599 }; 1600 let query_result = query(deps.as_ref(), env.clone(), query_msg).unwrap(); 1601 assert_eq!(query_result.as_slice(), b"{\"allowance\":\"0\"}"); 1602 } 1603 } 1604 }