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

Integrate Metamask #27

Merged
merged 9 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from 7 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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

- [Metamask integration](https://github.com/multiversx/mx-sdk-dapp-core/pull/27)
- [Extension integration](https://github.com/multiversx/mx-sdk-dapp-core/pull/26)
- [Ledger integration](https://github.com/multiversx/mx-sdk-dapp-core/pull/22)
- [Added sign, send, & track transactions with websocket connection](https://github.com/multiversx/mx-sdk-dapp-core/pull/21)
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@
"url": "git+https://github.com/multiversx/mx-sdk-dapp-core.git"
},
"scripts": {
"unpublish-verdaccio": "npm unpublish @multiversx/sdk-dapp-core@0.0.0-alpha.10 --registry http://localhost:4873",
"publish-verdaccio": "npm run unpublish-verdaccio && npm run compile && npm publish --registry http://localhost:4873/",
"compile": "tsc && tsc-alias",
"build-esbuild": "rimraf out && node esbuild.js",
"publish-verdaccio": "npm unpublish --registry http://localhost:4873 @multiversx/sdk-dapp-core@0.0.0-alpha.11 && rimraf out && yarn compile && npm publish --registry http://localhost:4873",
"build": "yarn build-esbuild && yarn compile",
"test": "jest",
"compile-next": "rimraf out && tsc --p tsconfig.next.json && tsc-alias --project tsconfig.next.json"
Expand All @@ -38,6 +39,7 @@
"@multiversx/sdk-opera-provider": "1.0.0-alpha.1",
"@multiversx/sdk-wallet": "4.5.1",
"@multiversx/sdk-wallet-connect-provider": "4.1.2",
"@multiversx/sdk-web-wallet-iframe-provider": "2.0.1",
"@multiversx/sdk-web-wallet-provider": "3.2.1",
"isomorphic-fetch": "3.0.0",
"lodash": "4.17.21",
Expand Down
3 changes: 3 additions & 0 deletions src/constants/network.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const fallbackNetworkConfigurations: Record<
walletConnectBridgeAddresses: ['https://bridge.walletconnect.org'],
walletConnectV2RelayAddresses: ['wss://relay.walletconnect.com'],
walletAddress: 'https://devnet-wallet.multiversx.com',
metamaskSnapWalletAddress: 'https://devnet-snap-wallet.multiversx.com',
xAliasAddress: 'https://devnet.xalias.com',
apiAddress: 'https://devnet-api.multiversx.com',
explorerAddress: 'http://devnet-explorer.multiversx.com',
Expand All @@ -37,6 +38,7 @@ export const fallbackNetworkConfigurations: Record<
walletConnectBridgeAddresses: ['https://bridge.walletconnect.org'],
walletConnectV2RelayAddresses: ['wss://relay.walletconnect.com'],
walletAddress: 'https://testnet-wallet.multiversx.com',
metamaskSnapWalletAddress: 'https://testnet-snap-wallet.multiversx.com',
xAliasAddress: 'https://testnet.xalias.com',
apiAddress: 'https://testnet-api.multiversx.com',
explorerAddress: 'http://testnet-explorer.multiversx.com',
Expand All @@ -56,6 +58,7 @@ export const fallbackNetworkConfigurations: Record<
walletConnectBridgeAddresses: ['https://bridge.walletconnect.org'],
walletConnectV2RelayAddresses: ['wss://relay.walletconnect.com'],
walletAddress: 'https://wallet.multiversx.com',
metamaskSnapWalletAddress: 'https://snap-wallet.multiversx.com',
xAliasAddress: 'https://xalias.com',
apiAddress: 'https://api.multiversx.com',
explorerAddress: 'https://explorer.multiversx.com',
Expand Down
1 change: 0 additions & 1 deletion src/core/methods/sendTransactions/sendTransactions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Transaction } from '@multiversx/sdk-core/out';
import { AxiosError } from 'axios';
import { sendSignedTransactions } from './helpers/sendSignedTransactions';
import { SignedTransactionType } from 'types/transactions.types';
import { createTransactionsSession } from 'store/actions/transactions/transactionsActions';

export const sendTransactions = async (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Transaction } from '@multiversx/sdk-core';
import { getAreAllTransactionsSignedByGuardian } from './getAreAllTransactionsSignedByGuardian';
import { ProviderFactory } from 'core/providers/ProviderFactory';
import { getAccount } from 'core/methods/account/getAccount';
import { walletAddressSelector } from 'store/selectors';
import { getState } from 'store/store';
import { createCrossWindowProvider } from 'core/providers/helpers/crossWindow/createCrossWindowProvider';

export const getGuardedTransactions = async ({
transactions
Expand All @@ -22,8 +22,7 @@ export const getGuardedTransactions = async ({
return transactions;
}

const factory = new ProviderFactory();
const provider = await factory.createCrossWindowProvider({
const provider = await createCrossWindowProvider({
address,
walletAddress
});
Expand Down
2 changes: 1 addition & 1 deletion src/core/methods/signTransactions/signTransactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const signTransactions = async (
const { isGuarded, activeGuardianAddress } = getAccount();

const transacitonsToSign =
activeGuardianAddress && !options.skipGuardian
activeGuardianAddress && isGuarded && !options.skipGuardian
? transactions?.map((transaction) => {
transaction.setVersion(TransactionVersion.withTxOptions());
transaction.setOptions(
Expand Down
149 changes: 23 additions & 126 deletions src/core/providers/ProviderFactory.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import { CrossWindowProvider } from 'lib/sdkWebWalletCrossWindowProvider';
import { ExtensionProvider } from '@multiversx/sdk-extension-provider';
import {
IProvider,
IProviderConfig,
IProviderFactory,
ProviderTypeEnum
} from './types/providerFactory.types';
import { isBrowserWithPopupConfirmation } from '../../constants';
import { fetchAccount } from 'utils';
import { setLedgerLogin } from 'store/actions/loginInfo/loginInfoActions';
import { setLedgerAccount } from 'store/actions/account/accountActions';
import { getLedgerProvider } from './helpers/getLedgerProvider';
import { createLedgerProvider } from './helpers/ledger/createLedgerProvider';
import { createCrossWindowProvider } from './helpers/crossWindow/createCrossWindowProvider';
import { createExtensionProvider } from './helpers/extension/createExtensionProvider';
import { createMetamaskProvider } from './helpers/iframe/createMetamaskProvider';

export class ProviderFactory {
public async create({
Expand All @@ -22,7 +18,7 @@ export class ProviderFactory {

switch (type) {
case ProviderTypeEnum.extension: {
const provider = await this.getExtensionProvider();
const provider = await createExtensionProvider();
createdProvider = provider as unknown as IProvider;

createdProvider.getType = () => {
Expand All @@ -35,9 +31,9 @@ export class ProviderFactory {
case ProviderTypeEnum.crossWindow: {
const { walletAddress } = config.network;

const provider = await this.createCrossWindowProvider({
const provider = await createCrossWindowProvider({
walletAddress,
address: config.account?.address || ''
address: config.account?.address
});
createdProvider = provider as unknown as IProvider;

Expand All @@ -49,104 +45,30 @@ export class ProviderFactory {
}

case ProviderTypeEnum.ledger: {
const data = await getLedgerProvider();
const ledgerProvider = await createLedgerProvider();

if (!data) {
if (!ledgerProvider) {
return;
}

const { ledgerProvider: provider, ledgerConfig } = data;
createdProvider = ledgerProvider;

createdProvider = provider as unknown as IProvider;
break;
}

const hwProviderLogin = provider.login;
case ProviderTypeEnum.metamask: {
const provider = await createMetamaskProvider({
address: config.account?.address
});

createdProvider.getType = () => {
return ProviderTypeEnum.ledger;
};
if (!provider) {
return;
}

createdProvider.login = async (options?: {
callbackUrl?: string | undefined;
token?: string | undefined;
}): Promise<{
address: string;
signature: string;
}> => {
const isConnected = provider.isConnected();

if (!isConnected) {
throw new Error('Ledger device is not connected');
}

// TODO: perform additional UI logic here
// maybe extract to file
const startIndex = 0;
const addressesPerPage = 10;

const accounts = await provider.getAccounts(
startIndex,
addressesPerPage
);

const accountsWithBalance: {
address: string;
balance: string;
index: number;
}[] = [];

const balancePromises = accounts.map((address) =>
fetchAccount(address)
);

const balances = await Promise.all(balancePromises);

balances.forEach((account, index) => {
if (!account) {
return;
}
accountsWithBalance.push({
address: account.address,
balance: account.balance,
index
});
});

// Suppose user selects the first account
const selectedIndex = 0;

setLedgerLogin({
index: selectedIndex,
loginType: ProviderTypeEnum.ledger
});

const { version, dataEnabled } = ledgerConfig;

setLedgerAccount({
address: accountsWithBalance[selectedIndex].address,
index: selectedIndex,
version,
hasContractDataEnabled: dataEnabled
});

if (options?.token) {
const loginInfo = await provider.tokenLogin({
token: Buffer.from(`${options?.token}{}`),
addressIndex: accountsWithBalance[selectedIndex].index
});

return {
address: loginInfo.address,
signature: loginInfo.signature.toString('hex')
};
} else {
const { address } = await hwProviderLogin({
addressIndex: accountsWithBalance[selectedIndex].index
});
return {
address,
signature: ''
};
}
createdProvider = provider as unknown as IProvider;

createdProvider.getType = () => {
return ProviderTypeEnum.metamask;
};

break;
Expand All @@ -163,29 +85,4 @@ export class ProviderFactory {

return createdProvider;
}

public async createCrossWindowProvider({
address,
walletAddress
}: {
address: string;
walletAddress: string;
}) {
const provider = CrossWindowProvider.getInstance();
await provider.init();
provider.setWalletUrl(String(walletAddress));
provider.setAddress(address);

if (isBrowserWithPopupConfirmation) {
provider.setShouldShowConsentPopup(true);
}

return provider;
}

private async getExtensionProvider() {
const provider = ExtensionProvider.getInstance();
await provider.init();
return provider;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { isBrowserWithPopupConfirmation } from 'constants/browser.constants';
import { CrossWindowProvider } from 'lib/sdkWebWalletCrossWindowProvider';

type CreateCrossWindowProviderType = {
arhtudormorar marked this conversation as resolved.
Show resolved Hide resolved
address?: string;
walletAddress: string;
};

export async function createCrossWindowProvider({
arhtudormorar marked this conversation as resolved.
Show resolved Hide resolved
address = '',
walletAddress
}: CreateCrossWindowProviderType) {
const provider = CrossWindowProvider.getInstance();
await provider.init();
provider.setWalletUrl(String(walletAddress));
provider.setAddress(address);

if (isBrowserWithPopupConfirmation) {
provider.setShouldShowConsentPopup(true);
}

return provider;
}
6 changes: 4 additions & 2 deletions src/core/providers/helpers/emptyProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,16 @@ export class EmptyProvider implements IProvider {
): Promise<TResponse> {
throw new Error(
notInitializedError(
`signTransaction with transactions: ${transaction} options: ${options}`
`signTransaction with transactions: ${JSON.stringify(transaction)} options: ${options}`
)
);
}

signTransactions<T>(transactions: T[]): Promise<T[]> {
throw new Error(
notInitializedError(`signTransactions with transactions: ${transactions}`)
notInitializedError(
`signTransactions with transactions: ${JSON.stringify(transactions)}`
)
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ExtensionProvider } from '@multiversx/sdk-extension-provider/out/extensionProvider';

export async function createExtensionProvider() {
arhtudormorar marked this conversation as resolved.
Show resolved Hide resolved
const provider = ExtensionProvider.getInstance();
await provider.init();
return provider;
}
28 changes: 28 additions & 0 deletions src/core/providers/helpers/iframe/createMetamaskProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { IframeProvider } from '@multiversx/sdk-web-wallet-iframe-provider/out';
import { IframeLoginTypes } from '@multiversx/sdk-web-wallet-iframe-provider/out/constants';
import { networkSelector } from 'store/selectors';
import { getState } from 'store/store';

type CreateMetamaskProviderType = {
arhtudormorar marked this conversation as resolved.
Show resolved Hide resolved
address?: string;
metamaskSnapWalletAddress?: string;
};

export async function createMetamaskProvider({
arhtudormorar marked this conversation as resolved.
Show resolved Hide resolved
metamaskSnapWalletAddress,
address = ''
}: CreateMetamaskProviderType) {
const network = networkSelector(getState());
const provider = IframeProvider.getInstance();
provider.setLoginType(IframeLoginTypes.metamask);
provider.setWalletUrl(
metamaskSnapWalletAddress || network.metamaskSnapWalletAddress || ''
);

if (address) {
provider.setAccount({ address });
}

await provider.init();
return provider;
}
Loading