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

handle wallet address change gracefully do not require reload #496

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"oreid-js": "^4.7.1",
"oreid-webpopup": "^2.4.0",
"quasar": "2",
"rxjs": "^7.8.0",
"ual-anchor": "^1.0.0",
"ual-ledger": "^0.3.0",
"ual-wombat": "^0.3.3",
Expand Down
3 changes: 3 additions & 0 deletions src/antelope/mocks/AccountStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ class AccountStore {
async loginEVM({ authenticator, network }: LoginEVMActionData): Promise<boolean> {
currentAuthenticator = authenticator;
currentAccount = await authenticator.login(network);
const account = useAccountStore().getAccount(authenticator.label);
getAntelope().events.onLoggedIn.next(account);
return true;
}

Expand All @@ -62,6 +64,7 @@ class AccountStore {
currentAuthenticator.logout();
currentAuthenticator = {} as EVMAuthenticator;
currentAccount = null;
getAntelope().events.onLoggedOut.next();
}

get loggedAccount() {
Expand Down
7 changes: 7 additions & 0 deletions src/antelope/mocks/AntelopeConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { EVMAuthenticator } from 'src/antelope/wallets/authenticators/EVMAuthent
import { AntelopeError, AntelopeErrorPayload } from 'src/antelope/types';
import { App } from 'vue';
import { Authenticator } from 'universal-authenticator-library';
import { Subject } from 'rxjs';
import { AccountModel } from 'src/antelope/mocks/AccountStore';

export interface ComplexMessage {
tag: string,
Expand Down Expand Up @@ -286,9 +288,14 @@ export class AntelopeConfig {

const config = new AntelopeConfig();
const wallets = new AntelopeWallets();
const events = {
onLoggedIn: new Subject<AccountModel>(),
onLoggedOut: new Subject<void>(),
};
const Antelope = {
config,
wallets,
events,
};

export const getAntelope = () => Antelope;
Expand Down
1 change: 1 addition & 0 deletions src/antelope/wallets/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,4 @@ export function initAntelope(app: App) {
ant.wallets.addEVMAuthenticator(new BraveAuth());

}

158 changes: 14 additions & 144 deletions src/components/LoginModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,16 @@
import detectEthereumProvider from '@metamask/detect-provider';
import { defineComponent } from 'vue';
import { mapGetters, mapMutations } from 'vuex';
import { ethers } from 'ethers';
import {
WEI_PRECISION,
LOGIN_EVM,
LOGIN_NATIVE,
PROVIDER_WEB3_INJECTED,
PROVIDER_TELOS_CLOUD,
PROVIDER_WALLET_CONNECT,
PROVIDER_BRAVE,
PROVIDER_METAMASK,
DEFAULT_CHAIN_ID,
LOGIN_DATA_KEY,
} from 'src/lib/utils';
import { tlos } from 'src/lib/logos';
import { CURRENT_CONTEXT, getAntelope, useAccountStore, useChainStore } from 'src/antelope/mocks';
import { AccountModel, CURRENT_CONTEXT, EvmAccountModel, getAntelope, useAccountStore, useChainStore } from 'src/antelope/mocks';
import { Authenticator } from 'universal-authenticator-library';
import InlineSvg from 'vue-inline-svg';
import { isTodayBeforeTelosCloudDown } from 'src/App.vue';
Expand Down Expand Up @@ -65,6 +60,19 @@ export default defineComponent({
await this.detectProvider();
this.detectMobile();

// On login we must set the address and record the provider
getAntelope().events.onLoggedIn.subscribe((account: AccountModel) => {
const evm_account = account as EvmAccountModel;
const address = evm_account.account;
const pr_name = evm_account.authenticator.getName();
this.setLogin({ address });

localStorage.setItem(LOGIN_DATA_KEY, JSON.stringify({
type: LOGIN_EVM,
provider: pr_name,
}));
});

const loginData = localStorage.getItem(LOGIN_DATA_KEY);
if (!loginData) {
return;
Expand Down Expand Up @@ -110,57 +118,9 @@ export default defineComponent({
getLoginDisplay() {
return this.isNative ? this.nativeAccount : `0x...${this.address.slice(this.address.length - 4)}`;
},
connect() {
this.showLogin = true;
},
disconnect() {
if (this.isNative) {
const loginData = localStorage.getItem(LOGIN_DATA_KEY);
if (!loginData) {
return;
}

const loginObj = JSON.parse(loginData);
const wallet = this.authenticators.find((a: { getName: () => any; }) => a.getName() === loginObj.provider);
wallet?.logout();
}

this.setLogin({});
localStorage.removeItem(LOGIN_DATA_KEY);
this.$providerManager.setProvider(null);
},
goToAddress() {
this.$router.push(`/address/${this.address}`);
},
async injectedWeb3Login() {
const address = await this.getInjectedAddress();
if (address) {
this.setLogin({
address,
});
let provider = this.getInjectedProvider();
let checkProvider = new ethers.providers.Web3Provider(provider);
this.$providerManager.setProvider(provider);
const { chainId } = await checkProvider.getNetwork();
localStorage.setItem(LOGIN_DATA_KEY, JSON.stringify({
type: LOGIN_EVM,
provider: PROVIDER_WEB3_INJECTED,
chain: chainId,
}));
provider.on('chainChanged', (newNetwork: number) => {
if(newNetwork !== chainId){
this.setLogin({});
this.$providerManager.setProvider(null);
}
});
provider.on('accountsChanged', (accounts: any[]) => {
this.setLogin({
address: accounts[0],
});
});
}
this.$emit('hide');
},
async ualLogin(wallet: Authenticator, account?: string) {
await wallet.init();
const users = await wallet.login(account);
Expand Down Expand Up @@ -188,96 +148,6 @@ export default defineComponent({
}
this.$emit('hide');
},
async getInjectedAddress() {
const provider = this.getInjectedProvider();
let checkProvider: ethers.providers.Web3Provider | undefined = new ethers.providers.Web3Provider(provider);

const newProviderInstance = await this.ensureCorrectChain(checkProvider);
if(newProviderInstance){
checkProvider = newProviderInstance;
}
const accounts = await checkProvider.listAccounts();
if (accounts.length > 0) {
checkProvider = await this.ensureCorrectChain(checkProvider);
return accounts[0];
} else {
const accessGranted = await provider.request({ method: 'eth_requestAccounts' });
if (accessGranted.length < 1) {
return false;
}

checkProvider = await this.ensureCorrectChain(checkProvider);
return accessGranted[0];
}
},
async ensureCorrectChain(checkProvider: ethers.providers.Web3Provider) {
const { chainId } = await checkProvider.getNetwork();
if (+chainId !== +(process.env.NETWORK_EVM_CHAIN_ID ?? DEFAULT_CHAIN_ID)) {
await this.switchChainInjected();
const provider = this.getInjectedProvider();
return new ethers.providers.Web3Provider(provider);
}
},
getInjectedProvider() {
// window.ethereum.isMetaMask includes Brave Wallet
const provider = window.ethereum.isMetaMask || window.ethereum.isCoinbaseWallet ?
window.ethereum :
null;
if (!provider) {
this.$q.notify({
position: 'top',
message: this.$t('components.no_provider_found'),
timeout: 6000,
});
}
return provider;
},
async switchChainInjected() {
const provider = this.getInjectedProvider();

if (provider) {
const chainId = parseInt(process.env.NETWORK_EVM_CHAIN_ID || DEFAULT_CHAIN_ID, 10);
const chainIdParam = `0x${chainId.toString(16)}`;
const mainnet = chainId === 40;
try {
await provider.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: chainIdParam }],
});
return true;
} catch (e) {
const chainNotAddedCodes = [
4902,
-32603, // https://github.com/MetaMask/metamask-mobile/issues/2944
];

if (chainNotAddedCodes.includes((e as {code:number}).code)) { // 'Chain <hex chain id> hasn't been added'
try {
await provider.request({
method: 'wallet_addEthereumChain',
params: [{
chainId: chainIdParam,
chainName: `Telos EVM ${mainnet ? 'Mainnet' : 'Testnet'}`,
nativeCurrency: {
name: 'Telos',
symbol: 'TLOS',
decimals: WEI_PRECISION,
},
rpcUrls: [`https://${mainnet ? 'mainnet' : 'testnet'}.telos.net/evm`],
blockExplorerUrls: [`https://${mainnet ? '' : 'testnet.'}teloscan.io`],
iconUrls: [tlos],
}],
});
return true;
} catch (e) {
console.error(e);
}
}
}
} else {
return false;
}
},
getIconForWallet(wallet: { getName: () => string; getStyle: () => { icon: string; }; }) {
if (wallet.getName() === 'wombat') {
// Wombat UAL logo is 32x32px; substitute for higher res
Expand Down
12 changes: 9 additions & 3 deletions src/components/header/AppHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import LanguageSwitcherModal from 'components/header/LanguageSwitcherModal.vue';
import LoginModal from 'components/LoginModal.vue';
import LoginStatus from 'components/header/LoginStatus.vue';
import { RouteLocationRaw } from 'vue-router';
import { useAccountStore } from 'src/antelope';
import { getAntelope, useAccountStore } from 'src/antelope';
import { defineComponent } from 'vue';

export default defineComponent({
Expand Down Expand Up @@ -73,6 +73,13 @@ export default defineComponent({
}
},
logout() {
useAccountStore().logout();
},
},
async mounted() {

// On login we must set the address and record the provider
getAntelope().events.onLoggedOut.subscribe(() => {
const loginData = localStorage.getItem('loginData');
if (this.isNative) {
if (!loginData) {
Expand All @@ -83,11 +90,10 @@ export default defineComponent({
const wallet = this.$ual.getAuthenticators().availableAuthenticators.find(a => a.getName() === loginObj.provider);
wallet?.logout();
}
useAccountStore().logout();
this.setLogin({});
localStorage.removeItem('loginData');
this.$providerManager.setProvider(null);
},
});
},
});
</script>
Expand Down
2 changes: 1 addition & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10453,7 +10453,7 @@ rxjs@^6.5.3, rxjs@^6.6.3:
dependencies:
tslib "^1.9.0"

rxjs@^7.5.5:
rxjs@^7.5.5, rxjs@^7.8.0:
version "7.8.1"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543"
integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==
Expand Down
Loading