diff --git a/packages/snap/snap.manifest.json b/packages/snap/snap.manifest.json index 1b9b16d..3b35251 100644 --- a/packages/snap/snap.manifest.json +++ b/packages/snap/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/massalabs/metamask-massa.git" }, "source": { - "shasum": "KqCZqbU7xRPsvxPn66moq8eFjwZsgofIwrqDPK7DHAs=", + "shasum": "PcT+u0rwzqYHXfkiy34U1v/1YjSDtvnBBJntrE1PXRc=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/snap/src/components/CallSc.tsx b/packages/snap/src/components/CallSc.tsx index 148a8df..48b8db8 100644 --- a/packages/snap/src/components/CallSc.tsx +++ b/packages/snap/src/components/CallSc.tsx @@ -47,9 +47,7 @@ export const CallSc: SnapComponent = (params: CallScProps) => { Arguments: {params.args.toString()} - ) : ( - <> - )} + ) : null} diff --git a/packages/snap/src/components/DeploySc.tsx b/packages/snap/src/components/DeploySc.tsx new file mode 100644 index 0000000..d9ac4d6 --- /dev/null +++ b/packages/snap/src/components/DeploySc.tsx @@ -0,0 +1,54 @@ +import { Mas } from '@massalabs/massa-web3'; +import { + SnapComponent, + Container, + Box, + Heading, + Text, + Section, + Bold, +} from '@metamask/snaps-sdk/jsx'; + +type DeployScProps = { + fee: string; + args: number[]; + coins: string; + maxCoins: string; +}; + +export const DeploySc: SnapComponent = ({ + fee, + args, + coins, + maxCoins, +}) => { + return ( + + + Deploying Smart contract +
+ + Coins: + {Mas.toString(BigInt(coins))} MAS + + + Max coins: + {maxCoins !== 'none' + ? `${Mas.toString(BigInt(maxCoins))} MAS` + : 'none'} + + + Fee: + {Mas.toString(BigInt(fee))} MAS + + {args.length ? ( + + Constructor arguments: + {args.toString()} + + ) : null} +
+
+
+ ); +}; diff --git a/packages/snap/src/handlers/call-smart-contract.tsx b/packages/snap/src/handlers/call-smart-contract.tsx index ed1f6de..2f8c878 100644 --- a/packages/snap/src/handlers/call-smart-contract.tsx +++ b/packages/snap/src/handlers/call-smart-contract.tsx @@ -45,10 +45,9 @@ const validate = (params: CallSCParameters) => { }; /** * @description Calls a smart contract with the given parameters - * @param params - The call smart contract parameters (see `CallSCParameters` type and massa standard) + * @param params - CallSCParameters * @returns The operation id * @throws If the user denies the transaction - * @throws If the client or account is not found */ export const callSmartContract: Handler< CallSCParameters, diff --git a/packages/snap/src/handlers/deploy.tsx b/packages/snap/src/handlers/deploy.tsx new file mode 100644 index 0000000..e283cfc --- /dev/null +++ b/packages/snap/src/handlers/deploy.tsx @@ -0,0 +1,96 @@ +import { getProvider } from '../accounts/provider'; +import { addAccountOperation } from '../operations'; +import type { Handler } from './handler'; +import { DeploySc } from '../components/DeploySc'; +import { DeploySCParams } from '@massalabs/massa-web3'; +import { getActiveNetwork } from '../active-chain'; + +export type DeploySCParameters = { + bytecode: number[]; + fee?: string; + args?: number[]; + coins?: string; + maxCoins?: string; + maxGas?: string; +}; + +export type DeploySCResponse = { + operationId: string; +}; + +const validate = (params: DeploySCParameters) => { + // mandatory parameters + if (!params.bytecode || !Array.isArray(params.bytecode)) { + throw new Error('Invalid params: bytecode must be an array'); + } + + // optionnal parameters + if (params.args && !Array.isArray(params.args)) { + throw new Error('Invalid params: args must be an array'); + } + if (params.fee && typeof params.fee !== 'string') { + throw new Error('Invalid params: fee must be a string'); + } + if (params?.coins && typeof params.coins !== 'string') { + throw new Error('Invalid params: coins must be a string'); + } + if (params?.maxGas && typeof params.maxGas !== 'string') { + throw new Error('Invalid params: maxGas must be a string'); + } + if (params?.maxCoins && typeof params.maxCoins !== 'string') { + throw new Error('Invalid params: maxCoins must be a string'); + } +}; +/** + * @description Deploy a smart contract + * @param params - DeploySCParameters + * @returns The operation id + * @throws If the user denies the transaction + */ +export const deployContract: Handler< + DeploySCParameters, + DeploySCResponse +> = async (params) => { + validate(params); + const provider = await getProvider(); + + const networkInfos = await getActiveNetwork(); + const fee = params.fee ? params.fee : networkInfos.minimalFees; + + const confirm = await snap.request({ + method: 'snap_dialog', + params: { + type: 'confirmation', + content: ( + + ), + }, + }); + + if (!confirm) { + throw new Error('User denied calling smart contract'); + } + const deploySCParams: DeploySCParams = { + parameter: Uint8Array.from(params.args ?? []), + byteCode: Uint8Array.from(params.bytecode), + fee: BigInt(fee), + }; + + if (params.coins) { + deploySCParams.coins = BigInt(params.coins); + } + if (params.maxGas) { + deploySCParams.maxGas = BigInt(params.maxGas); + } + // bypass protected attribute of deploy function + const operationId: string = await (provider as any).deploy(deploySCParams); + await addAccountOperation(provider.address, operationId); + return { + operationId, + }; +}; diff --git a/packages/snap/src/handlers/index.ts b/packages/snap/src/handlers/index.ts index 98e7b83..b87959f 100644 --- a/packages/snap/src/handlers/index.ts +++ b/packages/snap/src/handlers/index.ts @@ -1,4 +1,5 @@ export * from './call-smart-contract'; +export * from './deploy'; export * from './sign-message'; export * from './transfer'; export * from './get-balance'; diff --git a/packages/snap/src/index.tsx b/packages/snap/src/index.tsx index 2851afb..f556266 100644 --- a/packages/snap/src/index.tsx +++ b/packages/snap/src/index.tsx @@ -20,6 +20,7 @@ import type { GetOperationsParams, ClearOperationsParams, GetBalanceParams, + DeploySCParameters, } from './handlers'; import { getBalance, @@ -36,6 +37,7 @@ import { getTokens, getOperations, clearOperations, + deployContract, } from './handlers'; import { getActiveAccount } from './handlers/get-active-account'; @@ -61,6 +63,8 @@ export const onRpcRequest: OnRpcRequestHandler = async ({ request }) => { return signMessage(request.params as SignMessageParams); case 'account.callSC': return callSmartContract(request.params as CallSCParameters); + case 'account.deploySC': + return deployContract(request.params as DeploySCParameters); case 'account.sendTransaction': return transfer(request.params as TransferParams); case 'account.sellRolls': diff --git a/packages/snap/test/call-sc.test.tsx b/packages/snap/test/call-sc.test.tsx index 044cf4d..159df7a 100644 --- a/packages/snap/test/call-sc.test.tsx +++ b/packages/snap/test/call-sc.test.tsx @@ -31,6 +31,8 @@ describe('call-sc', () => { params: baseParams, }); + await response.getInterface(); + const ui = (await response.getInterface()) as SnapConfirmationInterface; expect(ui.type).toBe('confirmation'); diff --git a/packages/snap/test/deploy-sc.test.tsx b/packages/snap/test/deploy-sc.test.tsx new file mode 100644 index 0000000..93a405a --- /dev/null +++ b/packages/snap/test/deploy-sc.test.tsx @@ -0,0 +1,35 @@ +import { expect } from '@jest/globals'; +import type { SnapConfirmationInterface } from '@metamask/snaps-jest'; +import { installSnap } from '@metamask/snaps-jest'; +import { DeploySc } from '../src/components/DeploySc'; + +const baseParams = { + bytecode: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + fee: '1000000000000000', +}; + +describe('deploy-sc', () => { + const origin = 'Jest'; + + it('should render ui', async () => { + const { request } = await installSnap(); + + const response = request({ + method: 'account.deploySC', + origin, + params: baseParams, + }); + + const ui = (await response.getInterface()) as SnapConfirmationInterface; + + expect(ui.type).toBe('confirmation'); + expect(ui).toRender( + , + ); + }); +});