Skip to content

Commit d270d42

Browse files
author
ci-bot
committed
Merge branch 'master' of https://github.com/remix-project-org/remix-project into fixci3
2 parents 0365dbd + 637a219 commit d270d42

File tree

15 files changed

+183
-167
lines changed

15 files changed

+183
-167
lines changed

.circleci/config.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,6 @@ jobs:
621621
path: ./reports/screenshots
622622

623623
deploy-build:
624-
625624
docker:
626625
- image: cimg/node:20.19.0-browsers
627626
resource_class: xlarge

apps/remix-ide/src/app/tabs/analysis-tab.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ var EventManager = require('../../lib/events')
1111
const profile = {
1212
name: 'solidityStaticAnalysis',
1313
displayName: 'Solidity Analyzers',
14-
methods: [],
14+
methods: ['analyze'],
1515
events: [],
1616
icon: 'assets/img/staticAnalysis.webp',
1717
description: 'Analyze your code using Remix, Solhint and Slither.',
@@ -70,6 +70,10 @@ export default class AnalysisTab extends ViewPlugin {
7070
})
7171
}
7272

73+
analyze () {
74+
this.emit('analyze')
75+
}
76+
7377
setDispatch (dispatch) {
7478
this.dispatch = dispatch
7579
this.renderComponent()

libs/remix-ai-core/src/inferencers/mcp/mcpInferencer.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -975,9 +975,12 @@ export class MCPInferencer extends RemoteInferencer implements ICompletions, IGe
975975
}
976976
}
977977

