Skip to content

Commit

Permalink
Merge pull request #693 from telosnetwork/develop
Browse files Browse the repository at this point in the history
v2.3.2-rc
  • Loading branch information
pmjanus authored Nov 28, 2023
2 parents 7b2cd28 + 3569889 commit 94d5031
Show file tree
Hide file tree
Showing 21 changed files with 399 additions and 155 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "telos-web-wallet",
"version": "2.3.2",
"version": "2.3.3",
"description": "A Web Wallet for Telos",
"productName": "Telos Web Wallet",
"private": true,
Expand Down
74 changes: 48 additions & 26 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -1,42 +1,47 @@
<script lang="ts">
import { useChainStore } from 'src/antelope';
import { getAntelope, useChainStore } from 'src/antelope';
import { ComplexMessage } from 'src/antelope/config';
import EVMChainSettings from 'src/antelope/chains/EVMChainSettings';
import { TELOS_CHAIN_IDS } from 'src/antelope/chains/chain-constants';
import packageInfo from '../package.json';
import { defineComponent } from 'vue';
export default {
export const isTodayBeforeTelosCloudDown = new Date().getTime() < new Date('2023-12-31').getTime();
export default defineComponent({
name: 'App',
created() {
const appVersionJustUpdated = 'UPDATED_NOTIFY_USER';
const userIsNew = !localStorage.getItem('account');
const currentVersion = packageInfo.version;
const clientVersion = localStorage.getItem('appVersion');
console.info('Wallet version: ', packageInfo.version);
if (clientVersion && clientVersion !== appVersionJustUpdated) {
console.info('Client version: ', clientVersion);
}
if (clientVersion !== currentVersion) {
if (clientVersion && clientVersion !== appVersionJustUpdated) {
console.info('Client version: ', clientVersion);
}
// when localstorage is cleared, we need to reload the page for it to take effect.
// however if we immediately reload the page here we cannot show a notification to the user.
// so the const appVersionUpdated lets us know after the reload that we just cleared the old localStorage
// and need to notify the user that they need to log in again
if (clientVersion === appVersionJustUpdated) {
console.info('App version mismatch, local storage cleared');
// App version was updated, localStorage was cleared, and the page reloaded
// Now inform the user that the app was updated & have them login again, and set the client app version
localStorage.setItem('appVersion', currentVersion);
(this as any).$notifySuccessMessage(
(this as any).$t('global.new_app_version'),
);
} else if (userIsNew) {
localStorage.clear();
localStorage.setItem('appVersion', currentVersion);
} else if (clientVersion !== currentVersion) {
localStorage.clear();
localStorage.setItem('appVersion', appVersionJustUpdated);
window.location.reload();
// when localstorage is cleared, we need to reload the page for it to take effect.
// however if we immediately reload the page here we cannot show a notification to the user.
// so the const appVersionUpdated lets us know after the reload that we just cleared the old localStorage
// and need to notify the user that they need to log in again
if (clientVersion === appVersionJustUpdated) {
console.info('App version mismatch, local storage cleared');
// App version was updated, localStorage was cleared, and the page reloaded
// Now inform the user that the app was updated & have them login again, and set the client app version
localStorage.setItem('appVersion', currentVersion);
(this as any).$notifySuccessMessage(
(this as any).$t('global.new_app_version'),
);
} else if (userIsNew) {
localStorage.clear();
localStorage.setItem('appVersion', currentVersion);
} else {
localStorage.clear();
localStorage.setItem('appVersion', appVersionJustUpdated);
window.location.reload();
}
}
},
mounted() {
Expand All @@ -51,8 +56,25 @@ export default {
script.defer = true;
document.body.appendChild(script);
}
if (isTodayBeforeTelosCloudDown) {
getAntelope().config.notifyRememberInfoHandler(
this.$t('temporal.telos_cloud_discontinued_title'),
[{
tag: 'p',
class: 'c-notify__message--subtitle',
text: this.$t('temporal.telos_cloud_discontinued_message_title'),
}, {
tag: 'p',
class: '',
text: this.$t('temporal.telos_cloud_discontinued_message_body'),
}],
'',
'telos-cloud-discontinued',
);
}
},
};
});
</script>

<template>
Expand Down
6 changes: 6 additions & 0 deletions src/antelope/chains/EVMChainSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,12 @@ export default abstract class EVMChainSettings implements ChainSettings {
supply: nftResponse.supply,
}));

