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

feat(snap): block ui if already shown #16

Draft
wants to merge 1 commit into
base: feat/display-tx-info
Choose a base branch
from
Draft
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
1 change: 1 addition & 0 deletions packages/snap/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"@metamask/eslint-config-jest": "^10.0.0",
"@metamask/eslint-config-nodejs": "^10.0.0",
"@metamask/eslint-config-typescript": "^10.0.0",
"@metamask/rpc-methods": "^0.30.0",
"@metamask/snaps-cli": "^0.30.0",
"@metamask/snaps-types": "^0.30.0",
"@metamask/snaps-ui": "^0.30.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/snap/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/airgap-it/tezos-metamask-snap.git"
},
"source": {
"shasum": "QSzx20eIc9oYUmnwcuhNl3z8pT9fTpCsfaCqsWaqit0=",
"shasum": "G5i6CdhytGvbtI2hIVHWOFHcM4NRRXRqJ+gYY9uGcYc=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
9 changes: 5 additions & 4 deletions packages/snap/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { tezosGetRpc } from './rpc-methods/get-rpc';
import { tezosSetRpc } from './rpc-methods/set-rpc';
import { tezosClearRpc } from './rpc-methods/clear-rpc';
import { METHOD_NOT_FOUND_ERROR } from './utils/errors';
import { ReturnWrapper } from './types';

// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
globalThis.Buffer = require('buffer/').Buffer;
Expand All @@ -32,7 +33,7 @@ type TezosSnapRpcMethods =
export const onRpcRequest: OnRpcRequestHandler = async ({
origin,
request,
}) => {
}): ReturnWrapper<any> => {
const { method, params } = request;
const typedMethod = method as TezosSnapRpcMethods;

Expand All @@ -41,16 +42,16 @@ export const onRpcRequest: OnRpcRequestHandler = async ({
return tezosGetAccount(origin);

case 'tezos_sendOperation':
return tezosSendOperation(origin, params);
return tezosSendOperation(origin, params as any);

case 'tezos_signPayload':
return tezosSignPayload(origin, params);
return tezosSignPayload(origin, params as any);

case 'tezos_getRpc':
return tezosGetRpc(origin);

case 'tezos_setRpc':
return tezosSetRpc(origin, params);
return tezosSetRpc(origin, params as any);

case 'tezos_clearRpc':
return tezosClearRpc(origin);
Expand Down
24 changes: 19 additions & 5 deletions packages/snap/src/rpc-methods/clear-rpc.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
import { panel, heading, text, divider } from '@metamask/snaps-ui';
import { DEFAULT_NODE_URL } from '../constants';
import { USER_REJECTED_ERROR } from '../utils/errors';
import { METAMASK_UI_BUSY_ERROR, USER_REJECTED_ERROR } from '../utils/errors';
import { createOriginElement } from '../ui/origin-element';
import { confirmationWrapper } from '../utils/confirmation-wrapper';
import { isUiBusy } from '../utils/ui-busy';
import { ReturnWrapper } from '../types';

export const tezosClearRpc = async (origin: string) => {
const approved = await snap.request({
export const tezosClearRpc = async (
origin: string,
): ReturnWrapper<{
network: string;
nodeUrl: string;
}> => {
if (isUiBusy()) {
return { error: METAMASK_UI_BUSY_ERROR() };
}

const approved = await confirmationWrapper({
method: 'snap_dialog',
params: {
type: 'confirmation',
Expand All @@ -31,7 +43,9 @@ export const tezosClearRpc = async (origin: string) => {
});

return {
network: 'mainnet',
nodeUrl: DEFAULT_NODE_URL,
result: {
network: 'mainnet',
nodeUrl: DEFAULT_NODE_URL,
},
};
};
29 changes: 22 additions & 7 deletions packages/snap/src/rpc-methods/get-account.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,29 @@
import { panel, heading, text, copyable, divider } from '@metamask/snaps-ui';
import { getSigner } from '../utils/get-signer';
import { getWallet } from '../utils/get-wallet';
import { USER_REJECTED_ERROR } from '../utils/errors';
import { METAMASK_UI_BUSY_ERROR, USER_REJECTED_ERROR } from '../utils/errors';
import { confirmationWrapper } from '../utils/confirmation-wrapper';
import { ReturnWrapper } from '../types';
import { isUiBusy } from '../utils/ui-busy';

export const tezosGetAccount = async (
origin: string,
): ReturnWrapper<{
curve: string;
publicKey: string;
address: string;
}> => {
if (isUiBusy()) {
return { error: METAMASK_UI_BUSY_ERROR() };
}

export const tezosGetAccount = async (origin: string) => {
const wallet = await getWallet();
const signer = await getSigner(wallet);

const publicKey = await signer.publicKey();
const address = await signer.publicKeyHash();

const approved = await snap.request({
const approved = await confirmationWrapper({
method: 'snap_dialog',
params: {
type: 'confirmation',
Expand All @@ -27,12 +40,14 @@ export const tezosGetAccount = async (origin: string) => {
});

if (!approved) {
throw USER_REJECTED_ERROR();
return { error: USER_REJECTED_ERROR() };
}

return {
curve: 'ed25519',
publicKey,
address,
result: {
curve: 'ed25519',
publicKey,
address,
},
};
};
26 changes: 20 additions & 6 deletions packages/snap/src/rpc-methods/get-rpc.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
import { panel, heading, text, copyable, divider } from '@metamask/snaps-ui';
import { getRpc } from '../utils/get-rpc';
import { USER_REJECTED_ERROR } from '../utils/errors';
import { METAMASK_UI_BUSY_ERROR, USER_REJECTED_ERROR } from '../utils/errors';
import { createOriginElement } from '../ui/origin-element';
import { confirmationWrapper } from '../utils/confirmation-wrapper';
import { isUiBusy } from '../utils/ui-busy';
import { ReturnWrapper } from '../types';

export const tezosGetRpc = async (
origin: string,
): ReturnWrapper<{
network: string;
nodeUrl: string;
}> => {
if (isUiBusy()) {
return { error: METAMASK_UI_BUSY_ERROR() };
}

export const tezosGetRpc = async (origin: string) => {
const rpc = await getRpc();

const approved = await snap.request({
const approved = await confirmationWrapper({
method: 'snap_dialog',
params: {
type: 'confirmation',
Expand All @@ -25,11 +37,13 @@ export const tezosGetRpc = async (origin: string) => {
});

if (!approved) {
throw USER_REJECTED_ERROR();
return { error: USER_REJECTED_ERROR() };
}

return {
network: rpc.network,
nodeUrl: rpc.nodeUrl,
result: {
network: rpc.network,
nodeUrl: rpc.nodeUrl,
},
};
};
76 changes: 39 additions & 37 deletions packages/snap/src/rpc-methods/send-operation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,34 @@ import BigNumber from 'bignumber.js';
import { getWallet } from '../utils/get-wallet';
import { prepare } from '../utils/prepare';
import { getRpc } from '../utils/get-rpc';
import { to } from '../utils/to';
import { NO_OPERATION_ERROR, USER_REJECTED_ERROR } from '../utils/errors';
import { TezosTransactionOperation } from '../tezos/types';
import {
METAMASK_UI_BUSY_ERROR,
NO_OPERATION_ERROR,
USER_REJECTED_ERROR,
} from '../utils/errors';
import { TezosOperation, TezosTransactionOperation } from '../tezos/types';
import { createOriginElement } from '../ui/origin-element';
import { sign } from '../utils/sign';
import { injectTransaction } from '../tezos/inject-transaction';
import { confirmationWrapper } from '../utils/confirmation-wrapper';
import { aggregate } from '../utils/aggregate';
import { isUiBusy } from '../utils/ui-busy';
import { ReturnWrapper } from '../types';

const mutezToTez = (mutez: string): string => {
return BigNumber(mutez).shiftedBy(-6).toString(10);
};

const aggregate = (array: any[], field: string) => {
return array
.reduce((pv, cv): BigNumber => {
return pv.plus(cv[field]);
}, new BigNumber(0))
.toString();
};
export const tezosSendOperation = async (
origin: string,
params: { payload: TezosOperation[] },
): ReturnWrapper<{
opHash: string;
}> => {
if (isUiBusy()) {
return { error: METAMASK_UI_BUSY_ERROR() };
}

export const tezosSendOperation = async (origin: string, params: any) => {
const { payload } = params;
const wallet = await getWallet();
const rpc = await getRpc();
Expand All @@ -32,7 +40,7 @@ export const tezosSendOperation = async (origin: string, params: any) => {
.contents as any;

if (typedPayload.length === 0) {
throw NO_OPERATION_ERROR();
return { error: NO_OPERATION_ERROR() };
}

const humanReadable = [];
Expand Down Expand Up @@ -88,32 +96,26 @@ export const tezosSendOperation = async (origin: string, params: any) => {
);
}

const [approveError, approved] = await to<string | boolean | null>(
snap.request({
method: 'snap_dialog',
params: {
type: 'confirmation',
content: panel([
heading('Sign Operation'),
text('Do you want to sign the following payload?'),
...humanReadable,
copyable(JSON.stringify(payload, null, 2)),
divider(),
text(`The operation will be submit to the following node:`),
copyable(rpc.nodeUrl),
divider(),
...createOriginElement(origin),
]),
},
}),
);

if (approveError) {
throw new Error(`APPROVE ERROR ${approveError.message}`);
}
const approved = await confirmationWrapper({
method: 'snap_dialog',
params: {
type: 'confirmation',
content: panel([
heading('Sign Operation'),
text('Do you want to sign the following payload?'),
...humanReadable,
copyable(JSON.stringify(payload, null, 2)),
divider(),
text(`The operation will be submit to the following node:`),
copyable(rpc.nodeUrl),
divider(),
...createOriginElement(origin),
]),
},
});

if (!approved) {
throw USER_REJECTED_ERROR();
return { error: USER_REJECTED_ERROR() };
}

const operationWatermark = new Uint8Array([3]);
Expand All @@ -128,5 +130,5 @@ export const tezosSendOperation = async (origin: string, params: any) => {
rpc.nodeUrl,
);

return { opHash };
return { result: { opHash } };
};
36 changes: 26 additions & 10 deletions packages/snap/src/rpc-methods/set-rpc.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,36 @@
import { panel, heading, text, copyable, divider } from '@metamask/snaps-ui';
import { SnapStorage } from '../types';
import { ReturnWrapper, SnapStorage } from '../types';
import {
RPC_NO_HTTPS_ERROR,
RPC_INVALID_URL_ERROR,
RPC_INVALID_RESPONSE_ERROR,
USER_REJECTED_ERROR,
RPC_NO_URL_ERROR,
METAMASK_UI_BUSY_ERROR,
} from '../utils/errors';
import { createOriginElement } from '../ui/origin-element';
import { confirmationWrapper } from '../utils/confirmation-wrapper';
import { isUiBusy } from '../utils/ui-busy';

export const tezosSetRpc = async (
origin: string,
params: { network: string; nodeUrl: string },
): ReturnWrapper<{
network: string;
nodeUrl: string;
}> => {
if (isUiBusy()) {
return { error: METAMASK_UI_BUSY_ERROR() };
}

export const tezosSetRpc = async (origin: string, params: any) => {
const { network, nodeUrl }: { network: string; nodeUrl: string } = params;

if (!nodeUrl) {
throw RPC_NO_URL_ERROR();
return { error: RPC_NO_URL_ERROR() };
}

if (!nodeUrl.startsWith('https://')) {
throw RPC_NO_HTTPS_ERROR();
return { error: RPC_NO_HTTPS_ERROR() };
}

const normalisedNodeUrl = `${nodeUrl}${nodeUrl.endsWith('/') ? '' : '/'}`;
Expand All @@ -27,14 +40,15 @@ export const tezosSetRpc = async (origin: string, params: any) => {
)
.then((res) => res.json())
.catch(() => {
throw RPC_INVALID_URL_ERROR();
// TODO: Check return
return { error: RPC_INVALID_URL_ERROR() };
});

if (!header.hash || !header.chain_id) {
throw RPC_INVALID_RESPONSE_ERROR();
return { error: RPC_INVALID_RESPONSE_ERROR() };
}

const approved = await snap.request({
const approved = await confirmationWrapper({
method: 'snap_dialog',
params: {
type: 'confirmation',
Expand All @@ -51,7 +65,7 @@ export const tezosSetRpc = async (origin: string, params: any) => {
});

if (!approved) {
throw USER_REJECTED_ERROR();
return { error: USER_REJECTED_ERROR() };
}

const newState: SnapStorage = {
Expand All @@ -67,7 +81,9 @@ export const tezosSetRpc = async (origin: string, params: any) => {
});

return {
network,
nodeUrl,
result: {
network,
nodeUrl,
},
};
};
Loading
Loading