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

feat: MetaMask Snap install page #1048

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/boot/api.ts
Expand Up @@ -17,7 +17,7 @@ import { useMetaExtensions } from 'src/hooks/useMetaExtensions';
import { computed, ref, watchPostEffect } from 'vue';
import Web3 from 'web3';
import { SupportWallet, supportWalletObj } from 'src/config/wallets';
import { initiatePolkdatodSnap } from 'src/modules/snap';
import { initiatePolkadotSnap } from 'src/modules/snap';
import { initPolkadotSnap } from '@astar-network/metamask-astar-adapter';

let $api: ApiPromise | undefined;
Expand Down Expand Up @@ -146,7 +146,7 @@ export default boot(async ({ store }) => {
const isSubstrateWallet = supportWalletObj.hasOwnProperty(wallet);

if (wallet === SupportWallet.Snap) {
const isSnapInstalled = await initiatePolkdatodSnap();
const isSnapInstalled = await initiatePolkadotSnap();
isSnapInstalled && (await initPolkadotSnap());
}

Expand Down
2 changes: 1 addition & 1 deletion src/components/common/themes/DividerNewYear.vue
Expand Up @@ -56,4 +56,4 @@ export default defineComponent({
#793b02 104.05%
);
}
</style>
</style>
2 changes: 1 addition & 1 deletion src/components/common/themes/DividerNewYearMobile.vue
Expand Up @@ -28,4 +28,4 @@ export default defineComponent({
#793b02 104.05%
);
}
</style>
</style>
2 changes: 1 addition & 1 deletion src/components/common/themes/LunarPack.vue
Expand Up @@ -151,4 +151,4 @@ export default defineComponent({
height: 240px;
z-index: 9999;
}
</style>
</style>
2 changes: 1 addition & 1 deletion src/components/common/themes/SnowPack.vue
Expand Up @@ -210,4 +210,4 @@ export default defineComponent({
}
}
}
</style>
</style>
2 changes: 1 addition & 1 deletion src/components/dapp-staking/my-staking/BannerArea.vue
Expand Up @@ -31,4 +31,4 @@ export default defineComponent({
gap: 24px;
}
}
</style>
</style>
7 changes: 5 additions & 2 deletions src/components/header/Header.vue
Expand Up @@ -4,7 +4,8 @@
<template #left>
<div class="icon"><logo /></div>
</template>
<div v-if="!currentAccount">
<div v-if="path.includes('snap')" />
<div v-else-if="!currentAccount">
<connect-button :class="isLoading && 'cursor--disabled'" @click="openSelectModal">
<astar-icon-wallet />
</connect-button>
Expand All @@ -19,7 +20,7 @@
@click="clickAccountBtn"
/>
</template>
<network-button @show-network="clickNetworkBtn" />
<network-button v-if="!path.includes('snap')" @show-network="clickNetworkBtn" />
<trouble-help />
<mobile-nav v-if="width <= screenSize.lg" />
</header-comp>
Expand All @@ -33,6 +34,7 @@
/>

<modal-connect-wallet
v-if="!path.includes('snap')"
:is-modal-connect-wallet="
modalName === WalletModalOption.SelectWallet ||
modalName === WalletModalOption.NoExtension ||
Expand Down Expand Up @@ -238,6 +240,7 @@ export default defineComponent({
selectedWallet,
modalAccountSelect,
modalPolkasafeSelect,
path,
width,
screenSize,
isLoading,
Expand Down
4 changes: 2 additions & 2 deletions src/components/header/modals/ModalConnectWallet.vue
Expand Up @@ -168,7 +168,7 @@ import {
import { useAccount, useNetworkInfo } from 'src/hooks';
import { getInjectedExtensions, isMobileDevice } from 'src/hooks/helper/wallet';
import { useExtensions } from 'src/hooks/useExtensions';
import { initiatePolkdatodSnap } from 'src/modules/snap';
import { initiatePolkadotSnap } from 'src/modules/snap';
import { useStore } from 'src/store';
import { SubstrateAccount } from 'src/store/general/state';
import { PropType, computed, defineComponent, ref } from 'vue';
Expand Down Expand Up @@ -271,7 +271,7 @@ export default defineComponent({
const provider = get(window, supportEvmWalletObj[SupportWallet.MetaMask].ethExtension);
const [address] = (await provider.request({ method: 'eth_requestAccounts' })) as string;
if (!address) return;
const isSnapInstalled = await initiatePolkdatodSnap();
const isSnapInstalled = await initiatePolkadotSnap();
if (isSnapInstalled) {
await initPolkadotSnap();
useExtensions($api!!, store);
Expand Down
15 changes: 14 additions & 1 deletion src/i18n/en-US/index.ts
Expand Up @@ -183,7 +183,7 @@ export default {
introduce: 'Introducing new technology, unified account',
general: 'General',
onceUnified: 'Once accounts are unified, they will not be separated.',
evmWallet: 'EVM wallet (Metamask only)',
evmWallet: 'EVM wallet (MetaMask only)',
brandNewAccount: 'A brand-new empty account is recommended.',
unstakedFirst:
'If the EVM account holds any staked ASTR token, those need to be unstaked first, it will not be merged automatically.',
Expand Down Expand Up @@ -228,6 +228,19 @@ export default {
"You don't have NFTs minted at the moment. When you mint some you will be able to update your unified account with a NFT. For the moment default icon will be used.",
},
},
snap: {
name: 'Astar Snap',
purpose: '',
description:
'Astar Snap is a new wallet that generates an Astar Native address within MetaMask. By installing Astar Snap, you will obtain a substrate address and be able to sign transactions.',
documentation: 'Check the documentation',
pleaseNote: 'Please note',
pn1: 'Make sure you have MetaMask installed in your browser (if not please check https://metamask.io/)',
pn2: 'Only one substrate address is created per MetaMask account',
pn3: 'Crosschain transfers (XCM) are not recommended until the other chain supports Astar Snap',
install: 'Install the Astar Snap in MetaMask',
gotoAssets: 'Go to Assets',
},
installWallet: {
getWallet: 'Haven’t got {value} yet?',
installWallet:
Expand Down
2 changes: 1 addition & 1 deletion src/modules/snap/index.ts
Expand Up @@ -83,7 +83,7 @@ export interface SnapInitializationResponse {
snap?: MetamaskPolkadotSnap;
}

export async function initiatePolkdatodSnap(): Promise<SnapInitializationResponse> {
export async function initiatePolkadotSnap(): Promise<SnapInitializationResponse> {
try {
console.info('Attempting to connect to snap...');
// Todo: update network params
Expand Down
187 changes: 187 additions & 0 deletions src/pages/Snap.vue
@@ -0,0 +1,187 @@
<template>
<div v-if="isReady" class="">
<div class="container--main">
<div class="wrapper--title">
<div class="txt--title animate__animated animate__zoomInRight">
{{ $t('snap.name') }}
</div>
<div class="txt--subtitle animate__animated animate__fadeIn animate__delay-1s">
{{ $t('snap.purpose') }}
</div>
</div>

<div class="staking-container">
<div class="wrapper--tabs responsive">
<div class="wrapper--panel">
<p>
{{ $t('snap.description') }}
<a
href="https://docs.astar.network/docs/use/manage-wallets/wallet-providers/metamask-astar-snap/"
target="_blank"
>
{{ $t('snap.documentation') }}
</a>
</p>
<br />
<b>{{ $t('snap.pleaseNote') }}:</b>
<ul>
<li>
{{ $t('snap.pn1') }}
</li>
<li>
{{ $t('snap.pn2') }}
</li>
<li>
{{ $t('snap.pn3') }}
</li>
</ul>
</div>
</div>
</div>
</div>

<div class="cards responsive">
<button
class="card"
:class="{ 'card-hover': isHovered }"
@mouseover="isHovered = true"
@mouseleave="isHovered = false"
@click="isSnapInstalled ? $router.push(Path.Assets) : handleMetaMaskSnap()"
>
<div class="card__logo">
<img :src="icon_img.metamask" width="30" />
</div>
<div v-if="!isLoading" class="card__title">
{{ isSnapInstalled ? $t('snap.gotoAssets') : $t('snap.install') }}
</div>
<astar-spinner v-else />
</button>
</div>
</div>
<astar-spinner v-else />
</template>

<script lang="ts">
import { usePageReady } from 'src/hooks';
import { useStore } from 'src/store';
import { defineComponent, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { Path } from 'src/router';
import { $api } from 'src/boot/api';
import { LOCAL_STORAGE } from 'src/config/localStorage';
import { web3Accounts } from '@polkadot/extension-dapp';
import { wait } from '@astar-network/astar-sdk-core';
import { getInjectedExtensions } from 'src/hooks/helper/wallet';
import { useExtensions } from 'src/hooks/useExtensions';
import { initiatePolkadotSnap } from 'src/modules/snap';
import { initPolkadotSnap } from '@astar-network/metamask-astar-adapter';

export default defineComponent({
name: 'Snap',
setup() {
const store = useStore();
const { isReady } = usePageReady();
const { t } = useI18n();

const isHovered = ref(false);
const isSnapInstalled = ref(false);
const isWalletSet = ref(false);
const isLoading = ref(false);
const address = ref('');

const icon_img = {
metamask: require('/src/assets/img/metamask.png'),
};

const handleMetaMaskSnap = async (): Promise<void> => {
const snap = await initiatePolkadotSnap();
isSnapInstalled.value = snap.isSnapInstalled;

if (isSnapInstalled.value) {
isLoading.value = true;
await initPolkadotSnap();
useExtensions($api!!, store);
const extensions = await getInjectedExtensions(true);
const isExtensionsUpdated = extensions.some((it) => it.name === 'Snap');
!isExtensionsUpdated && (await wait(3000));
const accounts = await web3Accounts({ ss58Format: 5 });
address.value = accounts.find((account) => account.meta.source === 'Snap')?.address || '';
setWallet();
isLoading.value = false;
}
};

const setWallet = () => {
if (isSnapInstalled.value) {
store.commit('general/setCurrentWallet', 'Snap');
console.log('snap address is', address.value);

localStorage.setItem(LOCAL_STORAGE.SELECTED_WALLET, 'Snap');
localStorage.setItem(LOCAL_STORAGE.SELECTED_ADDRESS, address.value ?? '');

isWalletSet.value = true;
}
};

return {
Path,
icon_img,
isReady,
isHovered,
isLoading,
isSnapInstalled,
isWalletSet,
setWallet,
handleMetaMaskSnap,
};
},
});
</script>
<style lang="scss" scoped>
@import 'src/css/quasar.variables.scss';
@import 'src/components/dapp-staking/my-staking/styles/top-metric.scss';
@import 'src/components/dapp-staking/my-staking/styles/my-staking.scss';

.container--main {
width: 100%;
padding: 0px 0px 24px 0px;
margin: 0 auto;

@media (min-width: $md) {
max-width: 720px;
}

@media (min-width: $widthCardLineUp) {
max-width: 100%;
}
@media (min-width: $lg) {
margin-top: 50px;
}
}

.card {
position: relative;
padding: 20px;
min-height: 200px;
}

.card__logo {
position: absolute;
top: 20px;
right: 20px;
}

.card__title {
position: absolute;
bottom: 20px;
left: 20px;
text-align: left;
font-weight: 600;
font-size: 32px;
line-height: 1.2;
}

.card-hover {
background-color: $astar-blue;
}
</style>
7 changes: 7 additions & 0 deletions src/router/routes.ts
Expand Up @@ -7,6 +7,7 @@ import BridgeSelection from 'src/components/bridge/BridgeSelection.vue';
import XvmTransfer from 'pages/XvmTransfer.vue';
import { endpointKey, getNetworkName } from 'src/config/chainEndpoints';
import { LOCAL_STORAGE } from 'src/config/localStorage';
import Snap from 'src/pages/Snap.vue';
import Store from 'src/pages/DappStaking.vue';
import StakingTop from 'components/dapp-staking/StakingTop.vue';
import Dashboard from 'src/pages/Dashboard.vue';
Expand Down Expand Up @@ -40,6 +41,7 @@ export enum Path {
Transfer = '/transfer',
XvmTransfer = '/xvm-transfer',
Register = '/register',
Snap = '/snap',
}

const routes: RouteRecordRaw[] = [
Expand Down Expand Up @@ -158,6 +160,11 @@ const routes: RouteRecordRaw[] = [
},
],
},
{
path: Path.Snap,
name: 'Snap',
component: Snap,
},

// Always leave this as last one,
// but you can also remove it
Expand Down
2 changes: 2 additions & 0 deletions src/router/utils/index.ts
Expand Up @@ -15,6 +15,8 @@ export const getHeaderName = (path: string): string => {
return 'dApp Staking';
} else if (path.includes('bridge')) {
return 'Bridge';
} else if (path.includes('snap')) {
return 'Astar Snap';
}
return '';
};
Expand Down