content: package org.aion.avm.embed.testExchange;
  
  import avm.Address;
  import avm.Blockchain;
  import org.aion.avm.userlib.AionMap;
  
  public class ERC20Token {
  
      private static String name;
      private static String symbol;
      private static int decimals;
  
      private static Address minter;
  
      private static AionMap<Address, Long> ledger;
  
      private static AionMap<Address, AionMap<Address, Long>> allowance;
  
      private static long totalSupply;
  
      public static void init(String name, String symbol, int decimals, Address minter) {
          ERC20Token.name = name;
          ERC20Token.symbol = symbol;
          ERC20Token.decimals = decimals;
          ERC20Token.minter = minter;
          ERC20Token.ledger = new AionMap<>();
          ERC20Token.allowance = new AionMap<>();
      }
  
      public String name() {
          return name;
      }
  
      public String symbol() {
          return symbol;
      }
  
      public int decimals() {
          return decimals;
      }
  
      public static long totalSupply() {
          return totalSupply;
      }
  
      public static long balanceOf(Address tokenOwner) {
          return ledger.getOrDefault(tokenOwner, 0L);
      }
  
      public static long allowance(Address tokenOwner, Address spender) {
          if (!allowance.containsKey(tokenOwner)) {
              return 0L;
          }
  
          return allowance.get(tokenOwner).getOrDefault(spender, 0L);
      }
  
      public static boolean transfer(Address receiver, long tokens) {
          Address sender = Blockchain.getCaller();
  
          long senderBalance = ledger.getOrDefault(sender, 0L);
          long receiverBalance = ledger.getOrDefault(receiver, 0L);
  
          if ((senderBalance >= tokens) && (tokens > 0) && (receiverBalance + tokens > 0)) {
              ledger.put(sender, senderBalance - tokens);
              ledger.put(receiver, receiverBalance + tokens);
              Blockchain.log("Transfer".getBytes(), sender.toByteArray(), receiver.toByteArray(), Long.toString(tokens).getBytes());
              return true;
          }
  
          return false;
      }
  
      public static boolean approve(Address spender, long tokens) {
          Address sender = Blockchain.getCaller();
  
          if (!allowance.containsKey(sender)) {
              AionMap<Address, Long> newEntry = new AionMap<>();
              allowance.put(sender, newEntry);
          }
  
          Blockchain.log("Approval".getBytes(), sender.toByteArray(), spender.toByteArray(), Long.toString(tokens).getBytes());
          allowance.get(sender).put(spender, tokens);
  
          return true;
      }
  
      public static boolean transferFrom(Address from, Address to, long tokens) {
          Address sender = Blockchain.getCaller();
  
          long fromBalance = ledger.getOrDefault(from, 0L);
          long toBalance = ledger.getOrDefault(to, 0L);
  
          long limit = allowance(from, sender);
  
          if ((fromBalance > tokens) && (limit > tokens) && (toBalance + tokens > 0)) {
              Blockchain.log("Transfer".getBytes(), from.toByteArray(), to.toByteArray(), Long.toString(tokens).getBytes());
              ledger.put(from, fromBalance - tokens);
              allowance.get(from).put(sender, limit - tokens);
              ledger.put(to, toBalance + tokens);
              return true;
          }
  
          return false;
      }
  
      public static boolean mint(Address receiver, long tokens) {
          if (Blockchain.getCaller().equals(minter)) {
              long receiverBalance = ledger.getOrDefault(receiver, 0L);
              if ((tokens > 0) && (receiverBalance + tokens > 0)) {
                  Blockchain.log("Mint".getBytes(), receiver.toByteArray(), Long.toString(tokens).getBytes());
                  ledger.put(receiver, receiverBalance + tokens);
                  totalSupply += tokens;
                  return true;
              }
          }
          return false;
      }
  }
  