diff --git a/app/build.gradle b/app/build.gradle index c693aab79f..5c9e3fb082 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -282,13 +282,14 @@ dependencies { //Timber implementation libs.timber - //noinspection UseTomlInstead,GradleDependency - implementation platform('com.walletconnect:android-bom:1.13.1') //TODO: Upgrade + implementation platform("com.walletconnect:android-bom:1.31.4") //TODO: Upgrade implementation("com.walletconnect:android-core", { exclude group: 'org.web3j', module: '*' + exclude group: 'org.bouncycastle', module: '*' }) implementation("com.walletconnect:web3wallet", { exclude group: 'org.web3j', module: '*' + exclude group: 'org.bouncycastle', module: '*' }) runtimeOnly libs.work.runtime.ktx diff --git a/app/src/main/java/com/alphawallet/app/C.java b/app/src/main/java/com/alphawallet/app/C.java index fb89ee7f06..ac62a77ec3 100644 --- a/app/src/main/java/com/alphawallet/app/C.java +++ b/app/src/main/java/com/alphawallet/app/C.java @@ -61,6 +61,8 @@ public abstract class C { public static final String AMOY_TESTNET_NAME = "Amoy (Test)"; public static final String BASE_MAINNET_NAME = "Base"; public static final String BASE_TESTNET_NAME = "Base Sepolia (Test)"; + public static final String MANTLE_MAINNET_NAME = "Mantle"; + public static final String MANTLE_TESTNET_NAME = "Mantle Sepolia (Test)"; public static final String ETHEREUM_TICKER_NAME = "ethereum"; public static final String CLASSIC_TICKER_NAME = "ethereum-classic"; @@ -71,6 +73,8 @@ public abstract class C { public static final String USD_SYMBOL = "$"; public static final String ETH_SYMBOL = "ETH"; + + public static final String MANTLE_SYMBOL = "MNT"; public static final String xDAI_SYMBOL = "xDai"; public static final String ETC_SYMBOL = "ETC"; public static final String GOERLI_SYMBOL = "GĂ–ETH"; diff --git a/app/src/main/java/com/alphawallet/app/repository/EthereumNetworkBase.java b/app/src/main/java/com/alphawallet/app/repository/EthereumNetworkBase.java index 841a8a7a4a..6dd5831757 100644 --- a/app/src/main/java/com/alphawallet/app/repository/EthereumNetworkBase.java +++ b/app/src/main/java/com/alphawallet/app/repository/EthereumNetworkBase.java @@ -51,6 +51,10 @@ import static com.alphawallet.ethereum.EthereumNetworkBase.LINEA_TEST_FREE_RPC; import static com.alphawallet.ethereum.EthereumNetworkBase.LINEA_TEST_ID; import static com.alphawallet.ethereum.EthereumNetworkBase.MAINNET_ID; +import static com.alphawallet.ethereum.EthereumNetworkBase.MANTLE_MAINNET_ID; +import static com.alphawallet.ethereum.EthereumNetworkBase.MANTLE_MAINNET_RPC; +import static com.alphawallet.ethereum.EthereumNetworkBase.MANTLE_TESTNET_ID; +import static com.alphawallet.ethereum.EthereumNetworkBase.MANTLE_TESTNET_RPC; import static com.alphawallet.ethereum.EthereumNetworkBase.MILKOMEDA_C1_ID; import static com.alphawallet.ethereum.EthereumNetworkBase.MILKOMEDA_C1_RPC; import static com.alphawallet.ethereum.EthereumNetworkBase.MILKOMEDA_C1_TEST_ID; @@ -208,12 +212,12 @@ public abstract class EthereumNetworkBase implements EthereumNetworkRepositoryTy //If your wallet prioritises xDai for example, you may want to move the XDAI_ID to the front of this list, //Then xDai would appear as the first token at the top of the wallet private static final List hasValue = new ArrayList<>(Arrays.asList( - MAINNET_ID, GNOSIS_ID, POLYGON_ID, ROOTSTOCK_MAINNET_ID, CLASSIC_ID, LINEA_ID, BASE_MAINNET_ID, BINANCE_MAIN_ID, HECO_ID, AVALANCHE_ID, + MAINNET_ID, GNOSIS_ID, POLYGON_ID, ROOTSTOCK_MAINNET_ID, CLASSIC_ID, LINEA_ID, BASE_MAINNET_ID, MANTLE_MAINNET_ID, BINANCE_MAIN_ID, HECO_ID, AVALANCHE_ID, FANTOM_ID, OPTIMISTIC_MAIN_ID, CRONOS_MAIN_ID, ARBITRUM_MAIN_ID, PALM_ID, KLAYTN_ID, IOTEX_MAINNET_ID, AURORA_MAINNET_ID, MILKOMEDA_C1_ID, OKX_ID)); private static final List testnetList = new ArrayList<>(Arrays.asList( SEPOLIA_TESTNET_ID, POLYGON_AMOY_ID, HOLESKY_ID, BASE_TESTNET_ID, GOERLI_ID, BINANCE_TEST_ID, - ROOTSTOCK_TESTNET_ID, CRONOS_TEST_ID, OPTIMISM_GOERLI_TEST_ID, POLYGON_TEST_ID, ARBITRUM_GOERLI_TEST_ID, LINEA_TEST_ID, KLAYTN_BAOBAB_ID, + ROOTSTOCK_TESTNET_ID, CRONOS_TEST_ID, MANTLE_TESTNET_ID, OPTIMISM_GOERLI_TEST_ID, POLYGON_TEST_ID, ARBITRUM_GOERLI_TEST_ID, LINEA_TEST_ID, KLAYTN_BAOBAB_ID, FANTOM_TEST_ID, IOTEX_TESTNET_ID, FUJI_TEST_ID, MILKOMEDA_C1_TEST_ID, AURORA_TESTNET_ID, PALM_TEST_ID)); @@ -393,6 +397,16 @@ public static boolean isOKX(NetworkInfo networkInfo) "https://sepolia.basescan.org/tx/", BASE_TESTNET_ID, BASE_TEST_FALLBACK_RPC, "https://api-sepolia.basescan.org/api?")); + put(MANTLE_MAINNET_ID, new NetworkInfo(C.MANTLE_MAINNET_NAME, C.MANTLE_SYMBOL, + MANTLE_MAINNET_RPC, + "https://explorer.mantle.xyz/tx/", MANTLE_MAINNET_ID, MANTLE_MAINNET_RPC, + "https://explorer.mantle.xyz/api?")); + + put(MANTLE_TESTNET_ID, new NetworkInfo(C.MANTLE_TESTNET_NAME, C.MANTLE_SYMBOL, + MANTLE_TESTNET_RPC, + "https://explorer.sepolia.mantle.xyz/tx/", MANTLE_TESTNET_ID, MANTLE_TESTNET_RPC, + "https://explorer.sepolia.mantle.xyz/api?")); + // Add deprecated networks after this line } }; @@ -443,6 +457,8 @@ public static boolean isOKX(NetworkInfo networkInfo) put(POLYGON_TEST_ID, R.drawable.ic_icons_tokens_mumbai); put(BASE_MAINNET_ID, R.drawable.ic_base_logo); put(BASE_TESTNET_ID, R.drawable.ic_base_test_logo); + put(MANTLE_MAINNET_ID, R.drawable.ic_mantle_logo); + put(MANTLE_TESTNET_ID, R.drawable.ic_mantle_test_logo); } }; @@ -488,6 +504,8 @@ public static boolean isOKX(NetworkInfo networkInfo) put(HOLESKY_ID, R.drawable.ic_icons_holesky); put(BASE_MAINNET_ID, R.drawable.ic_base_logo); put(BASE_TESTNET_ID, R.drawable.ic_base_test_logo); + put(MANTLE_MAINNET_ID, R.drawable.ic_mantle_logo); + put(MANTLE_TESTNET_ID, R.drawable.ic_mantle_test_logo); } }; @@ -533,6 +551,8 @@ public static boolean isOKX(NetworkInfo networkInfo) put(HOLESKY_ID, R.color.azure); put(BASE_MAINNET_ID, R.color.base_logo); put(BASE_TESTNET_ID, R.color.base_logo); + put(MANTLE_MAINNET_ID, R.color.rootstock); + put(MANTLE_TESTNET_ID, R.color.rootstock); } }; diff --git a/app/src/main/java/com/alphawallet/app/service/TickerService.java b/app/src/main/java/com/alphawallet/app/service/TickerService.java index 785737dce6..e8c46d4a66 100644 --- a/app/src/main/java/com/alphawallet/app/service/TickerService.java +++ b/app/src/main/java/com/alphawallet/app/service/TickerService.java @@ -15,6 +15,7 @@ import static com.alphawallet.ethereum.EthereumNetworkBase.KLAYTN_ID; import static com.alphawallet.ethereum.EthereumNetworkBase.LINEA_ID; import static com.alphawallet.ethereum.EthereumNetworkBase.MAINNET_ID; +import static com.alphawallet.ethereum.EthereumNetworkBase.MANTLE_MAINNET_ID; import static com.alphawallet.ethereum.EthereumNetworkBase.MILKOMEDA_C1_ID; import static com.alphawallet.ethereum.EthereumNetworkBase.OKX_ID; import static com.alphawallet.ethereum.EthereumNetworkBase.OPTIMISTIC_MAIN_ID; @@ -839,6 +840,7 @@ private void resetTickerUpdate() put(ROOTSTOCK_MAINNET_ID, "rootstock"); put(LINEA_ID, "linea"); put(BASE_MAINNET_ID, "base"); + put(MANTLE_MAINNET_ID, "mantle"); }}; // For now, don't use Dexguru unless we obtain API key @@ -878,6 +880,7 @@ public void deleteTickers() put(ROOTSTOCK_MAINNET_ID, "rootstock"); put(LINEA_ID, "ethereum"); put(BASE_MAINNET_ID, "base"); + put(MANTLE_MAINNET_ID, "mantle"); }}; public static boolean validateCoinGeckoAPI(Token token) diff --git a/app/src/main/java/com/alphawallet/app/service/TransactionsNetworkClient.java b/app/src/main/java/com/alphawallet/app/service/TransactionsNetworkClient.java index 9b351491f3..d4d530deee 100644 --- a/app/src/main/java/com/alphawallet/app/service/TransactionsNetworkClient.java +++ b/app/src/main/java/com/alphawallet/app/service/TransactionsNetworkClient.java @@ -503,7 +503,8 @@ private EtherscanEvent[] fetchEvents(Realm instance, String walletAddress, Netwo } else { - long upperBlock = 99999999999L; + long currentBlock = TransactionsService.getCurrentBlock(networkInfo.chainId).longValue(); + long upperBlock = currentBlock > 0L ? currentBlock + 1 : 99999999L; long lowerBlock = (lastBlockFound == 0) ? 1 : lastBlockFound; while (true) diff --git a/app/src/main/java/com/alphawallet/app/walletconnect/AWWalletConnectClient.java b/app/src/main/java/com/alphawallet/app/walletconnect/AWWalletConnectClient.java index 7a60abc9f0..2ff438fc4e 100644 --- a/app/src/main/java/com/alphawallet/app/walletconnect/AWWalletConnectClient.java +++ b/app/src/main/java/com/alphawallet/app/walletconnect/AWWalletConnectClient.java @@ -23,6 +23,7 @@ import androidx.activity.result.ActivityResultLauncher; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.core.app.ActivityCompat; import androidx.core.app.NotificationCompat; @@ -77,6 +78,7 @@ import java.util.stream.Collectors; import kotlin.Unit; +import kotlin.jvm.functions.Function2; import timber.log.Timber; public class AWWalletConnectClient implements Web3Wallet.WalletDelegate @@ -723,6 +725,31 @@ public Intent getSessionIntent(Context appContext) return intent; } + @Nullable + @Override + public Function2 getOnSessionAuthenticate() + { + return null; + } + + @Override + public void onProposalExpired(@NonNull Model.ExpiredProposal expiredProposal) + { + // TODO: Remove popup if still showing + } + + @Override + public void onRequestExpired(@NonNull Model.ExpiredRequest expiredRequest) + { + // TODO: remove popup if still showing + } + + @Override + public void onSessionExtend(@NonNull Session session) + { + //Session extension. Do we use a timeout here? + } + public interface WalletConnectV2Callback { default void onSessionProposalApproved() diff --git a/app/src/main/res/drawable/ic_mantle_logo.xml b/app/src/main/res/drawable/ic_mantle_logo.xml new file mode 100644 index 0000000000..930c6a3017 --- /dev/null +++ b/app/src/main/res/drawable/ic_mantle_logo.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_mantle_logo_dark.xml b/app/src/main/res/drawable/ic_mantle_logo_dark.xml new file mode 100644 index 0000000000..2fbcc680c6 --- /dev/null +++ b/app/src/main/res/drawable/ic_mantle_logo_dark.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_mantle_test_logo.xml b/app/src/main/res/drawable/ic_mantle_test_logo.xml new file mode 100644 index 0000000000..73402e79ee --- /dev/null +++ b/app/src/main/res/drawable/ic_mantle_test_logo.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/raw/init_token.js b/app/src/main/res/raw/init_token.js index a07101aacf..8811376d47 100644 --- a/app/src/main/res/raw/init_token.js +++ b/app/src/main/res/raw/init_token.js @@ -1,4 +1,4 @@ -const _currentTokenInstance = { +const currentTokenInstance = { %1$s } @@ -39,10 +39,10 @@ web3 = { } } -web3.tokens.data.currentInstance = _currentTokenInstance +web3.tokens.data.currentInstance = currentTokenInstance function refresh() { web3.tokens.dataChanged('test', web3.tokens.data, '%5$s') //TODO: Cache previous value of token to feed into first arg } -window.onload = refresh; \ No newline at end of file +window.onload = refresh; diff --git a/lib/src/main/java/com/alphawallet/ethereum/EthereumNetworkBase.java b/lib/src/main/java/com/alphawallet/ethereum/EthereumNetworkBase.java index 1a79dc4c22..9407dfa28a 100644 --- a/lib/src/main/java/com/alphawallet/ethereum/EthereumNetworkBase.java +++ b/lib/src/main/java/com/alphawallet/ethereum/EthereumNetworkBase.java @@ -48,6 +48,8 @@ public abstract class EthereumNetworkBase public static final long POLYGON_AMOY_ID = 80002; public static final long BASE_MAINNET_ID = 8453; public static final long BASE_TESTNET_ID = 84532; + public static final long MANTLE_MAINNET_ID = 5000; + public static final long MANTLE_TESTNET_ID = 5003; public static final String MAINNET_RPC_URL = "https://mainnet.infura.io/v3/da3717f25f824cc1baa32d812386d93f"; @@ -92,6 +94,9 @@ public abstract class EthereumNetworkBase public static final String BASE_FREE_MAINNET_RPC = "https://base-rpc.publicnode.com"; public static final String BASE_FREE_TESTNET_RPC = "https://base-sepolia-rpc.publicnode.com"; + public static final String MANTLE_MAINNET_RPC = "https://rpc.mantle.xyz"; + public static final String MANTLE_TESTNET_RPC = "https://rpc.sepolia.mantle.xyz"; + static Map networkMap = new LinkedHashMap() { @@ -179,6 +184,10 @@ public abstract class EthereumNetworkBase BASE_MAINNET_ID, false)); put(BASE_TESTNET_ID, new NetworkInfo("Base (Test)", "ETH", BASE_FREE_TESTNET_RPC, "https://sepolia.basescan.org/tx/", BASE_TESTNET_ID, false)); + put(MANTLE_MAINNET_ID, new NetworkInfo("Mantle", "ETH", BASE_FREE_MAINNET_RPC, "https://basescan.org/tx/", + MANTLE_MAINNET_ID, false)); + put(MANTLE_TESTNET_ID, new NetworkInfo("Mantle Sepolia (Test)", "ETH", BASE_FREE_TESTNET_RPC, "https://sepolia.basescan.org/tx/", + MANTLE_TESTNET_ID, false)); } }; diff --git a/lib/src/main/java/com/alphawallet/token/entity/TSTokenView.java b/lib/src/main/java/com/alphawallet/token/entity/TSTokenView.java index 97356ebbe7..697622f2e2 100644 --- a/lib/src/main/java/com/alphawallet/token/entity/TSTokenView.java +++ b/lib/src/main/java/com/alphawallet/token/entity/TSTokenView.java @@ -41,7 +41,7 @@ public TSTokenView(Element element, TokenDefinition td) throws SAXException switch (node.getLocalName()) { case "name": - label = node.getTextContent(); + label = node.getTextContent().strip(); break; default: break; @@ -119,7 +119,7 @@ private void generateTokenView(Element element, TokenDefinition td) lStyle += getHTMLContent(child); break; case "viewContent": - String name = child.getAttributes().getNamedItem("name").getTextContent(); + String name = child.getAttributes().getNamedItem("name").getTextContent().strip(); Element content = td.getViewContent(name); generateTokenView(content, td); break; @@ -132,7 +132,7 @@ private void generateTokenView(Element element, TokenDefinition td) if (element.getChildNodes().getLength() == 1) { //handle text item-view - lView = child.getTextContent().replace("\u2019", "’"); + lView = child.getTextContent().replace("\u2019", "’").strip(); } break; default: @@ -207,7 +207,7 @@ private String getHTMLContent(Node content) break; case Node.ENTITY_REFERENCE_NODE: //load in external content - String entityRef = child.getTextContent(); + String entityRef = child.getTextContent().strip(); EntityReference ref = (EntityReference) child; System.out.println(entityRef); @@ -215,7 +215,7 @@ private String getHTMLContent(Node content) default: if (child != null && child.getTextContent() != null) { - String parsed = child.getTextContent().replace("\u2019", "’"); + String parsed = child.getTextContent().replace("\u2019", "’").strip(); sb.append(parsed); } break; @@ -236,7 +236,7 @@ private String htmlAttributes(Node attribute) sb.append(" "); sb.append(node.getLocalName()); sb.append("=\""); - sb.append(node.getTextContent()); + sb.append(node.getTextContent().strip()); sb.append("\""); } } diff --git a/lib/src/main/java/com/alphawallet/token/tools/TokenDefinition.java b/lib/src/main/java/com/alphawallet/token/tools/TokenDefinition.java index 1729093ce2..9471dcfc39 100644 --- a/lib/src/main/java/com/alphawallet/token/tools/TokenDefinition.java +++ b/lib/src/main/java/com/alphawallet/token/tools/TokenDefinition.java @@ -376,9 +376,9 @@ public String getLocalisedString(Element nameContainer, String tagName) { name = (Element) nList.item(i); String langAttr = getLocalisationLang(name); if (langAttr.equals(locale.getLanguage())) { - return name.getTextContent(); + return name.getTextContent().strip(); } - else if (langAttr.equals("en")) nonLocalised = name.getTextContent(); + else if (langAttr.equals("en")) nonLocalised = name.getTextContent().strip(); } if (nonLocalised != null) return nonLocalised; @@ -386,7 +386,7 @@ public String getLocalisedString(Element nameContainer, String tagName) { { name = (Element) nList.item(0); // TODO: catch the indice out of bound exception and throw it again suggesting dev to check schema - if (name != null) return name.getTextContent(); + if (name != null) return name.getTextContent().strip(); else return null; } } @@ -422,11 +422,11 @@ public String getLocalisedString(Element container) { String langAttr = getLocalisationLang((Element)n); if (langAttr.equals(locale.getLanguage())) { - return n.getTextContent(); + return processText(n.getTextContent()).strip(); } else if (nonLocalised == null && (langAttr.equals("") || langAttr.equals("en"))) { - nonLocalised = n.getTextContent(); + nonLocalised = n.getTextContent().strip(); } } } @@ -434,6 +434,12 @@ else if (nonLocalised == null && (langAttr.equals("") || langAttr.equals("en"))) return nonLocalised; } + private String processText(String text) + { + //strip out whitespace and cr/lf + return text.strip(); + } + private boolean hasAttribute(Element name, String typeAttr) { if (name.hasAttributes()) @@ -460,7 +466,7 @@ private String getLocalisationLang(Element name) Node thisAttr = name.getAttributes().item(i); if (thisAttr.getLocalName().equals("lang")) { - return thisAttr.getTextContent(); + return thisAttr.getTextContent().strip(); } } } @@ -608,7 +614,7 @@ private TSSelection parseSelection(Element node) throws SAXException break; case "denial": Node denialNode = getLocalisedNode(element, "string"); - selection.denialMessage = (denialNode != null) ? denialNode.getTextContent() : null; + selection.denialMessage = (denialNode != null) ? denialNode.getTextContent().strip() : null; break; } } @@ -969,7 +975,7 @@ private void extractSignedInfo(Document xml) { NodeList nList; nList = xml.getElementsByTagNameNS("http://www.w3.org/2000/09/xmldsig#", "KeyName"); if (nList.getLength() > 0) { - this.keyName = nList.item(0).getTextContent(); + this.keyName = nList.item(0).getTextContent().strip(); } return; // even if the document is signed, often it doesn't have KeyName } @@ -1060,7 +1066,7 @@ private String getElementName(Node attribute) switch (node.getLocalName()) { case "name": - name = node.getTextContent(); + name = node.getTextContent().strip(); break; default: break; @@ -1383,7 +1389,7 @@ private void handleNameNode(Map localNames, Node node) { Element element = (Element) node; String quantity = element.getAttribute("quantity"); - String name = element.getTextContent(); + String name = element.getTextContent().strip(); if (quantity != null && name != null) { localNames.put(quantity, name); @@ -1514,7 +1520,7 @@ private void handleAddress(Element addressElement, ContractInfo info) String networkStr = addressElement.getAttribute("network"); long network = 1; if (networkStr != null) network = Long.parseLong(networkStr); - String address = addressElement.getTextContent().toLowerCase(); + String address = addressElement.getTextContent().toLowerCase().strip(); List addresses = info.addresses.get(network); if (addresses == null) { @@ -1552,7 +1558,7 @@ private String getHTMLContent(Node content) break; case Node.ENTITY_REFERENCE_NODE: //load in external content - String entityRef = child.getTextContent(); + String entityRef = child.getTextContent().strip(); EntityReference ref = (EntityReference) child; System.out.println(entityRef); @@ -1560,7 +1566,7 @@ private String getHTMLContent(Node content) default: if (child != null && child.getTextContent() != null) { - String parsed = child.getTextContent().replace("\u2019", "’"); + String parsed = child.getTextContent().replace("\u2019", "’").strip(); sb.append(parsed); } break; @@ -1581,7 +1587,7 @@ private String htmlAttributes(Node attribute) sb.append(" "); sb.append(node.getLocalName()); sb.append("=\""); - sb.append(node.getTextContent()); + sb.append(node.getTextContent().strip()); sb.append("\""); } } @@ -1651,7 +1657,7 @@ private TokenscriptElement parseTxTag(Element input) { TokenscriptElement tse = new TokenscriptElement(); tse.ref = input.getAttribute("ref"); - tse.value = input.getTextContent(); + tse.value = input.getTextContent().strip(); tse.localRef = input.getAttribute("local-ref"); return tse; }