@@ -14,7 +14,12 @@ import {
14
14
AccountInfo ,
15
15
ContractInteractionResult
16
16
} from '../types/mcpTools' ;
17
+ import { bytesToHex } from '@ethereumjs/util'
17
18
import { Plugin } from '@remixproject/engine' ;
19
+ import { getContractData } from '@remix-project/core-plugin'
20
+ import type { TxResult } from '@remix-project/remix-lib' ;
21
+ import type { TransactionReceipt } from 'web3'
22
+ import web3 from 'web3'
18
23
19
24
/**
20
25
* Deploy Contract Tool Handler
@@ -54,6 +59,10 @@ export class DeployContractHandler extends BaseToolHandler {
54
59
account : {
55
60
type : 'string' ,
56
61
description : 'Account to deploy from (address or index)'
62
+ } ,
63
+ file : {
64
+ type : 'string' ,
65
+ description : 'The file containing the contract to deploy'
57
66
}
58
67
} ,
59
68
required : [ 'contractName' ]
@@ -86,56 +95,54 @@ export class DeployContractHandler extends BaseToolHandler {
86
95
async execute ( args : DeployContractArgs , plugin : Plugin ) : Promise < IMCPToolResult > {
87
96
try {
88
97
// Get compilation result to find contract
89
- // TODO: Get actual compilation result
90
- const contracts = { } ; // await plugin.solidity.getCompilationResult();
91
-
92
- if ( ! contracts || Object . keys ( contracts ) . length === 0 ) {
93
- return this . createErrorResult ( 'No compiled contracts found. Please compile first.' ) ;
98
+ const compilerAbstract = await plugin . call ( 'compilerArtefacts' , 'getCompilerAbstract' , args . file ) as any ;
99
+ const data = getContractData ( args . contractName , compilerAbstract )
100
+ if ( ! data ) {
101
+ return this . createErrorResult ( `Could not retrieve contract data for '${ args . contractName } '` ) ;
94
102
}
95
103
96
- // Find the contract to deploy
97
- const contractKey = Object . keys ( contracts ) . find ( key =>
98
- key . includes ( args . contractName )
99
- ) ;
100
-
101
- if ( ! contractKey ) {
102
- return this . createErrorResult ( `Contract '${ args . contractName } ' not found in compilation result` ) ;
103
- }
104
-
105
- // Get current account
106
- const accounts = await this . getAccounts ( plugin ) ;
107
- const deployAccount = args . account || accounts [ 0 ] ;
108
-
109
- if ( ! deployAccount ) {
110
- return this . createErrorResult ( 'No account available for deployment' ) ;
104
+ let txReturn
105
+ try {
106
+ txReturn = await new Promise ( async ( resolve , reject ) => {
107
+ const callbacks = { continueCb : ( error , continueTxExecution , cancelCb ) => {
108
+ continueTxExecution ( )
109
+ } , promptCb : ( ) => { } , statusCb : ( ) => { } , finalCb : ( error , contractObject , address : string , txResult : TxResult ) => {
110
+ if ( error ) return reject ( error )
111
+ resolve ( { contractObject, address, txResult} )
112
+ } }
113
+ const confirmationCb = ( network , tx , gasEstimation , continueTxExecution , cancelCb ) => {
114
+ continueTxExecution ( null )
115
+ }
116
+ const compilerContracts = await plugin . call ( 'compilerArtefacts' , 'getLastCompilationResult' )
117
+ plugin . call ( 'blockchain' , 'deployContractAndLibraries' ,
118
+ data ,
119
+ args . constructorArgs ? args : [ ] ,
120
+ null ,
121
+ compilerContracts . getData ( ) . contracts ,
122
+ callbacks ,
123
+ confirmationCb
124
+ )
125
+ } )
126
+ } catch ( e ) {
127
+ return this . createErrorResult ( `Deployment error: ${ e . message } ` ) ;
111
128
}
112
-
113
- // Prepare deployment transaction
114
- const deploymentData = {
115
- contractName : args . contractName ,
116
- account : deployAccount ,
117
- constructorArgs : args . constructorArgs || [ ] ,
118
- gasLimit : args . gasLimit ,
119
- gasPrice : args . gasPrice ,
120
- value : args . value || '0'
121
- } ;
122
-
123
- // TODO: Execute actual deployment via Remix Run Tab API
124
- const mockResult : DeploymentResult = {
125
- success : false ,
126
- contractAddress : undefined ,
127
- transactionHash : '0x' + Math . random ( ) . toString ( 16 ) . substr ( 2 , 64 ) ,
128
- gasUsed : args . gasLimit || 1000000 ,
129
+
130
+
131
+ console . log ( 'txReturn' , txReturn )
132
+ const receipt = ( txReturn . txResult . receipt as TransactionReceipt )
133
+ const result : DeploymentResult = {
134
+ transactionHash : web3 . utils . bytesToHex ( receipt . transactionHash ) ,
135
+ gasUsed : web3 . utils . toNumber ( receipt . gasUsed ) ,
129
136
effectiveGasPrice : args . gasPrice || '20000000000' ,
130
- blockNumber : Math . floor ( Math . random ( ) * 1000000 ) ,
131
- logs : [ ]
137
+ blockNumber : web3 . utils . toNumber ( receipt . blockNumber ) ,
138
+ logs : receipt . logs ,
139
+ contractAddress : receipt . contractAddress ,
140
+ success : receipt . status === BigInt ( 1 ) ? true : false
132
141
} ;
133
142
134
- // Mock implementation - in real implementation, use Remix deployment API
135
- mockResult . success = true ;
136
- mockResult . contractAddress = '0x' + Math . random ( ) . toString ( 16 ) . substr ( 2 , 40 ) ;
143
+ plugin . call ( 'udapp' , 'addInstance' , result . contractAddress , data . abi , args . contractName , data )
137
144
138
- return this . createSuccessResult ( mockResult ) ;
145
+ return this . createSuccessResult ( result ) ;
139
146
140
147
} catch ( error ) {
141
148
return this . createErrorResult ( `Deployment failed: ${ error . message } ` ) ;
@@ -161,6 +168,11 @@ export class CallContractHandler extends BaseToolHandler {
161
168
inputSchema = {
162
169
type : 'object' ,
163
170
properties : {
171
+ contractName : {
172
+ type : 'string' ,
173
+ description : 'Contract name' ,
174
+ pattern : '^0x[a-fA-F0-9]{40}$'
175
+ } ,
164
176
address : {
165
177
type : 'string' ,
166
178
description : 'Contract address' ,
@@ -238,43 +250,65 @@ export class CallContractHandler extends BaseToolHandler {
238
250
239
251
async execute ( args : CallContractArgs , plugin : Plugin ) : Promise < IMCPToolResult > {
240
252
try {
241
- // Find the method in ABI
242
- const method = args . abi . find ( ( item : any ) =>
243
- item . name === args . methodName && item . type === 'function'
244
- ) ;
245
-
246
- if ( ! method ) {
247
- return this . createErrorResult ( `Method '${ args . methodName } ' not found in ABI` ) ;
248
- }
249
-
250
- // Get accounts
251
- const accounts = await this . getAccounts ( plugin ) ;
252
- const callAccount = args . account || accounts [ 0 ] ;
253
-
254
- if ( ! callAccount ) {
255
- return this . createErrorResult ( 'No account available for contract call' ) ;
253
+ const funcABI = args . abi . find ( ( item : any ) => item . name === args . methodName && item . type === 'function' )
254
+ const isView = funcABI . stateMutability === 'view' || funcABI . stateMutability === 'pure' ;
255
+ let txReturn
256
+ try {
257
+ txReturn = await new Promise ( async ( resolve , reject ) => {
258
+ const params = funcABI . type !== 'fallback' ? args . args . join ( ',' ) : ''
259
+ plugin . call ( 'blockchain' , 'runOrCallContractMethod' ,
260
+ args . contractName ,
261
+ args . abi ,
262
+ funcABI ,
263
+ undefined ,
264
+ args . args ? args : [ ] ,
265
+ args . address ,
266
+ params ,
267
+ isView ,
268
+ ( msg ) => {
269
+ // logMsg
270
+ } ,
271
+ ( msg ) => {
272
+ // logCallback
273
+ } ,
274
+ ( returnValue ) => {
275
+ // outputCb
276
+ } ,
277
+ ( network , tx , gasEstimation , continueTxExecution , cancelCb ) => {
278
+ // confirmationCb
279
+ continueTxExecution ( null )
280
+ } ,
281
+ ( error , continueTxExecution , cancelCb ) => {
282
+ // continueCb
283
+ continueTxExecution ( )
284
+ } ,
285
+ ( okCb , cancelCb ) => {
286
+ // promptCb
287
+ } ,
288
+ ( error , cancelCb ) => {
289
+ // promptCb
290
+ } ,
291
+ ( error , { txResult, address, returnValue} ) => {
292
+ if ( error ) return reject ( error )
293
+ resolve ( { txResult, address, returnValue} )
294
+ } ,
295
+ )
296
+ } )
297
+ } catch ( e ) {
298
+ return this . createErrorResult ( `Deployment error: ${ e . message } ` ) ;
256
299
}
257
300
258
- // Determine if this is a view function or transaction
259
- const isView = method . stateMutability === 'view' || method . stateMutability === 'pure' ;
260
-
261
301
// TODO: Execute contract call via Remix Run Tab API
262
- const mockResult : ContractInteractionResult = {
263
- success : true ,
264
- result : isView ? 'mock_view_result' : undefined ,
265
- transactionHash : isView ? undefined : '0x' + Math . random ( ) . toString ( 16 ) . substr ( 2 , 64 ) ,
266
- gasUsed : isView ? 0 : ( args . gasLimit || 100000 ) ,
267
- logs : [ ]
302
+ const receipt = ( txReturn . txResult . receipt as TransactionReceipt )
303
+ const result : ContractInteractionResult = {
304
+ result : txReturn . returnValue ,
305
+ transactionHash : isView ? undefined : web3 . utils . bytesToHex ( receipt . transactionHash ) ,
306
+ gasUsed : web3 . utils . toNumber ( receipt . gasUsed ) ,
307
+ logs : receipt . logs ,
308
+ success : receipt . status === BigInt ( 1 ) ? true : false
268
309
} ;
269
310
270
- if ( isView ) {
271
- mockResult . result = `View function result for ${ args . methodName } ` ;
272
- } else {
273
- mockResult . transactionHash = '0x' + Math . random ( ) . toString ( 16 ) . substr ( 2 , 64 ) ;
274
- mockResult . gasUsed = args . gasLimit || 100000 ;
275
- }
276
-
277
- return this . createSuccessResult ( mockResult ) ;
311
+ return this . createSuccessResult ( result ) ;
278
312
279
313
} catch ( error ) {
280
314
return this . createErrorResult ( `Contract call failed: ${ error . message } ` ) ;
0 commit comments