diff --git a/packages/hedera-wallet-snap/packages/site/src/components/cards/hts/UpdateTokenFeeSchedule.tsx b/packages/hedera-wallet-snap/packages/site/src/components/cards/hts/UpdateTokenFeeSchedule.tsx new file mode 100644 index 00000000..92a65931 --- /dev/null +++ b/packages/hedera-wallet-snap/packages/site/src/components/cards/hts/UpdateTokenFeeSchedule.tsx @@ -0,0 +1,206 @@ +/*- + * + * Hedera Wallet Snap + * + * Copyright (C) 2024 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { FC, useContext, useRef, useState } from 'react'; +import { + MetaMaskContext, + MetamaskActions, +} from '../../../contexts/MetamaskContext'; +import useModal from '../../../hooks/useModal'; +import { Account, TokenCustomFee } from '../../../types/snap'; +import { + shouldDisplayReconnectButton, + updateTokenFeeSchedule, +} from '../../../utils'; +import { Card, SendHelloButton } from '../../base'; +import ExternalAccount, { + GetExternalAccountRef, +} from '../../sections/ExternalAccount'; + +type Props = { + network: string; + mirrorNodeUrl: string; + setAccountInfo: React.Dispatch>; +}; + +const UpdateTokenFeeSchedule: FC = ({ + network, + mirrorNodeUrl, + setAccountInfo, +}) => { + const [state, dispatch] = useContext(MetaMaskContext); + const [loading, setLoading] = useState(false); + const { showModal } = useModal(); + const [tokenId, setTokenId] = useState(''); + const [feeCollectorAccountId, setTokenFeeCollectorAccountId] = useState(''); + const [hbarAmount, setHbarAmount] = useState(''); + const [tokenAmount, setTokenAmount] = useState(''); + const [denominatingTokenId, setDenominatingTokenId] = useState(''); + const [allCollectorsAreExempt, setCollectorsExempt] = useState(false); + + const externalAccountRef = useRef(null); + + const handleUpdateTokenClick = async () => { + setLoading(true); + try { + const externalAccountParams = + externalAccountRef.current?.handleGetAccountParams(); + + const numberHbarAmount = Number(hbarAmount); + const numberTokenAmount = Number(tokenAmount); + const booleanAllCollectors = Boolean(allCollectorsAreExempt); + + const tokenCustomFee = { + feeCollectorAccountId, + hbarAmount: numberHbarAmount, + tokenAmount: numberTokenAmount, + denominatingTokenId, + allCollectorsAreExempt: booleanAllCollectors, + } as TokenCustomFee; + + const customFees = [tokenCustomFee]; + + const updateTokenFeeScheduleParams = { + tokenId, + customFees, + }; + + const response: any = await updateTokenFeeSchedule( + network, + mirrorNodeUrl, + updateTokenFeeScheduleParams, + externalAccountParams, + ); + + const { receipt, currentAccount } = response; + + setAccountInfo(currentAccount); + console.log('receipt: ', receipt); + + showModal({ + title: 'Transaction Receipt', + content: JSON.stringify({ receipt }, null, 4), + }); + } catch (error) { + console.error(error); + dispatch({ type: MetamaskActions.SetError, payload: error }); + } + setLoading(false); + }; + + return ( + + + +
+ +
+ +
+ +
+ +
+ + + ), + button: ( + + ), + }} + disabled={!state.installedSnap} + fullWidth={ + state.isFlask && + Boolean(state.installedSnap) && + !shouldDisplayReconnectButton(state.installedSnap) + } + /> + ); +}; + +export { UpdateTokenFeeSchedule }; diff --git a/packages/hedera-wallet-snap/packages/site/src/pages/index.tsx b/packages/hedera-wallet-snap/packages/site/src/pages/index.tsx index 87e16ad7..7b98d1b7 100644 --- a/packages/hedera-wallet-snap/packages/site/src/pages/index.tsx +++ b/packages/hedera-wallet-snap/packages/site/src/pages/index.tsx @@ -59,6 +59,7 @@ import { Account } from '../types/snap'; import { connectSnap, getSnap } from '../utils'; import { DeleteToken } from '../components/cards/hts/DeleteToken'; import { UpdateToken } from '../components/cards/hts/UpdateToken'; +import { UpdateTokenFeeSchedule } from '../components/cards/hts/UpdateTokenFeeSchedule'; const Index = () => { const [state, dispatch] = useContext(MetaMaskContext); @@ -214,6 +215,12 @@ const Index = () => { setAccountInfo={setAccountInfo} /> + + { + return await window.ethereum.request({ + method: 'wallet_invokeSnap', + params: { + snapId: defaultSnapOrigin, + request: { + method: 'hts/updateTokenFeeSchedule', + params: { + network, + mirrorNodeUrl, + ...updateTokenFeeScheduleRequestParams, + ...externalAccountparams, + }, + }, + }, + }); +}; + /** * Invoke the "mintToken" method from the snap. * diff --git a/packages/hedera-wallet-snap/packages/snap/jest.config.js b/packages/hedera-wallet-snap/packages/snap/jest.config.js index 4235138b..7be87afa 100644 --- a/packages/hedera-wallet-snap/packages/snap/jest.config.js +++ b/packages/hedera-wallet-snap/packages/snap/jest.config.js @@ -28,6 +28,7 @@ module.exports = { window: { location: { hostname: 'hedera-pulse', + href: 'http://localhost', }, }, }, diff --git a/packages/hedera-wallet-snap/packages/snap/src/facades/hts/UpdateTokenFacade.ts b/packages/hedera-wallet-snap/packages/snap/src/facades/hts/UpdateTokenFacade.ts index becc5764..c2c38489 100644 --- a/packages/hedera-wallet-snap/packages/snap/src/facades/hts/UpdateTokenFacade.ts +++ b/packages/hedera-wallet-snap/packages/snap/src/facades/hts/UpdateTokenFacade.ts @@ -32,7 +32,7 @@ import { UpdateTokenCommand } from '../../commands/hts/UpdateTokenCommand'; export class UpdateTokenFacade { /** - * Updates prioerties for a token. + * Updates priorities for a token. * * @param walletSnapParams - Wallet snap params. * @param updateTokenRequestParams - Parameters for updating a token. @@ -59,7 +59,6 @@ export class UpdateTokenFacade { wipePublicKey, supplyPublicKey, feeSchedulePublicKey, - customFees, expirationTime, autoRenewAccountId = hederaAccountId, tokenMemo = 'Created via Hedera Wallet Snap', diff --git a/packages/hedera-wallet-snap/packages/snap/src/facades/hts/UpdateTokenFeeScheduleFacade.ts b/packages/hedera-wallet-snap/packages/snap/src/facades/hts/UpdateTokenFeeScheduleFacade.ts index d92de95d..d8965a72 100644 --- a/packages/hedera-wallet-snap/packages/snap/src/facades/hts/UpdateTokenFeeScheduleFacade.ts +++ b/packages/hedera-wallet-snap/packages/snap/src/facades/hts/UpdateTokenFeeScheduleFacade.ts @@ -22,10 +22,9 @@ import { providerErrors } from '@metamask/rpc-errors'; import { divider, heading, text } from '@metamask/snaps-ui'; -import _ from 'lodash'; import { HederaClientImplFactory } from '../../client/HederaClientImplFactory'; import { TxReceipt } from '../../types/hedera'; -import { UpdateTokenRequestParams } from '../../types/params'; +import { UpdateTokenFeeScheduleRequestParams } from '../../types/params'; import { SnapDialogParams, WalletSnapParams } from '../../types/state'; import { SnapUtils } from '../../utils/SnapUtils'; import { UpdateTokenFeeScheduleCommand } from '../../commands/hts/UpdateTokenFeeScheduleCommand'; @@ -36,14 +35,14 @@ export class UpdateTokenFeeScheduleFacade { * Updates the fee schedule for a token. * * @param walletSnapParams - Wallet snap params. - * @param updateTokenRequestParams - Parameters for updating a token. + * @param updateTokenFeeScheduleRequestParams - Fee sched request params. * @returns Receipt of the transaction. */ public static async updateTokenFeeSchedule( walletSnapParams: WalletSnapParams, - updateTokenRequestParams: UpdateTokenRequestParams, + updateTokenFeeScheduleRequestParams: UpdateTokenFeeScheduleRequestParams, ): Promise { - if (updateTokenRequestParams.customFees === undefined) { + if (updateTokenFeeScheduleRequestParams.customFees === undefined) { throw new Error('null custom fee schedule given'); } @@ -52,17 +51,33 @@ export class UpdateTokenFeeScheduleFacade { const { hederaEvmAddress, hederaAccountId, network, mirrorNodeUrl } = state.currentAccount; - const { privateKey, publicKey, curve } = + const { privateKey, curve } = state.accountState[hederaEvmAddress][network].keyStore; - const { tokenId, name, symbol, feeSchedulePublicKey } = - updateTokenRequestParams; + const { tokenId, customFees } = updateTokenFeeScheduleRequestParams; const mirrorTokenInfo = await CryptoUtils.getTokenById( tokenId, mirrorNodeUrl, ); + let feeScheduleDisplayStatements = ''; + + for (const fee of customFees) { + const { + feeCollectorAccountId, + hbarAmount, + tokenAmount, + denominatingTokenId, + allCollectorsAreExempt, + } = fee; + + feeScheduleDisplayStatements += + `fee collector id:${feeCollectorAccountId}\nhbarAmount: ${hbarAmount}\n` + + `tokenAmount: ${tokenAmount}\ndenominatingTokenId: ${denominatingTokenId}\n` + + `allCollectorsAreExempt: ${allCollectorsAreExempt}`; + } + let txReceipt = {} as TxReceipt; try { const panelToShow = [ @@ -75,21 +90,9 @@ export class UpdateTokenFeeScheduleFacade { ), divider(), text(`Id: ${tokenId}`), - - text(`Name: ${name}`), - text(`Symbol: ${symbol}`), ]; - panelToShow.push( - text(`Admin Key: ${publicKey}`), - text( - `Fee Schedule Public Key: ${ - _.isEmpty(feeSchedulePublicKey) - ? 'Not set' - : (feeSchedulePublicKey as string) - }`, - ), - ); + panelToShow.push(text(feeScheduleDisplayStatements)); const dialogParams: SnapDialogParams = { type: 'confirmation', @@ -121,7 +124,7 @@ export class UpdateTokenFeeScheduleFacade { tokenId, privateKeyObj, Number(mirrorTokenInfo.decimals), - updateTokenRequestParams.customFees, + updateTokenFeeScheduleRequestParams.customFees, ); txReceipt = await command.execute(hederaClient.getClient()); diff --git a/packages/hedera-wallet-snap/packages/snap/src/index.ts b/packages/hedera-wallet-snap/packages/snap/src/index.ts index 0259c0c6..8d1e02b0 100644 --- a/packages/hedera-wallet-snap/packages/snap/src/index.ts +++ b/packages/hedera-wallet-snap/packages/snap/src/index.ts @@ -46,6 +46,7 @@ import { MintTokenFacade } from './facades/hts/MintTokenFacade'; import { BurnTokenFacade } from './facades/hts/BurnTokenFacade'; import { DeleteTokenFacade } from './facades/hts/DeleteTokenFacade'; import { UpdateTokenFacade } from './facades/hts/UpdateTokenFacade'; +import { UpdateTokenFeeScheduleFacade } from './facades/hts/UpdateTokenFeeScheduleFacade'; /** * Handle incoming JSON-RPC requests, sent through `wallet_invokeSnap`. @@ -328,10 +329,10 @@ export const onRpcRequest: OnRpcRequestHandler = async ({ } case 'hts/updateTokenFeeSchedule': { - HederaUtils.isValidUpdateTokenParams(request.params); + HederaUtils.isValidUpdateTokenFeeScheduleParams(request.params); return { currentAccount: state.currentAccount, - receipt: await UpdateTokenFacade.updateToken( + receipt: await UpdateTokenFeeScheduleFacade.updateTokenFeeSchedule( walletSnapParams, request.params, ), diff --git a/packages/hedera-wallet-snap/packages/snap/src/types/params.ts b/packages/hedera-wallet-snap/packages/snap/src/types/params.ts index 9c4208dc..2b7920db 100644 --- a/packages/hedera-wallet-snap/packages/snap/src/types/params.ts +++ b/packages/hedera-wallet-snap/packages/snap/src/types/params.ts @@ -163,3 +163,8 @@ export type UpdateTokenRequestParams = { autoRenewAccountId?: string; autoRenewPeriod?: number; }; + +export type UpdateTokenFeeScheduleRequestParams = { + tokenId: string; + customFees: TokenCustomFee[]; +}; diff --git a/packages/hedera-wallet-snap/packages/snap/src/utils/HederaUtils.ts b/packages/hedera-wallet-snap/packages/snap/src/utils/HederaUtils.ts index 1311c152..29bcb02d 100644 --- a/packages/hedera-wallet-snap/packages/snap/src/utils/HederaUtils.ts +++ b/packages/hedera-wallet-snap/packages/snap/src/utils/HederaUtils.ts @@ -60,6 +60,7 @@ import { StakeHbarRequestParams, TokenCustomFee, TransferCryptoRequestParams, + UpdateTokenFeeScheduleRequestParams, UpdateTokenRequestParams, WipeTokenRequestParams, } from '../types/params'; @@ -1198,6 +1199,26 @@ export class HederaUtils { } } + /** + * Check Validation of updateToken request. + * + * @param params - Request params. + */ + public static isValidUpdateTokenFeeScheduleParams( + params: unknown, + ): asserts params is UpdateTokenFeeScheduleRequestParams { + if (params === null || _.isEmpty(params) || !('tokenId' in params)) { + console.error( + 'Invalid updateTokenFeeSchedule Params passed. "tokenId" must be included.', + ); + throw providerErrors.unsupportedMethod( + 'Invalid updateTokenFeeSchedule Params passed. "tokenId" must be included.', + ); + } + + // const parameter = params as UpdateTokenRequestParams; + } + /** * Check Validation of updateToken request. *