Skip to content

Commit

Permalink
Metakeep prototype working
Browse files Browse the repository at this point in the history
  • Loading branch information
Viterbo committed Dec 6, 2023
1 parent 402189a commit b7e9263
Show file tree
Hide file tree
Showing 6 changed files with 352 additions and 17 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@
"eosjs": "^21.0.3",
"eosjs-ecc": "^4.0.7",
"erc-20-abi": "^1.0.0",
"ethers": "^5.5.1",
"ethers": "5.7.0",
"jdenticon": "^3.1.1",
"metakeep": "^2.2.0",
"mitt": "^3.0.0",
"node-polyfill-webpack-plugin": "^2.0.1",
"numeral": "^2.0.6",
Expand Down
136 changes: 136 additions & 0 deletions src/antelope/wallets/authenticators/MetaKeepAuth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@

import { EthereumProvider } from 'src/antelope/types/Providers';
import { EVMAuthenticator } from 'src/antelope/wallets/authenticators/EVMAuthenticator';
import { InjectedProviderAuth } from 'src/antelope/wallets/authenticators/InjectedProviderAuth';
import { MetaKeep } from 'metakeep';
import { AntelopeError, addressString } from 'src/antelope/types';

import { ethers } from 'ethers';
import { RpcEndpoint } from 'universal-authenticator-library';
import { useFeedbackStore } from 'src/antelope/stores/feedback';

let metakeep: MetaKeep | null = null;

