Created
February 18, 2025 14:21
-
-
Save yann300/f99d8270ad52a759037eeb8eeb32621a 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=
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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); | |
| } | |
| } | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { callMistralAI } from "./mistral-api" | |
| let message = 'Write a soulbound ERC721 contract and mocha/chai unit tests.' | |
| const contractImpl = `@openzeppelin/contracts/token/ERC721/ERC721.sol` | |
| const params = { | |
| optimize: false, | |
| evmVersion: null, | |
| language: 'Solidity', | |
| version: '0.8.28+commit.7893614a' | |
| } | |
| let messages = [] | |
| const mmaxNbIteration = 6 | |
| let iteration = 0 | |
| let addedContent = {} | |
| const docMigration = `ERC20, ERC721, and ERC1155 | |
| These breaking changes will require modifications to ERC20, ERC721, and ERC1155 contracts, since the _afterTokenTransfer and _beforeTokenTransfer functions were removed. | |
| Thus, any customization made through those hooks should now be done overriding the new _update function instead. | |
| Minting and burning are implemented by _update and customizations should be done by overriding this function as well. _transfer, _mint and _burn are no longer virtual (meaning they are not overridable) to guard against possible inconsistencies. | |
| For example, a contract using ERC20's _beforeTokenTransfer hook would have to be changed in the following way. | |
| More about ERC721 | |
| In the case of ERC721, the _update function does not include a from parameter, as the sender is implicitly the previous owner of the tokenId. The address of this previous owner is returned by the _update function, so it can be used for a posteriori checks. In addition to to and tokenId, a third parameter (auth) is present in this function. This parameter enabled an optional check that the caller/spender is approved to do the transfer. This check cannot be performed after the transfer (because the transfer resets the approval), and doing it before _update would require a duplicate call to _ownerOf. | |
| In this logic of removing hidden SLOADs, the _isApprovedOrOwner function was removed in favor of a new _isAuthorized function. Overrides that used to target the _isApprovedOrOwner should now be performed on the _isAuthorized function. Calls to _isApprovedOrOwner that preceded a call to _transfer, _burn or _approve should be removed in favor of using the auth argument in _update and _approve. This is showcased in ERC721Burnable.burn and in ERC721Wrapper.withdrawTo. | |
| The _exists function was removed. Calls to this function can be replaced by _ownerOf(tokenId) != address(0).` | |
| let json_mes | |
| const generate = 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) | |
| let compiled = false | |
| 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 | |
| console.log(data.errors) | |
| const error = data.errors.find((error) => error.type !== 'Warning') | |
| if (data.errors && data.errors.length && error) { | |
| iteration++ | |
| const imports = extractImportPaths(file.content) | |
| const importsToAdd = {} | |
| for (const file of imports) { | |
| const content = await remix.call('contentImport', 'resolve', file) | |
| if (!addedContent[file]) { | |
| addedContent[file] = content | |
| importsToAdd[file] = content | |
| } | |
| } | |
| // console.log(JSON.stringify(data.errors, null, '\t')) | |
| const dataError = await callMistralAI([{ | |
| role: 'user', | |
| content: `How to fix that solidity error: ${error.formattedMessage}` | |
| }]) | |
| // console.log(dataError.choices[0].message.content) | |
| messages.push({ | |
| role: 'user', | |
| content: `This is not compiling. | |
| - Imports that weren't previously included: ${JSON.stringify(importsToAdd)}. | |
| - Compilation errors: ${data.errors.map((e) => e.formattedMessage)}. | |
| - ${dataError.choices[0].message.content} | |
| - Here is a list of known error and a way to fix it: | |
| - "Function has override specified but does not override anything" . This means this function is probably not existing anymore in ${contractImpl}. In that case, don't use that function and try using a function from ${contractImpl} that you could override. | |
| - Fix compilation error.` | |
| }) | |
| remix.call('fileManager', 'writeFile', 'scripts/messages.json', JSON.stringify(messages, null, '\t')) | |
| await generate() | |
| return | |
| } else { | |
| console.log('compiled successfully...') | |
| console.log('set compilation config...') | |
| await remix.call('solidity' as any, 'setCompilerConfig', params) | |
| await remix.call('solidity' as any, 'compile', file.fileName) | |
| await new Promise((resolve) => setTimeout(() => { resolve({}) }, 5000)) | |
| compiled = true | |
| } | |
| } | |
| } | |
| console.log('reach compiled') | |
| if (compiled) { | |
| console.log('running tests...') | |
| 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; | |
| } | |
| const run = async () => { | |
| // console.log(messages) | |
| addedContent[contractImpl] = await remix.call('contentImport', 'resolve', contractImpl) | |
| messages.push( | |
| { | |
| role: 'user', | |
| content: `- Compiler configuration: ${JSON.stringify(params)}. | |
| - Use the following implementation reference to generate the code: ${JSON.stringify(addedContent[contractImpl])}. | |
| - Some more information: ${docMigration} | |
| - ${message}` | |
| }) | |
| await generate() | |
| } | |
| const runExisting = async () => { | |
| messages = JSON.parse(await remix.call('fileManager', 'readFile', 'scripts/messages.json')) | |
| await generate() | |
| } | |
| runExisting().catch((e) => console.error(e.message)) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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