diff --git a/apps/remix-ide-e2e/src/tests/vyper_api.test.ts b/apps/remix-ide-e2e/src/tests/vyper_api.test.ts index 717edd43c9..e38583a9ae 100644 --- a/apps/remix-ide-e2e/src/tests/vyper_api.test.ts +++ b/apps/remix-ide-e2e/src/tests/vyper_api.test.ts @@ -27,7 +27,7 @@ module.exports = { .frameParent() .clickLaunchIcon('filePanel') .waitForElementVisible({ - selector: "//*[@data-id='workspacesSelect' and contains(.,'snekmate')]", + selector: "//*[@data-id='workspacesSelect' and contains(.,'vyper')]", locateStrategy: 'xpath', timeout: 120000 }) @@ -36,37 +36,38 @@ module.exports = { locateStrategy: 'xpath', timeout: 120000 }) + .openFile('examples') + .openFile('examples/auctions') + .openFile('examples/auctions/blind_auction.vy') }, - // 'Add vyper file to run tests #group1': function (browser: NightwatchBrowser) { - // browser.addFile('TestBallot.sol', sources[0]['TestBallot.sol']) + + // '@sources': () => sources, + // 'Context menu click to compile blind_auction should succeed #group1': function (browser: NightwatchBrowser) { + // browser + // // .click('*[data-id="treeViewLitreeViewItemblind_auction.vy"]') + // // .rightClick('*[data-id="treeViewLitreeViewItemblind_auction.vy"]') + // // .waitForElementPresent('[data-id="contextMenuItemvyper"]') + // // .click('[data-id="contextMenuItemvyper"]') + // .clickLaunchIcon('vyper') + // // @ts-ignore + // .frame(0) + // .waitForElementVisible({ + // selector:'[data-id="compilation-details"]', + // timeout: 120000 + // }) + // .click('[data-id="compilation-details"]') + // .frameParent() + // .waitForElementVisible('[data-id="copy-abi"]') + // .waitForElementVisible({ + // selector: "//*[@class='variable-value' and contains(.,'highestBidder')]", + // locateStrategy: 'xpath', + // }) // }, - '@sources': () => sources, - 'Context menu click to compile blind_auction should succeed #group1': function (browser: NightwatchBrowser) { - browser - .addFileSnekmate('blind_auction.vy', sources[0]['blindAuction']) - .click('*[data-id="treeViewLitreeViewItemblind_auction.vy"]') - .rightClick('*[data-id="treeViewLitreeViewItemblind_auction.vy"]') - .waitForElementPresent('[data-id="contextMenuItemvyper"]') - .click('[data-id="contextMenuItemvyper"]') - .clickLaunchIcon('vyper') - // @ts-ignore - .frame(0) - .waitForElementVisible({ - selector:'[data-id="compilation-details"]', - timeout: 120000 - }) - .click('[data-id="compilation-details"]') - .frameParent() - .waitForElementVisible('[data-id="copy-abi"]') - .waitForElementVisible({ - selector: "//*[@class='variable-value' and contains(.,'highestBidder')]", - locateStrategy: 'xpath', - }) - }, 'Compile blind_auction should success #group1': function (browser: NightwatchBrowser) { browser // @ts-ignore + .clickLaunchIcon('vyper') .frame(0) .click('[data-id="compile"]') .waitForElementVisible({ @@ -145,32 +146,32 @@ module.exports = { }) }, - 'Compile Ownable contract from snekmate #group1': function (browser: NightwatchBrowser) { - let contractAddress - browser - .frameParent() - .clickLaunchIcon('filePanel') - .switchWorkspace('snekmate') - .openFile('src') - .openFile('src/snekmate') - .openFile('src/snekmate/auth') - .openFile('src/snekmate/auth/Ownable.vy') - .rightClick('*[data-id="treeViewLitreeViewItemsrc/snekmate/auth/Ownable.vy"]') - .waitForElementVisible('*[data-id="contextMenuItemvyper"]') - .click('*[data-id="contextMenuItemvyper"]') - .clickLaunchIcon('vyper') - // @ts-ignore - .frame(0) - .click('[data-id="compile"]') - .waitForElementVisible({ - selector:'[data-id="compilation-details"]', - timeout: 60000 - }) - .click('[data-id="compilation-details"]') - .frameParent() - .waitForElementVisible('[data-id="copy-abi"]') - .end() - } + // 'Compile Ownable contract from snekmate #group1': function (browser: NightwatchBrowser) { + // let contractAddress + // browser + // .frameParent() + // .clickLaunchIcon('filePanel') + // .switchWorkspace('vyper') + // .openFile('src') + // .openFile('src/snekmate') + // .openFile('src/snekmate/auth') + // .openFile('src/snekmate/auth/Ownable.vy') + // .rightClick('*[data-id="treeViewLitreeViewItemsrc/snekmate/auth/Ownable.vy"]') + // .waitForElementVisible('*[data-id="contextMenuItemvyper"]') + // .click('*[data-id="contextMenuItemvyper"]') + // .clickLaunchIcon('vyper') + // // @ts-ignore + // .frame(0) + // .click('[data-id="compile"]') + // .waitForElementVisible({ + // selector:'[data-id="compilation-details"]', + // timeout: 60000 + // }) + // .click('[data-id="compilation-details"]') + // .frameParent() + // .waitForElementVisible('[data-id="copy-abi"]') + // .end() + // } } const testContract = ` @@ -209,7 +210,7 @@ const sources = [{ 'blindAuction' : { content: ` # Blind Auction. Adapted to Vyper from [Solidity by Example](https://github.com/ethereum/solidity/blob/develop/docs/solidity-by-example.rst#blind-auction-1) -#pragma version ^0.3.10 +#pragma version >0.3.10 struct Bid: blindedBid: bytes32 @@ -384,6 +385,6 @@ def auctionEnd(): # Transfer funds to beneficiary send(self.beneficiary, self.highestBid) -`} +` } } ] diff --git a/apps/vyper/src/app/components/CompilerButton.tsx b/apps/vyper/src/app/components/CompilerButton.tsx index cf06790c4e..d661416dbc 100644 --- a/apps/vyper/src/app/components/CompilerButton.tsx +++ b/apps/vyper/src/app/components/CompilerButton.tsx @@ -1,5 +1,5 @@ import React, { Fragment, useEffect, useState } from 'react' -import {isVyper, compile, toStandardOutput, isCompilationError, remixClient, normalizeContractPath, compileContract, RemixClient} from '../utils' +import { isVyper, compile, toStandardOutput, isCompilationError, remixClient, normalizeContractPath, compileContract, RemixClient } from '../utils' import Button from 'react-bootstrap/Button' interface Props { @@ -11,7 +11,7 @@ interface Props { remixClient: RemixClient } -function CompilerButton({contract, setOutput, compilerUrl, resetCompilerState, output, remixClient}: Props) { +function CompilerButton({ contract, setOutput, compilerUrl, resetCompilerState, output, remixClient }: Props) { const [loadingSpinner, setLoadingSpinnerState] = useState(false) if (!contract || !contract) { diff --git a/apps/vyper/src/app/utils/compiler.tsx b/apps/vyper/src/app/utils/compiler.tsx index 7fdf99e456..e48d41e3b5 100644 --- a/apps/vyper/src/app/utils/compiler.tsx +++ b/apps/vyper/src/app/utils/compiler.tsx @@ -45,22 +45,30 @@ export function normalizeContractPath(contractPath: string): string[] { return [folders,resultingPath, filename] } -function parseErrorString(errorString) { +function parseErrorString(errorStructure: string[]) { // Split the string into lines - let lines = errorString.trim().split('\n') - // Extract the line number and message - let message = errorString.trim() - let targetLine = lines[2].split(',') - let tline = lines[2].trim().split(' ')[1].split(':') + let errorType = '' + let message = '' + let tline = '' + errorStructure.forEach(errorMsg => { + const choppedup = errorMsg.split(': ') + errorType = choppedup[0].trim().split('\n')[1] + message = choppedup[1] + // if (errorStructure.length > 2) { + // console.log(choppedup[2].split(',')[1]) + // } + // console.log(choppedup) + }) + let lines = errorStructure[0].trim().split('\n') const errorObject = { status: 'failed', - message: message, - column: tline[1], - line: tline[0] + message: `${errorType} - ${message}`, + column: '', + line: '' } message = null - targetLine = null + // targetLine = null lines = null tline = null return errorObject @@ -109,10 +117,10 @@ const compileReturnType = (output, contract) => { const normal = normalizeContractPath(contract)[2] const abi = temp[normal]['abi'] const evm = _.merge(temp[normal]['evm']) - const dpb = evm.deployedBytecode + const depByteCode = evm.deployedBytecode const runtimeBytecode = evm.bytecode const methodIdentifiers = evm.methodIdentifiers - const version = output?.compilers[0]?.version ?? '0.3.10' + const version = output?.compilers[0]?.version ?? '0.4.0' const optimized = output?.compilers[0]?.settings?.optimize ?? true const evmVersion = '' @@ -129,7 +137,7 @@ const compileReturnType = (output, contract) => { } = { contractName: normal, abi, - bytecode: dpb, + bytecode: depByteCode, runtimeBytecode, ir: '', methodIdentifiers, @@ -140,26 +148,32 @@ const compileReturnType = (output, contract) => { return result } -const fixContractContent = (content: string) => { - if (content.length === 0) return - const pragmaFound = content.includes('#pragma version ^0.3.10') - const wrongpragmaFound = content.includes('# pragma version ^0.3.10') - const evmVerFound = content.includes('#pragma evm-version shanghai') - const pragma = '#pragma version ^0.3.10' - const evmVer = '#pragma evm-version shanghai' - - if (evmVerFound === false) { - content = `${evmVer}\n${content}` - } - if (wrongpragmaFound === true) { - content = content.replace('# pragma version ^0.3.10', '') +const updatePragmaDeclaration = (content: string) => { + const pragmaRegex = /#\s*pragma\s+[@]*version\s+([~<>!=^]+)\s*(\d+\.\d+\.\d+)/ + const oldPragmaRegex = /#\s*pragma\s+[@]*version\s+([\^^]+)\s*(\d+\.\d+\.\d+)/ + const oldPragmaDeclaration = ['# pragma version ^0.2.16', '# pragma version ^0.3.10', '#pragma version ^0.2.16', '#pragma version ^0.3.10'] + const pragmaFound = content.match(pragmaRegex) + const oldPragmaFound = content.match(oldPragmaRegex) + + const pragma = '# pragma version ~=0.4.0' + + if (oldPragmaFound) { + // oldPragmaDeclaration.forEach(declaration => { + // content = content.replace(declaration, '# pragma version >0.3.10') + // }) + return content } - if (pragmaFound === false ) { - content = `${pragma}\n${content}` + if (!pragmaFound) { + content = `${pragma}\n\n${content}` } return content } +const fixContractContent = (content: string) => { + if (content.length === 0) return + return updatePragmaDeclaration(content) +} + /** * Compile the a contract * @param url The url of the compiler @@ -174,13 +188,16 @@ export async function compile(url: string, contract: Contract): Promise { throw new Error('Use extension .vy for Vyper.') } + const cleanedUpContent = fixContractContent(contract.content) + let contractName = contract['name'] const compilePackage = { manifest: 'ethpm/3', sources: { - [contractName] : { content : fixContractContent(contract.content) } + [contractName] : { content : cleanedUpContent } } } + let response = await axios.post(`${url}compile`, compilePackage ) if (response.status === 404) { @@ -194,6 +211,7 @@ export async function compile(url: string, contract: Contract): Promise { contractName = null response = null let result: any + let intermediateError const status = await (await axios.get(url + 'status/' + compileCode , { method: 'Get' @@ -208,7 +226,9 @@ export async function compile(url: string, contract: Contract): Promise { const intermediate = await(await axios.get(url + 'exceptions/' + compileCode , { method: 'Get' })).data - result = parseErrorString(intermediate[0]) + // console.log('Errors found', intermediate) + result = parseErrorString(intermediate) + intermediateError = intermediate return result } await new Promise((resolve) => setTimeout(() => resolve({}), 3000)) diff --git a/apps/vyper/src/app/utils/remix-client.tsx b/apps/vyper/src/app/utils/remix-client.tsx index c2d57f64f6..0557149b1e 100644 --- a/apps/vyper/src/app/utils/remix-client.tsx +++ b/apps/vyper/src/app/utils/remix-client.tsx @@ -83,23 +83,23 @@ export class RemixClient extends PluginClient { try { // @ts-ignore - this.call('notification', 'toast', 'cloning Snekmate Vyper repository...') + this.call('notification', 'toast', 'cloning Snekmate Vyper repository...') await this.call( 'dgitApi', 'clone', - {url: 'https://github.com/pcaversaccio/snekmate', token: null, branch: 'main', singleBranch: false, workspaceName: 'snekmate'}, + { url: 'https://github.com/vyperlang/vyper', token: null, branch: 'master', singleBranch: false, workspaceName: 'vyper' }, ) - await this.call( - 'dgitApi', - 'checkout', - { - ref:'v0.0.5', - force: true, - refresh: true, - } - ) - + // await this.call( + // 'dgitApi', + // 'checkout', + // { + // ref:'v0.0.5', + // force: true, + // refresh: true, + // } + // ) + this.call( // @ts-ignore 'notification',