const name = 'MetaKeep';
export const MetaKeepAuthName = name;
export class MetaKeepAuth extends InjectedProviderAuth {

accountAddress = '';
accountEmail = '';

// this is just a dummy label to identify the authenticator base class
constructor(label = name) {
super(label);
}


// InjectedProviderAuth API ------------------------------------------------------

getProvider(): EthereumProvider | null {
return window.ethereum as unknown as EthereumProvider ?? null;
}

// EVMAuthenticator API ----------------------------------------------------------

getName(): string {
return name;
}

setEmail(email: string): void {
this.accountEmail = email;
}

// this is the important instance creation where we define a label to assign to this instance of the authenticator
newInstance(label: string): EVMAuthenticator {
this.trace('newInstance', label);
const auth = new MetaKeepAuth(label);
auth.setEmail(this.accountEmail);
return auth;
}

async login(network: string): Promise<addressString | null> {
this.trace('login', network);
useFeedbackStore().setLoading(`${this.getName()}.login`);

// const rpcNodeUrls = new Map<number, string>();
// rpcNodeUrls.set(41, 'https://testnet.telos.net/evm');
const rpcNodeUrls = {
41: 'https://testnet.telos.net/evm',
} as unknown as Map<number, string>;

metakeep = new MetaKeep({
// App id to configure UI
appId: 'd190c88f-1bb5-4e16-bc48-96dbf33b77e0',
// Default chain to use
chainId: 41,
// RPC node urls map
rpcNodeUrls,
// Signed in user's email address
user: {
email: this.accountEmail,
},
});
console.log('metakeep', metakeep);

await metakeep.ethereum;

const provider = await this.web3Provider();
this.accountAddress = await provider.getSigner().getAddress();

useFeedbackStore().unsetLoading(`${this.getName()}.login`);

return this.accountAddress as addressString;
}

async logout(): Promise<void> {
this.trace('logout');
return Promise.resolve();
}

async isConnectedTo(chainId: string): Promise<boolean> {
this.trace('isConnectedTo', chainId);
return true;
}

async web3Provider(): Promise<ethers.providers.Web3Provider> {
this.trace('web3Provider');
try {
if (metakeep) {
const provider = await metakeep.ethereum;
await provider.enable();
const web3Provider = new ethers.providers.Web3Provider(provider);
this.trace('web3Provider', '->', web3Provider);
return web3Provider;
} else {
const p:RpcEndpoint = this.getChainSettings().getRPCEndpoint();
const url = `${p.protocol}://${p.host}:${p.port}${p.path ?? ''}`;
const jsonRpcProvider = new ethers.providers.JsonRpcProvider(url);
await jsonRpcProvider.ready;
const web3Provider = jsonRpcProvider as ethers.providers.Web3Provider;
this.trace('web3Provider', '->', web3Provider);
return web3Provider;
}
} catch (e) {
console.error('web3Provider', e);
throw e;
}
}

// returns the associated account address acording to the label
getAccountAddress(): addressString {
// return this.userChainAccount?.chainAccount as addressString;
return this.accountAddress as addressString;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
handleCatchError(error: Error): AntelopeError {
this.trace('handleCatchError', error.message);
if (
(error as unknown as {status:string}).status === 'USER_REQUEST_DENIED'
) {
return new AntelopeError('antelope.evm.error_transaction_canceled');
} else {
return new AntelopeError('antelope.evm.error_send_transaction', { error });
}
}
}
1 change: 1 addition & 0 deletions src/antelope/wallets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export * from 'src/antelope/wallets/authenticators/MetamaskAuth';
export * from 'src/antelope/wallets/authenticators/OreIdAuth';
export * from 'src/antelope/wallets/authenticators/WalletConnectAuth';
export * from 'src/antelope/wallets/authenticators/SafePalAuth';
export * from 'src/antelope/wallets/authenticators/MetaKeepAuth';
export * from 'src/antelope/wallets/AntelopeWallets';
2 changes: 2 additions & 0 deletions src/boot/antelope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
WalletConnectAuth,
OreIdAuth,
SafePalAuth,
MetaKeepAuth,
} from 'src/antelope/wallets';
import { BraveAuth } from 'src/antelope/wallets/authenticators/BraveAuth';
import { App } from 'vue';
Expand Down Expand Up @@ -88,6 +89,7 @@ export default boot(({ app }) => {
appId: process.env.OREID_APP_ID as string,
};
ant.wallets.addEVMAuthenticator(new OreIdAuth(oreIdOptions));
ant.wallets.addEVMAuthenticator(new MetaKeepAuth());

// autologin --
ant.stores.account.autoLogin();
Expand Down
171 changes: 170 additions & 1 deletion src/pages/home/EVMLoginButtons.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,38 @@
import { CURRENT_CONTEXT, getAntelope, useAccountStore, useChainStore, useEVMStore, useFeedbackStore, usePlatformStore } from 'src/antelope';
import { ComponentInternalInstance, PropType, computed, defineComponent, getCurrentInstance, ref, watch } from 'vue';
import { QSpinnerFacebook } from 'quasar';
import { OreIdAuth } from 'src/antelope/wallets';
import { MetaKeepAuth, OreIdAuth } from 'src/antelope/wallets';
import { Menu } from 'src/pages/home/MenuType';
import InlineSvg from 'vue-inline-svg';
import { isTodayBeforeTelosCloudDown } from 'src/App.vue';
import { AntelopeError } from 'src/antelope/types';
import * as Buffer from 'buffer';
interface GoogleOneTap {
accounts: {
id: {
initialize: (config: { client_id: string, callback: (notification: GoogleNotification) => void }) => void;
prompt: (callback: (notification: GoogleNotification) => void) => void;
renderButton: (element: HTMLElement, config: { theme: string, size: string }) => void;
}
}
}
interface GoogleNotification {
getMomentType: () => string;
isDisplayed: () => boolean;
isNotDisplayed: () => boolean;
isSkippedMoment: () => boolean;
isDismissedMoment: () => boolean;
getNotDisplayedReason: () => string;
getSkippedReason: () => string;
getDismissedReason: () => string;
credential: string;
}
let google: GoogleOneTap | null = null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const _window = (window as any);
export default defineComponent({
name: 'EVMLoginButtons',
Expand Down Expand Up @@ -76,6 +104,15 @@ export default defineComponent({
const setMetamaskAuthenticator = async () => {
setAuthenticator('Metamask', CURRENT_CONTEXT);
};
const setMetaKeepAuthenticator = async (email:string) => {
const name = 'MetaKeep';
const auth = ant.wallets.getAuthenticator(name);
if (auth) {
(auth as MetaKeepAuth).setEmail(email);
console.log('ANTES: ', auth);
}
setAuthenticator(name, CURRENT_CONTEXT);
};
const setBraveAuthenticator = async () => {
setAuthenticator('Brave', CURRENT_CONTEXT);
};
Expand All @@ -88,6 +125,7 @@ export default defineComponent({
const setAuthenticator = async(name: string, label: string) => {
const auth = ant.wallets.getAuthenticator(name);
console.log('POSTA: ', auth);
if (!auth) {
console.error(`${name} authenticator not found`);
return;
Expand Down Expand Up @@ -130,6 +168,126 @@ export default defineComponent({
emit('update:modelValue', Menu.CLOUD);
};
// --- Google One Tap (ini) -------------------------------
const onGoogleOneTap = () => {
setAuthenticator('Metakeep', CURRENT_CONTEXT);
};
const onGoogleOneTapSuccess = (response: any) => {
console.log('response: ', response);
console.log('response.payload: ', response.payload);
console.log('response.payload.email: ', response.payload.email);
setMetaKeepAuthenticator(response.payload.email);
};
const onGoogleOneTapError = (error: any) => {
console.error('-------------------------------');
console.error('google one tap error', error);
console.error('-------------------------------');
};
const installGoogleOneTapScript = () => {
if (google) {
return;
}
console.log('installGoogleOneTapScript()');
const script = document.createElement('script');
script.src = 'https://accounts.google.com/gsi/client';
script.async = true;
script.defer = true;
document.body.appendChild(script);
_window.onGoogleLibraryLoad = () => {
oneTapInit();
};
};
const oneTapInit = () => {
console.log('oneTapInit()');
if(!google){
if (_window.google) {
google = _window.google;
} else {
// FIXME: use i18n
throw new AntelopeError('Google One Tap library not loaded');
}
}
if (google) {
console.log('----------------- google -->', google);
google.accounts.id.initialize({
client_id: '639241197544-kcubenhmti6u7ef3uj360n2lcl5cmn8c.apps.googleusercontent.com',
callback: oneTapCallback,
});
google.accounts.id.prompt((notification) => {
const momentType = notification.getMomentType();
if(notification.isDisplayed()) {
setTimeout(()=>{
handleOneTapMoment(momentType, 'displayed', 'displayed');
}, 500);
} else if(notification.isNotDisplayed()){
handleOneTapMoment(momentType, 'notdisplayed', notification.getNotDisplayedReason());
} else if(notification.isSkippedMoment()) {
handleOneTapMoment(momentType, 'skipped', notification.getSkippedReason());
} else if(notification.isDismissedMoment()) {
handleOneTapMoment(momentType, 'dismissed', notification.getDismissedReason());
}
});
}
};
const decodificarJWT = (token: string) => {
const parts = token.split('.');
const header = parts[0];
const payload = parts[1];
const dedodedHeader = Buffer.Buffer.from(header, 'base64').toString('utf8');
const decodedPayload = Buffer.Buffer.from(payload, 'base64').toString('utf8');
return {
header: JSON.parse(dedodedHeader),
payload: JSON.parse(decodedPayload),
};
};
const oneTapCallback = (response: GoogleNotification | null) => {
console.log('---------- oneTapCallback -------------->', response);
if (response) {
const credential = response.credential;
const decoded = decodificarJWT(credential);
onGoogleOneTapSuccess(decoded);
} else {
onGoogleOneTapError(response);
}
};
const handleOneTapMoment = (momentType: string, status: string, reason: string) => {
console.log('-- handleOneTapMoment -> ', momentType, status, reason);
};
watch(showTelosCloudMenu, (newValue) => {
console.log('showTelosCloudMenu changed', newValue);
if (newValue) {
setTimeout(() => {
showGoogleOneTap();
}, 100);
}
});
const showGoogleOneTap = () => {
const btn = document.getElementById('google_btn');
if (google && btn) {
console.log('--- google.accounts.id.renderButton() ---');
google.accounts.id.renderButton(
btn, { theme: 'outline', size: 'large' },
);
}
};
installGoogleOneTapScript();
showGoogleOneTap();
// --- Google One Tap (end) -------------------------------
console.log('EVMLoginButtons setup()');
return {
isLoading,
Expand All @@ -143,6 +301,7 @@ export default defineComponent({
showWalletConnectButton,
setOreIdAuthenticator,
setMetamaskAuthenticator,
setMetaKeepAuthenticator,
setBraveAuthenticator,
setSafePalAuthenticator,
setWalletConnectAuthenticator,
Expand All @@ -155,13 +314,17 @@ export default defineComponent({
showMainMenu,
showTelosCloudMenu,
setCloudMenu,
onGoogleOneTapSuccess,
onGoogleOneTapError,
onGoogleOneTap,
};
},
});
</script>

<template>
<div class="c-evm-login-buttons">

<!-- main menu -->
<template v-if="showMainMenu">

Expand Down Expand Up @@ -285,6 +448,12 @@ export default defineComponent({
<!-- telos cloud menu -->
<template v-if="showTelosCloudMenu">

<div
id="google_btn"
data-client_id="639241197544-kcubenhmti6u7ef3uj360n2lcl5cmn8c.apps.googleusercontent.com"
>
</div>

<!-- Google OAuth Provider -->
<div class="c-evm-login-buttons__option c-evm-login-buttons__option--web2" @click="setOreIdAuthenticator('google')">
<template v-if="isLoadingOreId('google')">
Expand Down
Loading

0 comments on commit b7e9263

Please sign in to comment.