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

IPFS gateway, web3provider, deeplink tx history, switchchain prompt #780

Merged
merged 36 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
8d8ed74
add empty allowances state
ezra-sg Jan 11, 2024
b17ae45
keep aside when empty
ezra-sg Jan 15, 2024
01ed71f
new assertNetworkConnection function - only switch network before tra…
Viterbo Jan 29, 2024
6c54dba
data is not being queried using injected provider anymore
Viterbo Jan 29, 2024
6f42d17
comment the line about simulating indexer down
Viterbo Jan 29, 2024
20dee02
removing console log
Viterbo Jan 30, 2024
fb6ebca
give feedback on switch cancelation
Viterbo Jan 30, 2024
ddb3351
removing the notification, going straight forward to switch networks.
Viterbo Jan 30, 2024
25bced2
autologin do not request connectign to the correct network
Viterbo Jan 30, 2024
713cd68
assert connected to the correct network before transfer NFTs
Viterbo Jan 31, 2024
5e4b490
adding the check before Approvals
Viterbo Feb 6, 2024
b6b8f70
Merge branch 'master' of https://github.com/telosnetwork/telos-wallet…
donnyquixotic Feb 8, 2024
67b36f9
adding deeplinking to transaction history tab on evm wallet
Viterbo Feb 14, 2024
7297d5a
restoring settings
Viterbo Feb 14, 2024
fe169e2
logs for debug
Viterbo Feb 15, 2024
f212e35
depplionk is working on transaction history page
Viterbo Feb 15, 2024
5f71461
adding logs
Viterbo Feb 16, 2024
98fa186
adding more logs
Viterbo Feb 16, 2024
5bace33
forcing getTransactions
Viterbo Feb 16, 2024
952c704
removing logs
Viterbo Feb 16, 2024
5c88cd2
cleaning the code
Viterbo Feb 19, 2024
e8a3f67
fixing all potencial ipfs links
Viterbo Feb 14, 2024
dbdb8fc
fixed the ipfs gateway for all potential links
Viterbo Feb 14, 2024
eb19041
Fixing the global getWeb3Provider
Viterbo Feb 14, 2024
7a87606
fix current label
ezra-sg Feb 14, 2024
05ba57c
fix concurrency
ezra-sg Feb 14, 2024
d6cfcc5
optimize RPC calls - WIP allowances page
ezra-sg Feb 15, 2024
dbffea4
optimize RPC calls in allowances store
ezra-sg Feb 15, 2024
3dd1a4f
handle non token contract case
ezra-sg Feb 15, 2024
793480c
fix maxSupply
ezra-sg Feb 15, 2024
c66d8a5
get NFT owner data from indexer
ezra-sg Feb 15, 2024
1db4948
remove RPC call for erc20 balance
ezra-sg Feb 15, 2024
a9043dd
use indexer instead of RPC to determine if address is contract
ezra-sg Feb 15, 2024
4603630
fix missing label on staking page
ezra-sg Feb 15, 2024
e2cf099
solving conflicts
Viterbo Feb 20, 2024
c6c4b32
Merge pull request #763 from telosnetwork/760-switch-chains-prompt
donnyquixotic Feb 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
"eslint.validate": ["javascript", "javascriptreact", "typescript", "vue"],

