Add ERC721Enumerable handling (#2827)

* Add ERC721Enumerable handling
pull/2829/head
James Brown 2 years ago committed by GitHub
parent 5995e1bae1
commit 883014b590
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      app/src/main/java/com/alphawallet/app/entity/ContractType.java
  2. 3
      app/src/main/java/com/alphawallet/app/entity/nftassets/NFTAsset.java
  3. 72
      app/src/main/java/com/alphawallet/app/entity/tokens/ERC721Token.java
  4. 6
      app/src/main/java/com/alphawallet/app/entity/tokens/Token.java
  5. 3
      app/src/main/java/com/alphawallet/app/entity/tokens/TokenFactory.java
  6. 9
      app/src/main/java/com/alphawallet/app/repository/TokenRepository.java
  7. 3
      app/src/main/java/com/alphawallet/app/repository/TokensRealmSource.java
  8. 1
      app/src/main/java/com/alphawallet/app/service/GasService.java
  9. 1
      app/src/main/java/com/alphawallet/app/ui/widget/adapter/NFTAssetsAdapter.java
  10. 1
      app/src/main/java/com/alphawallet/app/viewmodel/WalletViewModel.java

@ -25,5 +25,6 @@ public enum ContractType
ETHEREUM_INVISIBLE,
MAYBE_ERC20,
ERC1155,
ERC721_ENUMERABLE,
CREATION //Placeholder for generic, should be at end of list
}

@ -81,7 +81,8 @@ public class NFTAsset implements Parcelable
public NFTAsset(RealmNFTAsset realmAsset)
{
loadFromMetaData(realmAsset.getMetaData());
String metaData = realmAsset.getMetaData() != null ? realmAsset.getMetaData() : new NFTAsset(new BigInteger(realmAsset.getTokenId())).jsonMetaData();
loadFromMetaData(metaData);
balance = realmAsset.getBalance();
}

