diff --git a/packages/hathor-rpc-handler/src/helpers/index.ts b/packages/hathor-rpc-handler/src/helpers/index.ts index 70c1866b..d19f0e5e 100644 --- a/packages/hathor-rpc-handler/src/helpers/index.ts +++ b/packages/hathor-rpc-handler/src/helpers/index.ts @@ -5,7 +5,8 @@ * LICENSE file in the root directory of this source tree. */ import type { IHathorWallet } from '@hathor/wallet-lib'; -import { DifferentNetworkError } from '../errors'; +import { config, nanoUtils, Network } from '@hathor/wallet-lib'; +import { DifferentNetworkError, SendNanoContractTxError } from '../errors'; export function validateNetwork(wallet: IHathorWallet, network: string) { const currentNetwork = wallet.getNetwork(); @@ -14,3 +15,49 @@ export function validateNetwork(wallet: IHathorWallet, network: string) { throw new DifferentNetworkError(); } } + +export async function parseNanoArgs( + wallet: IHathorWallet, + blueprintId: string | undefined | null, + ncId: string | undefined | null, + method: string, + args: unknown[], + network: string +) { + if (!blueprintId && !ncId) { + throw new SendNanoContractTxError( + 'Either blueprintId or ncId is required.' + ); + } + + if (!blueprintId) { + let response; + try { + response = await wallet.getFullTxById(ncId!); + } catch { + // Error getting nano contract transaction data from the full node + throw new SendNanoContractTxError( + `Error getting nano contract transaction data with id ${ncId} from the full node` + ); + } + + if (!response.tx.nc_id) { + throw new SendNanoContractTxError( + `Transaction with id ${ncId} is not a nano contract transaction.` + ); + } + + blueprintId = response.tx.nc_blueprint_id; + } + + config.setServerUrl(wallet.getServerUrl()); + const result = await nanoUtils.validateAndParseBlueprintMethodArgs( + blueprintId!, + method, + args, + new Network(network) + ); + return result.map((data) => { + return { ...data, parsed: data.field.toUser() }; + }); +} diff --git a/packages/hathor-rpc-handler/src/rpcMethods/createNanoContractCreateTokenTx.ts b/packages/hathor-rpc-handler/src/rpcMethods/createNanoContractCreateTokenTx.ts index 175724d2..5971ce96 100644 --- a/packages/hathor-rpc-handler/src/rpcMethods/createNanoContractCreateTokenTx.ts +++ b/packages/hathor-rpc-handler/src/rpcMethods/createNanoContractCreateTokenTx.ts @@ -6,7 +6,7 @@ */ import { z } from 'zod'; -import type { HathorWallet } from '@hathor/wallet-lib'; +import type { IHathorWallet } from '@hathor/wallet-lib'; import { TriggerTypes, PinConfirmationPrompt, @@ -21,29 +21,23 @@ import { CreateNanoContractCreateTokenTxLoadingTrigger, CreateNanoContractCreateTokenTxLoadingFinishedTrigger, NanoContractParams, - CreateTokenParams, + NanoContractCreateTokenParams, } from '../types'; -import { PromptRejectedError, InvalidParamsError } from '../errors'; -import { INanoContractActionSchema } from '@hathor/wallet-lib'; -import { createTokenBaseSchema } from '../schemas'; - -// Extend CreateTokenParams to include nano contract specific fields -type NanoContractCreateTokenParams = CreateTokenParams & { - contractPaysTokenDeposit: boolean; -}; +import { PromptRejectedError, SendNanoContractTxError, InvalidParamsError } from '../errors'; +import { CreateTokenTransaction, INanoContractActionSchema } from '@hathor/wallet-lib'; +import { createNanoCreateTokenRpcSchema } from '../schemas'; +import { parseNanoArgs } from '../helpers'; const createNanoContractCreateTokenTxSchema = z.object({ + network: z.string().min(1), method: z.string().min(1), - address: z.string().min(1), data: z.object({ blueprint_id: z.string().nullable().optional(), nc_id: z.string().nullable().optional(), - actions: z.array(INanoContractActionSchema).optional(), - args: z.array(z.unknown()).optional(), - }).optional(), - createTokenOptions: createTokenBaseSchema.extend({ - contractPaysTokenDeposit: z.boolean(), - }).optional(), + actions: z.array(INanoContractActionSchema), + args: z.array(z.unknown()).default([]), + }), + createTokenOptions: createNanoCreateTokenRpcSchema, push_tx: z.boolean().default(true), }); @@ -64,7 +58,7 @@ const createNanoContractCreateTokenTxSchema = z.object({ */ export async function createNanoContractCreateTokenTx( rpcRequest: CreateNanoContractCreateTokenTxRpcRequest, - wallet: HathorWallet, + wallet: IHathorWallet, requestMetadata: RequestMetadata, promptHandler: TriggerHandler, ): Promise { @@ -72,33 +66,35 @@ export async function createNanoContractCreateTokenTx( if (!validationResult.success) { throw new InvalidParamsError(validationResult.error.errors.map(e => `${e.path.join('.')}: ${e.message}`).join(', ')); } - const { method, address, data, createTokenOptions, push_tx } = validationResult.data; + const { method, data, createTokenOptions, push_tx, network } = validationResult.data; + + const parsedArgs = await parseNanoArgs(wallet, data.blueprint_id, data.nc_id, method, data.args, network); // Prepare nano and token params for the confirmation prompt const nanoParams: NanoContractParams = { - blueprintId: data?.blueprint_id ?? null, - ncId: data?.nc_id ?? null, - actions: data?.actions ?? [], + blueprintId: data.blueprint_id ?? null, + ncId: data.nc_id ?? null, + actions: data.actions ?? [], method, - args: data?.args ?? [], - parsedArgs: [], + args: data.args ?? [], + parsedArgs, pushTx: push_tx, }; // Only pass CreateTokenParams fields, fallback to null/empty for missing const tokenParams: NanoContractCreateTokenParams = { - name: createTokenOptions?.name ?? '', - symbol: createTokenOptions?.symbol ?? '', - amount: typeof createTokenOptions?.amount === 'string' ? BigInt(createTokenOptions.amount) : (createTokenOptions?.amount ?? 0n), - mintAddress: createTokenOptions?.mintAddress ?? null, - changeAddress: createTokenOptions?.changeAddress ?? null, - createMint: createTokenOptions?.createMint ?? true, - mintAuthorityAddress: createTokenOptions?.mintAuthorityAddress ?? null, - allowExternalMintAuthorityAddress: createTokenOptions?.allowExternalMintAuthorityAddress ?? false, - createMelt: createTokenOptions?.createMelt ?? true, - meltAuthorityAddress: createTokenOptions?.meltAuthorityAddress ?? null, - allowExternalMeltAuthorityAddress: createTokenOptions?.allowExternalMeltAuthorityAddress ?? false, - data: createTokenOptions?.data ?? null, - contractPaysTokenDeposit: createTokenOptions?.contractPaysTokenDeposit ?? false, + name: createTokenOptions.name ?? '', + symbol: createTokenOptions.symbol ?? '', + amount: typeof createTokenOptions.amount === 'string' ? BigInt(createTokenOptions.amount) : (createTokenOptions.amount ?? 0n), + mintAddress: createTokenOptions.mintAddress ?? null, + changeAddress: createTokenOptions.changeAddress ?? null, + createMint: createTokenOptions.createMint ?? true, + mintAuthorityAddress: createTokenOptions.mintAuthorityAddress ?? null, + allowExternalMintAuthorityAddress: createTokenOptions.allowExternalMintAuthorityAddress ?? false, + createMelt: createTokenOptions.createMelt ?? true, + meltAuthorityAddress: createTokenOptions.meltAuthorityAddress ?? null, + allowExternalMeltAuthorityAddress: createTokenOptions.allowExternalMeltAuthorityAddress ?? false, + data: createTokenOptions.data ?? null, + contractPaysTokenDeposit: createTokenOptions.contractPaysTokenDeposit ?? false, }; const confirmationPrompt: CreateNanoContractCreateTokenTxConfirmationPrompt = { @@ -139,19 +135,26 @@ export async function createNanoContractCreateTokenTx( if (push_tx) { response = await wallet.createAndSendNanoContractCreateTokenTransaction( nano.method, - address, + nano.caller, nano, token, { pinCode: pinResponse.data.pinCode } - ); + ) as CreateTokenTransaction; } else { - response = await wallet.createNanoContractCreateTokenTransaction( + const sendTransactionObj = await wallet.createNanoContractCreateTokenTransaction( nano.method, - address, + nano.caller, nano, token, { pinCode: pinResponse.data.pinCode } ); + + if (!sendTransactionObj.transaction) { + // This should never happen, but we'll check anyway + throw new SendNanoContractTxError('Unable to create transaction object'); + } + // Convert the transaction object to hex format for the response + response = sendTransactionObj.transaction.toHex(); } // Emit loading finished trigger diff --git a/packages/hathor-rpc-handler/src/rpcMethods/sendNanoContractTx.ts b/packages/hathor-rpc-handler/src/rpcMethods/sendNanoContractTx.ts index bf0401a4..b743ffe2 100644 --- a/packages/hathor-rpc-handler/src/rpcMethods/sendNanoContractTx.ts +++ b/packages/hathor-rpc-handler/src/rpcMethods/sendNanoContractTx.ts @@ -22,7 +22,8 @@ import { SendNanoContractTxLoadingFinishedTrigger, } from '../types'; import { PromptRejectedError, SendNanoContractTxError, InvalidParamsError } from '../errors'; -import { INanoContractActionSchema, NanoContractAction, nanoUtils, Network, config } from '@hathor/wallet-lib'; +import { parseNanoArgs } from '../helpers'; +import { INanoContractActionSchema, NanoContractAction } from '@hathor/wallet-lib'; export type NanoContractActionWithStringAmount = Omit & { amount: string, @@ -75,32 +76,7 @@ export async function sendNanoContractTx( type: TriggerTypes.PinConfirmationPrompt, }; - let blueprintId = params.blueprintId; - if (!blueprintId) { - let response; - try { - response = await wallet.getFullTxById(params.ncId!); - } catch { - // Error getting nano contract transaction data from the full node - throw new SendNanoContractTxError( - `Error getting nano contract transaction data with id ${params.ncId} from the full node` - ); - } - - if (!response.tx.nc_id) { - throw new SendNanoContractTxError( - `Transaction with id ${params.ncId} is not a nano contract transaction.` - ); - } - - blueprintId = response.tx.nc_blueprint_id!; - } - - config.setServerUrl(wallet.getServerUrl()); - const result = await nanoUtils.validateAndParseBlueprintMethodArgs(blueprintId!, params.method, params.args, new Network(params.network)); - const parsedArgs = result.map((data) => { - return { ...data, parsed: data.field.toUser() }; - }); + const parsedArgs = await parseNanoArgs(wallet, params.blueprintId, params.ncId, params.method, params.args, params.network); const sendNanoContractTxPrompt: SendNanoContractTxConfirmationPrompt = { ...rpcRequest, diff --git a/packages/hathor-rpc-handler/src/schemas/createTokenSchema.ts b/packages/hathor-rpc-handler/src/schemas/createTokenSchema.ts index 8cf0bf0a..b59721e1 100644 --- a/packages/hathor-rpc-handler/src/schemas/createTokenSchema.ts +++ b/packages/hathor-rpc-handler/src/schemas/createTokenSchema.ts @@ -7,23 +7,7 @@ import { z } from 'zod'; -export const createTokenBaseSchema = z.object({ - name: z.string().min(1).max(30), - symbol: z.string().min(2).max(5), - amount: z.union([z.string(), z.bigint()]), - changeAddress: z.string().nullable().optional(), - createMint: z.boolean().optional(), - mintAuthorityAddress: z.string().nullable().optional(), - allowExternalMintAuthorityAddress: z.boolean().optional(), - createMelt: z.boolean().optional(), - meltAuthorityAddress: z.string().nullable().optional(), - allowExternalMeltAuthorityAddress: z.boolean().optional(), - data: z.array(z.string()).nullable().optional(), - mintAddress: z.string().nullable().optional(), -}); - -// Schema for the original createToken RPC method with snake_case fields -export const createTokenRpcSchema = z.object({ +const createTokenBaseSchema = z.object({ name: z.string().min(1).max(30), symbol: z.string().min(2).max(5), amount: z.string().regex(/^\d+$/) @@ -37,7 +21,28 @@ export const createTokenRpcSchema = z.object({ melt_authority_address: z.string().nullish().default(null), allow_external_melt_authority_address: z.boolean().default(false), data: z.string().array().nullish().default(null), +}); + +export const createNanoCreateTokenRpcSchema = createTokenBaseSchema.extend({ + contract_pays_token_deposit: z.boolean() }).transform(data => ({ + name: data.name, + symbol: data.symbol, + amount: data.amount, + mintAddress: data.address, + changeAddress: data.change_address, + createMint: data.create_mint, + mintAuthorityAddress: data.mint_authority_address, + allowExternalMintAuthorityAddress: data.allow_external_mint_authority_address, + createMelt: data.create_melt, + meltAuthorityAddress: data.melt_authority_address, + allowExternalMeltAuthorityAddress: data.allow_external_melt_authority_address, + data: data.data, + contractPaysTokenDeposit: data.contract_pays_token_deposit, +})); + +// Schema for the original createToken RPC method with snake_case fields +export const createTokenRpcSchema = createTokenBaseSchema.transform(data => ({ name: data.name, symbol: data.symbol, amount: data.amount, diff --git a/packages/hathor-rpc-handler/src/schemas/index.ts b/packages/hathor-rpc-handler/src/schemas/index.ts index 58021964..28db7756 100644 --- a/packages/hathor-rpc-handler/src/schemas/index.ts +++ b/packages/hathor-rpc-handler/src/schemas/index.ts @@ -5,4 +5,4 @@ * LICENSE file in the root directory of this source tree. */ -export { createTokenBaseSchema, createTokenRpcSchema } from './createTokenSchema'; \ No newline at end of file +export { createNanoCreateTokenRpcSchema, createTokenRpcSchema } from './createTokenSchema'; \ No newline at end of file diff --git a/packages/hathor-rpc-handler/src/types/prompt.ts b/packages/hathor-rpc-handler/src/types/prompt.ts index 0b76ce94..f0d69d9e 100644 --- a/packages/hathor-rpc-handler/src/types/prompt.ts +++ b/packages/hathor-rpc-handler/src/types/prompt.ts @@ -192,6 +192,10 @@ export interface CreateTokenParams { data: string[] | null, } +export type NanoContractCreateTokenParams = CreateTokenParams & { + contractPaysTokenDeposit: boolean; +}; + export type CreateTokenConfirmationPrompt = BaseConfirmationPrompt & { type: TriggerTypes.CreateTokenConfirmationPrompt; data: CreateTokenParams; @@ -294,7 +298,7 @@ export interface SendTransactionConfirmationResponse { export interface CreateNanoContractCreateTokenTxParams { nano: NanoContractParams; - token: CreateTokenParams; + token: NanoContractCreateTokenParams; } export type CreateNanoContractCreateTokenTxConfirmationPrompt = BaseConfirmationPrompt & { @@ -307,7 +311,7 @@ export interface CreateNanoContractCreateTokenTxConfirmationResponse { data: { accepted: true; nano: NanoContractParams & { caller: string }; - token: CreateTokenParams; + token: NanoContractCreateTokenParams; } | { accepted: false; } diff --git a/packages/hathor-rpc-handler/src/types/rpcResponse.ts b/packages/hathor-rpc-handler/src/types/rpcResponse.ts index 12477914..ba142567 100644 --- a/packages/hathor-rpc-handler/src/types/rpcResponse.ts +++ b/packages/hathor-rpc-handler/src/types/rpcResponse.ts @@ -86,7 +86,7 @@ export interface SendTransactionResponse extends BaseRpcResponse { export interface CreateNanoContractCreateTokenTxResponse extends BaseRpcResponse { type: RpcResponseTypes.CreateNanoContractCreateTokenTxResponse; - response: Transaction; + response: CreateTokenTransaction | string; } export interface ChangeNetworkResponse extends BaseRpcResponse { diff --git a/packages/snap/snap.manifest.json b/packages/snap/snap.manifest.json index c52c60dc..3bb93649 100644 --- a/packages/snap/snap.manifest.json +++ b/packages/snap/snap.manifest.json @@ -3,7 +3,7 @@ "description": "Hathor Network Snap integration", "proposedName": "Hathor Wallet", "source": { - "shasum": "K6uIBlCuUj5/93meVMmLeXpFuOFoiUR1mB6jLVpeS24=", + "shasum": "jny8ScQpCkc5cUCtRIzxtXVAHC6yP1hsZzKN5/ZkDAw=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/snap/src/dialogs/common.tsx b/packages/snap/src/dialogs/common.tsx new file mode 100644 index 00000000..dc0feb89 --- /dev/null +++ b/packages/snap/src/dialogs/common.tsx @@ -0,0 +1,203 @@ +import { REQUEST_METHODS, DIALOG_TYPES } from '../constants'; +import { Bold, Box, Card, Container, Copyable, Divider, Heading, Section, Text } from '@metamask/snaps-sdk/jsx'; +import { constants as libConstants, bigIntUtils, dateUtils, NanoContractActionType, numberUtils } from '@hathor/wallet-lib'; + +/* + * Some render helper methods used to render nano contract data + * This was moved to a common file because it's used by + * the send nano tx and send nano create token tx + */ +const renderOptionalContractDetail = (param, title) => { + if (!param) return null; + + return ( + + + + + ); +} + +const renderArguments = (args) => { + if (!args || args.length === 0) return null; + + return ( +
+ Arguments: + {renderArgsMap(args)} +
+ ); +} + +const renderArgsMap = (args) => { + return args.map((arg) => { + let parsed; + + if (arg.type === 'Timestamp') { + parsed = dateUtils.parseTimestamp(arg.parsed); + } else if (arg.type === 'Amount') { + parsed = numberUtils.prettyValue(arg.parsed); + } else { + parsed = bigIntUtils.JSONBigInt.stringify(arg.parsed); + } + + return + }); +} + +const renderActions = (actions) => { + if (!actions || actions.length === 0) return null; + + return ( +
+ Actions: + {renderActionsMap(actions)} +
+ ); +} + +const renderActionsMap = (actions) => { + return actions.map((action) => { + return renderAction(action) + }); +} + +const actionTitleMap = { + [NanoContractActionType.DEPOSIT]: 'Deposit', + [NanoContractActionType.WITHDRAWAL]: 'Withdrawal', + [NanoContractActionType.GRANT_AUTHORITY]: 'Grant Authority', + [NanoContractActionType.ACQUIRE_AUTHORITY]: 'Acquire Authority', +}; + +const renderAmount = (action) => { + if (action.type === NanoContractActionType.DEPOSIT || action.type === NanoContractActionType.WITHDRAWAL) { + return + } + + return +} + +const renderToken = (action) => { + if (action.token === libConstants.NATIVE_TOKEN_UID) { + return + } + + return ( + + Token + + + ); +} + +const renderAddresses = (action) => { + if (action.type === NanoContractActionType.WITHDRAWAL || action.type === NanoContractActionType.ACQUIRE_AUTHORITY) { + return ( + + Address to receive + + + ); + } + + if (action.type === NanoContractActionType.DEPOSIT) { + if (!action.address && !action.changeAddress) return null; + + return ( + + {action.address ? ( + + Address to filter UTXOs + + + ) : null} + {action.changeAddress ? ( + + Change address + + + ) : null} + + ); + } + + if (action.type === NanoContractActionType.GRANT_AUTHORITY) { + if (!action.address && !action.authorityAddress) return null; + + return ( + + {action.address ? ( + + Address to filter UTXOs + + + ) : null} + {action.authorityAddress ? ( + + Authority address + + + ) : null} + + ); + } +} + +const renderAction = (action) => { + return ( +
+ {actionTitleMap[action.type]} + {renderAmount(action)} + {renderToken(action)} + {renderAddresses(action)} +
+ ); +} + +export const renderNanoData = (data) => ( + +
+ Contract Details: + {renderOptionalContractDetail(data.ncId, "Nano Contract ID")} + {renderOptionalContractDetail(data.blueprintId, "Blueprint ID")} + +
+ {renderArguments(data.parsedArgs)} + {renderActions(data.actions)} +
+); + +/* + * Some render helper methods used to render token creation data + * This was moved to a common file because it's used by + * the create token and send nano create token tx + */ +const renderConditionalCard = (title, value, parsedValue = null) => { + if (value == null) { + return null; + } + + return +} + +const boolToString = (bool) => { + return bool ? 'true' : 'false'; +} + +export const renderCreateTokenData = (data) => ( +
+ + + + {renderConditionalCard('Address', data.address)} + {renderConditionalCard('Change address', data.change_address)} + {renderConditionalCard('Create mint authority', data.create_mint, boolToString(data.create_mint))} + {renderConditionalCard('Mint address', data.mint_authority_address)} + {renderConditionalCard('Allow external mint address', data.allow_external_mint_authority_address, boolToString(data.allow_external_mint_authority_address))} + {renderConditionalCard('Create melt authority', data.create_melt, boolToString(data.create_melt))} + {renderConditionalCard('Melt address', data.melt_authority_address)} + {renderConditionalCard('Allow external melt address', data.allow_external_melt_authority_address, boolToString(data.allow_external_melt_authority_address))} + {renderConditionalCard('Contract pays token deposit', data.contract_pays_token_deposit, boolToString(data.contract_pays_token_deposit))} + {renderConditionalCard('Data', data.data, data.data ? data.data.join(', ') : '')} +
+); \ No newline at end of file diff --git a/packages/snap/src/dialogs/createNano.tsx b/packages/snap/src/dialogs/createNano.tsx index 5246a451..022d6937 100644 --- a/packages/snap/src/dialogs/createNano.tsx +++ b/packages/snap/src/dialogs/createNano.tsx @@ -6,155 +6,8 @@ */ import { REQUEST_METHODS, DIALOG_TYPES } from '../constants'; -import { Bold, Box, Card, Container, Copyable, Divider, Heading, Section, Text } from '@metamask/snaps-sdk/jsx'; -import { constants as libConstants, bigIntUtils, dateUtils, NanoContractActionType, numberUtils } from '@hathor/wallet-lib'; - -const renderOptionalContractDetail = (param, title) => { - if (!param) return null; - - return ( - - - - - ); -} - -const renderArguments = (args) => { - if (!args || args.length === 0) return null; - - return ( -
- Arguments: - {renderArgsMap(args)} -
- ); -} - -const renderArgsMap = (args) => { - return args.map((arg) => { - let parsed; - - if (arg.type === 'Timestamp') { - parsed = dateUtils.parseTimestamp(arg.parsed); - } else if (arg.type === 'Amount') { - parsed = numberUtils.prettyValue(arg.parsed); - } else { - parsed = bigIntUtils.JSONBigInt.stringify(arg.parsed); - } - - return - }); -} - -const renderActions = (actions) => { - if (!actions || actions.length === 0) return null; - - return ( -
- Actions: - {renderActionsMap(actions)} -
- ); -} - -const renderActionsMap = (actions) => { - return actions.map((action) => { - return renderAction(action) - }); -} - -const actionTitleMap = { - [NanoContractActionType.DEPOSIT]: 'Deposit', - [NanoContractActionType.WITHDRAWAL]: 'Withdrawal', - [NanoContractActionType.GRANT_AUTHORITY]: 'Grant Authority', - [NanoContractActionType.ACQUIRE_AUTHORITY]: 'Acquire Authority', -}; - -const renderAmount = (action) => { - if (action.type === NanoContractActionType.DEPOSIT || action.type === NanoContractActionType.WITHDRAWAL) { - return - } - - return -} - -const renderToken = (action) => { - if (action.token === libConstants.NATIVE_TOKEN_UID) { - return - } - - return ( - - Token - - - ); -} - -const renderAddresses = (action) => { - if (action.type === NanoContractActionType.WITHDRAWAL || action.type === NanoContractActionType.ACQUIRE_AUTHORITY) { - return ( - - Address to receive - - - ); - } - - if (action.type === NanoContractActionType.DEPOSIT) { - if (!action.address && !action.changeAddress) return null; - - return ( - - {action.address ? ( - - Address to filter UTXOs - - - ) : null} - {action.changeAddress ? ( - - Change address - - - ) : null} - - ); - } - - if (action.type === NanoContractActionType.GRANT_AUTHORITY) { - if (!action.address && !action.authorityAddress) return null; - - return ( - - {action.address ? ( - - Address to filter UTXOs - - - ) : null} - {action.authorityAddress ? ( - - Authority address - - - ) : null} - - ); - } -} - -const renderAction = (action) => { - return ( -
- {actionTitleMap[action.type]} - {renderAmount(action)} - {renderToken(action)} - {renderAddresses(action)} -
- ); -} +import { Box, Container, Heading, Text } from '@metamask/snaps-sdk/jsx'; +import { renderNanoData } from './common'; export const createNanoPage = async (data, params, origin) => ( await snap.request({ @@ -168,14 +21,7 @@ export const createNanoPage = async (data, params, origin) => ( The dApp {origin} is requesting permission to execute a nano contract transaction on the Hathor Network -
- Contract Details: - {renderOptionalContractDetail(params.nc_id, "Nano Contract ID")} - {renderOptionalContractDetail(params.blueprint_id, "Blueprint ID")} - -
- {renderArguments(data.parsedArgs)} - {renderActions(params.actions)} + {renderNanoData(data)} ), diff --git a/packages/snap/src/dialogs/createNanoCreateToken.tsx b/packages/snap/src/dialogs/createNanoCreateToken.tsx new file mode 100644 index 00000000..90dfd40e --- /dev/null +++ b/packages/snap/src/dialogs/createNanoCreateToken.tsx @@ -0,0 +1,25 @@ +import { REQUEST_METHODS, DIALOG_TYPES } from '../constants'; +import { Bold, Box, Container, Heading, Text } from '@metamask/snaps-sdk/jsx'; +import { renderCreateTokenData, renderNanoData } from './common'; + +export const createNanoAndTokenPage = async (data, params, origin) => ( + await snap.request({ + method: REQUEST_METHODS.DIALOG, + params: { + type: DIALOG_TYPES.CONFIRMATION, + content: ( + + + Send Nano Contract Transaction with Token Creation + + The dApp {origin} is requesting permission to execute a nano contract transaction and create a token on the Hathor Network + + {renderNanoData(data.nano)} + Token creation details: + {renderCreateTokenData(params.createTokenOptions)} + + + ), + }, + }) +) \ No newline at end of file diff --git a/packages/snap/src/dialogs/createToken.tsx b/packages/snap/src/dialogs/createToken.tsx index 716c722c..c05f7c1f 100644 --- a/packages/snap/src/dialogs/createToken.tsx +++ b/packages/snap/src/dialogs/createToken.tsx @@ -6,20 +6,8 @@ */ import { REQUEST_METHODS, DIALOG_TYPES } from '../constants'; -import { Box, Card, Container, Divider, Heading, Section, Text } from '@metamask/snaps-sdk/jsx'; -import { numberUtils } from '@hathor/wallet-lib'; - -const renderConditionalCard = (title, value, parsedValue = null) => { - if (value == null) { - return null; - } - - return -} - -const boolToString = (bool) => { - return bool ? 'true' : 'false'; -} +import { renderCreateTokenData } from './common'; +import { Box, Container, Heading, Text } from '@metamask/snaps-sdk/jsx'; export const createTokenPage = async (data, params, origin) => ( await snap.request({ @@ -33,20 +21,7 @@ export const createTokenPage = async (data, params, origin) => ( The dApp {origin} is requesting permission to create a new token on the Hathor Network with the following details: -
- - - - {renderConditionalCard('Address', params.address)} - {renderConditionalCard('Change address', params.change_address)} - {renderConditionalCard('Create mint authority', params.create_mint, boolToString(params.create_mint))} - {renderConditionalCard('Mint address', params.mint_authority_address)} - {renderConditionalCard('Allow external mint address', params.allow_external_mint_authority_address, boolToString(params.allow_external_mint_authority_address))} - {renderConditionalCard('Create melt authority', params.create_melt, boolToString(params.create_melt))} - {renderConditionalCard('Melt address', params.melt_authority_address)} - {renderConditionalCard('Allow external melt address', params.allow_external_melt_authority_address, boolToString(params.allow_external_melt_authority_address))} - {renderConditionalCard('Data', params.data, params.data ? params.data.join(', ') : '')} -
+ {renderCreateTokenData(params)} ), diff --git a/packages/snap/src/dialogs/index.ts b/packages/snap/src/dialogs/index.ts index 1446d594..6003a766 100644 --- a/packages/snap/src/dialogs/index.ts +++ b/packages/snap/src/dialogs/index.ts @@ -12,5 +12,6 @@ export * from './sendTransaction'; export * from './signWithAddress'; export * from './createToken'; export * from './createNano'; +export * from './createNanoCreateToken'; export * from './changeNetwork'; export * from './oracleData'; \ No newline at end of file diff --git a/packages/snap/src/utils/prompt.ts b/packages/snap/src/utils/prompt.ts index e38a08ab..31b1df5f 100644 --- a/packages/snap/src/utils/prompt.ts +++ b/packages/snap/src/utils/prompt.ts @@ -5,7 +5,18 @@ * LICENSE file in the root directory of this source tree. */ -import { addressPage, balancePage, changeNetworkPage, createNanoPage, createTokenPage, oracleDataPage, sendTransactionPage, signWithAddressPage, utxosPage } from '../dialogs'; +import { + addressPage, + balancePage, + changeNetworkPage, + createNanoPage, + createTokenPage, + createNanoAndTokenPage, + oracleDataPage, + sendTransactionPage, + signWithAddressPage, + utxosPage +} from '../dialogs'; import { setNetwork } from '../utils/network'; import { DEFAULT_PIN_CODE, NETWORK_MAP } from '../constants'; import { RpcMethods, TriggerTypes } from '@hathor/hathor-rpc-handler'; @@ -13,7 +24,7 @@ import { RpcMethods, TriggerTypes } from '@hathor/hathor-rpc-handler'; export const promptHandler = (origin, wallet) => async (promptRequest) => { const data = promptRequest.data; const params = promptRequest.params; - let approved; + let approved, response, address0; switch (promptRequest.type) { case TriggerTypes.SignMessageWithAddressConfirmationPrompt: approved = await signWithAddressPage(data, params, origin); @@ -61,7 +72,7 @@ export const promptHandler = (origin, wallet) => async (promptRequest) => { }; case TriggerTypes.SendNanoContractTxConfirmationPrompt: approved = await createNanoPage(data, params, origin); - const response = { + response = { data: { accepted: approved } @@ -72,7 +83,7 @@ export const promptHandler = (origin, wallet) => async (promptRequest) => { } // For the snap, the nano caller is always the address0 - const address0 = await wallet.getAddressAtIndex(0); + address0 = await wallet.getAddressAtIndex(0); response['data']['nc'] = { caller: address0, blueprintId: params.blueprint_id, @@ -83,12 +94,32 @@ export const promptHandler = (origin, wallet) => async (promptRequest) => { case TriggerTypes.SignOracleDataConfirmationPrompt: approved = await oracleDataPage(data, params, origin); return { data: approved }; + case TriggerTypes.CreateNanoContractCreateTokenTxConfirmationPrompt: + approved = await createNanoAndTokenPage(data, params, origin); + response = { + data: { + accepted: approved + } + } + + if (!approved) { + return response; + } + + // For the snap, the nano caller is always the address0 + address0 = await wallet.getAddressAtIndex(0); + response['data']['nano'] = { ...data.nano, caller: address0 }; + response['data']['token'] = { ...data.token }; + return response; + case TriggerTypes.SendNanoContractTxLoadingTrigger: case TriggerTypes.SendNanoContractTxLoadingFinishedTrigger: case TriggerTypes.CreateTokenLoadingTrigger: case TriggerTypes.CreateTokenLoadingFinishedTrigger: case TriggerTypes.SendTransactionLoadingTrigger: case TriggerTypes.SendTransactionLoadingFinishedTrigger: + case TriggerTypes.CreateNanoContractCreateTokenTxLoadingTrigger: + case TriggerTypes.CreateNanoContractCreateTokenTxLoadingFinishedTrigger: break; default: throw new Error('Invalid request');