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

Telos Cloud Migration #720

Closed
wants to merge 16 commits into from
Closed
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ yarn-error.log*
*.ntvs*
*.njsproj
*.sln

.vscode
.env
3 changes: 3 additions & 0 deletions env.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ const sharedEnv = {
IMGUR_CLIENT_ID: 'b6f46df9d1da9d9',
EVM_CONTRACT: 'eosio.evm',
PROJECT_ID: '2392473d6d98499c7138cd2d705a791f',
METAKEEP_APP_ID_NATIVE: 'ad5e05fb-280a-41ae-b186-5a2654567b92',
METAKEEP_APP_ID_EVM: 'd190c88f-1bb5-4e16-bc48-96dbf33b77e0',
GOOGLE_APP_ID: '639241197544-kcubenhmti6u7ef3uj360n2lcl5cmn8c.apps.googleusercontent.com',
};

const TESTNET = {
Expand Down
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
87 changes: 87 additions & 0 deletions src/antelope/migration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import {
CURRENT_CONTEXT,
useAccountStore,
useBalancesStore,
useChainStore,
useNftsStore,
} from 'src/antelope';
import { metakeepCache } from 'src/antelope/wallets/ual/utils/metakeep-cache';

const nftsStore = useNftsStore();
const balanceStore = useBalancesStore();

export const isTodayBeforeTelosCloudDown = new Date().getTime() < new Date('2023-12-31').getTime();

// ----- EVM ------
const ethPubKey = () => {
const emails = metakeepCache.getMails();
if (emails.length > 0) {
const ethPubKey = metakeepCache.getEthAddress(emails[0]);
if (ethPubKey) {
return ethPubKey;
}
}
return '';
};

const hasAnyTokenBalance = () => balanceStore.currentBalances.some(balance => !balance.amount.isNegative() && !balance.amount.isZero());
const authenticator = () => useAccountStore().getAccount(CURRENT_CONTEXT).authenticator;
const currentAuthName = () => authenticator()?.getName() || '';
const hasNfts = () => nftsStore.loggedInventory?.length > 0;
const hasAssets = () => hasAnyTokenBalance() || hasNfts();
const isMigrationNeeded = () => currentAuthName() === 'OreId' && ethPubKey() !== '' && hasAssets() && isTodayBeforeTelosCloudDown;

const evm = {
hasNfts,
hasAssets,
ethPubKey,
isMigrationNeeded,
};


// ----- ZERO ------
const accountNameZero = () => {
const emails = metakeepCache.getMails();
if (emails.length > 0) {
const chainId = useChainStore().currentChain.settings.getChainId();
const names = metakeepCache.getAccountNames(emails[0], chainId);
if (names.length > 0) {
return names[0];
}
}
return '';
};

const currentAuthNameZero = () => localStorage.getItem('autoLogin');
const isMigrationNeededZero = () => currentAuthNameZero() === 'ual-oreid' && accountNameZero() !== '' && isTodayBeforeTelosCloudDown;

const zero = {
accountName: accountNameZero,
isMigrationNeeded: isMigrationNeededZero,
};


const update = () => {
metakeepCache.loadCache();
};

const print = () => {
console.log('-- EVM --');
console.log('ethPubKey', ethPubKey());
console.log('hasNfts', hasNfts());
console.log('hasAssets', hasAssets());
console.log('currentAuthName', currentAuthName());
console.log('hasAnyTokenBalance', hasAnyTokenBalance());
console.log('isMigrationNeeded ->', isMigrationNeeded());
console.log('-- ZERO --');
console.log('accountName ->', accountNameZero());
console.log('isMigrationNeeded ->', isMigrationNeededZero);
console.log('currentAuthNameZero ->', currentAuthNameZero());
};

export const migration = {
update,
print,
evm,
zero,
};
4 changes: 3 additions & 1 deletion src/antelope/stores/chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,9 @@ export const useChainStore = defineStore(store_name, {
return new Promise((resolve) => {
const sub = getAntelope().events.onAccountChanged.subscribe((result) => {
if (result.label === label) {
sub.unsubscribe();
setTimeout(() => {
sub.unsubscribe();
}, 0);
if (result.account) {
// we need the user to be logged because the way of getting the staked ratio is by
// executing an action from contract and that internally attempts retrieve the account from the provided signer
Expand Down
172 changes: 172 additions & 0 deletions src/antelope/wallets/authenticators/MetaKeepAuth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@

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 { useFeedbackStore } from 'src/antelope/stores/feedback';
import { metakeepCache } from 'src/antelope/wallets/ual/utils/metakeep-cache';
import { useChainStore } from 'src/antelope/stores/chain';
import { CURRENT_CONTEXT } from 'src/antelope';
import EVMChainSettings from 'src/antelope/chains/EVMChainSettings';


export interface MetakeepOptions {
appId: string;
appName: string;
reasonCallback?: (transaction: never) => string;
}

let metakeep: MetaKeep | null = null;
let web3Provider: ethers.providers.Web3Provider | null = null;

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

accountAddress = '';
accountEmail = '';
appId = '';
appName = '';
reasonCallback?: (transaction: never) => string;

// this is just a dummy label to identify the authenticator base class
constructor(options: MetakeepOptions, label = name) {
super(label);
if (!options?.appId) {
throw new AntelopeError('antelope.evm.error_metakeep_app_id');
}
this.appId = options.appId;
this.appName = options.appName;
this.reasonCallback = options.reasonCallback;
this.accountEmail = metakeepCache.getLogged() ?? '';
}

// 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;
metakeepCache.setLogged(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({
appId: this.appId,
appName: this.appName,
reasonCallback: this.reasonCallback,
}, label);
auth.setEmail(this.accountEmail);
return auth;
}

async assertMetakeepSDKReady(): Promise<void> {
if (!metakeep) {
const chainSettings = (useChainStore().getChain(CURRENT_CONTEXT).settings as EVMChainSettings);
const endpoint = chainSettings.getRPCEndpoint();
const url = `${endpoint.protocol}://${endpoint.host}:${endpoint.port}${endpoint.path ?? ''}`;

const chainId: number = parseInt(chainSettings.getChainId());


const rpcNodeUrls = {
[chainId]: url,
} as unknown as Map<number, string>;

metakeep = new MetaKeep({
// App id to configure UI
appId: this.appId,
// Default chain to use
chainId,
// RPC node urls map
rpcNodeUrls,
// Signed in user's email address
user: {
email: this.accountEmail,
},
});

const provider = await metakeep.ethereum;
await provider.enable();
web3Provider = new ethers.providers.Web3Provider(provider);
}
}

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

await this.assertMetakeepSDKReady();

// metakeepCache
const accountAddress = metakeepCache.getEthAddress(this.accountEmail);
if (accountAddress) {
this.accountAddress = accountAddress;
useFeedbackStore().unsetLoading(`${this.getName()}.login`);
return this.accountAddress as addressString;
}


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

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


const credentials = await metakeep?.getWallet();
metakeepCache.addCredentials(this.accountEmail, credentials.wallet);

return this.accountAddress as addressString;
}

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

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it not possible to check chainID with metakeep?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by default, they support any EOS, ETH, or SOL-compatible blockchains.

This is a good topic for a KT I think.

}

async web3Provider(): Promise<ethers.providers.Web3Provider> {
this.trace('web3Provider');
await this.assertMetakeepSDKReady();
if (web3Provider) {
return web3Provider;
} else {
console.error('web3Provider not ready');
throw new AntelopeError('antelope.evm.error_metakeep_web3_provider');
}
}

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

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';
Loading