From d887ad210c76c39d0fb2c273c2c44dc00ca6624a Mon Sep 17 00:00:00 2001 From: James Brown Date: Mon, 25 Mar 2024 16:57:49 +1100 Subject: [PATCH] STL launchpad TokenScript URI (#3370) * - Fix for WalletConnect V2 Foreground Service * - Fix for Opensea service update * - fix dappbrowser URL intercept * Add ability to use STL-Launchpad URI --- .../app/service/AssetDefinitionService.java | 74 +++++++++++++------ .../app/ui/GasSettingsActivity.java | 2 +- .../app/widget/ActionSheetDialog.java | 2 +- .../token/tools/TokenDefinition.java | 3 + 4 files changed, 58 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/alphawallet/app/service/AssetDefinitionService.java b/app/src/main/java/com/alphawallet/app/service/AssetDefinitionService.java index 56e19b4aa2..33d73bb989 100644 --- a/app/src/main/java/com/alphawallet/app/service/AssetDefinitionService.java +++ b/app/src/main/java/com/alphawallet/app/service/AssetDefinitionService.java @@ -2,8 +2,12 @@ import static com.alphawallet.app.repository.TokenRepository.getWeb3jService; import static com.alphawallet.app.repository.TokensRealmSource.IMAGES_DB; +import static com.alphawallet.ethereum.EthereumNetworkBase.MAINNET_ID; +import static com.alphawallet.token.tools.TokenDefinition.TOKENSCRIPT_ADDRESS; +import static com.alphawallet.token.tools.TokenDefinition.TOKENSCRIPT_CHAIN; import static com.alphawallet.token.tools.TokenDefinition.TOKENSCRIPT_CURRENT_SCHEMA; import static com.alphawallet.token.tools.TokenDefinition.TOKENSCRIPT_REPO_SERVER; +import static com.alphawallet.token.tools.TokenDefinition.TOKENSCRIPT_STORE_SERVER; import static com.alphawallet.token.tools.TokenDefinition.UNCHANGED_SCRIPT; import android.Manifest; @@ -67,6 +71,8 @@ import com.alphawallet.token.tools.TokenDefinition; import org.jetbrains.annotations.NotNull; +import org.json.JSONArray; +import org.json.JSONObject; import org.web3j.abi.DefaultFunctionEncoder; import org.web3j.abi.FunctionEncoder; import org.web3j.abi.datatypes.Function; @@ -385,9 +391,10 @@ private void handleFileLoadError(Throwable throwable, File file) private TokenDefinition fileLoadComplete(List originContracts, TokenScriptFile file, TokenDefinition td) { - if (originContracts.size() == 0 || td.getAttestation() != null) return td; //no action needed, not accessible to Attestation //TODO: Refactor once we handle multiple attestations + if (originContracts.isEmpty() || td.getAttestation() != null) return td; //no action needed, not accessible to Attestation //TODO: Refactor once we handle multiple attestations boolean hasEvents = td.hasEvents(); + long primaryChainId = !originContracts.isEmpty() ? originContracts.iterator().next().chainId : MAINNET_ID; try (Realm realm = realmManager.getRealmInstance(ASSET_DEFINITION_DB)) { @@ -396,7 +403,7 @@ private TokenDefinition fileLoadComplete(List originContracts, { //delete this file and check downloads for update removeFile(file.getAbsolutePath()); - loadScriptFromServer(getFileName(file)); + loadScriptFromServer(primaryChainId, getFileName(file)); return td; } @@ -1011,7 +1018,7 @@ private TokenScriptFile locateTokenScriptFile(String fileName) * Get asset definition given contract address * * @param address - * @return + * @return The Token Definition for the given chain, address */ public TokenDefinition getAssetDefinition(long chainId, String address) { @@ -1026,7 +1033,7 @@ public TokenDefinition getAssetDefinition(long chainId, String address) if (assetDef == null && !address.equals("ethereum")) { //try web - loadScriptFromServer(address.toLowerCase()); //this will complete asynchronously and display will be updated + loadScriptFromServer(chainId, address.toLowerCase()); //this will complete asynchronously and display will be updated } return assetDef; // if nothing found use default @@ -1066,10 +1073,10 @@ public Single getAssetDefinitionASync(long chainId, String addr } String convertedAddr = (address.equalsIgnoreCase(tokensService.getCurrentAddress())) ? "ethereum" : address.toLowerCase(); - return getAssetDefinitionASync(getDefinition(getTSDataKey(chainId, address)), convertedAddr); + return getAssetDefinitionASync(getDefinition(getTSDataKey(chainId, address)), chainId, convertedAddr); } - private Single getAssetDefinitionASync(final TokenDefinition assetDef, final String contractName) + private Single getAssetDefinitionASync(final TokenDefinition assetDef, final long chainId, final String contractName) { if (assetDef != null) { @@ -1078,7 +1085,7 @@ private Single getAssetDefinitionASync(final TokenDefinition as else if (!contractName.equals("ethereum")) { //at this stage, this script isn't replacing any existing script, so it's safe to write to database without checking if we need to delete anything - return fetchXMLFromServer(contractName.toLowerCase()) + return fetchXMLFromServer(chainId, contractName.toLowerCase()) .flatMap(this::handleNewTSFile); } else return Single.fromCallable(TokenDefinition::new); @@ -1095,7 +1102,7 @@ public Single getAssetDefinitionASync(Token token) // hold until asset definitions have finished loading waitForAssets(); - return getAssetDefinitionASync(getDefinition(token.getTSKey()), contractName); + return getAssetDefinitionASync(getDefinition(token.getTSKey()), token.tokenInfo.chainId, contractName); } private void waitForAssets() @@ -1169,12 +1176,12 @@ public String getIssuerName(Token token) return issuer; } - private void loadScriptFromServer(String correctedAddress) + private void loadScriptFromServer(long chainId, String correctedAddress) { //first check the last time we tried this session if (assetChecked.get(correctedAddress) == null || (System.currentTimeMillis() > (assetChecked.get(correctedAddress) + 1000L * 60L * 60L))) { - fetchXMLFromServer(correctedAddress) + fetchXMLFromServer(chainId, correctedAddress) .flatMap(this::handleNewTSFile) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) @@ -1416,7 +1423,7 @@ private boolean matchesExistingScript(Token token, String uri) return false; } - private Single tryServerIfRequired(File contractScript, String address) + private Single tryServerIfRequired(File contractScript, String address, long chainId) { if (contractScript.exists() || contractScript.getName().equals(UNCHANGED_SCRIPT)) { @@ -1424,15 +1431,15 @@ private Single tryServerIfRequired(File contractScript, String address) } else { - return fetchXMLFromServer(address); + return fetchXMLFromServer(chainId, address); } } - private Single fetchXMLFromServer(String address) + private Single fetchXMLFromServer(long chainId, String address) { return Single.fromCallable(() -> { final File defaultReturn = new File(""); - if (address.equals("")) return defaultReturn; + if (address.isEmpty()) return defaultReturn; File result = getDownloadedXMLFile(address); @@ -1459,15 +1466,19 @@ private Single fetchXMLFromServer(String address) if (assetChecked.get(address) != null && (System.currentTimeMillis() > (assetChecked.get(address) + 1000L * 60L * 60L))) return result; - String sb = TOKENSCRIPT_REPO_SERVER + - TOKENSCRIPT_CURRENT_SCHEMA + - "/" + - address; + //use the updated server + String sb = TOKENSCRIPT_STORE_SERVER.replace(TOKENSCRIPT_ADDRESS, address).replace(TOKENSCRIPT_CHAIN, Long.toString(chainId)); Pair downloadResponse = downloadScript(sb, fileTime); - if (!TextUtils.isEmpty(downloadResponse.first)) + String offchainScriptUri = getOffchainScriptUri(downloadResponse); + + if (!TextUtils.isEmpty(offchainScriptUri)) { - result = storeFile(address, downloadResponse); + downloadResponse = downloadScript(offchainScriptUri, fileTime); + if (!TextUtils.isEmpty(downloadResponse.first)) + { + storeFile(address, downloadResponse); + } } assetChecked.put(address, System.currentTimeMillis()); @@ -1476,6 +1487,27 @@ private Single fetchXMLFromServer(String address) }); } + private String getOffchainScriptUri(Pair downloadResponse) + { + String offchainScriptResponse = ""; + try + { + JSONObject response = new JSONObject(downloadResponse.first); + JSONObject scriptUri = response.getJSONObject("scriptURI"); + JSONArray offchainLinks = scriptUri.getJSONArray("offchain"); + if (offchainLinks.length() > 0) + { + offchainScriptResponse = offchainLinks.getString(0); + } + } + catch (Exception e) + { + offchainScriptResponse = ""; + } + + return offchainScriptResponse; + } + private Pair downloadScript(String Uri, long currentFileTime) { if (Uri.equals(UNCHANGED_SCRIPT)) @@ -3116,7 +3148,7 @@ public Single checkServerForScript(Token token, MutableLiveData //try the contractURI, then server return fetchTokenScriptFromContract(token, updateFlag) - .flatMap(file -> tryServerIfRequired(file, token.getAddress().toLowerCase())) + .flatMap(file -> tryServerIfRequired(file, token.getAddress().toLowerCase(), token.tokenInfo.chainId)) .flatMap(this::handleNewTSFile) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); diff --git a/app/src/main/java/com/alphawallet/app/ui/GasSettingsActivity.java b/app/src/main/java/com/alphawallet/app/ui/GasSettingsActivity.java index 4600ac150b..f438da9de9 100644 --- a/app/src/main/java/com/alphawallet/app/ui/GasSettingsActivity.java +++ b/app/src/main/java/com/alphawallet/app/ui/GasSettingsActivity.java @@ -126,7 +126,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) gasSliderView.setNonce(getIntent().getLongExtra(C.EXTRA_NONCE, -1)); gasSliderView.initGasLimit(customGasLimit.toBigInteger(), presetGasLimit.toBigInteger()); gasSpread = getIntent().getParcelableExtra(C.EXTRA_GAS_PRICE); - isUsing1559 = getIntent().getBooleanExtra(C.EXTRA_1559_TX, false); + isUsing1559 = getIntent().getBooleanExtra(C.EXTRA_1559_TX, true); gasSliderView.initGasPrice(gasSpread.getSelectedGasFee(TXSpeed.CUSTOM)); adapter = new CustomAdapter(this); diff --git a/app/src/main/java/com/alphawallet/app/widget/ActionSheetDialog.java b/app/src/main/java/com/alphawallet/app/widget/ActionSheetDialog.java index 8a9e546fa6..61790836a2 100644 --- a/app/src/main/java/com/alphawallet/app/widget/ActionSheetDialog.java +++ b/app/src/main/java/com/alphawallet/app/widget/ActionSheetDialog.java @@ -327,7 +327,7 @@ public ActionSheetDialog(Activity activity, ActionSheetMode mode) private GasWidgetInterface setupGasWidget() { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); - boolean canUse1559Transactions = prefs.getBoolean(SharedPreferenceRepository.EXPERIMENTAL_1559_TX, false); + boolean canUse1559Transactions = prefs.getBoolean(SharedPreferenceRepository.EXPERIMENTAL_1559_TX, true); use1559Transactions = canUse1559Transactions && has1559Gas() //1559 Transactions toggled on in settings and this chain supports 1559 && !(token.isEthereum() && candidateTransaction.leafPosition == -2) //User not sweeping wallet (if so we need to use legacy tx) 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 8fda51a6cd..1729093ce2 100644 --- a/lib/src/main/java/com/alphawallet/token/tools/TokenDefinition.java +++ b/lib/src/main/java/com/alphawallet/token/tools/TokenDefinition.java @@ -80,7 +80,10 @@ public class TokenDefinition public static final String TOKENSCRIPT_MINIMUM_SCHEMA = "2020/06"; public static final String TOKENSCRIPT_CURRENT_SCHEMA = "2022/09"; + public static final String TOKENSCRIPT_ADDRESS = "{TS_ADDRESS}"; + public static final String TOKENSCRIPT_CHAIN = "{TS_CHAIN}"; public static final String TOKENSCRIPT_REPO_SERVER = "https://repo.tokenscript.org/"; + public static final String TOKENSCRIPT_STORE_SERVER = "https://store-backend.smartlayer.network/tokenscript/" + TOKENSCRIPT_ADDRESS + "/chain/" + TOKENSCRIPT_CHAIN + "/script-uri"; public static final String TOKENSCRIPT_NAMESPACE = "http://tokenscript.org/" + TOKENSCRIPT_CURRENT_SCHEMA + "/tokenscript"; private static final String ATTESTATION = "http://attestation.id/ns/tbml";