// we fix the supportedInterfaces property if it is undefined in the response but present in the request
Object.values(response.contracts).forEach((contract) => {
contract.supportedInterfaces = contract.supportedInterfaces ||
params.type ? [params.type?.toLowerCase() as string] : undefined;
});

this.processNftContractsCalldata(response.contracts);
const shapedNftData = this.shapeNftRawData(shapedIndexerNftData, response.contracts);
return this.processNftRawData(shapedNftData);
Expand Down
20 changes: 20 additions & 0 deletions src/antelope/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ import { App } from 'vue';
import { getAntelope } from 'src/antelope';
import { AntelopeError, AntelopeErrorPayload } from 'src/antelope/types';

export interface ComplexMessage {
tag: string,
class: string,
text: string,
}

export class AntelopeConfig {
transactionError(description: string, error: unknown): AntelopeError {
if (error instanceof AntelopeError) {
Expand Down Expand Up @@ -36,6 +42,7 @@ export class AntelopeConfig {
private __notify_failure_action_handler: (message: string, payload?: AntelopeErrorPayload) => void = alert;
private __notify_disconnected_handler: () => void = alert;
private __notify_neutral_message_handler: (message: string) => (() => void) = () => (() => void 0);
private __notify_remember_info_handler: (title: string, message: string | ComplexMessage[], payload: string, key: string) => (() => void) = () => (() => void 0);

// ual authenticators list getter --
private __authenticators_getter: () => Authenticator[] = () => [];
Expand Down Expand Up @@ -159,6 +166,10 @@ export class AntelopeConfig {
return this.__notify_neutral_message_handler;
}

get notifyRememberInfoHandler() {
return this.__notify_remember_info_handler;
}

get authenticatorsGetter() {
return this.__authenticators_getter;
}
Expand Down Expand Up @@ -225,6 +236,15 @@ export class AntelopeConfig {
this.__notify_neutral_message_handler = handler;
}

public setNotifyRememberInfoHandler(handler: (
title: string,
message: string | ComplexMessage[],
payload: string,
key: string,
) => (() => void)) {
this.__notify_remember_info_handler = handler;
}

// setting authenticators getter --
public setAuthenticatorsGetter(getter: () => Authenticator[]) {
this.__authenticators_getter = getter;
Expand Down
11 changes: 9 additions & 2 deletions src/antelope/stores/balances.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,20 +307,27 @@ export const useBalancesStore = defineStore(store_name, {
const funcname = 'transferTokens';
this.trace(funcname, token, to, amount.toString(), memo);
const label = CURRENT_CONTEXT;
let promise = Promise.resolve({} as TransactionResponse);
try {
useFeedbackStore().setLoading(funcname);
const chain = useChainStore().loggedChain;
if (chain.settings.isNative()) {
const chain_settings = chain.settings as NativeChainSettings;
const account = useAccountStore().loggedAccount;
return await this.transferNativeTokens(chain_settings, account, token, to, amount, memo ?? '')
promise = this.transferNativeTokens(chain_settings, account, token, to, amount, memo ?? '')
.then(r => this.subscribeForTransactionReceipt(account, r));
} else {
const chain_settings = chain.settings as EVMChainSettings;
const account = useAccountStore().loggedAccount as EvmAccountModel;
return this.transferEVMTokens(label, chain_settings, account, token, to, amount)
promise = this.transferEVMTokens(label, chain_settings, account, token, to, amount)
.then(r => this.subscribeForTransactionReceipt(account, r as TransactionResponse));
}
promise.catch((error) => {
const trxError = getAntelope().config.transactionError('antelope.evm.error_transfer_failed', error);
getAntelope().config.transactionErrorHandler(trxError, funcname);
throw trxError;
});
return promise;
} catch (error) {
const trxError = getAntelope().config.transactionError('antelope.evm.error_transfer_failed', error);
getAntelope().config.transactionErrorHandler(trxError, funcname);
Expand Down
71 changes: 66 additions & 5 deletions src/antelope/stores/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,10 +261,16 @@ export const useContractStore = defineStore(store_name, {
return resolve(this.createAndStoreVerifiedContract(label, addressLower, metadata, creationInfo, suspectedToken));
}

const contract = await this.createAndStoreContractFromTokenList(label, address, suspectedToken, creationInfo);
if (contract) {
this.trace('fetchContractUsingHyperion', 'returning contract from token list', address, contract);
return resolve(contract);
const tokenContract = await this.createAndStoreContractFromTokenList(label, address, suspectedToken, creationInfo);
if (tokenContract) {
this.trace('fetchContractUsingHyperion', 'returning contract from token list', address, tokenContract);
return resolve(tokenContract);
}

const suspectedTokenContract = await this.createAndStoreContractFromSuspectedType(label, address, suspectedToken, creationInfo);
if (suspectedTokenContract) {
this.trace('fetchContractUsingHyperion', 'returning contract from suspected type', address, suspectedTokenContract);
return resolve(suspectedTokenContract);
}

if (creationInfo) {
Expand Down Expand Up @@ -395,6 +401,16 @@ export const useContractStore = defineStore(store_name, {
},

// utility functions ---------------------
/**
* This function creates a verified contract based on its metadata and creation info,
* which was used to verify this contract previously.
* @param label identifies the chain
* @param address address of the contract
* @param metadata verified metadata of the contract
* @param creationInfo creation info of the contract
* @param suspectedType type of the contract. It can be 'erc20', 'erc721' or 'erc1155'
* @returns the contract
*/
async createAndStoreVerifiedContract(
label: string,
address:string,
Expand All @@ -415,6 +431,13 @@ export const useContractStore = defineStore(store_name, {
} as EvmContractFactoryData);
},

/**
* This function creates an empty contract based on its creation info.
* @param label identifies the chain
* @param address address of the contract
* @param creationInfo creation info of the contract
* @returns the contract
*/
async createAndStoreEmptyContract(
label: string,
address:string,
Expand All @@ -425,10 +448,19 @@ export const useContractStore = defineStore(store_name, {
name: `0x${address.slice(0, 16)}...`,
address,
creationInfo,
supportedInterfaces: [],
supportedInterfaces: undefined,
} as EvmContractFactoryData);
},

/**
* This function tries to create a contract based on the known token list.
* If the address is not in the list, it will return null.
* @param label identifies the chain
* @param address address of the contract
* @param suspectedType type of the contract. It can be 'erc20', 'erc721' or 'erc1155'
* @param creationInfo creation info of the contract
* @returns the contract or null if the address is not in the token list
*/
async createAndStoreContractFromTokenList(
label:string,
address:string,
Expand All @@ -451,6 +483,35 @@ export const useContractStore = defineStore(store_name, {
}
},

/**
* This function tries to create a contract from a suspected type (using the corresponding known ABI).
* It will return null if the type is not supported.
* @param label identifies the chain
* @param address address of the contract
* @param suspectedType type of the contract. It can be 'erc20', 'erc721' or 'erc1155'
* @param creationInfo creation info of the contract
* @returns the contract or null if the type is not supported
*/
async createAndStoreContractFromSuspectedType(
label:string,
address:string,
suspectedType:string,
creationInfo:EvmContractCreationInfo | null,
): Promise<EvmContract | null> {
const abi = this.getTokenABI(suspectedType);
if (abi) {
return this.createAndStoreContract(label, address, {
name: `0x${address.slice(0, 16)}...`,
address,
creationInfo,
abi,
supportedInterfaces: [suspectedType],
} as EvmContractFactoryData);
} else {
return null;
}
},

// commits -----
createAndStoreContract(label: string, address: string, metadata: EvmContractFactoryData): EvmContract {
const network = useChainStore().getChain(label).settings.getNetwork();
Expand Down
1 change: 1 addition & 0 deletions src/antelope/stores/nfts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ export const useNftsStore = defineStore(store_name, {
const new_filter = {
...toRaw(this.__pagination_filter),
tokenId,
type,
};

// If we already have a contract for that network and contract, we search for the NFT in that list first
Expand Down
6 changes: 4 additions & 2 deletions src/antelope/stores/utils/nft-utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { IndexerNftMetadata, NFTSourceTypes, NftSourceType } from 'src/antelope/types';
import { urlIsAudio, urlIsPicture, urlIsVideo } from 'src/antelope/stores/utils/media-utils';

export const IPFS_GATEWAY = 'https://cloudflare-ipfs.com/ipfs/';
export const IPFS_GATEWAY = 'https://ipfs.telos.net/ipfs/';

/**
* Given an imageCache URL, tokenUri, and metadata, extract the image URL, mediaType, and mediaSource
Expand Down Expand Up @@ -109,7 +109,9 @@ export async function extractNftMetadata(
}
}

if (metadata?.image?.includes(IPFS_GATEWAY)) {
const metadataImageIsMediaUrl = await urlIsVideo(metadata?.image ?? '') || await urlIsAudio(metadata?.image ?? '') || urlIsPicture(metadata?.image ?? '');

if (metadata?.image?.includes(IPFS_GATEWAY) && !metadataImageIsMediaUrl) {
mediaType = await determineIpfsMediaType(metadata?.image);

if (mediaType === NFTSourceTypes.IMAGE) {
Expand Down
1 change: 1 addition & 0 deletions src/antelope/types/Filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,5 @@ export interface IndexerCollectionNftsFilter extends IndexerPaginationFilter {
includeAbi?: boolean; // indicate whether to include abi
tokenId?: string; // only query results for a specific token ID
includeTokenIdSupply?: boolean;
type?: NftTokenInterface;
}
23 changes: 18 additions & 5 deletions src/antelope/wallets/authenticators/OreIdAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,10 +202,17 @@ export class OreIdAuth extends EVMAuthenticator {
return this.userChainAccount?.chainAccount as addressString;
}

handleCatchError(error: never): AntelopeError {
this.trace('handleCatchError', error);
console.error(error);
return new AntelopeError('antelope.evm.error_send_transaction', { error });
// eslint-disable-next-line @typescript-eslint/no-explicit-any
handleCatchError(error: Error): AntelopeError {
this.trace('handleCatchError', error.message);
if (
error.message === 'Closed by user' ||
error.message === 'sign_transaction_cancelled_by_user'
) {
return new AntelopeError('antelope.evm.error_transaction_canceled');
} else {
return new AntelopeError('antelope.evm.error_send_transaction', { error });
}
}

/**
Expand All @@ -226,7 +233,7 @@ export class OreIdAuth extends EVMAuthenticator {
}

async performOreIdTransaction(from: addressString, json: JSONObject): Promise<EvmTransactionResponse> {

this.trace('performOreIdTransaction', from, json);
const oreIdInstance = oreId as OreId;

// sign a blockchain transaction
Expand All @@ -253,10 +260,16 @@ export class OreIdAuth extends EVMAuthenticator {
this.trace('sendSystemToken', to, amount.toString());
const from = this.getAccountAddress();
const value = amount.toHexString();

// Send the transaction
return this.performOreIdTransaction(from, {
from,
to,
value,
}).then(
(transaction: ethers.providers.TransactionResponse) => transaction,
).catch((error) => {
throw this.handleCatchError(error);
});
}

Expand Down
Loading

0 comments on commit 94d5031

Please sign in to comment.