"vetur.experimental.templateInterpolationService": true
}
}
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
33 changes: 31 additions & 2 deletions src/antelope/stores/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export interface LoginNativeActionData {
export interface LoginEVMActionData {
authenticator: EVMAuthenticator
network: string,
autoLogAccount?: string,
}

export interface SendActionData {
Expand Down Expand Up @@ -153,14 +154,14 @@ export const useAccountStore = defineStore(store_name, {
return success;
},

async loginEVM({ authenticator, network }: LoginEVMActionData): Promise<boolean> {
async loginEVM({ authenticator, network, autoLogAccount }: LoginEVMActionData): Promise<boolean> {
this.trace('loginEVM', network);
const label = authenticator.label;
getAntelope().events.onClear.next({ label });

let success = false;
try {
const rawAddress = await authenticator.login(network);
const rawAddress = autoLogAccount ? await authenticator.autoLogin(network, autoLogAccount) : await authenticator.login(network);
this.trace('loginEVM', 'authenticator finished with address', rawAddress);

if (rawAddress) {
Expand Down Expand Up @@ -242,6 +243,7 @@ export const useAccountStore = defineStore(store_name, {
useFeedbackStore().setLoading('account.autoLogin');
const network = localStorage.getItem('network');
const account = localStorage.getItem('account');
const rawAddress = localStorage.getItem('rawAddress');
const isNative = localStorage.getItem('isNative') === 'true';
const autoLogin = localStorage.getItem('autoLogin');
this.trace('autoLogin', account, isNative, autoLogin);
Expand All @@ -265,9 +267,11 @@ export const useAccountStore = defineStore(store_name, {
console.error(getAntelope().wallets);
throw new Error('antelope.account.error_auto_login');
}
const autoLogAccount = rawAddress ?? account;
return this.loginEVM({
authenticator,
network,
autoLogAccount,
});
}
}
Expand All @@ -293,6 +297,31 @@ export const useAccountStore = defineStore(store_name, {
}
},

async assertNetworkConnection(label: string): Promise<boolean> {
if (!await useAccountStore().isConnectedToCorrectNetwork(label)) {
return new Promise<boolean>(async (resolve) => {
const ant = getAntelope();
const authenticator = useAccountStore().loggedAccount.authenticator as EVMAuthenticator;
try {
await authenticator.ensureCorrectChain();
if (!await useAccountStore().isConnectedToCorrectNetwork(label)) {
resolve(false);
} else {
resolve(true);
}
} catch (error) {
const message = (error as Error).message;
if (message === 'antelope.evm.error_switch_chain_rejected') {
ant.config.notifyNeutralMessageHandler(message);
}
resolve(false);
}
});
} else {
return true;
}
},

async sendAction({ account, data, name, actor, permission }: SendActionData): Promise<NativeTransactionResponse> {
this.trace('sendAction', account, data, name, actor, permission);
try {
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
2 changes: 1 addition & 1 deletion src/antelope/stores/chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ export const useChainStore = defineStore(store_name, {
const stkToken = chain_settings.getStakedSystemToken();

const abi = [stlosAbiPreviewDeposit[0], stlosAbiPreviewRedeem[0]];
const provider = await getAntelope().wallets.getWeb3Provider();
const provider = await getAntelope().wallets.getWeb3Provider(label);
const contractInstance = new ethers.Contract(stkToken.address, abi, provider);
// Now we preview a deposit of 1 SYS to get the ratio
const oneSys = ethers.utils.parseUnits('1.0', sysToken.decimals);
Expand Down
56 changes: 39 additions & 17 deletions src/antelope/stores/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ import {

const LOCAL_SORAGE_CONTRACTS_KEY = 'antelope.contracts';

const createManager = (signer?: ethers.Signer):EvmContractManagerI => ({
const createManager = (label: string, signer?: ethers.Signer):EvmContractManagerI => ({
getSigner: async () => signer ?? null,
getWeb3Provider: () => getAntelope().wallets.getWeb3Provider(),
getWeb3Provider: () => getAntelope().wallets.getWeb3Provider(label),
getFunctionIface: (hash:string) => toRaw(useEVMStore().getFunctionIface(hash)),
getEventIface: (hash:string) => toRaw(useEVMStore().getEventIface(hash)),
});
Expand All @@ -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 @@ -183,7 +187,7 @@ export const useContractStore = defineStore(store_name, {
return this.__contracts[network].cached[addressLower];
}

const isContract = await this.addressIsContract(network, address);
const isContract = await this.addressIsContract(label, address);

if (!isContract) {
// address is an account, not a contract
Expand Down Expand Up @@ -538,7 +542,7 @@ export const useContractStore = defineStore(store_name, {
throw new AntelopeError('antelope.contracts.error_label_required');
}

const isContract = await this.addressIsContract(network, address);
const isContract = await this.addressIsContract(label, address);

if (!isContract) {
// address is an account, not a contract
Expand All @@ -557,7 +561,7 @@ export const useContractStore = defineStore(store_name, {
|| (metadata.abi ?? []).length > 0 && (metadata.abi ?? []).length > (this.__contracts[network].cached[index]?.abi?.length ?? 0)
) {
// This manager provides the signer and the web3 provider
metadata.manager = createManager(signer);
metadata.manager = createManager(label, signer);

// we create the contract using the factory
const contract = this.__factory.buildContract(metadata);
Expand All @@ -581,26 +585,44 @@ export const useContractStore = defineStore(store_name, {
this.__contracts[network].cached[index] = null;
},

async addressIsContract(network: string, address: string) {
async addressIsContract(label: string, address: string) {
const network = useChainStore().getChain(label).settings.getNetwork();
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';
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].includes(addressLower)) {
this.__accounts[network].push(addressLower);
}
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
Loading
Loading