Skip to content

Instantly share code, notes, and snippets.

@yann300
Created February 17, 2025 16:49
Show Gist options
  • Select an option

  • Save yann300/1917a92bb22c3d0aef0e98d2f082cd2c to your computer and use it in GitHub Desktop.

Select an option

Save yann300/1917a92bb22c3d0aef0e98d2f082cd2c to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.8.28+commit.7893614a.js&optimize=false&runs=NaN&gist=
// Replace with your actual API key and agent ID
const API_KEY = 'mRU8ku8wCiVYm1aDQWW6tnfsWY7qu0jm';
const AGENT_ID = 'ag:90ac4fd0:20250208:untitled-agent:3cb5361b';
const API_URL = 'https://api.mistral.ai/v1/agents/completions';
// Function to call the Mistral AI endpoint
export async function callMistralAI(messages) {
try {
const response = await fetch(API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${API_KEY}`
},
body: JSON.stringify({
agent_id: AGENT_ID,
messages: messages,
// Add other parameters as needed
})
});
if (!response.ok) {
// Log the response status and text for debugging
const errorText = await response.text();
throw new Error(`HTTP error! status: ${response.status}, response: ${errorText}`);
}
const data = await response.json();
// console.log('Response from Mistral AI:', data);
return data;
} catch (error) {
console.error('Error calling Mistral AI:', error);
}
}
import { callMistralAI } from "./mistral-api"
const message = 'Write a soulbound ERC721 contract and mocha/chai unit tests.'
const params = {
optimize: false,
evmVersion: null,
language: 'Solidity',
version: '0.8.28+commit.7893614a'
}
// Example usage
let messages = [
{
role: 'user',
content: `${message} using the following compiler config: ${JSON.stringify(params)}`
}
];
const mmaxNbIteration = 3
let iteration = 0
console.log(messages)
let json_mes
const run = async () => {
if (iteration > mmaxNbIteration) {
console.error('failed compiling')
return
}
console.log('running iteration', iteration)
json_mes = null
const data = await callMistralAI(messages)
const mes = data.choices[0].message.content.replace('```json\n', '').replace('```', '')
json_mes = JSON.parse(mes)
for (const index in json_mes) {
const file = json_mes[index]
await remix.call('fileManager', 'writeFile', file.fileName, file.content)
if (file.fileName.endsWith('.sol')) {
console.log(`compiling ${file.fileName}`)
const contract = {}
contract[file.fileName] = { content : file.content }
const result = await remix.call('solidity' as any, 'compileWithParameters', contract, params)
let data = result.data
if (data.errors && data.errors.length && data.errors.find((error) => error.type !== 'Warning')) {
iteration++
const imports = extractImportPaths(file.content)
let contentImports = {}
for (const file of imports) {
contentImports[file] = await remix.call('contentImport', 'resolve', file)
}
messages = [{
role: 'user',
content: `Here is the code you generated: ${file.content} . Here are the dependencies: ${JSON.stringify(contentImports)} . Here are compilation errors: ${JSON.stringify(data.errors)}. Please fix.`
}]
console.log(contentImports)
await run()
} else {
console.log('compiled successfully, running tests...')
console.log('reset compilation config...')
await remix.call('solidity' as any, 'setCompilerConfig', params)
await remix.call('solidity' as any, 'compile', file.fileName)
const scripts = json_mes.find((item) => item.fileName.endsWith('.ts'))
remix.call('scriptRunnerBridge' as any, 'execute', scripts.content, scripts.fileName)
}
}
}
}
function extractImportPaths(text) {
// Define the regex pattern to match import paths
const regex = /import\s*\"([^\"]+)\"\s*;/g;
const paths = [];
let match;
// Use the regex to find all matches in the text
while ((match = regex.exec(text)) !== null) {
// Push the captured path to the paths array
paths.push(match[1]);
}
return paths;
}
run().catch((e) => { console.error(e.message) })
export interface CompilationFileSources {
[fileName: string]: {
keccak256?: string;
content: string;
urls?: string[];
};
}
export interface SourceWithTarget {
sources?: CompilationFileSources;
target?: string | null | undefined;
}
export interface CompilationResult {
/** not present if no errors/warnings were encountered */
errors?: CompilationError[];
/** This contains the file-level outputs. In can be limited/filtered by the outputSelection settings */
sources: {
[contractName: string]: CompilationSource;
};
/** This contains the contract-level outputs. It can be limited/filtered by the outputSelection settings */
contracts: {
/** If the language used has no contract names, this field should equal to an empty string. */
[fileName: string]: {
[contract: string]: CompiledContract;
};
};
}
export interface lastCompilationResult {
data: CompilationResult | null;
source: SourceWithTarget | null | undefined;
}
export interface CompilationError {
/** Location within the source file */
sourceLocation?: {
file: string;
start: number;
end: number;
};
/** Error type */
type: CompilationErrorType;
/** Component where the error originated, such as "general", "ewasm", etc. */
component: 'general' | 'ewasm' | string;
severity: 'error' | 'warning';
message: string;
/** the message formatted with source location */
formattedMessage?: string;
}
declare type CompilationErrorType = 'JSONError' | 'IOError' | 'ParserError' | 'DocstringParsingError' | 'SyntaxError' | 'DeclarationError' | 'TypeError' | 'UnimplementedFeatureError' | 'InternalCompilerError' | 'Exception' | 'CompilerError' | 'FatalError' | 'Warning';
export interface CompilationSource {
/** Identifier of the source (used in source maps) */
id: number;
/** The AST object */
ast: AstNode;
/** The legacy AST object */
legacyAST: AstNodeLegacy;
}
export interface AstNode {
absolutePath?: string;
exportedSymbols?: Object;
id: number;
nodeType: string;
nodes?: Array<AstNode>;
src: string;
literals?: Array<string>;
file?: string;
scope?: number;
sourceUnit?: number;
symbolAliases?: Array<string>;
[x: string]: any;
}
export interface AstNodeLegacy {
id: number;
name: string;
src: string;
children?: Array<AstNodeLegacy>;
attributes?: AstNodeAtt;
}
export interface AstNodeAtt {
operator?: string;
string?: null;
type?: string;
value?: string;
constant?: boolean;
name?: string;
public?: boolean;
exportedSymbols?: Object;
argumentTypes?: null;
absolutePath?: string;
[x: string]: any;
}
export interface CompiledContract {
/** The Ethereum Contract ABI. If empty, it is represented as an empty array. */
abi: ABIDescription[];
metadata: string;
/** User documentation (natural specification) */
userdoc: UserDocumentation;
/** Developer documentation (natural specification) */
devdoc: DeveloperDocumentation;
/** Intermediate representation (string) */
ir: string;
/** EVM-related outputs */
evm: {
assembly: string;
legacyAssembly: {};
/** Bytecode and related details. */
bytecode: BytecodeObject;
deployedBytecode: BytecodeObject;
/** The list of function hashes */
methodIdentifiers: {
[functionIdentifier: string]: string;
};
gasEstimates: {
creation: {
codeDepositCost: string;
executionCost: 'infinite' | string;
totalCost: 'infinite' | string;
};
external: {
[functionIdentifier: string]: string;
};
internal: {
[functionIdentifier: string]: 'infinite' | string;
};
};
};
/** eWASM related outputs */
ewasm: {
/** S-expressions format */
wast: string;
/** Binary format (hex string) */
wasm: string;
};
}
export declare type ABIDescription = FunctionDescription | EventDescription;
export interface FunctionDescription {
/** Type of the method. default is 'function' */
type?: 'function' | 'constructor' | 'fallback';
/** The name of the function. Constructor and fallback functions never have a name */
name?: string;
/** List of parameters of the method. Fallback functions don’t have inputs. */
inputs?: ABIParameter[];
/** List of the output parameters for the method, if any */
outputs?: ABIParameter[];
/** State mutability of the method */
stateMutability: 'pure' | 'view' | 'nonpayable' | 'payable';
/** true if function accepts Ether, false otherwise. Default is false */
payable?: boolean;
/** true if function is either pure or view, false otherwise. Default is false */
constant?: boolean;
}
export interface EventDescription {
type: 'event';
name: string;
inputs: ABIParameter & {
/** true if the field is part of the log’s topics, false if it one of the log’s data segment. */
indexed: boolean;
}[];
/** true if the event was declared as anonymous. */
anonymous: boolean;
}
export interface ABIParameter {
/** The name of the parameter */
name: string;
/** The canonical type of the parameter */
type: ABITypeParameter;
/** Used for tuple types */
components?: ABIParameter[];
}
export declare type ABITypeParameter = 'uint' | 'uint[]' | 'int' | 'int[]' | 'address' | 'address[]' | 'bool' | 'bool[]' | 'fixed' | 'fixed[]' | 'ufixed' | 'ufixed[]' | 'bytes' | 'bytes[]' | 'function' | 'function[]' | 'tuple' | 'tuple[]' | string;
export interface UserDocumentation {
methods: UserMethodList;
notice: string;
}
export declare type UserMethodList = {
[functionIdentifier: string]: UserMethodDoc;
} & {
'constructor'?: string;
};
export interface UserMethodDoc {
notice: string;
}
export interface DeveloperDocumentation {
author: string;
title: string;
details: string;
methods: DevMethodList;
}
export interface DevMethodList {
[functionIdentifier: string]: DevMethodDoc;
}
export interface DevMethodDoc {
author: string;
details: string;
return: string;
returns: {
[param: string]: string;
};
params: {
[param: string]: string;
};
}
export interface BytecodeObject {
/** The bytecode as a hex string. */
object: string;
/** Opcodes list */
opcodes: string;
/** The source mapping as a string. See the source mapping definition. */
sourceMap: string;
/** If given, this is an unlinked object. */
linkReferences?: {
[contractName: string]: {
/** Byte offsets into the bytecode. */
[library: string]: {
start: number;
length: number;
}[];
};
};
}
export {};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment