Repository: alphawallet/alpha-wallet-android
Issue: Transactions involving ether crash

Go to WETH tokencard and wrap ether for WETH, it will crash. We need to fix this before the tutorial 

Changed files:
app/src/main/java/io/stormbird/wallet/ui/FunctionActivity.java, app/src/main/java/io/stormbird/wallet/service/AssetDefinitionService.java, app/src/main/java/io/stormbird/wallet/ui/widget/adapter/TokensAdapter.java, lib/src/test/java/io/stormbird/token/tools/TokenDefinitionTest.java, lib/src/main/java/io/stormbird/token/tools/TokenDefinition.java, app/src/main/java/io/stormbird/wallet/entity/Token.java

diff --git a/app/src/main/java/io/stormbird/wallet/entity/Token.java b/app/src/main/java/io/stormbird/wallet/entity/Token.java
index 0d751151b..f990f8d60 100644
--- a/app/src/main/java/io/stormbird/wallet/entity/Token.java
+++ b/app/src/main/java/io/stormbird/wallet/entity/Token.java
@@ -41,6 +41,7 @@ public class Token implements Parcelable
     public boolean balanceChanged;
     public boolean walletUIUpdateRequired;
     public boolean hasTokenScript;
+    public boolean hasDebugTokenscript;
     public boolean refreshCheck;
     public long    lastTxUpdate = 0;
 
@@ -80,6 +81,7 @@ public class Token implements Parcelable
             lastTxUpdate = oldToken.lastTxUpdate;
             balanceChanged = oldToken.balanceChanged;
             hasTokenScript = oldToken.hasTokenScript;
+            hasDebugTokenscript = oldToken.hasDebugTokenscript;
         }
         refreshCheck = false;
     }
@@ -96,7 +98,9 @@ public class Token implements Parcelable
         lastTxCheck = in.readLong();
         lastTxUpdate = in.readLong();
         int readTS = in.readByte();
+        int readDTS = in.readByte();
         hasTokenScript = readTS == 1;
+        hasDebugTokenscript = readDTS == 1;
         balanceChanged = false;
         if (readType <= ContractType.CREATION.ordinal())
         {
@@ -154,6 +158,7 @@ public class Token implements Parcelable
         dest.writeLong(lastTxCheck);
         dest.writeLong(lastTxUpdate);
         dest.writeByte(hasTokenScript?(byte)1:(byte)0);
+        dest.writeByte(hasDebugTokenscript?(byte)1:(byte)0);
     }
 
     public void setRealmBalance(RealmToken realmToken)
@@ -201,6 +206,7 @@ public class Token implements Parcelable
 
     public BigDecimal getCorrectedBalance(int scale)
     {
+        if (balance.equals(BigDecimal.ZERO)) return BigDecimal.ZERO;
         BigDecimal decimalDivisor = new BigDecimal(Math.pow(10, tokenInfo.decimals));
         BigDecimal ethBalance = tokenInfo.decimals > 0
                 ? balance.divide(decimalDivisor) : balance;
diff --git a/app/src/main/java/io/stormbird/wallet/service/AssetDefinitionService.java b/app/src/main/java/io/stormbird/wallet/service/AssetDefinitionService.java
index 2b84fe3c3..f0c243ffc 100644
--- a/app/src/main/java/io/stormbird/wallet/service/AssetDefinitionService.java
+++ b/app/src/main/java/io/stormbird/wallet/service/AssetDefinitionService.java
@@ -885,6 +885,8 @@ public class AssetDefinitionService implements ParseResult, AttributeInterface
                 Token token = tokensService.getToken(networkId, address);
                 if (token != null)
                 {
+                    File tokenDef = defMap.get(address);
+                    if (tokenDef != null && tokenDef.getAbsolutePath().contains(ALPHAWALLET_DIR)) token.hasDebugTokenscript = true;
                     token.hasTokenScript = true;
                     TokenDefinition td = getAssetDefinition(networkId, address);
                     ContractInfo cInfo = td.contracts.get(td.holdingToken);
diff --git a/app/src/main/java/io/stormbird/wallet/ui/FunctionActivity.java b/app/src/main/java/io/stormbird/wallet/ui/FunctionActivity.java
index cb82363b9..0effaf4a9 100644
--- a/app/src/main/java/io/stormbird/wallet/ui/FunctionActivity.java
+++ b/app/src/main/java/io/stormbird/wallet/ui/FunctionActivity.java
@@ -437,7 +437,17 @@ public class FunctionActivity extends BaseActivity implements View.OnClickListen
             isValid = false;
         }
 
-        String to = function.tx.args.get("to").value;
+        //is 'to' overridden?
+        String to = null;
+        if (function.tx.args.get("to") != null)
+        {
+            to = function.tx.args.get("to").value;
+        }
+        else if (function.contract.addresses.get(token.tokenInfo.chainId) != null)
+        {
+            to = function.contract.addresses.get(token.tokenInfo.chainId).get(0);
+        }
+
         if (to == null || !Utils.isAddressValid(to))
         {
             errorInvalidAddress(to);
diff --git a/app/src/main/java/io/stormbird/wallet/ui/widget/adapter/TokensAdapter.java b/app/src/main/java/io/stormbird/wallet/ui/widget/adapter/TokensAdapter.java
index b86f48af2..a909fa808 100644
--- a/app/src/main/java/io/stormbird/wallet/ui/widget/adapter/TokensAdapter.java
+++ b/app/src/main/java/io/stormbird/wallet/ui/widget/adapter/TokensAdapter.java
@@ -222,7 +222,7 @@ public class TokensAdapter extends RecyclerView.Adapter<BinderViewHolder> {
         return allowThroughFilter &&  //Add token to display list if it's the base currency, or if it has balance
                 ((token.isEthereum() && token.tokenInfo.chainId == MAINNET_ID) ||
                         (!token.isTerminated() && !token.isBad() &&
-                                (token.hasPositiveBalance())));
+                                (token.hasDebugTokenscript || token.hasPositiveBalance())));
     }
 
     private void populateTokens(Token[] tokens)