978+
// Sort resources from less relevant to most relevant (ascending by score) -> contex reduction when sending payload
979+
const sortedResources = selectedResources.sort((a, b) => a.score - b.score);
980+
978981
// Build context from selected resources
979982
let mcpContext = "";
980-
for (const scoredResource of selectedResources) {
983+
for (const scoredResource of sortedResources) {
981984
const { resource, serverName } = scoredResource;
982985

983986
try {
@@ -1088,6 +1091,7 @@ export class MCPInferencer extends RemoteInferencer implements ICompletions, IGe
10881091
const mcpToolCall = this.convertLLMToolCallToMCP(llmToolCall);
10891092
const result = await this.executeToolForLLM(mcpToolCall);
10901093
console.log(`[MCP] Tool ${mcpToolCall.name} executed successfully`);
1094+
console.log("[MCP] Tool result", result);
10911095

10921096
// Extract full text content from MCP result
10931097
const extractContent = (mcpResult: any): string => {

libs/remix-ai-core/src/inferencers/remote/remoteInference.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,37 @@ export class RemoteInferencer implements ICompletions, IGeneration {
2121
this.event = new EventEmitter()
2222
}
2323

24+
protected sanitizePromptByteSize(prompt: string, maxBytes: number = 50000): string {
25+
const encoder = new TextEncoder();
26+
const promptBytes = encoder.encode(prompt); // rough estimation, real size might be 10% more
27+
28+
if (promptBytes.length <= maxBytes) {
29+
return prompt;
30+
}
31+
32+
let trimmedPrompt = prompt;
33+
let currentBytes = promptBytes.length;
34+
35+
while (currentBytes > maxBytes && trimmedPrompt.length > 0) {
36+
// Remove characters from the beginning (1% at a time for efficiency)
37+
const charsToRemove = Math.max(1, Math.floor(trimmedPrompt.length * 0.01));
38+
trimmedPrompt = trimmedPrompt.substring(charsToRemove);
39+
currentBytes = encoder.encode(trimmedPrompt).length;
40+
}
41+
42+
console.warn(`[RemoteInferencer] Prompt exceeded ${maxBytes} bytes. Trimmed from ${promptBytes.length} to ${currentBytes} bytes.`);
43+
return trimmedPrompt;
44+
}
45+
2446
async _makeRequest(payload, rType:AIRequestType){
2547
this.event.emit("onInference")
2648
const requestURL = rType === AIRequestType.COMPLETION ? this.completion_url : this.api_url
2749

50+
// Sanitize prompt in payload if it exists
51+
if (payload.prompt) {
52+
payload.prompt = this.sanitizePromptByteSize(payload.prompt);
53+
}
54+
2855
try {
2956
const options = AIRequestType.COMPLETION ? { headers: { 'Content-Type': 'application/json', }, timeout: 3000 } : { headers: { 'Content-Type': 'application/json', } }
3057
const result = await axios.post(requestURL, payload, options)
@@ -58,6 +85,12 @@ export class RemoteInferencer implements ICompletions, IGeneration {
5885

5986
async _streamInferenceRequest(payload, rType:AIRequestType){
6087
let resultText = ""
88+
89+
// Sanitize prompt in payload if it exists
90+
if (payload.prompt) {
91+
payload.prompt = this.sanitizePromptByteSize(payload.prompt);
92+
}
93+
6194
try {
6295
this.event.emit('onInference')
6396
const requestURL = rType === AIRequestType.COMPLETION ? this.completion_url : this.api_url
@@ -131,7 +164,7 @@ export class RemoteInferencer implements ICompletions, IGeneration {
131164
}
132165

133166
async answer(prompt, options:IParams=GenerationParams): Promise<any> {
134-
options.chatHistory = options.provider === 'anthropic' || options.provider === 'mistralai' ? buildChatPrompt() : []
167+
options.chatHistory = buildChatPrompt()
135168
const payload = { 'prompt': prompt, "endpoint":"answer", ...options }
136169
if (options.stream_result) return this._streamInferenceRequest(payload, AIRequestType.GENERAL)
137170
else return this._makeRequest(payload, AIRequestType.GENERAL)

libs/remix-ai-core/src/remix-mcp-server/handlers/CodeAnalysisHandler.ts

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -64,43 +64,13 @@ export class SolidityScanHandler extends BaseToolHandler {
6464
// Use the core scanning function from remix-core-plugin
6565
const scanReport = await performSolidityScan(plugin, args.filePath);
6666

67-
// Process scan results into structured format
68-
const findings = [];
69-
70-
for (const template of scanReport.multi_file_scan_details || []) {
71-
if (template.metric_wise_aggregated_findings?.length) {
72-
for (const details of template.metric_wise_aggregated_findings) {
73-
for (const finding of details.findings) {
74-
findings.push({
75-
metric: details.metric_name,
76-
severity: details.severity || 'unknown',
77-
title: finding.title || details.metric_name,
78-
description: finding.description || details.description,
79-
lineStart: finding.line_nos_start?.[0],
80-
lineEnd: finding.line_nos_end?.[0],
81-
file: template.file_name,
82-
recommendation: finding.recommendation
83-
});
84-
}
85-
}
86-
}
87-
}
88-
8967
const result = {
9068
success: true,
9169
fileName,
9270
scanCompletedAt: new Date().toISOString(),
93-
totalFindings: findings.length,
94-
findings,
95-
summary: {
96-
critical: findings.filter(f => f.severity === 'critical').length,
97-
high: findings.filter(f => f.severity === 'high').length,
98-
medium: findings.filter(f => f.severity === 'medium').length,
99-
low: findings.filter(f => f.severity === 'low').length,
100-
informational: findings.filter(f => f.severity === 'informational').length
101-
}
71+
multi_file_scan_details: scanReport.multi_file_scan_details,
72+
multi_file_scan_summary: scanReport.multi_file_scan_summary
10273
};
103-
10474
return this.createSuccessResult(result);
10575

10676
} catch (error) {

libs/remix-ai-core/src/remix-mcp-server/handlers/CompilationHandler.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ export class SolidityCompileHandler extends BaseToolHandler {
7676
try {
7777
let compilerConfig: any = {};
7878

79+
await plugin.call('sidePanel', 'showContent', 'solidity' )
80+
7981
try {
8082
// Try to get existing compiler config
8183
compilerConfig = await plugin.call('solidity' as any , 'getCurrentCompilerConfig');
@@ -373,6 +375,57 @@ export class CompileWithHardhatHandler extends BaseToolHandler {
373375
}
374376
}
375377

378+
/**
379+
* Compile with Foundry Tool Handler
380+
*/
381+
export class CompileWithFoundryHandler extends BaseToolHandler {
382+
name = 'compile_with_foundry';
383+
description = 'Compile using Foundry framework';
384+
inputSchema = {
385+
type: 'object',
386+
properties: {
387+
configPath: {
388+
type: 'string',
389+
description: 'Path to foundry.toml file',
390+
default: 'foundry.toml'
391+
}
392+
}
393+
};
394+
395+
getPermissions(): string[] {
396+
return ['compile:foundry'];
397+
}
398+
399+
validate(args: { configPath?: string }): boolean | string {
400+
const types = this.validateTypes(args, { configPath: 'string' });
401+
if (types !== true) return types;
402+
403+
return true;
404+
}
405+
406+
async execute(args: { configPath?: string }, plugin: Plugin): Promise<IMCPToolResult> {
407+
try {
408+
const configPath = args.configPath || 'foundry.toml';
409+
410+
// Check if hardhat config exists
411+
const exists = await plugin.call('fileManager', 'exists', configPath);
412+
if (!exists) {
413+
return this.createErrorResult(`Foundry config file not found: ${configPath}`);
414+
}
415+
416+
const result = await plugin.call('solidity' as any , 'compileWithFoundry', configPath);
417+
418+
return this.createSuccessResult({
419+
success: true,
420+
message: 'Compiled with Foundry successfully',
421+
result: result
422+
});
423+
} catch (error) {
424+
return this.createErrorResult(`Foundry compilation failed: ${error.message}`);
425+
}
426+
}
427+
}
428+
376429
/**
377430
* Compile with Truffle Tool Handler
378431
*/
@@ -502,6 +555,14 @@ export function createCompilationTools(): RemixToolDefinition[] {
502555
permissions: ['compile:hardhat'],
503556
handler: new CompileWithHardhatHandler()
504557
},
558+
{
559+
name: 'compile_with_foundry',
560+
description: 'Compile using Foundry framework',
561+
inputSchema: new CompileWithFoundryHandler().inputSchema,
562+
category: ToolCategory.COMPILATION,
563+
permissions: ['compile:foundry'],
564+
handler: new CompileWithFoundryHandler()
565+
},
505566
{
506567
name: 'compile_with_truffle',
507568
description: 'Compile using Truffle framework',

libs/remix-ai-core/src/remix-mcp-server/handlers/DeploymentHandler.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import { getContractData } from '@remix-project/core-plugin'
2222
import type { TxResult } from '@remix-project/remix-lib';
2323
import { BrowserProvider } from "ethers"
2424
import { toNumber, ethers } from 'ethers'
25+
import { execution } from '@remix-project/remix-lib';
26+
const { txFormat, txHelper: { makeFullTypeDefinition } } = execution;
2527

2628
/**
2729
* Deploy Contract Tool Handler
@@ -96,6 +98,9 @@ export class DeployContractHandler extends BaseToolHandler {
9698

9799
async execute(args: DeployContractArgs, plugin: Plugin): Promise<IMCPToolResult> {
98100
try {
101+
102+
await plugin.call('sidePanel', 'showContent', 'udapp' )
103+
99104
// Get compilation result to find contract
100105
const compilerAbstract = await plugin.call('compilerArtefacts', 'getCompilerAbstract', args.file) as any;
101106
const data = getContractData(args.contractName, compilerAbstract)
@@ -299,8 +304,9 @@ export class CallContractHandler extends BaseToolHandler {
299304

300305
// TODO: Execute contract call via Remix Run Tab API
301306
const receipt = (txReturn.txResult.receipt)
307+
console.log('function call transaction payload:', txReturn)
302308
const result: ContractInteractionResult = {
303-
result: txReturn.returnValue,
309+
result: isView ? txFormat.decodeResponse(txReturn.txResult.result, funcABI) : txReturn.returnValue,
304310
transactionHash: isView ? txReturn.txResult.transactionHash : receipt.hash,
305311
gasUsed: isView ? 0 : receipt.gasUsed,
306312
logs: isView ? undefined : receipt.logs,
@@ -536,6 +542,8 @@ export class SetExecutionEnvironmentHandler extends BaseToolHandler {
536542
}
537543

538544
async execute(args: { environment: string }, plugin: Plugin): Promise<IMCPToolResult> {
545+
await plugin.call('sidePanel', 'showContent', 'udapp' )
546+
539547
try {
540548
const providers = await plugin.call('blockchain', 'getAllProviders')
541549
const provider = Object.keys(providers).find((p) => p === args.environment)
@@ -724,6 +732,8 @@ export class SetSelectedAccountHandler extends BaseToolHandler {
724732
}
725733

726734
async execute(args: { address: string }, plugin: Plugin): Promise<IMCPToolResult> {
735+
await plugin.call('sidePanel', 'showContent', 'udapp' )
736+
727737
try {
728738
// Set the selected account through the udapp plugin
729739
await plugin.call('udapp' as any, 'setAccount', args.address);

libs/remix-ai-core/src/remix-mcp-server/handlers/TutorialsHandler.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import {
1111
import { Plugin } from '@remixproject/engine';
1212

1313
/**
14-
* Solidity Scan Tool Handler
15-
* Analyzes Solidity code for security vulnerabilities and code quality issues
14+
* Learneth tutorial Tool Handler
15+
* Starts a tutorial using learneth
1616
*/
1717
export class TutorialsHandler extends BaseToolHandler {
1818
name = 'tutorials';
@@ -40,7 +40,9 @@ export class TutorialsHandler extends BaseToolHandler {
4040

4141
async execute(args: { tutorialId: string }, plugin: Plugin): Promise<IMCPToolResult> {
4242
try {
43-
plugin.call('LearnEth', 'startTutorial', "remix-project-org/remix-workshops", "master", args.tutorialId)
43+
await plugin.call('LearnEth', 'startTutorial', "remix-project-org/remix-workshops", "master", args.tutorialId)
44+
await plugin.call('sidePanel', 'showContent', 'LearnEth' )
45+
return this.createSuccessResult(`Tutorial ${args.tutorialId} started successfully.`);
4446
} catch (error) {
4547
return this.createErrorResult(`Starting tutorial failed: ${error.message}`);
4648
}

libs/remix-ai-core/src/remix-mcp-server/providers/CompilationResourceProvider.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,9 +219,22 @@ export class CompilationResourceProvider extends BaseResourceProvider {
219219
try {
220220
const compiledContracts = await plugin.call('compilerArtefacts', 'getAllContractDatas')
221221

222+
// Filter to only include abi and metadata for each contract
223+
const filteredContracts = {};
224+
for (const [fileName, fileContracts] of Object.entries(compiledContracts)) {
225+
filteredContracts[fileName] = {};
226+
for (const [contractName, contractData] of Object.entries(fileContracts as any)) {
227+
const contract = contractData as any;
228+
filteredContracts[fileName][contractName] = {
229+
abi: contract.abi,
230+
metadata: contract.metadata
231+
};
232+
}
233+
}
234+
222235
return this.createJsonContent('compilation://contracts', {
223-
compiledContracts,
224-
count: Object.keys(compiledContracts).length,
236+
compiledContracts: filteredContracts,
237+
count: Object.keys(filteredContracts).length,
225238
generatedAt: new Date().toISOString()
226239
});
227240
} catch (error) {

libs/remix-ai-core/src/remix-mcp-server/providers/TutorialsResourceProvider.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export class TutorialsResourceProvider extends BaseResourceProvider {
4343
}
4444

4545
async getResourceContent(uri: string, plugin: Plugin): Promise<IMCPResourceContent> {
46+
console.log('Getting resource content for URI:', uri);
4647
if (uri === 'tutorials://list') {
4748
return this.getTutorialsList(plugin);
4849
}
@@ -57,6 +58,7 @@ export class TutorialsResourceProvider extends BaseResourceProvider {
5758
private async getTutorialsList(plugin: Plugin): Promise<IMCPResourceContent> {
5859
try {
5960
const tutorials = await axios('https://raw.githubusercontent.com/remix-project-org/remix-workshops/refs/heads/json_desc/config-properties.json')
61+
console.log(tutorials)
6062
return this.createJsonContent('tutorials://list', tutorials);
6163
} catch (error) {
6264
return this.createTextContent('tutorials://list', `Error getting tutorials: ${error.message}`);

0 commit comments

Comments
 (0)