Skip to content

Commit

Permalink
refactor(HomePage): revamp snap onHomePage handler (#345)
Browse files Browse the repository at this point in the history
* refactor: dappUrl function

* chore: update config

* chore: fix lint

* refactor: revamp on home page handler

* fix: lint

* chore: update home page controller location

* fix: lint

* chore: update home page test

* chore: update network state mgr with default network config

* chore: lint fix

* chore: lint

* chore: get balance with pending block only in HP

---------

Co-authored-by: khanti42 <[email protected]>
  • Loading branch information
stanleyyconsensys and khanti42 authored Oct 8, 2024
1 parent 5b2696f commit 52bf68f
Show file tree
Hide file tree
Showing 4 changed files with 370 additions and 231 deletions.
163 changes: 9 additions & 154 deletions packages/starknet-snap/src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,9 @@
import { MethodNotFoundError, SnapError } from '@metamask/snaps-sdk';
import { constants } from 'starknet';
import { text, MethodNotFoundError, SnapError } from '@metamask/snaps-sdk';

import { onRpcRequest, onHomePage } from '.';
import { manageStateSpy } from '../test/snap-provider.mock';
import { generateAccounts, type StarknetAccount } from './__tests__/helper';
import { onHomePage, onRpcRequest } from '.';
import * as createAccountApi from './createAccount';
import type { SnapState } from './types/snapState';
import {
ETHER_MAINNET,
ETHER_SEPOLIA_TESTNET,
STARKNET_MAINNET_NETWORK,
STARKNET_SEPOLIA_TESTNET_NETWORK,
} from './utils/constants';
import { HomePageController } from './on-home-page';
import * as keyPairUtils from './utils/keyPair';
import * as starknetUtils from './utils/starknetUtils';

jest.mock('./utils/logger');

Expand Down Expand Up @@ -79,153 +69,18 @@ describe('onRpcRequest', () => {
});

describe('onHomePage', () => {
const state: SnapState = {
accContracts: [],
erc20Tokens: [ETHER_MAINNET, ETHER_SEPOLIA_TESTNET],
networks: [STARKNET_MAINNET_NETWORK, STARKNET_SEPOLIA_TESTNET_NETWORK],
transactions: [],
currentNetwork: undefined,
};

const mockState = (snapState: SnapState) => {
manageStateSpy.mockResolvedValue(snapState);
};

const mockAccount = async (chainId: constants.StarknetChainId) => {
const accounts = await generateAccounts(chainId);
return accounts[0];
};

const mockAccountDiscovery = (account: StarknetAccount) => {
const getKeysFromAddressIndexSpy = jest.spyOn(
starknetUtils,
'getKeysFromAddressIndex',
);
const getCorrectContractAddressSpy = jest.spyOn(
starknetUtils,
'getCorrectContractAddress',
);

getKeysFromAddressIndexSpy.mockResolvedValue({
privateKey: account.privateKey,
publicKey: account.publicKey,
addressIndex: account.addressIndex,
derivationPath: account.derivationPath as unknown as any,
});

getCorrectContractAddressSpy.mockResolvedValue({
address: account.address,
signerPubKey: account.publicKey,
upgradeRequired: false,
deployRequired: false,
});

return {
getKeysFromAddressIndexSpy,
getCorrectContractAddressSpy,
};
};

const mockGetBalance = (balance: string) => {
const getBalanceSpy = jest.spyOn(starknetUtils, 'getBalance');
getBalanceSpy.mockResolvedValue(balance);
};

it('renders user address, user balance and network', async () => {
const account = await mockAccount(constants.StarknetChainId.SN_SEPOLIA);
mockState(state);
mockAccountDiscovery(account);
mockGetBalance('1000');

const result = await onHomePage();

expect(result).toStrictEqual({
content: {
type: 'panel',
children: [
{ type: 'text', value: 'Address' },
{
type: 'copyable',
value: account.address,
},
{
type: 'row',
label: 'Network',
value: {
type: 'text',
value: STARKNET_MAINNET_NETWORK.name,
},
},
{
type: 'row',
label: 'Balance',
value: {
type: 'text',
value: '0.000000000000001 ETH',
},
},
{ type: 'divider' },
{
type: 'text',
value:
'Visit the [companion dapp for Starknet](https://snaps.consensys.io/starknet) to manage your account.',
},
],
},
});
});

it('renders with network from state if `currentNetwork` is not undefined', async () => {
const network = STARKNET_MAINNET_NETWORK;
const account = await mockAccount(constants.StarknetChainId.SN_MAIN);
mockState({
...state,
currentNetwork: network,
});
mockAccountDiscovery(account);
mockGetBalance('1000');
it('executes homePageController', async () => {
const executeSpy = jest.spyOn(HomePageController.prototype, 'execute');
executeSpy.mockResolvedValue({ content: text('test') });

const result = await onHomePage();

expect(executeSpy).toHaveBeenCalledTimes(1);
expect(result).toStrictEqual({
content: {
type: 'panel',
children: [
{ type: 'text', value: 'Address' },
{
type: 'copyable',
value: account.address,
},
{
type: 'row',
label: 'Network',
value: {
type: 'text',
value: network.name,
},
},
{
type: 'row',
label: 'Balance',
value: {
type: 'text',
value: '0.000000000000001 ETH',
},
},
{ type: 'divider' },
{
type: 'text',
value:
'Visit the [companion dapp for Starknet](https://snaps.consensys.io/starknet) to manage your account.',
},
],
type: 'text',
value: 'test',
},
});
});

it('throws `Unable to initialize Snap HomePage` error when state not found', async () => {
await expect(onHomePage()).rejects.toThrow(
'Unable to initialize Snap HomePage',
);
});
});
79 changes: 2 additions & 77 deletions packages/starknet-snap/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,13 @@ import type {
OnHomePageHandler,
OnInstallHandler,
OnUpdateHandler,
Component,
} from '@metamask/snaps-sdk';
import {
panel,
row,
divider,
text,
copyable,
SnapError,
MethodNotFoundError,
} from '@metamask/snaps-sdk';
import { ethers } from 'ethers';

import { addErc20Token } from './addErc20Token';
import { addNetwork } from './addNetwork';
Expand All @@ -34,6 +29,7 @@ import { getStoredUserAccounts } from './getStoredUserAccounts';
import { getTransactions } from './getTransactions';
import { getTransactionStatus } from './getTransactionStatus';
import { getValue } from './getValue';
import { homePageController } from './on-home-page';
import { recoverAccounts } from './recoverAccounts';
import type {
DisplayPrivateKeyParams,
Expand Down Expand Up @@ -66,8 +62,6 @@ import { upgradeAccContract } from './upgradeAccContract';
import { getDappUrl, isSnapRpcError } from './utils';
import {
CAIRO_VERSION_LEGACY,
ETHER_MAINNET,
ETHER_SEPOLIA_TESTNET,
PRELOADED_TOKENS,
STARKNET_MAINNET_NETWORK,
STARKNET_SEPOLIA_TESTNET_NETWORK,
Expand All @@ -82,11 +76,6 @@ import {
upsertNetwork,
removeNetwork,
} from './utils/snapUtils';
import {
getBalance,
getCorrectContractAddress,
getKeysFromAddressIndex,
} from './utils/starknetUtils';

declare const snap;
logger.logLevel = parseInt(Config.logLevel, 10);
Expand Down Expand Up @@ -344,69 +333,5 @@ export const onUpdate: OnUpdateHandler = async () => {
};

export const onHomePage: OnHomePageHandler = async () => {
try {
const state: SnapState = await snap.request({
method: 'snap_manageState',
params: {
operation: 'get',
},
});

if (!state) {
throw new Error('State not found.');
}

// default network is mainnet
let network = STARKNET_MAINNET_NETWORK;
if (
state.currentNetwork &&
state.currentNetwork.chainId !== STARKNET_TESTNET_NETWORK.chainId
) {
network = state.currentNetwork;
}

// we only support 1 address at this moment
const idx = 0;
const keyDeriver = await getAddressKeyDeriver(snap);
const { publicKey } = await getKeysFromAddressIndex(
keyDeriver,
network.chainId,
state,
idx,
);
const { address } = await getCorrectContractAddress(network, publicKey);

const ethToken =
network.chainId === ETHER_SEPOLIA_TESTNET.chainId
? ETHER_SEPOLIA_TESTNET
: ETHER_MAINNET;
const balance =
(await getBalance(address, ethToken.address, network)) ?? BigInt(0);
const displayBalance = ethers.utils.formatUnits(
ethers.BigNumber.from(balance),
ethToken.decimals,
);

const panelItems: Component[] = [];
panelItems.push(text('Address'));
panelItems.push(copyable(`${address}`));
panelItems.push(row('Network', text(`${network.name}`)));
panelItems.push(row('Balance', text(`${displayBalance} ETH`)));
panelItems.push(divider());
panelItems.push(
text(
`Visit the [companion dapp for Starknet](${getDappUrl()}) to manage your account.`,
),
);

return {
content: panel(panelItems),
};
} catch (error) {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
logger.error(`Error: ${error}`);
throw new SnapError(
'Unable to initialize Snap HomePage',
) as unknown as Error;
}
return await homePageController.execute();
};
Loading

0 comments on commit 52bf68f

Please sign in to comment.