diff --git a/lib/src/main/java/io/stormbird/token/tools/TokenDefinition.java b/lib/src/main/java/io/stormbird/token/tools/TokenDefinition.java
index 0c2017ff0..66592f1f7 100644
--- a/lib/src/main/java/io/stormbird/token/tools/TokenDefinition.java
+++ b/lib/src/main/java/io/stormbird/token/tools/TokenDefinition.java
@@ -11,6 +11,9 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.UnsupportedEncodingException;
 import java.math.BigInteger;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.time.format.DateTimeFormatter;
 import java.util.*;
 
 import static org.w3c.dom.Node.ELEMENT_NODE;
@@ -283,7 +286,7 @@ public class TokenDefinition {
 
         NodeList nList = xml.getElementsByTagNameNS(nameSpace, "token");
 
-        if (nList.getLength() == 0 || !nameSpace.equals(TOKENSCRIPT))
+        if (nList.getLength() == 0 || nameSpace == null)
         {
             System.out.println("Legacy XML format - no longer supported");
             return;
@@ -413,15 +416,51 @@ public class TokenDefinition {
             //check attributes
             for (int j = 0; j < n.getAttributes().getLength(); j++)
             {
-                Node thisAttr = n.getAttributes().item(j);
-                if (thisAttr.getNodeValue().contains(TOKENSCRIPTBASE))
+                try
                 {
-                    nameSpace = TOKENSCRIPT;
-                    if (result != null && !thisAttr.getNodeValue().equals(TOKENSCRIPT))
+                    Node thisAttr = n.getAttributes().item(j);
+                    if (thisAttr.getNodeValue().contains(TOKENSCRIPTBASE))
                     {
-                        result.parseMessage(ParseResult.ParseResultId.OK);
+                        nameSpace = thisAttr.getNodeValue();
+
+                        int dateIndex = nameSpace.indexOf(TOKENSCRIPTBASE) + TOKENSCRIPTBASE.length();
+                        int lastSeparator = nameSpace.lastIndexOf("/");
+                        if ((lastSeparator - dateIndex) == 7)
+                        {
+                            DateFormat format = new SimpleDateFormat("yyyy/MM", Locale.ENGLISH);
+                            Date thisDate = format.parse(nameSpace.substring(dateIndex, lastSeparator));
+                            Date schemaDate = format.parse(TOKENSCRIPT_CURRENT_SCHEMA);
+
+                            if (thisDate.equals(schemaDate))
+                            {
+                                //all good
+                                if (result != null) result.parseMessage(ParseResult.ParseResultId.OK);
+                            }
+                            else if (thisDate.before(schemaDate))
+                            {
+                                //still acceptable
+                                if (result != null) result.parseMessage(ParseResult.ParseResultId.XML_OUT_OF_DATE);
+                            }
+                            else
+                            {
+                                //cannot parse future schema
+                                if (result != null) result.parseMessage(ParseResult.ParseResultId.PARSER_OUT_OF_DATE);
+                                nameSpace = null;
+                            }
+                        }
+                        else
+                        {
+                            if (result != null) result.parseMessage(ParseResult.ParseResultId.PARSE_FAILED);
+                            nameSpace = null;
+                        }
+                        return;
                     }
-                    return;
+                }
+                catch (Exception e)
+                {
+                    if (result != null) result.parseMessage(ParseResult.ParseResultId.PARSE_FAILED);
+                    nameSpace = null;
+                    e.printStackTrace();
                 }
             }
         }
diff --git a/lib/src/test/java/io/stormbird/token/tools/TokenDefinitionTest.java b/lib/src/test/java/io/stormbird/token/tools/TokenDefinitionTest.java
index b0f4176bc..a07f6919d 100644
--- a/lib/src/test/java/io/stormbird/token/tools/TokenDefinitionTest.java
+++ b/lib/src/test/java/io/stormbird/token/tools/TokenDefinitionTest.java
@@ -14,7 +14,7 @@ import java.util.stream.Stream;
 import static org.junit.Assert.*;
 
 
-public class TokenDefinitionTest implements AttributeInterface
+public class TokenDefinitionTest implements AttributeInterface, ParseResult
 {
     Stream<BigInteger> ticketIDs = Stream.of(
             "1015b4a28f000000000000000000000", "1015b4a28f000000000000000000000",
@@ -25,7 +25,7 @@ public class TokenDefinitionTest implements AttributeInterface
     @Test
     public void TokenInformationCanBeExtracted() throws IOException, SAXException {
         assertTrue(file.exists());
-        TokenDefinition entryToken = new TokenDefinition(new FileInputStream(file), new Locale("en"), null);
+        TokenDefinition entryToken = new TokenDefinition(new FileInputStream(file), new Locale("en"), this);
         assertFalse(entryToken.attributeTypes.isEmpty());
         for (String contractName : entryToken.contracts.keySet())
         {
@@ -49,7 +49,7 @@ public class TokenDefinitionTest implements AttributeInterface
 
     @Test
     public void AttributeTypesShouldParse() throws IOException, SAXException {
-        TokenDefinition entryToken = new TokenDefinition(new FileInputStream(file), new Locale("en"), null);
+        TokenDefinition entryToken = new TokenDefinition(new FileInputStream(file), new Locale("en"), this);
         BigInteger tokenId = new BigInteger("1015b4a28f000000000000000000000", 16);
 
         //StringBuilder tokenData = new StringBuilder();
@@ -72,14 +72,14 @@ public class TokenDefinitionTest implements AttributeInterface
 
     @Test
     public void XMLSignatureShouldValidate() throws IOException, SAXException {
-        TokenDefinition entryToken = new TokenDefinition(new FileInputStream(file), new Locale("en"), null);
+        TokenDefinition entryToken = new TokenDefinition(new FileInputStream(file), new Locale("en"), this);
         assertEquals("EntryToken", entryToken.holdingToken);
         // TODO: actually validate XML signature
     }
 
     @Test(expected = SAXException.class)
     public void BadLocaleShouldThrowException() throws IOException, SAXException {
-        TokenDefinition ticketAsset = new TokenDefinition(new FileInputStream(file), new Locale("asdf"), null);
+        TokenDefinition ticketAsset = new TokenDefinition(new FileInputStream(file), new Locale("asdf"), this);
     }
 
 
@@ -103,4 +103,26 @@ public class TokenDefinitionTest implements AttributeInterface
     {
         return false;
     }
+
+    @Override
+    public void parseMessage(ParseResultId parseResult)
+    {
+        switch (parseResult)
+        {
+            case OK:
+                System.out.println("Schema date is correct.");
+                break;
+            case XML_OUT_OF_DATE:
+                System.out.println("Parsing outdated schema. It's an older schema but it checks out.");
+                break;
+            case PARSER_OUT_OF_DATE:
+                System.out.println("Parser attempting to parse future schema. Code base needs to be updated.");
+                fail();
+                break;
+            case PARSE_FAILED:
+                System.out.println("Parser Error.");
+                fail();
+                break;
+        }
+    }
 }