@ -1,5 +1,6 @@
package com.alphawallet.app.entity.tokens;
import static com.alphawallet.app.repository.TokensRealmSource.databaseKey;
import static com.alphawallet.app.util.Utils.parseTokenId;
import static org.web3j.protocol.core.methods.request.Transaction.createEthCallTransaction;
import static org.web3j.tx.Contract.staticExtractEventParameters;
@ -53,7 +54,9 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import io.realm.Case;
import io.realm.Realm;
import io.realm.RealmResults;
import timber.log.Timber;
/**
@ -340,11 +343,19 @@ public class ERC721Token extends Token
try
{
final Web3j web3j = TokenRepository.getWeb3jService(tokenInfo.chainId);
if (contractType == ContractType.ERC721_ENUMERABLE)
{
updateEnumerableBalance(web3j, realm);
}
Pair<Integer, Pair<HashSet<BigInteger>, HashSet<BigInteger>>> evRead = eventSync.processTransferEvents(web3j,
getTransferEvents(), startBlock, endBlock, realm);
eventSync.updateEventReads(realm, sync, currentBlock, evRead.first); //means our event read was fine
//No need to go any further if this is enumerable
if (contractType == ContractType.ERC721_ENUMERABLE) return balance;
HashSet<BigInteger> allMovingTokens = new HashSet<>(evRead.second.first);
allMovingTokens.addAll(evRead.second.second);
@ -373,6 +384,26 @@ public class ERC721Token extends Token
return balance;
}
/***********
* For ERC721Enumerable interface
**********/
private void updateEnumerableBalance(Web3j web3j, Realm realm)
{
HashSet<BigInteger> tokenIdsHeld = new HashSet<>();
//get enumerable balance
//find tokenIds held
long currentBalance = balance != null ? balance.longValue() : 0;
for (long tokenIndex = 0; tokenIndex < currentBalance; tokenIndex++)
{
// find tokenId from index
String tokenId = callSmartContractFunction(web3j, tokenOfOwnerByIndex(BigInteger.valueOf(tokenIndex)), getAddress(), getWallet());
if (tokenId == null) continue;
tokenIdsHeld.add(new BigInteger(tokenId));
}
updateRealmForEnumerable(realm, tokenIdsHeld);
}
private void updateRealmBalance(Realm realm, Set<BigInteger> tokenIds, Set<BigInteger> allMovingTokens)
{
boolean updated = false;
@ -419,6 +450,40 @@ public class ERC721Token extends Token
});
}
private void updateRealmForEnumerable(Realm realm, HashSet<BigInteger> currentTokens)
{
HashSet<BigInteger> storedBalance = new HashSet<>();
RealmResults<RealmNFTAsset> results = realm.where(RealmNFTAsset.class)
.like("tokenIdAddr", databaseKey(this) + "-*", Case.INSENSITIVE)
.findAll();
for (RealmNFTAsset t : results)
{
storedBalance.add(new BigInteger(t.getTokenId()));
}
if (!currentTokens.equals(storedBalance))
{
realm.executeTransaction(r -> {
results.deleteAllFromRealm();
for (BigInteger tokenId : currentTokens)
{
String key = RealmNFTAsset.databaseKey(this, tokenId);
RealmNFTAsset realmAsset = realm.where(RealmNFTAsset.class)
.equalTo("tokenIdAddr", key)
.findFirst();
if (realmAsset == null)
{
realmAsset = r.createObject(RealmNFTAsset.class, key); //create asset in realm
realmAsset.setMetaData(new NFTAsset(tokenId).jsonMetaData());
r.insertOrUpdate(realmAsset);
}
}
});
}
}
private void updateRealmBalances(Realm realm, Set<BigInteger> tokenIds)
{
if (realm == null) return;
@ -700,6 +765,13 @@ public class ERC721Token extends Token
}));
}
private Function tokenOfOwnerByIndex(BigInteger index)
{
return new Function("tokenOfOwnerByIndex",
Arrays.asList(new Address(getWallet()), new Uint256(index)),
Collections.singletonList(new TypeReference<Uint256>() {}));
}
@Override
public List<Integer> getStandardFunctions()
{

@ -2,8 +2,6 @@ package com.alphawallet.app.entity.tokens;
import static android.text.Html.FROM_HTML_MODE_COMPACT;
import static androidx.core.content.ContextCompat.getColorStateList;
import android.app.Activity;
import android.content.Context;
import android.os.Build;
@ -11,7 +9,6 @@ import android.text.Html;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Pair;
import android.view.View;
import com.alphawallet.app.R;
import com.alphawallet.app.entity.ContractInteract;
@ -23,7 +20,6 @@ import com.alphawallet.app.entity.TransactionInput;
import com.alphawallet.app.entity.nftassets.NFTAsset;
import com.alphawallet.app.entity.opensea.AssetContract;
import com.alphawallet.app.entity.tokendata.TokenGroup;
import com.alphawallet.app.repository.EthereumNetworkBase;
import com.alphawallet.app.repository.EthereumNetworkRepository;
import com.alphawallet.app.repository.EventResult;
import com.alphawallet.app.repository.entity.RealmToken;
@ -53,7 +49,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@ -852,6 +847,7 @@ public class Token
case OTHER:
case NOT_SET:
case ERC721:
case ERC721_ENUMERABLE:
case ERC721_LEGACY:
case ERC721_UNDETERMINED:
case CREATION:

@ -34,6 +34,7 @@ public class TokenFactory
thisToken = new ERC721Ticket(tokenInfo, balances, updateBlancaTime, networkName, type);
break;
case ERC721:
case ERC721_ENUMERABLE:
case ERC721_LEGACY:
if (tokenInfo.decimals != 0)
{
@ -122,6 +123,7 @@ public class TokenFactory
case ERC721:
case ERC721_LEGACY:
case ERC721_ENUMERABLE:
thisToken = new ERC721Token(tokenInfo, null, decimalBalance, updateBlancaTime, networkName, type);
break;
@ -157,6 +159,7 @@ public class TokenFactory
case ERC721:
case ERC721_LEGACY:
case ERC721_UNDETERMINED:
case ERC721_ENUMERABLE:
thisToken = new ERC721Token(tokenInfo, null, BigDecimal.ZERO, currentTime, networkName, type);
break;
case ETHEREUM:

@ -92,6 +92,7 @@ public class TokenRepository implements TokenRepositoryType {
public static final BigInteger INTERFACE_BALANCES_721_TICKET = new BigInteger ("c84aae17", 16);
public static final BigInteger INTERFACE_SUPERRARE = new BigInteger ("5b5e139f", 16);
public static final BigInteger INTERFACE_ERC1155 = new BigInteger("d9b67a26", 16);
public static final BigInteger INTERFACE_ERC721_ENUMERABLE = new BigInteger("780e9d63", 16);
private static final int NODE_COMMS_ERROR = -1;
private static final int CONTRACT_BALANCE_NULL = -2;
@ -166,6 +167,7 @@ public class TokenRepository implements TokenRepositoryType {
case ERC1155:
break;
case ERC721:
case ERC721_ENUMERABLE:
case ERC721_LEGACY:
Map<BigInteger, NFTAsset> NFTBalance = t.getTokenAssets(); //add balance from Opensea
t.balance = checkUint256Balance(wallet, tInfo.chainId, tInfo.address); //get balance for wallet from contract
@ -383,6 +385,7 @@ public class TokenRepository implements TokenRepositoryType {
switch (type)
{
case ERC721:
case ERC721_ENUMERABLE:
case ERC875_LEGACY:
case ERC721_LEGACY:
case ERC721_UNDETERMINED:
@ -429,6 +432,7 @@ public class TokenRepository implements TokenRepositoryType {
break;
case ERC721_LEGACY:
case ERC721:
case ERC721_ENUMERABLE:
balance = updateERC721Balance(token, wallet);
break;
case ERC20:
@ -477,12 +481,13 @@ public class TokenRepository implements TokenRepositoryType {
private BigDecimal updateERC721Balance(Token token, Wallet wallet)
{
token.setTokenWallet(wallet.address);
token.balance = checkUint256Balance(wallet, token.tokenInfo.chainId, token.getAddress());
try (Realm realm = getRealmInstance(wallet))
{
token.updateBalance(realm);
}
return checkUint256Balance(wallet, token.tokenInfo.chainId, token.getAddress());
return token.balance;
}
private BigDecimal updateERC1155Balance(Token token, Wallet wallet)
@ -1139,6 +1144,8 @@ public class TokenRepository implements TokenRepositoryType {
{
if (getContractData(network, tokenInfo.address, supportsInterface(INTERFACE_BALANCES_721_TICKET), Boolean.TRUE))
returnType = ContractType.ERC721_TICKET;
else if (getContractData(network, tokenInfo.address, supportsInterface(INTERFACE_ERC721_ENUMERABLE), Boolean.TRUE))
returnType = ContractType.ERC721_ENUMERABLE;
else if (getContractData(network, tokenInfo.address, supportsInterface(INTERFACE_OFFICIAL_ERC721), Boolean.TRUE))
returnType = ContractType.ERC721;
else if (getContractData(network, tokenInfo.address, supportsInterface(INTERFACE_SUPERRARE), Boolean.TRUE))

@ -174,6 +174,7 @@ public class TokensRealmSource implements TokenLocalSource {
case MAYBE_ERC20:
case ERC721:
case ERC721_LEGACY:
case ERC721_ENUMERABLE:
case ERC1155:
saveToken(r, token);
break;
@ -1367,6 +1368,7 @@ public class TokensRealmSource implements TokenLocalSource {
case ERC721_UNDETERMINED:
case ERC721:
case ERC721_LEGACY:
case ERC721_ENUMERABLE:
case ERC1155:
default:
return balance;
@ -1573,6 +1575,7 @@ public class TokensRealmSource implements TokenLocalSource {
return tg;
case ERC721:
case ERC721_ENUMERABLE:
case ERC875_LEGACY:
case ERC875:
case ERC1155:

@ -454,6 +454,7 @@ public class GasService implements ContractGasProvider
case ERC721_LEGACY:
case ERC721_TICKET:
case ERC721_UNDETERMINED:
case ERC721_ENUMERABLE:
return new BigInteger(DEFAULT_GAS_LIMIT_FOR_NONFUNGIBLE_TOKENS);
default:
//unknown

@ -56,6 +56,7 @@ public class NFTAssetsAdapter extends RecyclerView.Adapter<NFTAssetsAdapter.View
case ERC721_LEGACY:
case ERC721_TICKET:
case ERC721_UNDETERMINED:
case ERC721_ENUMERABLE:
for (BigInteger i : token.getUniqueTokenIds())
{
NFTAsset asset = token.getAssetForToken(i);

@ -307,6 +307,7 @@ public class WalletViewModel extends BaseViewModel
case ERC721_LEGACY:
case ERC721_TICKET:
case ERC721_UNDETERMINED:
case ERC721_ENUMERABLE:
tokenDetailRouter.open(activity, token, defaultWallet.getValue(), false); //TODO: Fold this into tokenDetailRouter
break;

Loading…
Cancel
Save