diff --git a/apps/remix-ide-e2e/src/tests/sol2uml.test.ts b/apps/remix-ide-e2e/src/tests/sol2uml.test.ts index fc33a0df3a..8ef79eb69b 100644 --- a/apps/remix-ide-e2e/src/tests/sol2uml.test.ts +++ b/apps/remix-ide-e2e/src/tests/sol2uml.test.ts @@ -4,30 +4,30 @@ import init from '../helpers/init' module.exports = { '@disabled': true, - before: function (browser: NightwatchBrowser, done: VoidFunction) { + before: function (browser: NightwatchBrowser, done: VoidFunction) { init(browser, done) }, '@sources': () => sources, - 'Generate uml diagram from contract #group1': function (browser: NightwatchBrowser) { + 'Generate uml diagram from contract #group1': function (browser: NightwatchBrowser) { browser.addFile('TestBallot.sol', sources[0]['TestBallot.sol']) .waitForElementVisible('*[data-id="treeViewLitreeViewItemTestBallot.sol"') .rightClick('*[data-id="treeViewLitreeViewItemTestBallot.sol"]') .click('*[id="menuitemgeneratecustomaction"') .waitForElementVisible('*[id="sol-uml-gen"]') - }, - 'Generate uml for contracts with imports #group1': function (browser: NightwatchBrowser) { - browser.addFile('secondContract.sol', sources[1]['secondContract.sol']) - .waitForElementVisible('*[data-id="treeViewLitreeViewItemsecondContract.sol"') - .pause(3000) - .rightClick('*[data-id="treeViewLitreeViewItemsecondContract.sol"]') - .click('*[id="menuitemgeneratecustomaction"') - .waitForElementVisible('*[id="sol-uml-gen"]') - .waitForElementVisible('*[data-id="treeViewLitreeViewItemsecondContract_flattened.sol"]') - }, - 'Zoom into uml diagram #group1': function (browser: NightwatchBrowser) { - browser - .click('*[data-id="umlZoominbtn"]') - } + }, + 'Generate uml for contracts with imports #group1': function (browser: NightwatchBrowser) { + browser.addFile('secondContract.sol', sources[1]['secondContract.sol']) + .waitForElementVisible('*[data-id="treeViewLitreeViewItemsecondContract.sol"') + .pause(3000) + .rightClick('*[data-id="treeViewLitreeViewItemsecondContract.sol"]') + .click('*[id="menuitemgeneratecustomaction"') + .waitForElementVisible('*[id="sol-uml-gen"]') + .waitForElementVisible('*[data-id="treeViewLitreeViewItemsecondContract_flattened.sol"]') + }, + 'Zoom into uml diagram #group1': function (browser: NightwatchBrowser) { + browser + .click('*[data-id="umlZoominbtn"]') + } } const sources = [ @@ -38,7 +38,7 @@ const sources = [ pragma solidity >=0.7.0 <0.9.0; -/** +/** * @title Ballot * @dev Implements voting process along with vote delegation */ @@ -52,7 +52,7 @@ contract Ballot { } struct Proposal { - // If you can limit the length to a certain number of bytes, + // If you can limit the length to a certain number of bytes, // always use one of bytes1 to bytes32 because they are much cheaper bytes32 name; // short name (up to 32 bytes) uint voteCount; // number of accumulated votes @@ -64,7 +64,7 @@ contract Ballot { Proposal[] public proposals; - /** + /** * @dev Create a new ballot to choose one of 'proposalNames'. * @param proposalNames names of proposals */ @@ -83,7 +83,7 @@ contract Ballot { } } - /** + /** * @dev Give 'voter' the right to vote on this ballot. May only be called by 'chairperson'. * @param voter address of voter */ @@ -146,7 +146,7 @@ contract Ballot { proposals[proposal].voteCount += sender.weight; } - /** + /** * @dev Computes the winning proposal taking all previous votes into account. * @return winningProposal_ index of winning proposal in the proposals array */ @@ -162,7 +162,7 @@ contract Ballot { } } - /** + /** * @dev Calls winningProposal() function to get the index of the winner contained in the proposals array and then * @return winnerName_ the name of the winner */ @@ -208,7 +208,7 @@ contract SampleERC20 is ERC20Token { balances[msg.sender] = _totalSupply; } } - + `} -} + } ] 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 ba77570263..3ef6892284 100644 --- a/apps/remix-ide-e2e/src/tests/vyper_api.test.ts +++ b/apps/remix-ide-e2e/src/tests/vyper_api.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ 'use strict' import { NightwatchBrowser } from 'nightwatch' import init from '../helpers/init' @@ -40,6 +41,9 @@ module.exports = { .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']) + // }, 'Context menu click to compile blind_auction should succeed #group1': function (browser: NightwatchBrowser) { browser @@ -176,3 +180,182 @@ def _createPokemon(_name: String[32], _dna: uint256, _HP: uint256): wins: 0 }) self.totalPokemonCount += 1` + +const blindAuction = ` +# 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 + +struct Bid: + blindedBid: bytes32 + deposit: uint256 + +# Note: because Vyper does not allow for dynamic arrays, we have limited the +# number of bids that can be placed by one address to 128 in this example +MAX_BIDS: constant(int128) = 128 + +# Event for logging that auction has ended +event AuctionEnded: + highestBidder: address + highestBid: uint256 + +# Auction parameters +beneficiary: public(address) +biddingEnd: public(uint256) +revealEnd: public(uint256) + +# Set to true at the end of auction, disallowing any new bids +ended: public(bool) + +# Final auction state +highestBid: public(uint256) +highestBidder: public(address) + +# State of the bids +bids: HashMap[address, Bid[128]] +bidCounts: HashMap[address, int128] + +# Allowed withdrawals of previous bids +pendingReturns: HashMap[address, uint256] + + + +@external +def __init__(_beneficiary: address, _biddingTime: uint256, _revealTime: uint256): + self.beneficiary = _beneficiary + self.biddingEnd = block.timestamp + _biddingTime + self.revealEnd = self.biddingEnd + _revealTime + + +# Place a blinded bid with: +# +# _blindedBid = keccak256(concat( +# convert(value, bytes32), +# convert(fake, bytes32), +# secret) +# ) +# +# The sent ether is only refunded if the bid is correctly revealed in the +# revealing phase. The bid is valid if the ether sent together with the bid is +# at least "value" and "fake" is not true. Setting "fake" to true and sending +# not the exact amount are ways to hide the real bid but still make the +# required deposit. The same address can place multiple bids. +@external +@payable +def bid(_blindedBid: bytes32): + # Check if bidding period is still open + assert block.timestamp < self.biddingEnd + + # Check that payer hasn't already placed maximum number of bids + numBids: int128 = self.bidCounts[msg.sender] + assert numBids < MAX_BIDS + + # Add bid to mapping of all bids + self.bids[msg.sender][numBids] = Bid({ + blindedBid: _blindedBid, + deposit: msg.value + }) + self.bidCounts[msg.sender] += 1 + + +# Returns a boolean value, 'True' if bid placed successfully, 'False' otherwise. +@internal +def placeBid(bidder: address, _value: uint256) -> bool: + # If bid is less than highest bid, bid fails + if (_value <= self.highestBid): + return False + + # Refund the previously highest bidder + if (self.highestBidder != empty(address)): + self.pendingReturns[self.highestBidder] += self.highestBid + + # Place bid successfully and update auction state + self.highestBid = _value + self.highestBidder = bidder + + return True + + +# Reveal your blinded bids. You will get a refund for all correctly blinded +# invalid bids and for all bids except for the totally highest. +@external +def reveal(_numBids: int128, _values: uint256[128], _fakes: bool[128], _secrets: bytes32[128]): + # Check that bidding period is over + assert block.timestamp > self.biddingEnd + + # Check that reveal end has not passed + assert block.timestamp < self.revealEnd + + # Check that number of bids being revealed matches log for sender + assert _numBids == self.bidCounts[msg.sender] + + # Calculate refund for sender + refund: uint256 = 0 + for i in range(MAX_BIDS): + # Note that loop may break sooner than 128 iterations if i >= _numBids + if (i >= _numBids): + break + + # Get bid to check + bidToCheck: Bid = (self.bids[msg.sender])[i] + + # Check against encoded packet + value: uint256 = _values[i] + fake: bool = _fakes[i] + secret: bytes32 = _secrets[i] + blindedBid: bytes32 = keccak256(concat( + convert(value, bytes32), + convert(fake, bytes32), + secret + )) + + # Bid was not actually revealed + # Do not refund deposit + assert blindedBid == bidToCheck.blindedBid + + # Add deposit to refund if bid was indeed revealed + refund += bidToCheck.deposit + if (not fake and bidToCheck.deposit >= value): + if (self.placeBid(msg.sender, value)): + refund -= value + + # Make it impossible for the sender to re-claim the same deposit + zeroBytes32: bytes32 = empty(bytes32) + bidToCheck.blindedBid = zeroBytes32 + + # Send refund if non-zero + if (refund != 0): + send(msg.sender, refund) + + +# Withdraw a bid that was overbid. +@external +def withdraw(): + # Check that there is an allowed pending return. + pendingAmount: uint256 = self.pendingReturns[msg.sender] + if (pendingAmount > 0): + # If so, set pending returns to zero to prevent recipient from calling + # this function again as part of the receiving call before 'transfer' + # returns (see the remark above about conditions -> effects -> + # interaction). + self.pendingReturns[msg.sender] = 0 + + # Then send return + send(msg.sender, pendingAmount) + + +# End the auction and send the highest bid to the beneficiary. +@external +def auctionEnd(): + # Check that reveal end has passed + assert block.timestamp > self.revealEnd + + # Check that auction has not already been marked as ended + assert not self.ended + + # Log auction ending and set flag + log AuctionEnded(self.highestBidder, self.highestBid) + self.ended = True + + # Transfer funds to beneficiary + send(self.beneficiary, self.highestBid) +` diff --git a/apps/vyper/src/app/app.tsx b/apps/vyper/src/app/app.tsx index f6f0ef2279..bd0e12417b 100644 --- a/apps/vyper/src/app/app.tsx +++ b/apps/vyper/src/app/app.tsx @@ -47,6 +47,7 @@ const App = () => { try { await remixClient.loaded() remixClient.onFileChange((name) => { + !name.endsWith('.vy') && remixClient.changeStatus({ key: 'none' }) setOutput({}) setContract(name) }) @@ -161,7 +162,7 @@ const App = () => { in the .vy file.
- setOutput({...output, [name]: update})} resetCompilerState={resetCompilerResultState} /> + setOutput({...output, [name]: update})} resetCompilerState={resetCompilerResultState} output={output} remixClient={remixClient}/>
diff --git a/apps/vyper/src/app/components/CompilerButton.tsx b/apps/vyper/src/app/components/CompilerButton.tsx index 54597171e9..b6fc4f2e64 100644 --- a/apps/vyper/src/app/components/CompilerButton.tsx +++ b/apps/vyper/src/app/components/CompilerButton.tsx @@ -1,16 +1,19 @@ -import React, { Fragment, useState } from 'react' -import {isVyper, compile, toStandardOutput, isCompilationError, remixClient, normalizeContractPath, compileContract} from '../utils' +import React, { Fragment, useEffect, useState } from 'react' +import {isVyper, compile, toStandardOutput, isCompilationError, remixClient, normalizeContractPath, compileContract, RemixClient} from '../utils' import Button from 'react-bootstrap/Button' interface Props { compilerUrl: string contract?: string + output?: any setOutput: (name: string, output: any) => void resetCompilerState: () => void + remixClient: RemixClient } -function CompilerButton({contract, setOutput, compilerUrl, resetCompilerState}: Props) { +function CompilerButton({contract, setOutput, compilerUrl, resetCompilerState, output, remixClient}: Props) { const [loadingSpinner, setLoadingSpinnerState] = useState(false) + if (!contract || !contract) { return } @@ -24,12 +27,15 @@ function CompilerButton({contract, setOutput, compilerUrl, resetCompilerState}: return (