From 013526aefa8d800b00c4e045b01d12e7cb2eeebf Mon Sep 17 00:00:00 2001 From: Kiran Pachhai Date: Tue, 26 Sep 2023 11:18:26 -0400 Subject: [PATCH] Added snap dialog before sending crypto --- .../src/components/cards/TransferCrypto.tsx | 31 ++++++- packages/snap/snap.manifest.json | 2 +- .../snap/src/rpc/account/transferCrypto.ts | 84 +++++++++++++------ .../impl/hedera/client/transferCrypto.ts | 2 +- packages/snap/src/snap/account.ts | 9 +- packages/snap/src/snap/dialog.ts | 13 +-- packages/snap/src/utils/init.ts | 2 +- 7 files changed, 93 insertions(+), 50 deletions(-) diff --git a/packages/site/src/components/cards/TransferCrypto.tsx b/packages/site/src/components/cards/TransferCrypto.tsx index 490de3a..f647aef 100644 --- a/packages/site/src/components/cards/TransferCrypto.tsx +++ b/packages/site/src/components/cards/TransferCrypto.tsx @@ -29,6 +29,8 @@ const TransferCrypto: FC = ({ const [loading, setLoading] = useState(false); const { showModal } = useModal(); const [sendToAddress, setSendToAddress] = useState(''); + const [sendMemo, setSendMemo] = useState(''); + const [sendAmount, setSendAmount] = useState(0); const externalAccountRef = useRef(null); @@ -50,16 +52,15 @@ const TransferCrypto: FC = ({ { asset: 'HBAR', to: sendToAddress, - amount: 0.01, + amount: sendAmount, } as SimpleTransfer, ]; - const memo = ''; - // const maxFee = 1; // Note that this value is in tinybars and if you don't pass it, default is 1 HBAR + // const maxFee = 1; // Note that if you don't pass this, default is 1 HBAR const response: any = await transferCrypto( network, transfers, - memo, + sendMemo, undefined, externalAccountParams, ); @@ -98,6 +99,28 @@ const TransferCrypto: FC = ({ />
+ +
+ +
), button: ( diff --git a/packages/snap/snap.manifest.json b/packages/snap/snap.manifest.json index 938d24a..65b39d6 100644 --- a/packages/snap/snap.manifest.json +++ b/packages/snap/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/tuum-tech/hedera-pulse.git" }, "source": { - "shasum": "3Z/5TE7OM8ZF3m4WVbq+Byn/sl+KPlBhL7+4vYktYlQ=", + "shasum": "+GQraBauxdm43Ba4RQf0FFrA59AzXZeNalbkYslzVMg=", "location": { "npm": { "filePath": "dist/snap.js", diff --git a/packages/snap/src/rpc/account/transferCrypto.ts b/packages/snap/src/rpc/account/transferCrypto.ts index d2f9553..52fdd69 100644 --- a/packages/snap/src/rpc/account/transferCrypto.ts +++ b/packages/snap/src/rpc/account/transferCrypto.ts @@ -1,11 +1,14 @@ +import { divider, heading, panel, text } from '@metamask/snaps-ui'; +import _ from 'lodash'; import { AccountBalance, SimpleTransfer, TxRecord, } from '../../services/hedera'; import { createHederaClient } from '../../snap/account'; +import { snapDialog } from '../../snap/dialog'; import { TransferCryptoRequestParams } from '../../types/params'; -import { PulseSnapParams } from '../../types/state'; +import { PulseSnapParams, SnapDialogParams } from '../../types/state'; /** * Transfer crypto(hbar or other tokens). @@ -18,7 +21,7 @@ export async function transferCrypto( pulseSnapParams: PulseSnapParams, transferCryptoParams: TransferCryptoRequestParams, ): Promise { - const { state } = pulseSnapParams; + const { origin, state } = pulseSnapParams; const { transfers = [] as SimpleTransfer[], @@ -28,30 +31,59 @@ export async function transferCrypto( const { metamaskAddress, hederaAccountId, network } = state.currentAccount; - let record = {} as TxRecord; - try { - let currentBalance = - state.accountState[metamaskAddress].accountInfo.balance; - if (!currentBalance) { - currentBalance = {} as AccountBalance; - } + const panelToShow = [ + text(`Origin: ${origin}`), + divider(), + heading('Transfer Crypto'), + text('Are you sure you want to execute the following transaction(s)?'), + divider(), + text(`Memo: ${memo === null || _.isEmpty(memo) ? 'N/A' : memo}`), + text(`Max Transaction Fee: ${maxFee ?? 1} Hbar`), + ]; - const hederaClient = await createHederaClient( - state.accountState[metamaskAddress].keyStore.privateKey, - hederaAccountId, - network, - ); - - record = await hederaClient.transferCrypto({ - currentBalance, - transfers, - memo, - maxFee, - }); - } catch (error: any) { - console.error(`Error while trying to transfer crypto: ${String(error)}`); - throw new Error(`Error while trying to transfer crypto: ${String(error)}`); - } + transfers.forEach((transfer, index) => { + panelToShow.push(divider()); + + const txNumber = (index + 1).toString(); + panelToShow.push(text(`Transaction #${txNumber}`)); + panelToShow.push(divider()); + + panelToShow.push(text(`Asset: ${transfer.asset}`)); + panelToShow.push(text(`To: ${transfer.to}`)); + panelToShow.push(text(`Amount: ${transfer.amount} Hbar`)); + }); + + const dialogParams: SnapDialogParams = { + type: 'confirmation', + content: panel(panelToShow), + }; - return record; + if (await snapDialog(dialogParams)) { + try { + let currentBalance = + state.accountState[metamaskAddress].accountInfo.balance; + if (!currentBalance) { + currentBalance = {} as AccountBalance; + } + + const hederaClient = await createHederaClient( + state.accountState[metamaskAddress].keyStore.privateKey, + hederaAccountId, + network, + ); + + return await hederaClient.transferCrypto({ + currentBalance, + transfers, + memo, + maxFee, + }); + } catch (error: any) { + console.error(`Error while trying to transfer crypto: ${String(error)}`); + throw new Error( + `Error while trying to transfer crypto: ${String(error)}`, + ); + } + } + throw new Error('User rejected the transaction'); } diff --git a/packages/snap/src/services/impl/hedera/client/transferCrypto.ts b/packages/snap/src/services/impl/hedera/client/transferCrypto.ts index 9feff11..046f357 100644 --- a/packages/snap/src/services/impl/hedera/client/transferCrypto.ts +++ b/packages/snap/src/services/impl/hedera/client/transferCrypto.ts @@ -40,7 +40,7 @@ export async function transferCrypto( let outgoingHbarAmount = 0; transaction.setTransactionMemo(options.memo ?? ''); - transaction.setMaxTransactionFee(options.maxFee ?? new Hbar(1)); + transaction.setMaxTransactionFee(new Hbar(options.maxFee) ?? new Hbar(1)); for (const transfer of options.transfers) { if (transfer.asset === 'HBAR') { diff --git a/packages/snap/src/snap/account.ts b/packages/snap/src/snap/account.ts index c1cb4b0..df288de 100644 --- a/packages/snap/src/snap/account.ts +++ b/packages/snap/src/snap/account.ts @@ -1,5 +1,3 @@ -import { SnapsGlobalObject } from '@metamask/snaps-types'; - import { divider, heading, text } from '@metamask/snaps-ui'; import _ from 'lodash'; import { @@ -73,7 +71,7 @@ export async function setCurrentAccount( await initAccountState(snap, state, metamaskAddress); } - await importMetaMaskAccount(origin, snap, state, network, metamaskAddress); + await importMetaMaskAccount(origin, state, network, metamaskAddress); } catch (error: any) { console.error(`Error while trying to get the account: ${String(error)}`); throw new Error(`Error while trying to get the account: ${String(error)}`); @@ -84,14 +82,12 @@ export async function setCurrentAccount( * Veramo Import metamask account. * * @param origin - Source. - * @param snap - SnapsGlobalObject. * @param state - IdentitySnapState. * @param network - Hedera network. * @param metamaskAddress - EVM address. */ export async function importMetaMaskAccount( origin: string, - snap: SnapsGlobalObject, state: PulseSnapState, network: string, metamaskAddress: string, @@ -194,7 +190,8 @@ export async function importMetaMaskAccount( divider(), ]), }; - await snapDialog(snap, dialogParamsForHederaAccountId); + await snapDialog(dialogParamsForHederaAccountId); + // TODO: Maybe offer the user an "Activate" option that will charge them "x" amount of ETH console.error( `This Hedera account is not yet active. Please activate it by sending some HBAR to this account. Public Key: ${publicKey}, EVM Address: ${address}`, diff --git a/packages/snap/src/snap/dialog.ts b/packages/snap/src/snap/dialog.ts index 2c29f2e..d5b06af 100644 --- a/packages/snap/src/snap/dialog.ts +++ b/packages/snap/src/snap/dialog.ts @@ -1,4 +1,3 @@ -import { SnapsGlobalObject } from '@metamask/snaps-types'; import { divider, heading, panel, Panel, text } from '@metamask/snaps-ui'; import { PulseSnapState, SnapDialogParams } from '../types/state'; @@ -7,13 +6,9 @@ import { updateSnapState } from './state'; /** * Function that toggles the disablePopups flag in the config. * - * @param snap - Snap. * @param state - PulseSnapState. */ -export async function updatePopups( - snap: SnapsGlobalObject, - state: PulseSnapState, -) { +export async function updatePopups(state: PulseSnapState) { state.snapConfig.dApp.disablePopups = !state.snapConfig.dApp.disablePopups; await updateSnapState(snap, state); } @@ -21,11 +16,9 @@ export async function updatePopups( /** * Function that opens snap dialog. * - * @param snap - Snap. * @param params - Snap dialog params. */ export async function snapDialog( - snap: SnapsGlobalObject, params: SnapDialogParams, ): Promise { return (await snap.request({ @@ -52,13 +45,11 @@ export async function generateCommonPanel( * Request Hedera Account Id. * * @param origin - Source. - * @param snap - SnapGlobalObject. * @param publicKey - Public key. * @param address - EVM address. */ export async function requestHederaAccountId( origin: string, - snap: SnapsGlobalObject, publicKey: string, address: string, ): Promise { @@ -76,5 +67,5 @@ export async function requestHederaAccountId( ]), placeholder: '0.0.3658062', }; - return (await snapDialog(snap, dialogParamsForHederaAccountId)) as string; + return (await snapDialog(dialogParamsForHederaAccountId)) as string; } diff --git a/packages/snap/src/utils/init.ts b/packages/snap/src/utils/init.ts index 9165633..5373d1f 100644 --- a/packages/snap/src/utils/init.ts +++ b/packages/snap/src/utils/init.ts @@ -25,7 +25,7 @@ export async function init( ]), }; - await snapDialog(snap, dialogParams); + await snapDialog(dialogParams); console.log('starting init'); return await initSnapState(snap); }