Skip to content

Commit

Permalink
chore: show snap error if it is dev (#299)
Browse files Browse the repository at this point in the history
* chore: show snap error if it is dev

* chore: reset logger log level

---------

Co-authored-by: khanti42 <florin.dzeladini@consensys.net>
  • Loading branch information
stanleyyconsensys and khanti42 authored Jul 29, 2024
1 parent 141fa20 commit 63a7789
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 18 deletions.
43 changes: 29 additions & 14 deletions packages/starknet-snap/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import type {
Component,
} from '@metamask/snaps-sdk';
import {
InternalError,
panel,
row,
divider,
text,
copyable,
SnapError,
} from '@metamask/snaps-sdk';
import { Mutex } from 'async-mutex';
import { ethers } from 'ethers';
Expand Down Expand Up @@ -61,7 +61,7 @@ import {
STARKNET_TESTNET_NETWORK,
} from './utils/constants';
import { getAddressKeyDeriver } from './utils/keyPair';
import { logger } from './utils/logger';
import { logger, LogLevel } from './utils/logger';
import { toJson } from './utils/serializer';
import {
dappUrl,
Expand All @@ -80,17 +80,17 @@ declare const snap;
const saveMutex = new Mutex();

export const onRpcRequest: OnRpcRequestHandler = async ({ request }) => {
const requestParams = request?.params as unknown as ApiRequestParams;
const debugLevel = requestParams?.debugLevel;
logger.init(debugLevel as unknown as string);
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
console.log(`debugLevel: ${logger.getLogLevel()}`);

logger.log(`${request.method}:\nrequestParams: ${toJson(requestParams)}`);

try {
const requestParams = request?.params as unknown as ApiRequestParams;
const isDev = Boolean(requestParams?.isDev);
const debugLevel = requestParams?.debugLevel;

logger.init(debugLevel as unknown as string);
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
console.log(`debugLevel: ${logger.getLogLevel()}`);

logger.log(`${request.method}:\nrequestParams: ${toJson(requestParams)}`);
// Switch statement for methods not requiring state to speed things up a bit
if (request.method === 'ping') {
logger.log('pong');
return 'pong';
Expand Down Expand Up @@ -148,11 +148,13 @@ export const onRpcRequest: OnRpcRequestHandler = async ({ request }) => {
switch (request.method) {
case 'starkNet_createAccount':
apiParams.keyDeriver = await getAddressKeyDeriver(snap);
return createAccount(apiParams as unknown as ApiParamsWithKeyDeriver);
return await createAccount(
apiParams as unknown as ApiParamsWithKeyDeriver,
);

case 'starkNet_createAccountLegacy':
apiParams.keyDeriver = await getAddressKeyDeriver(snap);
return createAccount(
return await createAccount(
apiParams as unknown as ApiParamsWithKeyDeriver,
false,
true,
Expand Down Expand Up @@ -292,7 +294,16 @@ export const onRpcRequest: OnRpcRequestHandler = async ({ request }) => {
throw new Error('Method not found.');
}
} catch (error) {
throw new InternalError(error) as unknown as Error;
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
logger.error(`Error: ${error}`);
// We don't want to expose the error message to the user when it is a production build
if (logger.getLogLevel() === LogLevel.OFF) {
throw new SnapError(
'Unable to execute the rpc request',
) as unknown as Error;
} else {
throw new SnapError(error.message) as unknown as Error;
}
}
};

Expand Down Expand Up @@ -395,6 +406,10 @@ export const onHomePage: OnHomePageHandler = async () => {
content: panel(panelItems),
};
} catch (error) {
throw new InternalError(error) as unknown as 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;
}
};
112 changes: 108 additions & 4 deletions packages/starknet-snap/test/src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import chai, { expect } from 'chai';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';
import { SnapError } from '@metamask/snaps-sdk';

import { WalletMock } from '../wallet.mock.test';
import { getBip44EntropyStub, account1 } from '../constants.test';
import { SnapState } from '../../src/types/snapState';
Expand All @@ -11,12 +13,113 @@ import {
STARKNET_SEPOLIA_TESTNET_NETWORK,
} from '../../src/utils/constants';
import * as starknetUtils from '../../src/utils/starknetUtils';
import { onHomePage } from '../../src';
import * as createAccountApi from '../../src/createAccount';
import * as keyPairUtils from '../../src/utils/keyPair';
import * as logger from '../../src/utils/logger';
import { onHomePage, onRpcRequest } from '../../src';

chai.use(sinonChai);
const sandbox = sinon.createSandbox();

describe('Test function: onHomePage', function () {
describe('onRpcRequest', function () {
const walletStub = new WalletMock();

const mockSnap = () => {
const globalAny: any = global;
globalAny.snap = walletStub;
};

afterEach(function () {
walletStub.reset();
sandbox.restore();
// Temp solution: Switch off logger after each test
logger.logger.init('off');
});

it('processes request successfully', async function () {
mockSnap();
const createAccountSpy = sandbox.stub(createAccountApi, 'createAccount');
const keyPairSpy = sandbox.stub(keyPairUtils, 'getAddressKeyDeriver');

createAccountSpy.resolvesThis();
keyPairSpy.resolvesThis();

await onRpcRequest({
origin: 'http://localhost:3000',
request: {
method: 'starkNet_createAccount',
params: [],
jsonrpc: '2.0',
id: 1,
},
});

expect(keyPairSpy).to.have.been.calledOnce;
expect(createAccountSpy).to.have.been.calledOnce;
});

it('throws `Unable to execute the rpc request` error if an error has thrown and `LogLevel` is `OFF`', async function () {
mockSnap();
const createAccountSpy = sandbox.stub(createAccountApi, 'createAccount');
const keyPairSpy = sandbox.stub(keyPairUtils, 'getAddressKeyDeriver');

createAccountSpy.rejects(new Error('Custom Error'));
keyPairSpy.resolvesThis();

let expectedError;

try {
await onRpcRequest({
origin: 'http://localhost:3000',
request: {
method: 'starkNet_createAccount',
params: [],
jsonrpc: '2.0',
id: 1,
},
});
} catch (error) {
expectedError = error;
} finally {
expect(expectedError).to.be.instanceOf(SnapError);
expect(expectedError.message).to.equal(
'Unable to execute the rpc request',
);
}
});

it('does not hide the error message if an error is thrown and `LogLevel` is not `OFF`', async function () {
mockSnap();
const createAccountSpy = sandbox.stub(createAccountApi, 'createAccount');
const keyPairSpy = sandbox.stub(keyPairUtils, 'getAddressKeyDeriver');

createAccountSpy.rejects(new Error('Custom Error'));
keyPairSpy.resolvesThis();

let expectedError;

try {
await onRpcRequest({
origin: 'http://localhost:3000',
request: {
method: 'starkNet_createAccount',
params: {
debugLevel: 'DEBUG',
},
jsonrpc: '2.0',
id: 1,
},
});
} catch (error) {
expectedError = error;
} finally {
expect(expectedError).to.be.instanceOf(SnapError);
expect(expectedError.message).to.equal('Custom Error');
}
});
});

describe('onHomePage', function () {
const walletStub = new WalletMock();
// eslint-disable-next-line no-restricted-globals, @typescript-eslint/no-explicit-any
const globalAny: any = global;
Expand Down Expand Up @@ -152,14 +255,15 @@ describe('Test function: onHomePage', function () {
});
});

it('throws error when state not found', async function () {
it('throws `Unable to initialize Snap HomePage` error when state not found', async function () {
let error;
try {
await onHomePage();
} catch (err) {
error = err;
} finally {
expect(error).to.be.an('error');
expect(error).to.be.instanceOf(SnapError);
expect(error.message).to.equal('Unable to initialize Snap HomePage');
}
});
});

0 comments on commit 63a7789

Please sign in to comment.