Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Global getWeb3Provider fixed #775

21 changes: 19 additions & 2 deletions src/antelope/chains/EVMChainSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import { dateIsWithinXMinutes } from 'src/antelope/stores/utils/date-utils';
import { CURRENT_CONTEXT, getAntelope, useContractStore, useNftsStore } from 'src/antelope';
import { WEI_PRECISION, PRICE_UPDATE_INTERVAL_IN_MIN } from 'src/antelope/stores/utils';
import { BehaviorSubject, filter } from 'rxjs';
import { createTraceFunction } from 'src/antelope/config';


export default abstract class EVMChainSettings implements ChainSettings {
Expand Down Expand Up @@ -97,6 +98,9 @@ export default abstract class EVMChainSettings implements ChainSettings {
// This observable is used to check if the indexer health state was already checked
indexerChecked$ = new BehaviorSubject(false);

// This function is used to trace the execution of the code
trace = createTraceFunction('EVMChainSettings');

simulateIndexerDown(isBad: boolean) {
this.indexerBadHealthSimulated = isBad;
}
Expand Down Expand Up @@ -152,6 +156,7 @@ export default abstract class EVMChainSettings implements ChainSettings {
}

async init(): Promise<void> {
this.trace('init');
// this is called only when this chain is needed to avoid initialization of all chains
if (this.ready) {
return this.initPromise;
Expand Down Expand Up @@ -302,6 +307,7 @@ export default abstract class EVMChainSettings implements ChainSettings {
}

async getBalances(account: string): Promise<TokenBalance[]> {
this.trace('getBalances', account);
if (!this.hasIndexerSupport()) {
console.error('Indexer API not supported for this chain:', this.getNetwork());
return [];
Expand Down Expand Up @@ -366,6 +372,7 @@ export default abstract class EVMChainSettings implements ChainSettings {

// get the NFTs belonging to a particular contract (collection)
async getNftsForCollection(collection: string, params: IndexerCollectionNftsFilter): Promise<Collectible[]> {
this.trace('getNftsForCollection', collection, params);
if (!this.hasIndexerSupport()) {
console.error('Error fetching NFTs, Indexer API not supported for this chain:', this.getNetwork());
return [];
Expand All @@ -384,6 +391,7 @@ export default abstract class EVMChainSettings implements ChainSettings {
supply: nftResponse.supply,
owner: nftResponse.owner,
}));
this.trace('getNftsForCollection', { shapedIndexerNftData, response });

// we fix the supportedInterfaces property if it is undefined in the response but present in the request
Object.values(response.contracts).forEach((contract) => {
Expand All @@ -392,7 +400,10 @@ export default abstract class EVMChainSettings implements ChainSettings {

this.processNftContractsCalldata(response.contracts);
const shapedNftData = this.shapeNftRawData(shapedIndexerNftData, response.contracts);
return this.processNftRawData(shapedNftData);
this.trace('getNftsForCollection', { shapedNftData });
const finalNftData = this.processNftRawData(shapedNftData);
this.trace('getNftsForCollection', { finalNftData });
return finalNftData;
}

// get the NFTs belonging to a particular account
Expand Down Expand Up @@ -471,6 +482,7 @@ export default abstract class EVMChainSettings implements ChainSettings {

// process the shaped raw data into NFTs
async processNftRawData(shapedRawNfts: NftRawData[]): Promise<Collectible[]> {
this.trace('processNftRawData', shapedRawNfts);
const contractStore = useContractStore();
const nftsStore = useNftsStore();

Expand Down Expand Up @@ -502,6 +514,7 @@ export default abstract class EVMChainSettings implements ChainSettings {

return nft;
});
this.trace('processNftRawData', 'erc1155Nfts', erc1155Nfts);

const erc721RawData = shapedRawNfts.filter(({ contract }) => contract.supportedInterfaces.includes('erc721'));
const erc721Nfts = erc721RawData.map(async ({ data, contract }) => {
Expand All @@ -519,6 +532,7 @@ export default abstract class EVMChainSettings implements ChainSettings {

return nft;
});
this.trace('processNftRawData', 'erc721Nfts', erc721Nfts);

const settledPromises = await Promise.allSettled([...erc1155Nfts, ...erc721Nfts]);

Expand All @@ -529,7 +543,10 @@ export default abstract class EVMChainSettings implements ChainSettings {
console.error('Error constructing NFT', reason);
});

return fulfilledPromises.map(result => result.value as Collectible);
const nfts = fulfilledPromises.map(result => result.value as Collectible);
this.trace('processNftRawData', 'nfts', nfts);

return nfts;
}

constructTokenId(token: TokenSourceInfo): string {
Expand Down
46 changes: 23 additions & 23 deletions src/antelope/stores/allowances.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { defineStore } from 'pinia';
import { filter } from 'rxjs';
import { formatUnits } from 'ethers/lib/utils';
import { BigNumber } from 'ethers';

Expand Down Expand Up @@ -36,6 +35,7 @@ import {
CURRENT_CONTEXT,
getAntelope,
useAccountStore,
useBalancesStore,
useChainStore,
useContractStore,
useFeedbackStore,
Expand Down Expand Up @@ -217,15 +217,6 @@ export const useAllowancesStore = defineStore(store_name, {
init: () => {
const allowancesStore = useAllowancesStore();
const ant = getAntelope();
ant.events.onAccountChanged.pipe(
filter(({ label, account }) => !!label && !!account),
).subscribe({
next: ({ label, account }) => {
if (label === CURRENT_CONTEXT && account?.account) {
allowancesStore.fetchAllowancesForAccount(account?.account);
}
},
});

ant.events.onClear.subscribe(({ label }) => {
allowancesStore.clearAllowances(label);
Expand Down Expand Up @@ -554,9 +545,20 @@ export const useAllowancesStore = defineStore(store_name, {
const tokenInfo = useTokensStore().__tokens[CURRENT_CONTEXT].find(token => token.address.toLowerCase() === data.contract.toLowerCase());

const tokenContract = await useContractStore().getContract(CURRENT_CONTEXT, data.contract);
const tokenContractInstance = await tokenContract?.getContractInstance();
const maxSupply = await tokenContractInstance?.totalSupply() as BigNumber | undefined;
const balance = await tokenContractInstance?.balanceOf(data.owner) as BigNumber | undefined;

const maxSupply = tokenContract?.maxSupply;

const balancesStore = useBalancesStore();
let balance = balancesStore.__balances[CURRENT_CONTEXT]?.find(
balance => balance.token.address.toLowerCase() === data.contract.toLowerCase(),
)?.amount;

if (!balance) {
const indexer = (useChainStore().loggedChain.settings as EVMChainSettings).getIndexer();
const balanceString = (await indexer.get(`/v1/token/${data.contract}/holders?account=${data.owner}`)).data.results[0].balance;

balance = BigNumber.from(balanceString);
}

if (!balance || !tokenInfo || !maxSupply) {
return null;
Expand Down Expand Up @@ -612,7 +614,10 @@ export const useAllowancesStore = defineStore(store_name, {
}

const collectionInfo = await useContractStore().getContract(CURRENT_CONTEXT, data.contract);
const balance = await (await collectionInfo?.getContractInstance())?.balanceOf(data.owner);
const indexer = (useChainStore().loggedChain.settings as EVMChainSettings).getIndexer();
const balanceString = (await indexer.get(`/v1/token/${data.contract}/holders?account=${data.owner}`)).data.results[0].balance;

const balance = BigNumber.from(balanceString);

return collectionInfo ? {
...commonAttributes,
Expand Down Expand Up @@ -641,14 +646,9 @@ export const useAllowancesStore = defineStore(store_name, {
return null;
}

const balancePromises = collectionNftIds.map(async (tokenId) => {
const contractInstance = await collectionInfo?.getContractInstance();
return contractInstance?.balanceOf(data.owner, tokenId) as BigNumber;
});


const balancesOfAllIdsInCollection = await Promise.all(balancePromises);
const balance = balancesOfAllIdsInCollection.reduce((acc, balance) => acc.add(balance ?? 0), BigNumber.from(0));
const indexer = (useChainStore().loggedChain.settings as EVMChainSettings).getIndexer();
const holderInfoForOwner = (await indexer.get(`/v1/token/${data.contract}/holders?account=${data.owner}&limit=${ALLOWANCES_LIMIT}`)).data.results as { balance: string }[];
const totalNftsOwned = holderInfoForOwner.reduce((acc, holderInfo) => acc.add(holderInfo.balance), BigNumber.from(0));

return collectionInfo ? {
lastUpdated: data.updated,
Expand All @@ -657,7 +657,7 @@ export const useAllowancesStore = defineStore(store_name, {
allowed: data.approved,
collectionAddress: collectionInfo.address,
collectionName: collectionInfo.name,
balance,
balance: totalNftsOwned,
} : null;
} catch(e) {
console.error('Error shaping ERC1155 allowance row', e);
Expand Down
44 changes: 33 additions & 11 deletions src/antelope/stores/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,13 @@ export interface ContractStoreState {
processing: Record<string, Promise<EvmContract | null>>
},
}
// addresses which have been checked and are known not to be contract addresses
__accounts: {
[network: string]: string[],
[network: string]: {
// addresses which are being checked to see if they are account (non-contract) addresses
processing: Record<string, Promise<boolean>>,
// addresses which have been checked and are known not to be contract addresses
addresses: string[],
},
},
}

Expand Down Expand Up @@ -583,24 +587,42 @@ export const useContractStore = defineStore(store_name, {

async addressIsContract(network: string, address: string) {
const addressLower = address.toLowerCase();

if (this.__contracts[network]?.cached[addressLower] || this.__contracts[network]?.metadata[addressLower]) {
return true;
}

if (!this.__accounts[network]) {
this.__accounts[network] = [];
this.__accounts[network] = {
processing: {},
addresses: [],
};
}

if (this.__accounts[network].includes(addressLower)) {
if (this.__accounts[network].addresses.includes(addressLower)) {
return false;
}

const provider = await getAntelope().wallets.getWeb3Provider();
const code = await provider.getCode(address);
if (!!this.__accounts[network].processing[addressLower]) {
return this.__accounts[network].processing[addressLower];
}

const isContract = code !== '0x';

if (!isContract && !this.__accounts[network].includes(addressLower)) {
this.__accounts[network].push(addressLower);
}
this.__accounts[network].processing[addressLower] = new Promise(async (resolve) => {
const indexer = (useChainStore().loggedChain.settings as EVMChainSettings).getIndexer();
const isContract = (await indexer.get(`/v1/contract/${addressLower}`)).data.results.length > 0;

if (!isContract && !this.__accounts[network].addresses.includes(addressLower)) {
this.__accounts[network].addresses.push(addressLower);
}

return isContract;
resolve(isContract);
});

return this.__accounts[network].processing[addressLower].then((isContract) => {
delete this.__accounts[network].processing[addressLower];
return isContract;
});
},
},
});
Expand Down
4 changes: 4 additions & 0 deletions src/antelope/stores/nfts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ export const useNftsStore = defineStore(store_name, {
tokenId,
type,
};
this.trace('fetchNftDetails', 'filter:', new_filter);

// If we already have a contract for that network and contract, we search for the NFT in that list first
this.__contracts[network] = this.__contracts[network] || {};
Expand All @@ -246,6 +247,7 @@ export const useNftsStore = defineStore(store_name, {
nft => nft.contractAddress.toLowerCase() === contract.toLowerCase() && nft.id === tokenId,
);
if (nft) {
this.trace('fetchNftDetails', 'found in cache:', nft);
return nft;
}
} else {
Expand All @@ -260,6 +262,7 @@ export const useNftsStore = defineStore(store_name, {
// we don't have the NFT on any cache, we fetch it from the indexer
useFeedbackStore().setLoading('updateNFTsForAccount');
if (chain.settings.isNative() || (chain.settings as EVMChainSettings).hasIndexerSupport()) {
this.trace('fetchNftDetails', 'fetching from indexer');
promise = chain.settings.getNftsForCollection(contract, new_filter).then((nfts) => {
const contractLower = contract.toLowerCase();

Expand All @@ -273,6 +276,7 @@ export const useNftsStore = defineStore(store_name, {
if (!chain.settings.isNative()) {
// this means we have the indexer down
// we have the contract and the address so we try to fetch the NFT from the contract
this.trace('fetchNftDetails', 'indexer down, fetching from contract');
useEVMStore().getNFT(
contract,
tokenId,
Expand Down
2 changes: 1 addition & 1 deletion src/antelope/stores/rex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export const useRexStore = defineStore(store_name, {
filter(({ label, account }) => !!label && !!account),
).subscribe({
next: async ({ label, account }) => {
if (label === 'current') {
if (label === CURRENT_CONTEXT) {
await useRexStore().updateRexDataForAccount(label, toRaw(account));
}
},
Expand Down
18 changes: 17 additions & 1 deletion src/antelope/stores/utils/contracts/EvmContract.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ContractInterface, ethers } from 'ethers';
import { BigNumber, ContractInterface, ethers } from 'ethers';
import { markRaw } from 'vue';
import {
AntelopeError, EvmContractCalldata,
Expand All @@ -13,6 +13,7 @@ import {
TRANSFER_SIGNATURES,
} from 'src/antelope/types';
import { Interface } from 'ethers/lib/utils';
import { parseUnits } from 'ethers/lib/utils';


export default class EvmContract {
Expand All @@ -26,6 +27,7 @@ export default class EvmContract {
private readonly _manager?: EvmContractManagerI;
private readonly _token?: TokenSourceInfo | null;

private _contractInstance?: ethers.Contract;
private _verified?: boolean;

constructor({
Expand Down Expand Up @@ -117,6 +119,14 @@ export default class EvmContract {
return this._token;
}

get maxSupply() {
if (!this.isToken() || !this._properties?.supply || !this._properties?.decimals) {
return BigNumber.from(0);
}

return parseUnits(this._properties.supply, this._properties.decimals);
}

isNonFungible() {
return (this._supportedInterfaces.includes('erc721'));
}
Expand All @@ -137,6 +147,11 @@ export default class EvmContract {
if (!this.abi){
throw new AntelopeError('antelope.utils.error_contract_instance');
}

if (this._contractInstance) {
return this._contractInstance;
}

const signer = await this._manager?.getSigner();
let provider;

Expand All @@ -145,6 +160,7 @@ export default class EvmContract {
}

const contract = new ethers.Contract(this.address, this.abi, signer ?? provider ?? undefined);
this._contractInstance = contract;

return contract;
}
Expand Down
Loading
Loading