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  }