Skip to content

Commit

Permalink
STL launchpad TokenScript URI (#3370)
Browse files Browse the repository at this point in the history
* - Fix for WalletConnect V2 Foreground Service

* - Fix for Opensea service update

* - fix dappbrowser URL intercept

* Add ability to use STL-Launchpad URI
  • Loading branch information
JamesSmartCell authored Mar 25, 2024
1 parent 8961abf commit d887ad2
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -385,9 +391,10 @@ private void handleFileLoadError(Throwable throwable, File file)

private TokenDefinition fileLoadComplete(List<ContractLocator> 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))
{
Expand All @@ -396,7 +403,7 @@ private TokenDefinition fileLoadComplete(List<ContractLocator> originContracts,
{
//delete this file and check downloads for update
removeFile(file.getAbsolutePath());
loadScriptFromServer(getFileName(file));
loadScriptFromServer(primaryChainId, getFileName(file));
return td;
}

Expand Down Expand Up @@ -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)
{
Expand All @@ -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
Expand Down Expand Up @@ -1066,10 +1073,10 @@ public Single<TokenDefinition> 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<TokenDefinition> getAssetDefinitionASync(final TokenDefinition assetDef, final String contractName)
private Single<TokenDefinition> getAssetDefinitionASync(final TokenDefinition assetDef, final long chainId, final String contractName)
{
if (assetDef != null)
{
Expand All @@ -1078,7 +1085,7 @@ private Single<TokenDefinition> 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);
Expand All @@ -1095,7 +1102,7 @@ public Single<TokenDefinition> 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()
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -1416,23 +1423,23 @@ private boolean matchesExistingScript(Token token, String uri)
return false;
}

private Single<File> tryServerIfRequired(File contractScript, String address)
private Single<File> tryServerIfRequired(File contractScript, String address, long chainId)
{
if (contractScript.exists() || contractScript.getName().equals(UNCHANGED_SCRIPT))
{
return Single.fromCallable(() -> contractScript);
}
else
{
return fetchXMLFromServer(address);
return fetchXMLFromServer(chainId, address);
}
}

private Single<File> fetchXMLFromServer(String address)
private Single<File> 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);

Expand All @@ -1459,15 +1466,19 @@ private Single<File> 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<String, Boolean> 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());
Expand All @@ -1476,6 +1487,27 @@ private Single<File> fetchXMLFromServer(String address)
});
}

private String getOffchainScriptUri(Pair<String, Boolean> 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<String, Boolean> downloadScript(String Uri, long currentFileTime)
{
if (Uri.equals(UNCHANGED_SCRIPT))
Expand Down Expand Up @@ -3116,7 +3148,7 @@ public Single<TokenDefinition> 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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down

0 comments on commit d887ad2

Please sign in to comment.