Merge remote-tracking branch 'origin/master' into refactor_logic6

pull/1/head
yann300 5 years ago
commit 95c6bf03ec
  1. 4
      README.md
  2. 1
      ci/browser_tests.sh
  3. 2
      ci/makeMockCompiler.js
  4. 156
      contracts/node_modules/openzeppelin-solidity/contracts/math/SafeMath.sol
  5. 1
      contracts/node_modules/openzeppelin-solidity/contracts/sample.sol
  6. 17178
      package-lock.json
  7. 23
      package.json
  8. 10
      src/app.js
  9. 4
      src/app/editor/editor.js
  10. 253
      src/app/editor/example-contracts.js
  11. 1
      src/app/files/compiler-metadata.js
  12. 8
      src/app/files/file-explorer.js
  13. 4
      src/app/files/fileManager.js
  14. 2
      src/app/panels/terminal.js
  15. 15
      src/app/tabs/compile-tab.js
  16. 64
      src/app/tabs/compileTab/compileTab.js
  17. 3
      src/app/tabs/compileTab/compilerContainer.js
  18. 2
      src/app/tabs/debugger/debuggerUI/vmDebugger/MemoryPanel.js
  19. 2
      src/app/tabs/debugger/debuggerUI/vmDebugger/StackPanel.js
  20. 5
      src/app/tabs/runTab/contractDropdown.js
  21. 2
      src/app/tabs/runTab/model/recorder.js
  22. 3
      src/app/tabs/runTab/recorder.js
  23. 25
      src/app/tabs/test-tab.js
  24. 2
      src/app/tabs/testTab/testTab.js
  25. 4
      src/app/tabs/theme-module.js
  26. 2
      src/app/ui/landing-page/landing-page.js
  27. 7
      src/app/ui/multiParamManager.js
  28. 4
      src/app/ui/persmission-handler.js
  29. 109
      src/app/ui/sendTxCallbacks.js
  30. 10
      src/app/ui/txLogger.js
  31. 348
      src/app/ui/universal-dapp-ui.js
  32. 27
      src/blockchain/blockchain.js
  33. 9
      src/lib/helper.js
  34. 18
      src/lib/publishOnIpfs.js
  35. 18
      src/lib/publishOnSwarm.js
  36. 203
      src/remixAppManager.js
  37. 3
      test-browser/commands/clickInstance.js
  38. 2
      test-browser/commands/clickLaunchIcon.js
  39. 1
      test-browser/commands/executeScript.js
  40. 16
      test-browser/commands/getModalBody.js
  41. 8
      test-browser/commands/journalLastChild.js
  42. 19
      test-browser/commands/journalLastChildIncludes.js
  43. 17
      test-browser/commands/modalFooterCancelClick.js
  44. 2
      test-browser/commands/modalFooterOKClick.js
  45. 19
      test-browser/commands/sendLowLevelTx.js
  46. 15
      test-browser/commands/setSolidityCompilerVersion.js
  47. 1
      test-browser/commands/switchFile.js
  48. 11
      test-browser/commands/verifyContracts.js
  49. 19
      test-browser/helpers/init.js
  50. 200
      test-browser/tests/ballot.js
  51. 43
      test-browser/tests/gist.js
  52. 56
      test-browser/tests/importFromGist.js
  53. 37
      test-browser/tests/publishContract.js
  54. 32
      test-browser/tests/remixd.js
  55. 2
      test-browser/tests/signingMessage.js
  56. 160
      test-browser/tests/solidityImport.js
  57. 4
      test-browser/tests/solidityUnittests.js
  58. 273
      test-browser/tests/specialFunctions.js
  59. 17
      test-browser/tests/workspace.js
  60. 2
      test/compiler-test.js

@ -109,7 +109,9 @@ To run the Selenium tests via Nightwatch:
- npm run nightwatch_local_console
- npm run nightwatch_local_remixd # remixd needs to be run
**the `ballot` tests suite requires to run `ganache-cli` locally.**
**the `remixd` tests suite requires to run `remixd` locally.**
## Usage as a Chrome Extension

@ -15,6 +15,7 @@ BUILD_ID=${CIRCLE_BUILD_NUM:-${TRAVIS_JOB_NUMBER}}
echo "$BUILD_ID"
TEST_EXITCODE=0
npm run ganache-cli &
npm run serve &
setupRemixd

@ -3,7 +3,7 @@
var fs = require('fs')
var compiler = require('solc')
var compilerInput = require('remix-solidity').CompilerInput
var defaultVersion = 'v0.5.14+commit.1f1aaa4'
var defaultVersion = 'v0.6.1+commit.e6f7d5a4'
compiler.loadRemoteVersion(defaultVersion, (error, solcSnapshot) => {
if (error) console.log(error)

@ -0,0 +1,156 @@
pragma solidity ^0.5.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*
* _Available since v2.4.0._
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}

@ -0,0 +1 @@
import "https://github.com/OpenZeppelin/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; contract test11 {}

17178
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
{
"name": "remix-ide",
"version": "v0.9.2",
"version": "v0.9.4",
"description": "Extendable Web IDE for Ethereum",
"devDependencies": {
"@babel/core": "^7.4.5",
@ -37,6 +37,7 @@
"exorcist": "^0.4.0",
"fast-async": "^7.0.6",
"fast-levenshtein": "^2.0.6",
"ganache-cli": "^6.8.1",
"gists": "^1.0.1",
"ipfs-mini": "^1.1.5",
"is-electron": "^2.2.0",
@ -53,12 +54,12 @@
"npm-merge-driver": "^2.3.5",
"npm-run-all": "^4.0.2",
"onchange": "^3.2.1",
"remix-analyzer": "0.3.19",
"remix-debug": "0.3.21",
"remix-lib": "0.4.18",
"remix-solidity": "0.3.21",
"remix-analyzer": "0.3.23",
"remix-debug": "0.3.26",
"remix-lib": "0.4.22",
"remix-solidity": "0.3.25",
"remix-tabs": "1.0.48",
"remix-tests": "0.1.24",
"remix-tests": "0.1.28",
"remixd": "0.1.8-alpha.10",
"request": "^2.83.0",
"rimraf": "^2.6.1",
@ -154,7 +155,7 @@
"build_debugger": "browserify src/app/debugger/remix-debugger/index.js -o src/app/debugger/remix-debugger/build/app.js",
"browsertest": "sleep 5 && npm run nightwatch_local",
"csslint": "csslint --ignore=order-alphabetical --errors='errors,duplicate-properties,empty-rules' --exclude-list='assets/css/font-awesome.min.css' assets/css/",
"downloadsolc_root": "wget --no-check-certificate https://solc-bin.ethereum.org/bin/soljson-v0.5.14+commit.1f1aaa4.js -O soljson.js",
"downloadsolc_root": "wget --no-check-certificate https://solc-bin.ethereum.org/bin/soljson-v0.6.1+commit.e6f7d5a4.js -O soljson.js",
"lint": "standard | notify-error",
"make-mock-compiler": "node ci/makeMockCompiler.js",
"minify": "uglifyjs --in-source-map inline --source-map-inline -c warnings=false",
@ -167,9 +168,12 @@
"nightwatch_local_transactionExecution": "nightwatch ./test-browser/tests/transactionExecution.js --config nightwatch.js --env chrome ",
"nightwatch_local_staticAnalysis": "nightwatch ./test-browser/tests/staticAnalysis.js --config nightwatch.js --env chrome ",
"nightwatch_local_signingMessage": "nightwatch ./test-browser/tests/signingMessage.js --config nightwatch.js --env chrome ",
"nightwatch_local_specialFunctions": "nightwatch ./test-browser/tests/specialFunctions.js --config nightwatch.js --env chrome ",
"nightwatch_local_solidityUnittests": "nightwatch ./test-browser/tests/solidityUnittests.js --config nightwatch.js --env chrome ",
"nightwatch_local_remixd": "nightwatch ./test-browser/tests/remix.js --config nightwatch.js --env chrome ",
"nightwatch_local_remixd": "nightwatch ./test-browser/tests/remixd.js --config nightwatch.js --env chrome ",
"nightwatch_local_console": "nightwatch ./test-browser/tests/console.js --config nightwatch.js --env chrome ",
"nightwatch_local_gist": "nightwatch ./test-browser/tests/gist.js --config nightwatch.js --env chrome ",
"nightwatch_local_workspace": "nightwatch ./test-browser/tests/workspace.js --config nightwatch.js --env chrome ",
"onchange": "onchange build/app.js -- npm-run-all lint",
"prepublish": "mkdirp build; npm-run-all -ls downloadsolc_root build",
"remixd": "remixd -s ./contracts --remix-ide http://127.0.0.1:8080",
@ -181,6 +185,7 @@
"test": "csslint && standard && node test/index.js",
"test-browser": "npm-run-all -lpr selenium downloadsolc_root make-mock-compiler serve browsertest",
"watch": "watchify src/index.js -dv -p browserify-reload -o build/app.js --exclude solc",
"reinstall": "rm ./node-modules/ -rf; rm package-lock.json; rm ./build/ -rf; npm install; npm run build"
"reinstall": "rm ./node-modules/ -rf; rm package-lock.json; rm ./build/ -rf; npm install; npm run build",
"ganache-cli": "npx ganache-cli"
}
}

@ -1,4 +1,3 @@
/* global localStorage */
'use strict'
var isElectron = require('is-electron')
@ -208,7 +207,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
// APP_MANAGER
const appManager = new RemixAppManager({})
const workspace = JSON.parse(localStorage.getItem('workspace'))
const workspace = appManager.pluginLoader.get()
// SERVICES
// ----------------- import content servive ----------------------------
@ -351,12 +350,13 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
const queryParams = new QueryParams()
const loadedFromGist = gistHandler.loadFromGist(queryParams.get(), fileManager)
if (!loadedFromGist) {
// insert ballot contract if there are no files to show
// insert example contracts if there are no files to show
self._components.filesProviders['browser'].resolveDirectory('/', (error, filesList) => {
if (error) console.error(error)
if (Object.keys(filesList).length === 0) {
fileManager.setFile(examples.ballot.name, examples.ballot.content)
fileManager.setFile(examples.ballot_test.name, examples.ballot_test.content)
for (let file in examples) {
fileManager.setFile(examples[file].name, examples[file].content)
}
}
})
}

@ -19,6 +19,7 @@ require('ace-mode-zokrates')
require('brace/mode/javascript')
require('brace/mode/python')
require('brace/mode/json')
require('brace/mode/rust')
require('brace/theme/chaos')
require('brace/theme/chrome')
@ -85,7 +86,8 @@ class Editor extends Plugin {
zok: 'ace/mode/zokrates',
txt: 'ace/mode/text',
json: 'ace/mode/json',
abi: 'ace/mode/json'
abi: 'ace/mode/json',
rs: 'ace/mode/rust'
}
// Editor Setup

@ -1,96 +1,249 @@
'use strict'
var ballot = `pragma solidity >=0.4.22 <0.6.0;
contract Ballot {
const storage = `pragma solidity >=0.4.22 <0.7.0;
/**
* @title Storage
* @dev Store & retreive value in a variable
*/
contract Storage {
uint256 number;
/**
* @dev Store value in variable
* @param num value to store
*/
function store(uint256 num) public {
number = num;
}
/**
* @dev Return value
* @return value of 'number'
*/
function retreive() public view returns (uint256){
return number;
}
}`
const owner = `pragma solidity >=0.4.22 <0.7.0;
/**
* @title Owner
* @dev Set & change owner
*/
contract Owner {
address private owner;
// event for EVM logging
event OwnerSet(address indexed oldOwner, address indexed newOwner);
// modifier to check if caller is owner
modifier isOwner() {
// If the first argument of 'require' evaluates to 'false', execution terminates and all
// changes to the state and to Ether balances are reverted.
// This used to consume all gas in old EVM versions, but not anymore.
// It is often a good idea to use 'require' to check if functions are called correctly.
// As a second argument, you can also provide an explanation about what went wrong.
require(msg.sender == owner, "Caller is not owner");
_;
}
/**
* @dev Set contract deployer as owner
*/
constructor() public {
owner = msg.sender; // 'msg.sender' is sender of current call, contract deployer for a constructor
emit OwnerSet(address(0), owner);
}
/**
* @dev Change owner
* @param newOwner address of new owner
*/
function changeOwner(address newOwner) public isOwner {
emit OwnerSet(owner, newOwner);
owner = newOwner;
}
/**
* @dev Return owner address
* @return address of owner
*/
function getOwner() external view returns (address) {
return owner;
}
}`
const ballot = `pragma solidity >=0.4.22 <0.7.0;
/**
* @title Ballot
* @dev Implements voting process along with vote delegation
*/
contract Ballot {
struct Voter {
uint weight;
bool voted;
uint8 vote;
address delegate;
uint weight; // weight is accumulated by delegation
bool voted; // if true, that person already voted
address delegate; // person delegated to
uint vote; // index of the voted proposal
}
struct Proposal {
uint voteCount;
// 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
}
address chairperson;
mapping(address => Voter) voters;
Proposal[] proposals;
address public chairperson;
/// Create a new ballot with $(_numProposals) different proposals.
constructor(uint8 _numProposals) public {
mapping(address => Voter) public voters;
Proposal[] public proposals;
/**
* @dev Create a new ballot to choose one of 'proposalNames'.
* @param proposalNames names of proposals
*/
constructor(bytes32[] memory proposalNames) public {
chairperson = msg.sender;
voters[chairperson].weight = 1;
proposals.length = _numProposals;
}
/// Give $(toVoter) the right to vote on this ballot.
/// May only be called by $(chairperson).
function giveRightToVote(address toVoter) public {
if (msg.sender != chairperson || voters[toVoter].voted) return;
voters[toVoter].weight = 1;
for (uint i = 0; i < proposalNames.length; i++) {
// 'Proposal({...})' creates a temporary
// Proposal object and 'proposals.push(...)'
// appends it to the end of 'proposals'.
proposals.push(Proposal({
name: proposalNames[i],
voteCount: 0
}));
}
}
/**
* @dev Give 'voter' the right to vote on this ballot. May only be called by 'chairperson'.
* @param voter address of voter
*/
function giveRightToVote(address voter) public {
require(
msg.sender == chairperson,
"Only chairperson can give right to vote."
);
require(
!voters[voter].voted,
"The voter already voted."
);
require(voters[voter].weight == 0);
voters[voter].weight = 1;
}
/// Delegate your vote to the voter $(to).
/**
* @dev Delegate your vote to the voter 'to'.
* @param to address to which vote is delegated
*/
function delegate(address to) public {
Voter storage sender = voters[msg.sender]; // assigns reference
if (sender.voted) return;
while (voters[to].delegate != address(0) && voters[to].delegate != msg.sender)
Voter storage sender = voters[msg.sender];
require(!sender.voted, "You already voted.");
require(to != msg.sender, "Self-delegation is disallowed.");
while (voters[to].delegate != address(0)) {
to = voters[to].delegate;
if (to == msg.sender) return;
// We found a loop in the delegation, not allowed.
require(to != msg.sender, "Found loop in delegation.");
}
sender.voted = true;
sender.delegate = to;
Voter storage delegateTo = voters[to];
if (delegateTo.voted)
proposals[delegateTo.vote].voteCount += sender.weight;
else
delegateTo.weight += sender.weight;
Voter storage delegate_ = voters[to];
if (delegate_.voted) {
// If the delegate already voted,
// directly add to the number of votes
proposals[delegate_.vote].voteCount += sender.weight;
} else {
// If the delegate did not vote yet,
// add to her weight.
delegate_.weight += sender.weight;
}
}
/// Give a single vote to proposal $(toProposal).
function vote(uint8 toProposal) public {
/**
* @dev Give your vote (including votes delegated to you) to proposal 'proposals[proposal].name'.
* @param proposal index of proposal in the proposals array
*/
function vote(uint proposal) public {
Voter storage sender = voters[msg.sender];
if (sender.voted || toProposal >= proposals.length) return;
require(sender.weight != 0, "Has no right to vote");
require(!sender.voted, "Already voted.");
sender.voted = true;
sender.vote = toProposal;
proposals[toProposal].voteCount += sender.weight;
sender.vote = proposal;
// If 'proposal' is out of the range of the array,
// this will throw automatically and revert all
// changes.
proposals[proposal].voteCount += sender.weight;
}
function winningProposal() public view returns (uint8 _winningProposal) {
uint256 winningVoteCount = 0;
for (uint8 prop = 0; prop < proposals.length; prop++)
if (proposals[prop].voteCount > winningVoteCount) {
winningVoteCount = proposals[prop].voteCount;
_winningProposal = prop;
/**
* @dev Computes the winning proposal taking all previous votes into account.
* @return winningProposal_ index of winning proposal in the proposals array
*/
function winningProposal() public view
returns (uint winningProposal_)
{
uint winningVoteCount = 0;
for (uint p = 0; p < proposals.length; p++) {
if (proposals[p].voteCount > winningVoteCount) {
winningVoteCount = proposals[p].voteCount;
winningProposal_ = p;
}
}
}
/**
* @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
*/
function winnerName() public view
returns (bytes32 winnerName_)
{
winnerName_ = proposals[winningProposal()].name;
}
}
`
var ballotTest = `
pragma solidity >=0.4.22 <0.6.0;
var ballotTest = `pragma solidity >=0.4.22 <0.7.0;
import "remix_tests.sol"; // this import is automatically injected by Remix.
import "./ballot.sol";
import "./3_Ballot.sol";
contract test3 {
contract BallotTest {
bytes32[] proposalNames;
Ballot ballotToTest;
function beforeAll () public {
ballotToTest = new Ballot(2);
proposalNames.push(bytes32("candidate1"));
ballotToTest = new Ballot(proposalNames);
}
function checkWinningProposal () public {
ballotToTest.vote(1);
Assert.equal(ballotToTest.winningProposal(), uint(1), "1 should be the winning proposal");
ballotToTest.vote(0);
Assert.equal(ballotToTest.winningProposal(), uint(0), "proposal at index 0 should be the winning proposal");
Assert.equal(ballotToTest.winnerName(), bytes32("candidate1"), "candidate1 should be the winner name");
}
function checkWinninProposalWithReturnValue () public view returns (bool) {
return ballotToTest.winningProposal() == 1;
return ballotToTest.winningProposal() == 0;
}
}
`
module.exports = {
ballot: { name: 'ballot.sol', content: ballot },
ballot_test: { name: 'ballot_test.sol', content: ballotTest }
storage: { name: '1_Storage.sol', content: storage },
owner: { name: '2_Owner.sol', content: owner },
ballot: { name: '3_Ballot.sol', content: ballot },
ballot_test: { name: '4_Ballot_test.sol', content: ballotTest }
}

@ -98,6 +98,7 @@ class CompilerMetadata extends Plugin {
this.blockchain.detectNetwork((err, { id, name } = {}) => {
if (err) {
console.log(err)
reject(err)
} else {
var fileName = this._JSONFileName(path, contractName)
provider.get(fileName, (error, content) => {

@ -470,7 +470,9 @@ fileExplorer.prototype.toGist = function (id) {
return data.files || []
}
this.packageFiles(this.files, 'browser/gists/' + id, (error, packaged) => {
// If 'id' is not defined, it is not a gist update but a creation so we have to take the files from the browser explorer.
const folder = id ? 'browser/gists/' + id : 'browser/'
this.packageFiles(this.files, folder, (error, packaged) => {
if (error) {
console.log(error)
modalDialogCustom.alert('Failed to create gist: ' + error)
@ -632,7 +634,7 @@ fileExplorer.prototype.renderMenuItems = function () {
items = this.menuItems.map(({action, title, icon}) => {
if (action === 'uploadFile') {
return yo`
<label class="${icon} ${css.newFile}" title="${title}">
<label id=${action} class="${icon} ${css.newFile}" title="${title}">
<input type="file" onchange=${(event) => {
event.stopPropagation()
this.uploadFile(event)
@ -641,7 +643,7 @@ fileExplorer.prototype.renderMenuItems = function () {
`
} else {
return yo`
<span onclick=${(event) => { event.stopPropagation(); this[ action ]() }} class="newFile ${icon} ${css.newFile}" title=${title}></span>
<span id=${action} onclick=${(event) => { event.stopPropagation(); this[ action ]() }} class="newFile ${icon} ${css.newFile}" title=${title}></span>
`
}
})

@ -314,6 +314,10 @@ class FileManager extends Plugin {
})
}
getProvider (name) {
return this._deps.filesProviders[name]
}
fileProviderOf (file) {
if (file.indexOf('localhost') === 0) {
return this._deps.filesProviders['localhost']

@ -148,7 +148,7 @@ class Terminal extends Plugin {
${self._view.dragbar}
<div class="${css.menu} border-top border-dark bg-light">
${self._view.icon}
<div class=${css.clear} onclick=${clear}>
<div class=${css.clear} id="clearConsole" onclick=${clear}>
<i class="fas fa-ban" aria-hidden="true" title="Clear console"
onmouseenter=${hover} onmouseleave=${hover}></i>
</div>

@ -187,7 +187,7 @@ class CompileTab extends ViewPlugin {
}
getCompilationResult () {
return this.compileTabLogic.compiler.lastCompilationResult
return this.compileTabLogic.compiler.state.lastCompilationResult
}
// This function is used by remix-plugin
@ -200,6 +200,15 @@ class CompileTab extends ViewPlugin {
return this.compilerContainer.data.selectedVersion
}
// This function is used for passing the compiler configuration to 'remix-tests'
getCurrentCompilerConfig () {
return {
currentVersion: this.compilerContainer.data.selectedVersion,
evmVersion: this.compileTabLogic.evmVersion,
optimize: this.compileTabLogic.optimize
}
}
/*********
* SUB-COMPONENTS
*/
@ -239,11 +248,11 @@ class CompileTab extends ViewPlugin {
${selectEl}
</div>
<article class="px-2 mt-2 pb-0">
<button class="btn btn-secondary btn-block" title="Publish on Swarm" onclick="${() => { this.publish('swarm') }}">
<button id="publishOnSwarm" class="btn btn-secondary btn-block" title="Publish on Swarm" onclick="${() => { this.publish('swarm') }}">
<span>Publish on Swarm</span>
<img id="swarmLogo" class="${css.storageLogo} ml-2" src="${swarmImg}">
</button>
<button class="btn btn-secondary btn-block" title="Publish on Ipfs" onclick="${() => { this.publish('ipfs') }}">
<button id="publishOnIpfs" class="btn btn-secondary btn-block" title="Publish on Ipfs" onclick="${() => { this.publish('ipfs') }}">
<span>Publish on Ipfs</span>
<img id="ipfsLogo" class="${css.storageLogo} ml-2" src="${ipfsImg}">
</button>

@ -24,26 +24,26 @@ class CompileTab {
this.optimize = this.queryParams.get().optimize
this.optimize = this.optimize === 'true'
this.queryParams.update({ optimize: this.optimize })
this.compiler.setOptimize(this.optimize)
this.compiler.set('optimize', this.optimize)
this.evmVersion = this.queryParams.get().evmVersion
if (this.evmVersion === 'undefined' || this.evmVersion === 'null' || !this.evmVersion) {
this.evmVersion = null
}
this.queryParams.update({ evmVersion: this.evmVersion })
this.compiler.setEvmVersion(this.evmVersion)
this.compiler.set('evmVersion', this.evmVersion)
}
setOptimize (newOptimizeValue) {
this.optimize = newOptimizeValue
this.queryParams.update({ optimize: this.optimize })
this.compiler.setOptimize(this.optimize)
this.compiler.set('optimize', this.optimize)
}
setEvmVersion (newEvmVersion) {
this.evmVersion = newEvmVersion
this.queryParams.update({ evmVersion: this.evmVersion })
this.compiler.setEvmVersion(this.evmVersion)
this.compiler.set('evmVersion', this.evmVersion)
}
/**
@ -51,7 +51,7 @@ class CompileTab {
* @params lang {'Solidity' | 'Yul'} ...
*/
setLanguage (lang) {
this.compiler.setLanguage(lang)
this.compiler.set('language', lang)
}
/**
@ -99,6 +99,15 @@ class CompileTab {
})
}
/**
* import the content of @arg url.
* first look in the browser localstorage (browser explorer) or locahost explorer. if the url start with `browser/*` or `localhost/*`
* then check if the @arg url is located in the localhost, in the node_modules or installed_contracts folder
* then check if the @arg url match any external url
*
* @param {String} url - URL of the content. can be basically anything like file located in the browser explorer, in the localhost explorer, raw HTTP, github address etc...
* @param {Function} cb - callback
*/
importFileCb (url, filecb) {
if (url.indexOf('remix_tests.sol') !== -1) return filecb(null, remixTests.assertLibCode)
@ -107,26 +116,39 @@ class CompileTab {
if (provider.type === 'localhost' && !provider.isConnected()) {
return filecb(`file provider ${provider.type} not available while trying to resolve ${url}`)
}
return provider.exists(url, (error, exist) => {
provider.exists(url, (error, exist) => {
if (error) return filecb(error)
if (exist) {
return provider.get(url, filecb)
if (!exist && provider.type === 'localhost') return filecb(`not found ${url}`)
/*
if the path is absolute and the file does not exist, we can stop here
Doesn't make sense to try to resolve "localhost/node_modules/localhost/node_modules/<path>" and we'll end in an infinite loop.
*/
if (!exist && url.startsWith('browser/')) return filecb(`not found ${url}`)
if (!exist && url.startsWith('localhost/')) return filecb(`not found ${url}`)
if (exist) return provider.get(url, filecb)
// try to resolve localhost modules (aka truffle imports) - e.g from the node_modules folder
const localhostProvider = this.fileManager.getProvider('localhost')
if (localhostProvider.isConnected()) {
var splitted = /([^/]+)\/(.*)$/g.exec(url)
return async.tryEach([
(cb) => { this.importFileCb('localhost/installed_contracts/' + url, cb) },
(cb) => { if (!splitted) { cb('URL not parseable: ' + url) } else { this.importFileCb('localhost/installed_contracts/' + splitted[1] + '/contracts/' + splitted[2], cb) } },
(cb) => { this.importFileCb('localhost/node_modules/' + url, cb) },
(cb) => { if (!splitted) { cb('URL not parseable: ' + url) } else { this.importFileCb('localhost/node_modules/' + splitted[1] + '/contracts/' + splitted[2], cb) } }],
(error, result) => {
if (error) return this.importExternal(url, filecb)
filecb(null, result)
}
)
} else {
// try to resolve external content
this.importExternal(url, filecb)
}
this.importExternal(url, filecb)
})
}
if (this.compilerImport.isRelativeImport(url)) {
// try to resolve localhost modules (aka truffle imports)
var splitted = /([^/]+)\/(.*)$/g.exec(url)
return async.tryEach([
(cb) => { this.importFileCb('localhost/installed_contracts/' + url, cb) },
(cb) => { if (!splitted) { cb('URL not parseable: ' + url) } else { this.importFileCb('localhost/installed_contracts/' + splitted[1] + '/contracts/' + splitted[2], cb) } },
(cb) => { this.importFileCb('localhost/node_modules/' + url, cb) },
(cb) => { if (!splitted) { cb('URL not parseable: ' + url) } else { this.importFileCb('localhost/node_modules/' + splitted[1] + '/contracts/' + splitted[2], cb) } }],
(error, result) => { filecb(error, result) }
)
}
this.importExternal(url, filecb)
}
}

@ -24,7 +24,7 @@ class CompilerContainer {
timeout: 300,
allversions: null,
selectedVersion: null,
defaultVersion: 'soljson-v0.5.14+commit.1f1aaa4.js', // this default version is defined: in makeMockCompiler (for browser test) and in package.json (downloadsolc_root) for the builtin compiler
defaultVersion: 'soljson-v0.6.1+commit.e6f7d5a4.js', // this default version is defined: in makeMockCompiler (for browser test) and in package.json (downloadsolc_root) for the builtin compiler
baseurl: 'https://solc-bin.ethereum.org/bin'
}
}
@ -204,6 +204,7 @@ class CompilerContainer {
this._view.evmVersionSelector = yo`
<select onchange="${this.onchangeEvmVersion.bind(this)}" class="custom-select" id="evmVersionSelector">
<option value="default">compiler default</option>
<option>istanbul</option>
<option>petersburg</option>
<option>constantinople</option>
<option>byzantium</option>

@ -15,7 +15,7 @@ MemoryPanel.prototype.update = function (calldata) {
}
MemoryPanel.prototype.render = function () {
return yo`<div id="memorypanel" class="text-monospace small">${this.basicPanel.render()}</div>`
return yo`<div id="memorypanel">${this.basicPanel.render()}</div>`
}
module.exports = MemoryPanel

@ -11,7 +11,7 @@ StackPanel.prototype.update = function (calldata) {
}
StackPanel.prototype.render = function () {
return yo`<div id="stackpanel" class="text-monospace small">${this.basicPanel.render()}</div>`
return yo`<div id="stackpanel">${this.basicPanel.render()}</div>`
}
module.exports = StackPanel

@ -214,17 +214,18 @@ class ContractDropdownUI {
}
getConfirmationCb (modalDialog, confirmDialog) {
// this code is the same as in recorder.js. TODO need to be refactored out
const confirmationCb = (network, tx, gasEstimation, continueTxExecution, cancelCb) => {
if (network.name !== 'Main') {
return continueTxExecution(null)
}
const amount = this.blockchain.fromWei(tx.value, true, 'ether')
const content = confirmDialog(tx, amount, gasEstimation, null, this.blockchain.determineGasFees(tx), this.blockchain.determineGasPrice)
const content = confirmDialog(tx, amount, gasEstimation, null, this.blockchain.determineGasFees(tx), this.blockchain.determineGasPrice.bind(this.blockchain))
modalDialog('Confirm transaction', content,
{ label: 'Confirm',
fn: () => {
this.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked)
this.blockchain.udapp.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked)
// TODO: check if this is check is still valid given the refactor
if (!content.gasPriceStatus) {
cancelCb('Given gas price is not correct')

@ -211,6 +211,8 @@ class Recorder {
fnABI = txHelper.getConstructorInterface(abi)
} else if (tx.record.type === 'fallback') {
fnABI = txHelper.getFallbackInterface(abi)
} else if (tx.record.type === 'receive') {
fnABI = txHelper.getReceiveInterface(abi)
} else {
fnABI = txHelper.getFunction(abi, record.name + record.inputs)
}

@ -78,12 +78,13 @@ class RecorderUI {
}
getConfirmationCb (modalDialog, confirmDialog) {
// this code is the same as in contractDropdown.js. TODO need to be refactored out
const confirmationCb = (network, tx, gasEstimation, continueTxExecution, cancelCb) => {
if (network.name !== 'Main') {
return continueTxExecution(null)
}
const amount = this.blockchain.fromWei(tx.value, true, 'ether')
const content = confirmDialog(tx, amount, gasEstimation, null, this.blockchain.determineGasFees(tx), this.blockchain.determineGasPrice)
const content = confirmDialog(tx, amount, gasEstimation, null, this.blockchain.determineGasFees(tx), this.blockchain.determineGasPrice.bind(this.blockchain))
modalDialog('Confirm transaction', content,
{ label: 'Confirm',

@ -153,8 +153,15 @@ module.exports = class TestTab extends ViewPlugin {
return new Promise((resolve, reject) => {
let runningTest = {}
runningTest[path] = { content }
let currentCompilerUrl = this.baseurl + '/' + this.compileTab.getCurrentVersion()
remixTests.runTestSources(runningTest, currentCompilerUrl, canUseWorker(this.compileTab.getCurrentVersion()), () => {}, () => {}, (error, result) => {
const {currentVersion, evmVersion, optimize} = this.compileTab.getCurrentCompilerConfig()
const currentCompilerUrl = this.baseurl + '/' + currentVersion
const compilerConfig = {
currentCompilerUrl,
evmVersion,
optimize,
usingWorker: canUseWorker(currentVersion)
}
remixTests.runTestSources(runningTest, compilerConfig, () => {}, () => {}, (error, result) => {
if (error) return reject(error)
resolve(result)
}, (url, cb) => {
@ -166,13 +173,19 @@ module.exports = class TestTab extends ViewPlugin {
runTest (testFilePath, callback) {
this.loading.hidden = false
this.fileManager.getFile(testFilePath).then((content) => {
var runningTest = {}
const runningTest = {}
runningTest[testFilePath] = { content }
let currentCompilerUrl = this.baseurl + '/' + this.compileTab.getCurrentVersion()
const {currentVersion, evmVersion, optimize} = this.compileTab.getCurrentCompilerConfig()
const currentCompilerUrl = this.baseurl + '/' + currentVersion
const compilerConfig = {
currentCompilerUrl,
evmVersion,
optimize,
usingWorker: canUseWorker(currentVersion)
}
remixTests.runTestSources(
runningTest,
currentCompilerUrl,
canUseWorker(this.compileTab.getCurrentVersion()),
compilerConfig,
(result) => this.testCallback(result),
(_err, result, cb) => this.resultsCallback(_err, result, cb),
(error, result) => {

@ -37,7 +37,7 @@ class TestTabLogic {
}
generateTestContractSample () {
return `pragma solidity >=0.4.0 <0.6.0;
return `pragma solidity >=0.4.0 <0.7.0;
import "remix_tests.sol"; // this import is automatically injected by Remix.
// file name has to end with '_test.sol'

@ -3,8 +3,8 @@ import { EventEmitter } from 'events'
import * as packageJson from '../../../package.json'
const themes = [
{name: 'Dark', quality: 'dark', url: 'https://res.cloudinary.com/dvtmp0niu/raw/upload/v1574178106/remix-dark.css'},
{name: 'Light', quality: 'light', url: 'https://res.cloudinary.com/dvtmp0niu/raw/upload/v1574252300/remix-light-theme.css'},
{name: 'Dark', quality: 'dark', url: 'https://res.cloudinary.com/dvtmp0niu/raw/upload/v1578991867/remix-dark-theme.css'},
{name: 'Light', quality: 'light', url: 'https://res.cloudinary.com/dvtmp0niu/raw/upload/v1578991821/light-theme.css'},
// switching to the url Todo: remove when the theme is ready
// {name: 'Dark', quality: 'dark', url: 'assets/css/remix-dark-theme.css'},

@ -196,7 +196,6 @@ export class LandingPage extends ViewPlugin {
<div class="${css.enviroments} pt-2">
<button class="btn btn-lg btn-secondary mr-3" onclick=${() => startSolidity()}>Solidity</button>
<button class="btn btn-lg btn-secondary mr-3" onclick=${() => startVyper()}>Vyper</button>
<button class="btn btn-lg btn-secondary mr-3" onclick=${() => startWorkshop()}>Workshops</button>
</div>
</div>
<div class="file">
@ -231,6 +230,7 @@ export class LandingPage extends ViewPlugin {
<h4>Featured Plugins</h4>
<p class="mb-1 ${css.text}" onclick=${() => { startPipeline() }}>Pipeline</p>
<p class="mb-1 ${css.text}" onclick=${() => { startDebugger() }}>Debugger</p>
<p class="mb-1 ${css.text}" onclick=${() => startWorkshop()}>Workshops</p>
<p class="mb-1">
<button onclick=${() => { startPluginManager() }} class="btn btn-sm btn-secondary ${css.seeAll}">
See all Plugins

@ -113,7 +113,7 @@ class MultiParamManager {
} else if (this.funABI.name) {
title = this.funABI.name
} else {
title = '(fallback)'
title = this.funABI.type === 'receive' ? '(receive)' : '(fallback)'
}
this.basicInputField = yo`<input></input>`
@ -121,7 +121,7 @@ class MultiParamManager {
this.basicInputField.setAttribute('title', this.inputs)
this.basicInputField.setAttribute('style', 'flex: 4')
var onClick = (domEl) => {
var onClick = () => {
this.clickCallBack(this.funABI.inputs, this.basicInputField.value)
}
let funcButton = yo`<button onclick=${() => onClick()} class="${css.instanceButton} btn btn-sm">${title}</button>`
@ -204,8 +204,9 @@ class MultiParamManager {
if (this.funABI.inputs && this.funABI.inputs.length > 0) {
contractProperty.classList.add(css.hasArgs)
} else if (this.funABI.type === 'fallback') {
} else if (this.funABI.type === 'fallback' || this.funABI.type === 'receive') {
contractProperty.classList.add(css.hasArgs)
this.basicInputField.setAttribute('title', `'(${this.funABI.type}')`) // probably should pass name instead
this.contractActionsContainerSingle.querySelector('i').style.visibility = 'hidden'
} else {
this.contractActionsContainerSingle.querySelector('i').style.visibility = 'hidden'

@ -143,8 +143,8 @@ export class PermissionHandler {
? yo`<input type="checkbox" onchange="${switchMode}" checkbox class="custom-control-input" id="remember">`
: yo`<input type="checkbox" onchange="${switchMode}" class="custom-control-input" id="remember">`
const message = remember
? `${fromName} has changed and would like to access the plugin ${toName}.`
: `${fromName} would like to access plugin ${toName}.`
? `"${fromName}" has changed and would like to access "${toName}"`
: `"${fromName}" would like to access "${toName}"`
return yo`
<section class="${css.permission}">

@ -0,0 +1,109 @@
const yo = require('yo-yo')
const remixLib = require('remix-lib')
const confirmDialog = require('./confirmDialog')
const modalCustom = require('./modal-dialog-custom')
const modalDialog = require('./modaldialog')
const typeConversion = remixLib.execution.typeConversion
const Web3 = require('web3')
module.exports = {
getCallBacksWithContext: (udappUI, executionContext) => {
let callbacks = {}
callbacks.confirmationCb = confirmationCb
callbacks.continueCb = continueCb
callbacks.promptCb = promptCb
callbacks.udappUI = udappUI
callbacks.executionContext = executionContext
return callbacks
}
}
const continueCb = function (error, continueTxExecution, cancelCb) {
if (error) {
const msg = typeof error !== 'string' ? error.message : error
modalDialog(
'Gas estimation failed',
yo`
<div>Gas estimation errored with the following message (see below).
The transaction execution will likely fail. Do you want to force sending? <br>${msg}</div>
`,
{
label: 'Send Transaction',
fn: () => continueTxExecution()
},
{
label: 'Cancel Transaction',
fn: () => cancelCb()
}
)
} else {
continueTxExecution()
}
}
const promptCb = function (okCb, cancelCb) {
modalCustom.promptPassphrase('Passphrase requested', 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb)
}
const confirmationCb = function (network, tx, gasEstimation, continueTxExecution, cancelCb) {
let self = this
if (network.name !== 'Main') {
return continueTxExecution(null)
}
var amount = Web3.utils.fromWei(typeConversion.toInt(tx.value), 'ether')
var content = confirmDialog(tx, amount, gasEstimation, self.udappUI,
(gasPrice, cb) => {
let txFeeText, priceStatus
// TODO: this try catch feels like an anti pattern, can/should be
// removed, but for now keeping the original logic
try {
var fee = Web3.utils.toBN(tx.gas).mul(Web3.utils.toBN(Web3.utils.toWei(gasPrice.toString(10), 'gwei')))
txFeeText = ' ' + Web3.utils.fromWei(fee.toString(10), 'ether') + ' Ether'
priceStatus = true
} catch (e) {
txFeeText = ' Please fix this issue before sending any transaction. ' + e.message
priceStatus = false
}
cb(txFeeText, priceStatus)
},
(cb) => {
self.executionContext.web3().eth.getGasPrice((error, gasPrice) => {
const warnMessage = ' Please fix this issue before sending any transaction. '
if (error) {
return cb('Unable to retrieve the current network gas price.' + warnMessage + error)
}
try {
var gasPriceValue = Web3.utils.fromWei(gasPrice.toString(10), 'gwei')
cb(null, gasPriceValue)
} catch (e) {
cb(warnMessage + e.message, null, false)
}
})
}
)
modalDialog(
'Confirm transaction',
content,
{ label: 'Confirm',
fn: () => {
self.udappUI.udapp.config.setUnpersistedProperty(
'doNotShowTransactionConfirmationAgain',
content.querySelector('input#confirmsetting').checked
)
// TODO: check if this is check is still valid given the refactor
if (!content.gasPriceStatus) {
cancelCb('Given gas price is not correct')
} else {
var gasPrice = Web3.utils.toWei(content.querySelector('#gasprice').value, 'gwei')
continueTxExecution(gasPrice)
}
}
},
{
label: 'Cancel',
fn: () => {
return cancelCb('Transaction canceled by user.')
}
}
)
}

@ -278,12 +278,12 @@ function renderEmptyBlock (self, data) {
}
function checkTxStatus (tx, type) {
if (tx.status === '0x1') {
if (tx.status === '0x1' || tx.status === true) {
return yo`<i class="${css.txStatus} ${css.succeeded} fas fa-check-circle"></i>`
}
if (type === 'call' || type === 'unknownCall') {
return yo`<i class="${css.txStatus} ${css.call}">call</i>`
} else if (tx.status === '0x0') {
} else if (tx.status === '0x0' || tx.status === false) {
return yo`<i class="${css.txStatus} ${css.failed} fas fa-times-circle"></i>`
} else {
return yo`<i class="${css.txStatus} ${css.notavailable} fas fa-circle-thin" title='Status not available' ></i>`
@ -387,10 +387,10 @@ function createTable (opts) {
var table = yo`<table class="${css.txTable}" id="txTable"></table>`
if (!opts.isCall) {
var msg = ''
if (opts.status) {
if (opts.status === '0x0') {
if (opts.status !== undefined && opts.status !== null) {
if (opts.status === '0x0' || opts.status === false) {
msg = ' Transaction mined but execution failed'
} else if (opts.status === '0x1') {
} else if (opts.status === '0x1' || opts.status === true) {
msg = ' Transaction mined and execution succeed'
}
} else {

@ -1,27 +1,30 @@
/* global */
'use strict'
const $ = require('jquery')
const yo = require('yo-yo')
const ethJSUtil = require('ethereumjs-util')
const BN = ethJSUtil.BN
const helper = require('../../lib/helper')
const copyToClipboard = require('./copy-to-clipboard')
const css = require('../../universal-dapp-styles')
const MultiParamManager = require('./multiParamManager')
const remixLib = require('remix-lib')
const txFormat = remixLib.execution.txFormat
var $ = require('jquery')
var yo = require('yo-yo')
var ethJSUtil = require('ethereumjs-util')
var BN = ethJSUtil.BN
var helper = require('../../lib/helper')
var copyToClipboard = require('./copy-to-clipboard')
var css = require('../../universal-dapp-styles')
var MultiParamManager = require('./multiParamManager')
var remixLib = require('remix-lib')
var txFormat = remixLib.execution.txFormat
const txHelper = remixLib.execution.txHelper
var TreeView = require('./TreeView')
var txCallBacks = require('./sendTxCallbacks')
const confirmDialog = require('./confirmDialog')
const modalCustom = require('./modal-dialog-custom')
const modalDialog = require('./modaldialog')
const TreeView = require('./TreeView')
function UniversalDAppUI (blockchain, logCallback) {
this.blockchain = blockchain
this.logCallback = logCallback
this.compilerData = {contractsDetails: {}}
}
function decodeResponseToTreeView (response, fnabi) {
const treeView = new TreeView({
var treeView = new TreeView({
extractData: (item, parent, key) => {
let ret = {}
var ret = {}
if (BN.isBN(item)) {
ret.self = item.toString(10)
ret.children = []
@ -34,42 +37,28 @@ function decodeResponseToTreeView (response, fnabi) {
return treeView.render(txFormat.decodeResponse(response, fnabi))
}
class UniversalDAppUI {
constructor (blockchain, logCallback) {
this.blockchain = blockchain
this.logCallback = logCallback
this.compilerData = { contractsDetails: {} }
}
renderInstance (contract, address, contractName) {
const noInstances = document.querySelector('[class^="noInstancesText"]')
if (noInstances) {
noInstances.parentNode.removeChild(noInstances)
}
const abi = txHelper.sortAbiFunction(contract.abi)
return this.renderInstanceFromABI(abi, address, contractName)
UniversalDAppUI.prototype.renderInstance = function (contract, address, contractName) {
var noInstances = document.querySelector('[class^="noInstancesText"]')
if (noInstances) {
noInstances.parentNode.removeChild(noInstances)
}
const abi = txHelper.sortAbiFunction(contract.abi)
return this.renderInstanceFromABI(abi, address, contractName)
}
// TODO this function was named before "appendChild".
// this will render an instance: contract name, contract address, and all the public functions
// basically this has to be called for the "atAddress" (line 393) and when a contract creation succeed
// this returns a DOM element
renderInstanceFromABI (contractABI, address, contractName) {
address = (address.slice(0, 2) === '0x' ? '' : '0x') + address.toString('hex')
address = ethJSUtil.toChecksumAddress(address)
const instance = yo`<div class="instance ${css.instance} ${css.hidesub}" id="instance${address}"></div>`
const context = (this.blockchain.getProvider() === 'vm' ? 'memory' : 'blockchain')
function toggleClass (e) {
$(instance).toggleClass(`${css.hidesub}`)
// e.currentTarget.querySelector('i')
e.currentTarget.querySelector('i').classList.toggle(`fa-angle-right`)
e.currentTarget.querySelector('i').classList.toggle(`fa-angle-down`)
}
const shortAddress = helper.shortenAddress(address)
const title = yo`
// TODO this function was named before "appendChild".
// this will render an instance: contract name, contract address, and all the public functions
// basically this has to be called for the "atAddress" (line 393) and when a contract creation succeed
// this returns a DOM element
UniversalDAppUI.prototype.renderInstanceFromABI = function (contractABI, address, contractName) {
let self = this
address = (address.slice(0, 2) === '0x' ? '' : '0x') + address.toString('hex')
address = ethJSUtil.toChecksumAddress(address)
var instance = yo`<div class="instance ${css.instance} ${css.hidesub}" id="instance${address}"></div>`
const context = this.blockchain.context()
var shortAddress = helper.shortenAddress(address)
var title = yo`
<div class="${css.title} alert alert-secondary p-2">
<button class="btn ${css.titleExpander}" onclick="${(e) => { toggleClass(e) }}">
<i class="fas fa-angle-right" aria-hidden="true"></i>
@ -87,155 +76,174 @@ class UniversalDAppUI {
</div>
`
const close = yo`
var close = yo`
<button
class="${css.udappClose} p-1 btn btn-secondary"
onclick=${() => { instance.remove() }}
onclick=${remove}
title="Remove from the list"
>
<i class="${css.closeIcon} fas fa-times" aria-hidden="true"></i>
</button>`
title.querySelector('.btn-group').appendChild(close)
title.querySelector('.btn-group').appendChild(close)
const contractActionsWrapper = yo`
var contractActionsWrapper = yo`
<div class="${css.cActionsWrapper}">
</div>
`
instance.appendChild(title)
instance.appendChild(contractActionsWrapper)
// Add the fallback function
const fallback = txHelper.getFallbackInterface(contractABI)
if (fallback) {
contractActionsWrapper.appendChild(this.getCallButton({
funABI: fallback,
address: address,
contractAbi: contractABI,
contractName: contractName
}))
}
function remove () {
instance.remove()
// @TODO perhaps add a callack here to warn the caller that the instance has been removed
}
$.each(contractABI, (i, funABI) => {
if (funABI.type !== 'function') {
return
}
// @todo getData cannot be used with overloaded functions
contractActionsWrapper.appendChild(this.getCallButton({
funABI: funABI,
address: address,
contractAbi: contractABI,
contractName: contractName
}))
})
return instance
function toggleClass (e) {
$(instance).toggleClass(`${css.hidesub}`)
// e.currentTarget.querySelector('i')
e.currentTarget.querySelector('i').classList.toggle(`fa-angle-right`)
e.currentTarget.querySelector('i').classList.toggle(`fa-angle-down`)
}
getConfirmationCb (modalDialog, confirmDialog) {
const confirmationCb = (network, tx, gasEstimation, continueTxExecution, cancelCb) => {
if (network.name !== 'Main') {
return continueTxExecution(null)
}
const amount = this.blockchain.fromWei(tx.value, true, 'ether')
const content = confirmDialog(tx, amount, gasEstimation, null, this.blockchain.determineGasFees(tx), this.blockchain.determineGasPrice)
modalDialog('Confirm transaction', content,
{
label: 'Confirm',
fn: () => {
this.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked)
// TODO: check if this is check is still valid given the refactor
if (!content.gasPriceStatus) {
cancelCb('Given gas price is not correct')
} else {
const gasPrice = this.blockchain.toWei(content.querySelector('#gasprice').value, 'gwei')
continueTxExecution(gasPrice)
}
}
}, {
label: 'Cancel',
fn: () => {
return cancelCb('Transaction canceled by user.')
}
}
)
}
instance.appendChild(title)
instance.appendChild(contractActionsWrapper)
return confirmationCb
}
$.each(contractABI, (i, funABI) => {
if (funABI.type !== 'function') {
return
}
// @todo getData cannot be used with overloaded functions
contractActionsWrapper.appendChild(this.getCallButton({
funABI: funABI,
address: address,
contractABI: contractABI,
contractName: contractName
}))
})
// TODO this is used by renderInstance when a new instance is displayed.
// this returns a DOM element.
getCallButton (args) {
// args.funABI, args.address [fun only]
// args.contractName [constr only]
const lookupOnly = args.funABI.stateMutability === 'view' || args.funABI.stateMutability === 'pure' || args.funABI.constant
const calldataInput = yo`
<input id="deployAndRunLLTxCalldata" class="w-100 m-0" title="The Calldata to send to fallback function of the contract.">
`
const llIError = yo`
<label id="deployAndRunLLTxError" class="text-danger"></label>
`
// constract LLInteractions elements
const lowLevelInteracions = yo`
<div class="d-flex flex-column">
<div class="d-flex flex-row justify-content-between mt-2">
<label class="pt-2 border-top d-flex justify-content-start flex-grow-1">
Low level interactions
</label>
<a
href="https://solidity.readthedocs.io/en/v0.6.2/contracts.html#receive-ether-function"
title="check out docs for using 'receive'/'fallback'"
target="_blank"
>
<i aria-hidden="true" class="fas fa-info text-info my-2 mr-2"></i>
</a>
</div>
<div class="d-flex flex-column">
<div class="d-flex justify-content-end m-2 align-items-center">
<label class="mr-2 m-0">Calldata</label>
${calldataInput}
<button id="deployAndRunLLTxSendTransaction" class="btn btn-sm btn-secondary" title="Send data to contract." onclick=${() => sendData()}>Transact</button>
</div>
</div>
<div>
${llIError}
</div>
</div>
`
const outputOverride = yo`<div class=${css.value}></div>` // show return value
function sendData () {
function setLLIError (text) {
llIError.innerText = text
}
const clickButton = (valArr, inputsValues) => {
let logMsg
if (!lookupOnly) {
logMsg = `call to ${args.contractName}.${(args.funABI.name) ? args.funABI.name : '(fallback)'}`
} else {
logMsg = `transact to ${args.contractName}.${(args.funABI.name) ? args.funABI.name : '(fallback)'}`
setLLIError('')
const fallback = txHelper.getFallbackInterface(contractABI)
const receive = txHelper.getReceiveInterface(contractABI)
const args = {
funABI: fallback || receive,
address: address,
contractName: contractName,
contractABI: contractABI
}
const amount = document.querySelector('#value').value
if (amount !== '0') {
// check for numeric and receive/fallback
if (!helper.isNumeric(amount)) {
return setLLIError('Value to send should be a number')
} else if (!receive && !(fallback && fallback.stateMutability === 'payable')) {
return setLLIError("In order to receive Ether transfer the contract should have either 'receive' or payable 'fallback' function")
}
const confirmationCb = this.getConfirmationCb(modalDialog, confirmDialog)
const continueCb = (error, continueTxExecution, cancelCb) => {
if (error) {
const msg = typeof error !== 'string' ? error.message : error
modalDialog(
'Gas estimation failed',
yo`
<div>Gas estimation errored with the following message (see below).
The transaction execution will likely fail. Do you want to force sending? <br>${msg}</div>
`,
{
label: 'Send Transaction',
fn: () => continueTxExecution()
},
{
label: 'Cancel Transaction',
fn: () => cancelCb()
}
)
} else {
continueTxExecution()
}
let calldata = calldataInput.value
if (calldata) {
if (calldata.length < 2 || calldata.length < 4 && helper.is0XPrefixed(calldata)) {
return setLLIError('The calldata should be a valid hexadecimal value with size of at least one byte.')
} else {
if (helper.is0XPrefixed(calldata)) {
calldata = calldata.substr(2, calldata.length)
}
if (!helper.isHexadecimal(calldata)) {
return setLLIError('The calldata should be a valid hexadecimal value.')
}
}
const outputCb = (returnValue) => {
const decoded = decodeResponseToTreeView(returnValue, args.funABI)
outputOverride.innerHTML = ''
outputOverride.appendChild(decoded)
if (!fallback) {
return setLLIError("'Fallback' function is not defined")
}
}
const promptCb = (okCb, cancelCb) => {
modalCustom.promptPassphrase('Passphrase requested', 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb)
}
if (!receive && !fallback) return setLLIError(`Both 'receive' and 'fallback' functions are not defined`)
const callType = args.funABI.type !== 'fallback' ? inputsValues : ''
this.blockchain.runOrCallContractMethod(args.contractName, args.contractAbi, args.funABI, inputsValues, args.address, callType, lookupOnly, logMsg, this.logCallback, outputCb, confirmationCb, continueCb, promptCb)
}
// we have to put the right function ABI:
// if receive is defined and that there is no calldata => receive function is called
// if fallback is defined => fallback function is called
if (receive && !calldata) args.funABI = receive
else if (fallback) args.funABI = fallback
let inputs = ''
if (args.funABI.inputs) {
inputs = txHelper.inputParametersDeclarationToString(args.funABI.inputs)
}
if (!args.funABI) return setLLIError(`Please define a 'Fallback' function to send calldata and a either 'Receive' or payable 'Fallback' to send ethers`)
self.runTransaction(false, args, null, calldataInput.value, null)
}
const multiParamManager = new MultiParamManager(lookupOnly, args.funABI, (valArray, inputsValues, domEl) => {
clickButton(valArray, inputsValues, domEl)
}, inputs)
contractActionsWrapper.appendChild(lowLevelInteracions)
return instance
}
const contractActionsContainer = yo`<div class="${css.contractActionsContainer}" >${multiParamManager.render()}</div>`
contractActionsContainer.appendChild(outputOverride)
// TODO this is used by renderInstance when a new instance is displayed.
// this returns a DOM element.
UniversalDAppUI.prototype.getCallButton = function (args) {
let self = this
var outputOverride = yo`<div class=${css.value}></div>` // show return value
const isConstant = args.funABI.constant !== undefined ? args.funABI.constant : false
const lookupOnly = args.funABI.stateMutability === 'view' || args.funABI.stateMutability === 'pure' || isConstant
const multiParamManager = new MultiParamManager(
lookupOnly,
args.funABI,
(valArray, inputsValues) => self.runTransaction(lookupOnly, args, valArray, inputsValues, outputOverride),
self.blockchain.getInputs(args.funABI)
)
const contractActionsContainer = yo`<div class="${css.contractActionsContainer}" >${multiParamManager.render()}</div>`
contractActionsContainer.appendChild(outputOverride)
return contractActionsContainer
}
return contractActionsContainer
}
UniversalDAppUI.prototype.runTransaction = function (lookupOnly, args, valArr, inputsValues, outputOverride) {
const functionName = args.funABI.type === 'function' ? args.funABI.name : `(${args.funABI.type})`
const logMsg = `${lookupOnly ? 'call' : 'transact'} to ${args.contractName}.${functionName}`
const callbacksInContext = txCallBacks.getCallBacksWithContext(this, this.executionContext)
const outputCb = (returnValue) => {
if (outputOverride) {
const decoded = decodeResponseToTreeView(returnValue, args.funABI)
outputOverride.innerHTML = ''
outputOverride.appendChild(decoded)
}
}
const params = args.funABI.type !== 'fallback' ? inputsValues : ''
this.blockchain.runOrCallContractMethod(args.contractName, args.contractAbi, args.funABI, inputsValues, args.address, params, lookupOnly, logMsg, this.logCallback, outputCb, callbacksInContext)
}
module.exports = UniversalDAppUI

@ -2,8 +2,9 @@ const remixLib = require('remix-lib')
const txFormat = remixLib.execution.txFormat
const txExecution = remixLib.execution.txExecution
const typeConversion = remixLib.execution.typeConversion
const TxRunner = remixLib.execution.txRunner
const Txlistener = remixLib.execution.txListener
const TxRunner = remixLib.execution.txRunner
const txHelper = remixLib.execution.txHelper
const EventManager = remixLib.EventManager
const executionContext = remixLib.execution.executionContext
const ethJSUtil = require('ethereumjs-util')
@ -56,6 +57,18 @@ class Blockchain {
this.executionContext.event.register('removeProvider', (name) => {
this.event.trigger('removeProvider', [name])
})
// this.udapp.event.register('initiatingTransaction', (timestamp, tx, payLoad) => {
// this.event.trigger('initiatingTransaction', [timestamp, tx, payLoad])
// })
// this.udapp.event.register('transactionExecuted', (error, from, to, data, call, txResult, timestamp) => {
// this.event.trigger('transactionExecuted', [error, from, to, data, call, txResult, timestamp])
// })
// this.udapp.event.register('transactionBroadcasted', (txhash, networkName) => {
// this.event.trigger('transactionBroadcasted', [txhash, networkName])
// })
}
setupProviders () {
@ -146,6 +159,13 @@ class Blockchain {
})
}
getInputs (funABI) {
if (!funABI.inputs) {
return ''
}
return txHelper.inputParametersDeclarationToString(funABI.inputs)
}
fromWei (value, doTypeConversion, unit) {
if (doTypeConversion) {
return Web3.utils.fromWei(typeConversion.toInt(value), unit || 'ether')
@ -230,6 +250,7 @@ class Blockchain {
getTxListener (opts) {
opts.event = {
// udapp: this.udapp.event
udapp: this.event
}
const txlistener = new Txlistener(opts, this.executionContext)
@ -268,6 +289,10 @@ class Blockchain {
})
}
context () {
return (this.executionContext.isVM() ? 'memory' : 'blockchain')
}
// NOTE: the config is only needed because exectuionContext.init does
// if config.get('settings/always-use-vm'), we can simplify this later
resetAndInit (config, transactionContextAPI) {

@ -44,6 +44,15 @@ module.exports = {
checkSpecialChars (name) {
return name.match(/[:*?"<>\\'|]/) != null
},
isHexadecimal (value) {
return /^[0-9a-fA-F]+$/.test(value) && (value.length % 2 === 0)
},
is0XPrefixed (value) {
return value.substr(0, 2) === '0x'
},
isNumeric (value) {
return /^\+?(0|[1-9]\d*)$/.test(value)
},
find: find
}

@ -27,11 +27,21 @@ module.exports = (contract, fileManager, cb, ipfsVerifiedPublishCallBack) => {
async.eachSeries(Object.keys(metadata.sources), function (fileName, cb) {
// find hash
var hash
let hash = null
try {
hash = metadata.sources[fileName].urls[1].match('dweb:/ipfs/(.+)')[1]
// we try extract the hash defined in the metadata.json
// in order to check if the hash that we get after publishing is the same as the one located in metadata.json
// if it's not the same, we throw "hash mismatch between solidity bytecode and uploaded content"
// if we don't find the hash in the metadata.json, the check is not done.
//
// TODO: refactor this with publishOnSwarm
if (metadata.sources[fileName].urls) {
metadata.sources[fileName].urls.forEach(url => {
if (url.includes('ipfs')) hash = url.match('dweb:/ipfs/(.+)')[1]
})
}
} catch (e) {
return cb('Metadata inconsistency')
return cb('Error while extracting the hash from metadata.json')
}
fileManager.fileProviderOf(fileName).get(fileName, (error, content) => {
@ -94,7 +104,7 @@ module.exports = (contract, fileManager, cb, ipfsVerifiedPublishCallBack) => {
async function ipfsVerifiedPublish (content, expectedHash, cb) {
try {
const results = await severalGatewaysPush(content)
if (results !== expectedHash) {
if (expectedHash && results !== expectedHash) {
cb(null, { message: 'hash mismatch between solidity bytecode and uploaded content.', url: 'dweb:/ipfs/' + results, hash: results })
} else {
cb(null, { message: 'ok', url: 'dweb:/ipfs/' + results, hash: results })

@ -20,11 +20,21 @@ module.exports = (contract, fileManager, cb, swarmVerifiedPublishCallBack) => {
async.eachSeries(Object.keys(metadata.sources), function (fileName, cb) {
// find hash
var hash
let hash = null
try {
hash = metadata.sources[fileName].urls[0].match('(bzzr|bzz-raw)://(.+)')[1]
// we try extract the hash defined in the metadata.json
// in order to check if the hash that we get after publishing is the same as the one located in metadata.json
// if it's not the same, we throw "hash mismatch between solidity bytecode and uploaded content"
// if we don't find the hash in the metadata.json, the check is not done.
//
// TODO: refactor this with publishOnIpfs
if (metadata.sources[fileName].urls) {
metadata.sources[fileName].urls.forEach(url => {
if (url.includes('bzz')) hash = url.match('(bzzr|bzz-raw)://(.+)')[1]
})
}
} catch (e) {
return cb('Metadata inconsistency')
return cb('Error while extracting the hash from metadata.json')
}
fileManager.fileProviderOf(fileName).get(fileName, (error, content) => {
@ -90,7 +100,7 @@ function swarmVerifiedPublish (content, expectedHash, cb) {
swarmgw.put(content, function (err, ret) {
if (err) {
cb(err)
} else if (ret !== expectedHash) {
} else if (expectedHash && ret !== expectedHash) {
cb(null, { message: 'hash mismatch between solidity bytecode and uploaded content.', url: 'bzz-raw://' + ret, hash: ret })
} else {
cb(null, { message: 'ok', url: 'bzz-raw://' + ret, hash: ret })

@ -2,6 +2,7 @@
import { PluginEngine, IframePlugin } from '@remixproject/engine'
import { EventEmitter } from 'events'
import { PermissionHandler } from './app/ui/persmission-handler'
import QueryParams from './lib/query-params'
const requiredModules = [ // services + layout views + system views
'compilerArtefacts', 'compilerMetadata', 'contextualListener', 'editor', 'offsetToLineColumnConverter', 'network', 'theme', 'fileManager', 'contentImport',
@ -19,15 +20,13 @@ export class RemixAppManager extends PluginEngine {
constructor (plugins) {
super(plugins, settings)
this.event = new EventEmitter()
this.donotAutoReload = ['remixd'] // that would be a bad practice to force loading some plugins at page load.
this.registered = {}
this.pluginsDirectory = 'https://raw.githubusercontent.com/ethereum/remix-plugins-directory/master/build/profile.json'
this.pluginsDirectory = 'https://raw.githubusercontent.com/ethereum/remix-plugins-directory/master/build/metadata.json'
this.pluginLoader = new PluginLoader()
}
onActivated (plugin) {
if (!this.donotAutoReload.includes(plugin.name)) {
localStorage.setItem('workspace', JSON.stringify(this.actives))
}
this.pluginLoader.set(plugin, this.actives)
this.event.emit('activate', plugin.name)
}
@ -46,7 +45,7 @@ export class RemixAppManager extends PluginEngine {
}
onDeactivated (plugin) {
localStorage.setItem('workspace', JSON.stringify(this.actives))
this.pluginLoader.set(plugin, this.actives)
this.event.emit('deactivate', plugin.name)
}
@ -78,157 +77,51 @@ export class RemixAppManager extends PluginEngine {
}
async registeredPlugins () {
const pipeline = {
name: 'pipeline',
displayName: 'Pipeline',
events: [],
methods: [],
notifications: {
'solidity': ['compilationFinished']
},
url: 'https://pipeline-alpha.pipeos.one',
description: 'Visual IDE for contracts and dapps',
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAAAAACIM/FCAAAJU0lEQVR42u3ce1QUVRgA8EEemgoWvjkltIZlIJhCmmTmqVBJJSofaaWpYUUn08wHmkfR6GFWqKh51CDzLZkPKs2MNAkFVAgxw007WZklaWqsLutt0b13LusM7LDfN+x07vcXe3dmvvkxszN35rszEvmfhCQgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgHgspXbcsdVoybKQsXrPfqivk0qoBJqSImV+uG6Tyq34mxOi+7oI+EMt4E3L0P60H5PRjJvSIOYwPsejgsO9ev6FDXjXpEgMvIUM2mXSKibgQWz+9IJ3OoEJWcbtx+t4yM2gcK8jk/k9TUSHyefClUjNClM0NpQnCLiJCfmCOJDNSLGQpNiJCVgQ1b+YfcFPrdt1KsSDmwSFtAgP8AwLbjMeCVGYOu1FyRPvEfBzG4WlRXo4cfvGv/4sByYmQ+Gg8/hiC4/1W1ZIEZbgNWb62+ueKV/0kp+i2FZqR19/LOcnQ392EJEpdc7iP1u7S9XHDZlhH4a0KSW7+xV2IJD28hW2PxyWlaLEF0pEbrpgk6qTbEEnq8uW1TyMl5QiE/MmHqyTpesl9iH2rVB3NCyS1GAHnmKua5C0IiH2rZFdGqOaQ1kM58puo5vA/DQKxb1v5zzZPb8zb+XpnuSEGCjJWXqZpXPa+7EkmuWEyEESO4Y4z1AYf1vQpEOQGtsQZtmsd7VT5nHW+zpATCUqO/uxnt9qXts2EcexiSeRe71TWllU3iC2zZwMlxy1cjyGZNibAQF6jy+vDrUgf2jivTpC9d6n86DrlyROdpo0dYSCD6PKWc2vyFW0cXQdI5cvqhygp7JMrdLrmjqYmMJBuNMXxaqvniPvrABki1Rg9yhzTRdIW4LOhVQkSpR0ytWZH30I6IT06+sJA2O5crgS5RzNkd4OaGJE75SnphK1hIA/SHFwK8hdtHKgVUtG+Jke0TZ5yK228DwbyAl3eFG51ZtLGaVohu2vesbayCS+E0rZxMJBldHlNyliSMtZr+UArZFTNEP9sx3R/RrG2dBhIqXwBUuRIcvRm1nZEI8TWku2UwxUlvslV/bfKTWx7SAElQF2ULmyRzdOrTryW9ObyoVJrX+t7OmfTP7i+Vv+PmsofmsSOGsxfyE2E6jRm8pc5A0cP5C/et2uFfEznfEruNEblEpKiurO1OwIFkY9b10W85uuR2XI/wQF5ZFvVkcpiUsuxEsxhzm2kkqNxkWbIeDrvrmuQe3IdXxxQueoZYwaM91Qgq4lmyHN03n1VkDi5Y0W+aKiUYuiPkBBzip9SkneIdgj7YRSQxN77qp9hWihsD1iH/bLd97ocfiuIe5ATzlOefNT5d77EDB6bna8guhUSNyEK8W0MlyHgtSNmhDi2gD+uBK+/QhAghOQ/3NDXS5K8/Rr3KjEjxY+jmvrZk3j5+EVuryQEB0J2seLFKizInjtpivmEoEEudqJZInbjOArj2f+qBBFCZrI0HaYj1HrKFnRmCR4nmJDjoXK1MmxQ0ouw8WQXrqz7GSqEvK1XeTqe4EJ+7ayPo30uMoQcDtMFsplgQ0iWHo6ZBB9CcqKwGbevrNQDQn7ui+u4O4cQXSDk8sYBeIyeS88RvSD2KJ7RB0UxYWclIXpCqvorP31fDBtlZ0ldwx2IR8X/FMIKkvsNDmE1nhyDQ9jd7wyDQz6kkLEGhxSxO7x/GRtibUYlE/4xNISrj7Sds99QFifIF5IHh1ejkIcmbbjkEsQS7MkSx83HPBcgZI3nQySv0Rdqh5DpBpBIwUdqh9juMoKk9alaIeTcy0aQ3Hu5Vgghac0MIHnDBQgp/6hHA0+HND7uAsQeJz4eFx8T1dWjIiKEL80tdA3imWH9ZoI3hfQxMsQeKynE94KxIYSNX8oxOGQyhSwxOGQ9hUw3OGQPhbxkcEg+hSQaHFIgIMpR/u2m1bCxZsv+8zpDbCULHkKpKIQmrPxJR0jpMMwqT9IpvSAZobj1Kn6UNCLk7BD8SugUKz7E9rweNd0UfEiSLkV202JsyAZ9HKbQIlxIRS+dIKancCEZXKrBs9Lmg0Zachy3+AJMiLUnyzPmM4xRZxvk8v2zmJA9LM0InOFz5h9iaYbbziBCUoPbtmzRsm2wqfchrBGNOeGmdm1atmgVFJKFBvklNYRe9yfsxYJ8Hk2fOe245CwK5NAr/Bj8hmOyMRirHvHmkgTOOQkPyfBxutPn/TY442iSt1OSZruBIValR5YGHYV1HIi5PofPHFCI7QnF268DQN+4UxSmmGSu88rk0W9e0A6ZpHIjeSwkpLdyDi+nt4HIZc6JmiE7VW+JAz7Pk6qWw6es+trMo1/M0wqxdFWFhIM9sZCnXpMZWX11Yml7llbIfG6hvVIWpfTiPs+AgnAPp3jHv7VoaiSXZC+/Nj+z5t+0QjqwWSOvFYbz5DLjrUCv3cmXKx+xV28/XMkKYi1DuZUp6UhbOxCNkINsgXG0Vm+JY21Ab3hho3nk91T8fTvbRAs/uBoLU0fKD3JKs7RC1tI5g+QHtMvZvysNBvIsXd598tDMQz411t52aIUk0TnXKumehIF0psv7zoWj/tWIJ1oh/eisxVxjMfv1w0DoY7l+/OvaanqUu+kxzZBoOm8F11hBGyNgILSIHKbYGVGIbaTuEAt/bqGNnWAg9LVa4S5C3iTaIX3pzPwjTyXAu1agY3GNXNq1AneQOkA878c+Ruk9p7VDWD24A3f4ZSfJd2Egz7BzlXz4LQ5QQPjHpim/eFbTCdFSvyfEpZnZJTa11awdcll+5rS3o4vCjshSO6CLqzy5izLE0UVhBxkpwZVLDa2dxlmLZunQaUxX7zS6Afn3TtWj4G2HoSC71Lvxrj1YabwLKzcgKm8/s/evzYARrXKpu5jAQWzD9Lj5EO7azQd3IMQap5DifuAXJRy4Q2G/SiKgEE+6QecmhBwcUO2W6QPbzAjxYTRPCXjmJIGH1MNNbE2PI2oqK6TQssKDR7Eg+ayssI4QNIhHFHogINZoJkk8iOHYI5feniaIEI8ohoJAKmL0Kk8PJ6gQ/QYMFCJDPGAIBxDk4mg9HBMJOqT+hzlBQcjl97AHnq0hRA9IfQ8FBIQQ29fJWIMzZxcRoh+kKuptuCw0xNNCQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQATEY+M/DV/rjLxphEwAAAAASUVORK5CYII=',
location: 'mainPanel'
}
const provable = {
name: 'provable',
displayName: 'Provable - oracle service',
events: [],
methods: [],
notifications: {
'udapp': ['newTransaction'],
'network': ['providerChanged']
},
url: 'https://remix-plugin.provable.xyz',
documentation: 'https://docs.oraclize.it/#development-tools-remix-ide-provable-plugin',
description: 'request real-world data for your contracts',
icon: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNTcuNTMgMTU5Ij48ZGVmcz48c3R5bGU+LmNscy0xe2ZpbGw6bm9uZTtzdHJva2U6I2IzYjNiMztzdHJva2UtbWl0ZXJsaW1pdDoxMDtzdHJva2Utd2lkdGg6OHB4O308L3N0eWxlPjwvZGVmcz48dGl0bGU+bG9nby1vdXRibGFjay1pbm5lcmdyYXk8L3RpdGxlPjxnIGlkPSJmZzEiPjxwYXRoIGQ9Ik0xNjkuMjksNjZDMTU5LjM3LDQ1LjQ5LDE0MiwyOS4xMywxMTkuNzYsMjMuMTVBNzkuMDgsNzkuMDgsMCwwLDAsNDgsMzkuNTVjLTMuNjgsMy4xMywxLjY1LDguNDIsNS4zLDUuMzFhNzEuMjYsNzEuMjYsMCwwLDEsNjUuNzgtMTQuMTFjMTkuOTIsNS43NywzNC44MywyMC42Niw0My43MiwzOSwyLjEsNC4zNSw4LjU3LjU1LDYuNDgtMy43OFoiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMS42NSAtMjAuNDgpIi8+PHBhdGggZD0iTTEyNiwxNzAuNDJjMTUuNzQtNi4xOSwyOS41Ny0xNi4zMSwzOC42OC0zMC43NCw5LjY5LTE1LjMyLDEzLjA3LTM0LjE0LDEwLjc5LTUyLS42LTQuNzItOC4xMS00Ljc4LTcuNSwwLDIuMDcsMTYuMjEtLjU0LDMzLTkuMDgsNDcuMTEtOCwxMy4yMS0yMC42MSwyMi43OC0zNC44OCwyOC40LTQuNDQsMS43NC0yLjUxLDksMiw3LjIzWiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTIxLjY1IC0yMC40OCkiLz48cGF0aCBkPSJNMzQuMDgsNTUuNThjLTE1Ljc5LDI1LjQxLTE3LDU3LjQ0LTEsODMuMThBNzguMiw3OC4yLDAsMCwwLDEwMiwxNzUuNDFjNC44MS0uMTYsNC44NC03LjY2LDAtNy41LTI1LjE0LjgtNDkuMTItMTEuNDMtNjIuNDYtMzIuOTMtMTQuNjEtMjMuNTQtMTMuNDMtNTIuMzksMS03NS42MSwyLjU2LTQuMTEtMy45My03Ljg4LTYuNDctMy43OVoiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMS42NSAtMjAuNDgpIi8+PHBhdGggZD0iTTk4Ljg0LDQwLjg2YTU4LDU4LDAsMCwwLTQ5LDI3Ljc4Yy0xMi4xNiwyMC0xMC44NCw0My44OSwzLjM2LDYyLjQ1LDIuODksMy43OCw5LjQxLDAsNi40OC0zLjc5LTYuNTMtOC41Mi0xMS4yMS0xOC40OC0xMS0yOS40MkE1Mi4xMSw1Mi4xMSwwLDAsMSw1Ni4zLDcyLjQyLDUwLjI4LDUwLjI4LDAsMCwxLDk4Ljg0LDQ4LjM2YzQuODMtLjA2LDQuODQtNy41NiwwLTcuNVoiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMS42NSAtMjAuNDgpIi8+PHBhdGggZD0iTTcxLjIyLDE0OC4wNWMyMSwxMi4wOSw0OC4zMyw4LjQyLDY2LjIxLTcuODFBNTcuMjksNTcuMjksMCwwLDAsMTI0LjUsNDYuNzdjLTQuMjktMi4xOS04LjA5LDQuMjctMy43OSw2LjQ4LDI0LjYzLDEyLjYzLDM1LjM3LDQzLjMxLDIyLDY4LjEtMTIuOCwyMy44My00NCwzMy44Ni02Ny43NSwyMC4yMy00LjItMi40MS04LDQuMDctMy43OSw2LjQ3WiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTIxLjY1IC0yMC40OCkiLz48Y2lyY2xlIGNsYXNzPSJjbHMtMSIgY3g9Ijc3LjE5IiBjeT0iNzcuNDgiIHI9IjMzIi8+PGNpcmNsZSBjeD0iMjkuNTQiIGN5PSIyMS43NSIgcj0iNy43NSIvPjxjaXJjbGUgY3g9IjgwLjU0IiBjeT0iMTUxLjI1IiByPSI3Ljc1Ii8+PGNpcmNsZSBjeD0iMTQ5Ljc4IiBjeT0iNjcuMjUiIHI9IjcuNzUiLz48Y2lyY2xlIGN4PSIxMDAuNzkiIGN5PSIyOS41IiByPSI3Ljc1Ii8+PGNpcmNsZSBjeD0iMzQuNzkiIGN5PSIxMTAuNSIgcj0iNy43NSIvPjwvZz48L3N2Zz4=',
location: 'sidePanel'
}
const threeBox = {
name: 'box',
displayName: '3Box Spaces',
description: 'A decentralized storage for everything that happen on Remix',
methods: ['login', 'isEnabled', 'getUserAddress', 'openSpace', 'closeSpace', 'isSpaceOpened', 'getSpacePrivateValue', 'setSpacePrivateValue', 'getSpacePublicValue', 'setSpacePublicValue', 'getSpacePublicData'],
events: [],
version: '0.1.0-beta',
url: 'https://remix-3box.surge.sh',
icon: 'https://raw.githubusercontent.com/3box/3box-dapp/master/public/3Box3.png',
location: 'sidePanel'
}
const debugPlugin = {
name: 'debugPlugin',
displayName: 'Debug Tools for Remix plugins',
description: 'Easily test and debug your plugins !',
methods: ['sayHello', 'sayMyName', 'sayOurNames'], // test calls with 0, 1, and 2 args
events: [],
version: '0.1.0-alpha',
url: 'https://remix-debug-a.surge.sh',
icon: 'https://remix-debug-a.surge.sh/icon.png',
location: 'sidePanel'
}
const libraTools = {
name: 'libratools',
displayName: 'Libra and Move Tools',
events: [],
methods: [],
url: 'https://libra.pipeos.one',
description: 'Create, compile, deploy and interact with Libra modules and scripts',
icon: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzb2RpcG9kaT0iaHR0cDovL3NvZGlwb2RpLnNvdXJjZWZvcmdlLm5ldC9EVEQvc29kaXBvZGktMC5kdGQiCiAgIHhtbG5zOmlua3NjYXBlPSJodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy9uYW1lc3BhY2VzL2lua3NjYXBlIgogICB3aWR0aD0iMTAyNCIKICAgaGVpZ2h0PSIxMDI0IgogICB2aWV3Qm94PSIwIDAgMjcwLjkzMzMzIDI3MC45MzMzMyIKICAgdmVyc2lvbj0iMS4xIgogICBpZD0ic3ZnOCIKICAgaW5rc2NhcGU6dmVyc2lvbj0iMC45Mi4yIDVjM2U4MGQsIDIwMTctMDgtMDYiCiAgIHNvZGlwb2RpOmRvY25hbWU9ImxpYnJhLnN2ZyI+CiAgPGRlZnMKICAgICBpZD0iZGVmczIiIC8+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjAuNzczNDM3NSIKICAgICBpbmtzY2FwZTpjeD0iNTEyIgogICAgIGlua3NjYXBlOmN5PSI1MTIiCiAgICAgaW5rc2NhcGU6ZG9jdW1lbnQtdW5pdHM9InB4IgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImxheWVyMSIKICAgICBzaG93Z3JpZD0iZmFsc2UiCiAgICAgdW5pdHM9InB4IgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTY1MiIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSIxMDA1IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIwIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIxIgogICAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjEiIC8+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNSI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgICA8ZGM6dGl0bGU+PC9kYzp0aXRsZT4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiCiAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCwtMjYuMDY2NzEpIj4KICAgIDxnCiAgICAgICBpZD0iZzk3MCIKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAuODQzMTI1MjMsMCwwLDAuODQzMTI1MjMsMjEuMjUxMzAxLDI1LjM0MDUxMykiPgogICAgICA8cGF0aAogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNzc2MiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIGlkPSJwYXRoOTQ3IgogICAgICAgICBkPSJtIDQwLjc4NjQ0MywxMDcuNzM2MTcgYyAwLDAgMTkuMzI4MzA0LC0zNi44NDk4MzEgNDguOTMwMzg1LC0zNi44NDk4MzEgMjkuNjAyMDgyLDAgNjIuODI4ODkyLDI5LjYwMjA3MSA5Mi40MzA5ODIsMjkuNjAyMDcxIDI5LjYwMjA4LDAgNDcuNzI1NzksLTM4LjY2MzkzNSA0Ny43MjU3OSwtMzguNjYzOTM1IgogICAgICAgICBzdHlsZT0ib3BhY2l0eToxO2ZpbGw6bm9uZTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjcuNDg3NjQyMjk7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLW9wYWNpdHk6MSIgLz4KICAgICAgPHBhdGgKICAgICAgICAgc3R5bGU9Im9wYWNpdHk6MTtmaWxsOm5vbmU7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI3LjQ4NzY0MjI5O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICAgIGQ9Im0gNDAuNzg2NDQzLDE4NC4xNTc4NSBjIDAsMCAxOS4zMjgzMDQsLTM2Ljg0OTgyIDQ4LjkzMDM4NywtMzYuODQ5ODIgMjkuNjAyMDgsMCA2Mi44Mjg5LDI5LjYwMjA3IDkyLjQzMDk4LDI5LjYwMjA3IDI5LjYwMjA4LDAgNDcuNzI1NzksLTM4LjY2Mzk0IDQ3LjcyNTc5LC0zOC42NjM5NCIKICAgICAgICAgaWQ9InVzZTk1MSIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjc3NjIiAvPgogICAgICA8cGF0aAogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNzc2MiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgIGlkPSJ1c2U5NTUiCiAgICAgICAgIGQ9Im0gNDAuNzg2NDQzLDI2MC41Nzk1NSBjIDAsMCAxOS4zMjgzMDYsLTM2Ljg0OTgzIDQ4LjkzMDM4NywtMzYuODQ5ODMgMjkuNjAyMDgsMCA2Mi44Mjg5LDI5LjYwMjA3IDkyLjQzMDk4LDI5LjYwMjA3IDI5LjYwMjA4LDAgNDcuNzI1NzksLTM4LjY2MzkzIDQ3LjcyNTc5LC0zOC42NjM5MyIKICAgICAgICAgc3R5bGU9Im9wYWNpdHk6MTtmaWxsOm5vbmU7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI3LjQ4NzY0MjI5O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1vcGFjaXR5OjEiIC8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K',
location: 'sidePanel'
}
const oneClickDapp = {
name: 'oneClickDapp',
displayName: 'One Click Dapp',
events: [],
methods: [],
version: '0.1.0',
notifications: {
solidity: ['compilationFinished']
},
url: 'https://remix-one-click-dapp.surge.sh',
description: 'A free tool to generate smart contract interfaces.',
documentation: 'https://github.com/pi0neerpat/remix-plugin-one-click-dapp',
icon: 'https://remix-one-click-dapp.surge.sh/icon.png',
location: 'sidePanel'
}
const gasProfiler = {
name: 'gasProfiler',
displayName: 'Gas Profiler',
events: [],
methods: [],
version: '0.1.0-alpha',
url: 'https://remix-gas-profiler.surge.sh',
description: 'Profile gas costs',
icon: 'https://res.cloudinary.com/key-solutions/image/upload/v1565781702/gas-profiler_nxmsal.png',
location: 'sidePanel'
}
const flattener = {
name: 'flattener',
displayName: 'Flattener',
events: [],
methods: [],
version: '0.1.0',
url: 'https://remix-flattener.netlify.com',
description: 'Flattens compiled smart contracts',
documentation: 'https://github.com/Destiner/remix-flattener',
icon: 'https://remix-flattener.netlify.com/logo.svg',
location: 'sidePanel'
}
const ethpm = {
name: 'ethPM',
displayName: 'ethPM',
events: [],
methods: [],
notifications: {
solidity: ['compilationFinished']
const res = await fetch(this.pluginsDirectory)
const plugins = await res.json()
return plugins.map(plugin => new IframePlugin(plugin))
}
}
/** @class Reference loaders.
* A loader is a get,set based object which load a workspace from a defined sources.
* (localStorage, queryParams)
**/
class PluginLoader {
get currentLoader () {
return this.loaders[this.current]
}
constructor () {
const queryParams = new QueryParams()
this.donotAutoReload = ['remixd'] // that would be a bad practice to force loading some plugins at page load.
this.loaders = {}
this.loaders['localStorage'] = {
set: (plugin, actives) => {
if (!this.donotAutoReload.includes(plugin.name)) {
localStorage.setItem('workspace', JSON.stringify(actives))
}
},
url: 'https://ethpm.surge.sh',
description: 'Generate and import ethPM packages.',
documentation: 'https://docs.ethpm.com/ethpm-developer-guide/ethpm-and-remix-ide',
icon: 'https://ethpm.surge.sh/ethpmlogo.png',
location: 'mainPanel'
get: () => { return JSON.parse(localStorage.getItem('workspace')) }
}
const zokrates = {
name: 'ZoKrates',
displayName: 'ZoKrates',
description: 'ZoKrates toolbox for zkSNARKs on Ethereum',
documentation: 'https://zokrates.github.io/',
methods: [],
events: [],
version: '0.1.0-alpha',
url: 'https://zokrates.blockchain-it.hr/',
icon: 'https://zokrates.blockchain-it.hr/zokrates.svg',
location: 'sidePanel'
}
const quorum = {
name: 'quorum',
displayName: 'Quorum Network',
description: 'Deploy and interact with private contracts on a Quorum network.',
events: [],
methods: [],
url: '//remix-plugin.goquorum.com/',
icon: '//remix-plugin.goquorum.com/tab_icon.png',
documentation: 'https://docs.goquorum.com/en/latest/RemixPlugin/Overview/',
version: '0.1.4-beta',
location: 'sidePanel'
this.loaders['queryParams'] = {
set: () => {},
get: () => {
const { plugins } = queryParams.get()
if (!plugins) return []
return plugins.split(',')
}
}
const res = await fetch(this.pluginsDirectory)
const plugins = await res.json()
return [
new IframePlugin(pipeline),
new IframePlugin(provable),
new IframePlugin(threeBox),
new IframePlugin(debugPlugin),
new IframePlugin(libraTools),
new IframePlugin(oneClickDapp),
new IframePlugin(gasProfiler),
new IframePlugin(flattener),
new IframePlugin(ethpm),
new IframePlugin(zokrates),
new IframePlugin(quorum),
...plugins.map(plugin => new IframePlugin(plugin))
]
this.current = queryParams.get()['plugins'] ? 'queryParams' : 'localStorage'
}
set (plugin, actives) {
this.currentLoader.set(plugin, actives)
}
get () {
return this.currentLoader.get()
}
}

@ -3,7 +3,8 @@ const EventEmitter = require('events')
class ClickInstance extends EventEmitter {
command (index) {
index = index + 2
this.api.click('.instance:nth-of-type(' + index + ') > div > button').perform(() => { this.emit('complete') })
let selector = '.instance:nth-of-type(' + index + ') > div > button'
this.api.waitForElementPresent(selector).scrollAndClick(selector).perform(() => { this.emit('complete') })
return this
}
}

@ -2,7 +2,7 @@ const EventEmitter = require('events')
class ClickLaunchIcon extends EventEmitter {
command (icon) {
this.api.click('#icon-panel div[plugin="' + icon + '"]').perform(() => {
this.api.waitForElementVisible('#icon-panel div[plugin="' + icon + '"]').click('#icon-panel div[plugin="' + icon + '"]').perform(() => {
this.emit('complete')
})
return this

@ -6,6 +6,7 @@ class ExecuteScript extends EventEmitter {
.click('#terminalCli')
.keys(script)
.keys(this.api.Keys.ENTER)
.keys(this.api.Keys.ENTER) // that's a bug... sometimes we need to press 2 times to execute a command
.perform(() => {
this.emit('complete')
})

@ -0,0 +1,16 @@
const EventEmitter = require('events')
class GetModalBody extends EventEmitter {
command (callback) {
this.api.waitForElementVisible('.modal-body')
.getText('.modal-body', (result) => {
console.log(result)
callback(result.value, () => {
this.emit('complete')
})
})
return this
}
}
module.exports = GetModalBody

@ -3,10 +3,10 @@ const EventEmitter = require('events')
class JournalLastChild extends EventEmitter {
command (val) {
this.api
.waitForElementVisible('#journal div:last-child span.text-info', 10000)
.assert.containsText('#journal div:last-child span.text-info', val).perform(() => {
this.emit('complete')
})
.waitForElementVisible('#journal > div:last-child', 10000)
.assert.containsText('#journal > div:last-child', val).perform(() => {
this.emit('complete')
})
return this
}
}

@ -0,0 +1,19 @@
const EventEmitter = require('events')
/*
Check if the last log in the console contains a specific text
*/
class JournalLastChildIncludes extends EventEmitter {
command (val) {
this.api
.waitForElementVisible('#journal > div:last-child', 10000)
.getText('#journal > div:last-child', (result) => {
console.log('JournalLastChildIncludes', result.value)
if (result.value.indexOf(val) === -1) return this.api.assert.fail(`wait for ${val} in ${result.value}`)
this.emit('complete')
})
return this
}
}
module.exports = JournalLastChildIncludes

@ -0,0 +1,17 @@
const EventEmitter = require('events')
class ModalFooterOKClick extends EventEmitter {
command () {
this.api.waitForElementVisible('#modal-footer-cancel').perform((client, done) => {
this.api.execute(function () {
document.querySelector('#modal-footer-cancel').click()
}, [], (result) => {
done()
this.emit('complete')
})
})
return this
}
}
module.exports = ModalFooterOKClick

@ -2,7 +2,7 @@ const EventEmitter = require('events')
class ModalFooterOKClick extends EventEmitter {
command () {
this.api.perform((client, done) => {
this.api.waitForElementVisible('#modal-footer-ok').perform((client, done) => {
this.api.execute(function () {
document.querySelector('#modal-footer-ok').click()
}, [], (result) => {

@ -0,0 +1,19 @@
const EventEmitter = require('events')
class sendLowLevelTx extends EventEmitter {
command (address, value, callData) {
console.log('low level transact to ', address, value, callData)
this.api.waitForElementVisible(`#instance${address} #deployAndRunLLTxSendTransaction`, 1000)
.clearValue(`#instance${address} #deployAndRunLLTxCalldata`)
.setValue(`#instance${address} #deployAndRunLLTxCalldata`, callData)
.waitForElementVisible('#value')
.clearValue('#value')
.setValue('#value', value)
.scrollAndClick(`#instance${address} #deployAndRunLLTxSendTransaction`)
.perform(() => {
this.emit('complete')
})
return this
}
}
module.exports = sendLowLevelTx

@ -0,0 +1,15 @@
const EventEmitter = require('events')
class SetSolidityCompilerVersion extends EventEmitter {
command (version) {
this.api
.click(`#compileTabView #versionSelector [value="${version}"]`)
.pause(5000)
.perform(() => {
this.emit('complete')
})
return this
}
}
module.exports = SetSolidityCompilerVersion

@ -15,6 +15,7 @@ class SwitchFile extends EventEmitter {
// click on fileExplorer can toggle it. We go through settings to be sure FE is open
function switchFile (browser, name, done) {
browser.clickLaunchIcon('settings').clickLaunchIcon('fileExplorers')
.waitForElementVisible('li[key="' + name + '"]')
.click('li[key="' + name + '"]')
.pause(2000)
.perform(() => {

@ -1,9 +1,9 @@
const EventEmitter = require('events')
class VerifyContracts extends EventEmitter {
command (compiledContractNames) {
command (compiledContractNames, opts = { wait: 1000 }) {
this.api.perform((done) => {
verifyContracts(this.api, compiledContractNames, () => {
verifyContracts(this.api, compiledContractNames, opts, () => {
done()
this.emit('complete')
})
@ -12,9 +12,10 @@ class VerifyContracts extends EventEmitter {
}
}
function getCompiledContracts (browser, callback) {
function getCompiledContracts (browser, opts, callback) {
browser
.clickLaunchIcon('solidity')
.pause(opts.wait)
.waitForElementPresent('#compileTabView select#compiledContracts option')
.execute(function () {
var contracts = document.querySelectorAll('#compileTabView select#compiledContracts option')
@ -32,8 +33,8 @@ function getCompiledContracts (browser, callback) {
})
}
function verifyContracts (browser, compiledContractNames, callback) {
getCompiledContracts(browser, (result) => {
function verifyContracts (browser, compiledContractNames, opts, callback) {
getCompiledContracts(browser, opts, (result) => {
if (result.value) {
for (var contract in compiledContractNames) {
console.log(' - ' + compiledContractNames[contract], result.value)

@ -1,14 +1,16 @@
module.exports = function (browser, callback) {
module.exports = function (browser, callback, url, preloadPlugins = true) {
browser
.url('http://127.0.0.1:8080')
.url(url || 'http://127.0.0.1:8080')
.injectScript('test-browser/helpers/applytestmode.js', function () {
browser.resizeWindow(2560, 1440, () => {
initModules(browser, () => {
browser.clickLaunchIcon('solidity').click('#autoCompile')
.perform(function () {
callback()
if (preloadPlugins) {
initModules(browser, () => {
browser.clickLaunchIcon('solidity').click('#autoCompile')
.perform(function () {
callback()
})
})
})
} else callback()
})
})
}
@ -22,5 +24,8 @@ function initModules (browser, callback) {
.scrollAndClick('#pluginManager article[id="remixPluginManagerListItem_solidityStaticAnalysis"] button')
.scrollAndClick('#pluginManager article[id="remixPluginManagerListItem_debugger"] button')
.scrollAndClick('#icon-panel div[plugin="fileExplorers"]')
.clickLaunchIcon('settings')
.setValue('#gistaccesstoken', process.env.gist_token)
.click('#savegisttoken')
.perform(() => { callback() })
}

@ -20,7 +20,7 @@ module.exports = {
.clickLaunchIcon('solidity')
.testContracts('Untitled.sol', sources[0]['browser/Untitled.sol'], ['Ballot'])
.clickLaunchIcon('udapp')
.setValue('input[placeholder="uint8 _numProposals"]', '1')
.setValue('input[placeholder="bytes32[] proposalNames"]', '["0x48656c6c6f20576f726c64210000000000000000000000000000000000000000"]')
.click('#runTabView button[class^="instanceButton"]')
.waitForElementPresent('.instance:nth-of-type(2)')
.click('.instance:nth-of-type(2) > div > button')
@ -36,7 +36,7 @@ module.exports = {
.clickLaunchIcon('debugger')
.click('#jumppreviousbreakpoint')
.pause(2000)
.goToVMTraceStep(58)
.goToVMTraceStep(79)
.pause(1000)
.checkVariableDebug('soliditystate', stateCheck)
.checkVariableDebug('soliditylocals', localsCheck)
@ -55,8 +55,25 @@ module.exports = {
.testFunction('delegate - transact (not payable)', '0xca58080c8099429caeeffe43b8104df919c2c543dceb9edf9242fa55f045c803',
`[vm]\nfrom:0xca3...a733c\nto:Ballot.delegate(address) 0x692...77b3a\nvalue:0 wei\ndata:0x5c1...4d2db\nlogs:0\nhash:0xca5...5c803`,
{types: 'address to', values: '"0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db"'}, null, null)
},
'Deploy and use Ballot using external web3': function (browser) {
browser
.click('#selectExEnvOptions #web3-mode')
.modalFooterOKClick()
.clickLaunchIcon('solidity')
.testContracts('Untitled.sol', sources[0]['browser/Untitled.sol'], ['Ballot'])
.clickLaunchIcon('udapp')
.setValue('input[placeholder="bytes32[] proposalNames"]', '["0x48656c6c6f20576f726c64210000000000000000000000000000000000000000"]')
.click('#runTabView button[class^="instanceButton"]')
.clickInstance(0)
.click('#clearConsole')
.clickFunction('delegate - transact (not payable)', {types: 'address to', values: '0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c'})
.journalLastChildIncludes('Ballot.delegate(address)')
.journalLastChildIncludes('data:0x5c1...a733c')
.end()
},
tearDown: sauce
}
@ -85,13 +102,13 @@ var stateCheck = {
'value': false,
'type': 'bool'
},
'vote': {
'value': '0',
'type': 'uint8'
},
'delegate': {
'value': '0x0000000000000000000000000000000000000000',
'type': 'address'
},
'vote': {
'value': '0',
'type': 'uint256'
}
},
'type': 'struct Ballot.Voter'
@ -104,6 +121,10 @@ var stateCheck = {
'value': [
{
'value': {
'name': {
'value': '0x48656C6C6F20576F726C64210000000000000000000000000000000000000000',
'type': 'bytes32'
},
'voteCount': {
'value': '0',
'type': 'uint256'
@ -118,4 +139,169 @@ var stateCheck = {
}
}
var ballotABI = '[{"constant":false,"inputs":[{"name":"to","type":"address"}],"name":"delegate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"winningProposal","outputs":[{"name":"_winningProposal","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"toVoter","type":"address"}],"name":"giveRightToVote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"toProposal","type":"uint8"}],"name":"vote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_numProposals","type":"uint8"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]'
var ballotABI = `[
{
"inputs": [
{
"internalType": "bytes32[]",
"name": "proposalNames",
"type": "bytes32[]"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"constant": true,
"inputs": [],
"name": "chairperson",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
}
],
"name": "delegate",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "voter",
"type": "address"
}
],
"name": "giveRightToVote",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "proposals",
"outputs": [
{
"internalType": "bytes32",
"name": "name",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "voteCount",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "uint256",
"name": "proposal",
"type": "uint256"
}
],
"name": "vote",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "voters",
"outputs": [
{
"internalType": "uint256",
"name": "weight",
"type": "uint256"
},
{
"internalType": "bool",
"name": "voted",
"type": "bool"
},
{
"internalType": "address",
"name": "delegate",
"type": "address"
},
{
"internalType": "uint256",
"name": "vote",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "winnerName",
"outputs": [
{
"internalType": "bytes32",
"name": "winnerName_",
"type": "bytes32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "winningProposal",
"outputs": [
{
"internalType": "uint256",
"name": "winningProposal_",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
]`

@ -0,0 +1,43 @@
'use strict'
const init = require('../helpers/init')
const sauce = require('./sauce')
// 99266d6da54cc12f37f11586e8171546c7700d67
module.exports = {
before: function (browser, done) {
init(browser, done)
},
'UploadToGists': function (browser) {
/*
- set the access token
- publish to gist
- retrieve the gist
- switch to a file in the new gist
*/
console.log('token', process.env.gist_token)
browser
.waitForElementVisible('#icon-panel', 10000)
.clickLaunchIcon('fileExplorers')
.click('#publishToGist')
.modalFooterOKClick()
.getModalBody((value, done) => {
const reg = /gist.github.com\/([^.]+)/
const id = value.match(reg)
console.log('gist regex', id)
if (!id) {
browser.assert.fail('cannot get the gist id', '', '')
} else {
let gistid = id[1]
browser
.modalFooterCancelClick()
.executeScript(`remix.loadgist('${gistid}')`)
.switchFile('browser/gists')
.switchFile(`browser/gists/${gistid}`)
.switchFile(`browser/gists/${gistid}/1_Storage.sol`)
.perform(done)
}
})
.end()
},
tearDown: sauce
}

@ -0,0 +1,56 @@
'use strict'
const init = require('../helpers/init')
const sauce = require('./sauce')
const testData = {
validGistId: '1859c97c6e1efc91047d725d5225888e',
invalidGistId: '6368b389f9302v32902msk2402'
}
module.exports = {
before: function (browser, done) {
init(browser, done)
},
'Load Gist Modal': function (browser) {
browser
.waitForElementVisible('#icon-panel', 10000)
.clickLaunchIcon('fileExplorers')
.scrollAndClick('div.file > div.btn-group > button:nth-child(1)')
.waitForElementVisible('h6.modal-title')
.assert.containsText('h6.modal-title', 'Load a Gist')
.waitForElementVisible('div.modal-body > div')
.assert.containsText('div.modal-body > div', 'Enter the ID of the Gist or URL you would like to load.')
.waitForElementVisible('#prompt_text')
.click('#modal-footer-cancel')
},
'Display Error Message For Invalid Gist ID': function (browser) {
browser
.waitForElementVisible('#icon-panel', 10000)
.clickLaunchIcon('fileExplorers')
.scrollAndClick('div.file > div.btn-group > button:nth-child(1)')
.waitForElementVisible('#prompt_text')
.setValue('#prompt_text', testData.invalidGistId)
.modalFooterOKClick()
.waitForElementVisible('div.modal-body > div')
.assert.containsText('div.modal-body > div', 'Gist load error: Not Found')
.modalFooterOKClick()
},
'Import From Gist For Valid Gist ID': function (browser) {
browser
.waitForElementVisible('#icon-panel', 10000)
.clickLaunchIcon('fileExplorers')
.scrollAndClick('div.file > div.btn-group > button:nth-child(1)')
.waitForElementVisible('#prompt_text')
.setValue('#prompt_text', testData.validGistId)
.modalFooterOKClick()
.switchFile(`browser/gists/${testData.validGistId}`)
.switchFile(`browser/gists/${testData.validGistId}/ApplicationRegistry`)
.waitForElementVisible(`div[title='browser/gists/${testData.validGistId}/ApplicationRegistry']`)
.assert.containsText(`div[title='browser/gists/${testData.validGistId}/ApplicationRegistry'] > span`, 'ApplicationRegistry')
.end()
},
tearDown: sauce
}

@ -0,0 +1,37 @@
'use strict'
var init = require('../helpers/init')
var sauce = require('./sauce')
module.exports = {
before: function (browser, done) {
init(browser, done)
},
'@sources': function () {
return []
},
'Publish on IPFS': function (browser) {
browser
.waitForElementVisible('#icon-panel', 10000)
.clickLaunchIcon('fileExplorers')
.switchFile('browser/3_Ballot.sol')
.verifyContracts(['Ballot'])
.click('#publishOnIpfs')
.getModalBody((value, done) => {
if (value.indexOf('Metadata published successfully.') === -1) browser.assert.fail('ipfs deploy failed', '', '')
if (value.indexOf('dweb:/ipfs') === -1) browser.assert.fail('ipfs deploy failed', '', '')
done()
})
.modalFooterOKClick()
},
'Publish on Swarm': function (browser) {
browser
.click('#publishOnSwarm')
.getModalBody((value, done) => {
if (value.indexOf('Metadata published successfully.') === -1) browser.assert.fail('swarm deploy failed', '', '')
if (value.indexOf('bzz') === -1) browser.assert.fail('swarm deploy failed', '', '')
done()
})
.end()
},
tearDown: sauce
}

@ -28,6 +28,12 @@ var sources = [
{
'localhost/src/gmbh/company.sol': {content: assetsTestContract},
'localhost/src/gmbh/contract.sol': {content: gmbhTestContract}
},
{
'browser/test_import_node_modules.sol': {content: 'import "openzeppelin-solidity/contracts/math/SafeMath.sol";'}
},
{
'browser/test_import_node_modules_with_github_import.sol': {content: 'import "openzeppelin-solidity/contracts/sample.sol";'}
}
]
@ -41,6 +47,29 @@ module.exports = {
'Remixd': function (browser) {
runTests(browser)
},
'Import from node_modules ': function (browser) {
/*
when a relative import is used (i.e import "openzeppelin-solidity/contracts/math/SafeMath.sol")
remix (as well as truffle) try to resolve it against the node_modules and installed_contracts folder.
*/
browser
.waitForElementVisible('#icon-panel', 2000)
.clickLaunchIcon('fileExplorers')
.addFile('test_import_node_modules.sol', sources[3]['browser/test_import_node_modules.sol'])
.clickLaunchIcon('solidity')
.testContracts('test_import_node_modules.sol', sources[3]['browser/test_import_node_modules.sol'], ['SafeMath'])
},
'Import from node_modules and reference a github import': function (browser) {
browser
.waitForElementVisible('#icon-panel', 2000)
.clickLaunchIcon('fileExplorers')
.addFile('test_import_node_modules_with_github_import.sol', sources[4]['browser/test_import_node_modules_with_github_import.sol'])
.clickLaunchIcon('solidity')
.testContracts('test_import_node_modules_with_github_import.sol', sources[4]['browser/test_import_node_modules_with_github_import.sol'], ['ERC20', 'test11'])
.clickLaunchIcon('pluginManager')
.scrollAndClick('#pluginManager article[id="remixPluginManagerListItem_remixd"] button')
.end()
},
tearDown: sauce
}
@ -98,9 +127,6 @@ function runTests (browser, testData) {
.waitForElementNotPresent('[data-path="localhost/folder1/contract_' + browserName + '.sol"]') // check if renamed (old) file is not present
.waitForElementNotPresent('[data-path="localhost/folder1/contract_' + browserName + '_toremove.sol"]') // check if removed (old) file is not present
.click('[data-path="localhost/folder1/renamed_contract_' + browserName + '.sol"]')
.clickLaunchIcon('pluginManager')
.scrollAndClick('#pluginManager article[id="remixPluginManagerListItem_remixd"] button')
.end()
}
function testImportFromRemixd (browser, callback) {

@ -49,7 +49,7 @@ module.exports = {
var sources = [
{
'browser/signMassage.sol': {content: `
pragma solidity >=0.4.22 <0.6.0;
pragma solidity >=0.4.22 <0.7.0;
contract SignMassageTest {
function testRecovery(bytes32 h, uint8 v, bytes32 r, bytes32 s) public pure returns (address) {
return ecrecover(h, v, r, s);

@ -22,129 +22,40 @@ module.exports = {
'Test Failed Import': function (browser) {
browser.addFile('Untitled3.sol', sources[2]['browser/Untitled3.sol'])
.clickLaunchIcon('solidity')
.assert.containsText('#compileTabView .error pre', 'Unable to import "browser/Untitled11.sol": File not found')
.end()
.assert.containsText('#compileTabView .error pre', 'not found browser/Untitled11.sol')
},
tearDown: sauce
}
var abstractENS = `
contract AbstractENS {
function owner(bytes32 node) public view returns(address);
function resolver(bytes32 node) public view returns(address);
function ttl(bytes32 node) public view returns(uint64);
function setOwner(bytes32 node, address owner) public;
function setSubnodeOwner(bytes32 node, bytes32 label, address owner) public;
function setResolver(bytes32 node, address resolver) public;
function setTTL(bytes32 node, uint64 ttl) public;
// Logged when the owner of a node assigns a new owner to a subnode.
event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
// Logged when the owner of a node transfers ownership to a new account.
event Transfer(bytes32 indexed node, address owner);
// Logged when the resolver for a node changes.
event NewResolver(bytes32 indexed node, address resolver);
// Logged when the TTL of a node changes
event NewTTL(bytes32 indexed node, uint64 ttl);
}`
var ENS = `pragma solidity ^0.4.0;
import './AbstractENS.sol';
/**
* The ENS registry contract.
*/
contract ENS is AbstractENS {
struct Record {
address owner;
address resolver;
uint64 ttl;
}
mapping(bytes32=>Record) records;
// Permits modifications only by the owner of the specified node.
modifier only_owner(bytes32 node) {
if (records[node].owner != msg.sender) revert();
_;
}
/**
* Constructs a new ENS registrar.
*/
constructor() public {
records[0].owner = msg.sender;
}
/**
* Returns the address that owns the specified node.
*/
function owner(bytes32 node) public view returns (address) {
return records[node].owner;
}
/**
* Returns the address of the resolver for the specified node.
*/
function resolver(bytes32 node) public view returns (address) {
return records[node].resolver;
}
/**
* Returns the TTL of a node, and any records associated with it.
*/
function ttl(bytes32 node) public view returns (uint64) {
return records[node].ttl;
}
/**
* Transfers ownership of a node to a new address. May only be called by the current
* owner of the node.
* @param node The node to transfer ownership of.
* @param owner The address of the new owner.
*/
function setOwner(bytes32 node, address owner) public only_owner(node) {
emit Transfer(node, owner);
records[node].owner = owner;
}
'Test Github Import - from master branch': function (browser) {
browser
.setSolidityCompilerVersion('soljson-v0.5.0+commit.1d4f565a.js')
.addFile('Untitled4.sol', sources[3]['browser/Untitled4.sol'])
.clickLaunchIcon('fileExplorers')
.verifyContracts(['test7', 'ERC20', 'SafeMath'], {wait: 10000})
},
/**
* Transfers ownership of a subnode sha3(node, label) to a new address. May only be
* called by the owner of the parent node.
* @param node The parent node.
* @param label The hash of the label specifying the subnode.
* @param owner The address of the new owner.
*/
function setSubnodeOwner(bytes32 node, bytes32 label, address owner) public only_owner(node) {
bytes32 subnode = keccak256(abi.encodePacked(node, label));
emit NewOwner(node, label, owner);
records[subnode].owner = owner;
}
'Test Github Import - from other branch': function (browser) {
browser
.addFile('Untitled5.sol', sources[4]['browser/Untitled5.sol'])
.clickLaunchIcon('fileExplorers')
.verifyContracts(['test8', 'ERC20', 'SafeMath'], {wait: 10000})
},
/**
* Sets the resolver address for the specified node.
* @param node The node to update.
* @param resolver The address of the resolver.
*/
function setResolver(bytes32 node, address resolver) public only_owner(node) {
emit NewResolver(node, resolver);
records[node].resolver = resolver;
}
'Test Github Import - no branch specified': function (browser) {
browser
.addFile('Untitled6.sol', sources[5]['browser/Untitled6.sol'])
.clickLaunchIcon('fileExplorers')
.verifyContracts(['test10', 'ERC20', 'SafeMath'], {wait: 10000})
},
/**
* Sets the TTL for the specified node.
* @param node The node to update.
* @param ttl The TTL in seconds.
*/
function setTTL(bytes32 node, uint64 ttl) public only_owner(node) {
emit NewTTL(node, ttl);
records[node].ttl = ttl;
}
}`
'Test Github Import - raw URL': function (browser) {
browser
.addFile('Untitled7.sol', sources[6]['browser/Untitled7.sol'])
.clickLaunchIcon('fileExplorers')
.verifyContracts(['test11', 'ERC20', 'SafeMath'], {wait: 10000})
.end()
},
tearDown: sauce
}
var sources = [
{
@ -158,12 +69,15 @@ var sources = [
'browser/Untitled3.sol': {content: 'import "./Untitled11.sol"; contract test6 {}'}
},
{
'browser/Untitled4.sol': {content: 'import "github.com/ethereum/ens/contracts/ENS.sol"; contract test7 {}'},
'github.com/ethereum/ens/contracts/ENS.sol': {content: ENS}
'browser/Untitled4.sol': {content: 'import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol"; contract test7 {}'}
},
{
'browser/Untitled5.sol': {content: 'import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v2.3.0/contracts/token/ERC20/ERC20.sol"; contract test8 {}'}
},
{
'browser/Untitled6.sol': {content: 'import "https://github.com/OpenZeppelin/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; contract test10 {}'}
},
{
'browser/Untitled4.sol': {content: 'import "github.com/ethereum/ens/contracts/ENS.sol"; contract test7 {}'},
'github.com/ethereum/ens/contracts/ENS.sol': {content: ENS},
'github.com/ethereum/ens/contracts/AbstractENS.sol': {content: abstractENS}
'browser/Untitled7.sol': {content: 'import "https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/master/contracts/token/ERC20/ERC20.sol"; contract test11 {}'}
}
]

@ -23,12 +23,12 @@ function runTests (browser) {
.click('#icon-panel div[plugin="pluginManager"]')
.scrollAndClick('#pluginManager article[id="remixPluginManagerListItem_solidityUnitTesting"] button')
.clickLaunchIcon('fileExplorers')
.switchFile('browser/ballot.sol')
.switchFile('browser/3_Ballot.sol')
.clickLaunchIcon('solidityUnitTesting')
.scrollAndClick('#runTestsTabRunAction')
.waitForElementPresent('#solidityUnittestsOutput div[class^="testPass"]')
.pause(10000)
.assert.containsText('#solidityUnittestsOutput', 'browser/ballot_test.sol (test3)')
.assert.containsText('#solidityUnittestsOutput', 'browser/4_Ballot_test.sol (BallotTest)')
.assert.containsText('#solidityUnittestsOutput', '✓ (Check winning proposal)')
.assert.containsText('#solidityUnittestsOutput', '✓ (Check winnin proposal with return value)')
.end()

@ -0,0 +1,273 @@
'use strict'
var init = require('../helpers/init')
var sauce = require('./sauce')
module.exports = {
before: function (browser, done) {
init(browser, done)
},
'@sources': function () {
return sources
},
'Use special functions receive/fallback - both are declared, sending data': function (browser) {
browser.waitForElementVisible('#icon-panel', 10000)
.testContracts('receiveAndFallback.sol', sources[0]['browser/receiveAndFallback.sol'], ['CheckSpecials']) // compile
.clickLaunchIcon('udapp')
.selectContract('CheckSpecials')
.createContract('') // deploy
.clickInstance(0)
.perform((done) => {
browser.getAddressAtPosition(0, (address) => {
browser.sendLowLevelTx(address, '0', '0xaa')
.pause(1000)
.journalLastChildIncludes('to:CheckSpecials.(fallback)')
.journalLastChildIncludes('value:0 wei')
.journalLastChildIncludes('data:0xaa')
.perform(done)
})
})
},
'Use special functions receive/fallback - both are declared, failing sending data < 1 byte': function (browser) {
// don't need to redeploy it, same contract
browser.perform((done) => {
browser.getAddressAtPosition(0, (address) => {
browser.sendLowLevelTx(address, '0', '0xa')
.pause(1000)
.waitForElementVisible(`#instance${address} label[id="deployAndRunLLTxError"]`)
.assert.containsText(`#instance${address} label[id="deployAndRunLLTxError"]`, `The calldata should be a valid hexadecimal value with size of at least one byte.`)
.perform(done)
})
})
},
'Use special functions receive/fallback - both are declared, failing sending data with odd number of digits': function (browser) {
// don't need to redeploy it, same contract
browser.perform((done) => {
browser.getAddressAtPosition(0, (address) => {
browser.sendLowLevelTx(address, '0', '0x1aa')
.pause(1000)
.waitForElementVisible(`#instance${address} label[id="deployAndRunLLTxError"]`)
.assert.containsText(`#instance${address} label[id="deployAndRunLLTxError"]`, `The calldata should be a valid hexadecimal value.`)
.perform(done)
})
})
},
'Use special functions receive/fallback - both are declared - receive called, sending wei': function (browser) {
// don't need to redeploy it, same contract
browser.perform((done) => {
browser.getAddressAtPosition(0, (address) => {
browser.sendLowLevelTx(address, '1', '')
.pause(1000)
.journalLastChildIncludes('to:CheckSpecials.(receive)')
.journalLastChildIncludes('value:1 wei')
.journalLastChildIncludes('data:0x')
.perform(done)
})
})
},
'Use special functions receive/fallback - both are declared - fallback should fail cause not payable, sending data and wei': function (browser) {
// don't need to redeploy it, same contract
browser.perform((done) => {
browser.getAddressAtPosition(0, (address) => {
browser.sendLowLevelTx(address, '10', '0xaa')
.pause(1000)
.journalLastChildIncludes('to CheckSpecials.(fallback) errored:')
.journalLastChildIncludes('The called function should be payable if you send value')
.perform(done)
})
})
},
'Use special functions receive/fallback - only receive is declared, sending wei': function (browser) {
browser.waitForElementVisible('#icon-panel', 10000)
.testContracts('receiveOnly.sol', sources[1]['browser/receiveOnly.sol'], ['CheckSpecials'])
.clickLaunchIcon('udapp')
.selectContract('CheckSpecials')
.createContract('')
.clickInstance(1)
.perform((done) => {
browser.getAddressAtPosition(1, (address) => {
browser.sendLowLevelTx(address, '1', '')
.pause(1000)
.journalLastChildIncludes('to:CheckSpecials.(receive)')
.journalLastChildIncludes('value:1 wei')
.journalLastChildIncludes('data:0x')
.perform(done)
})
})
},
'Use special functions receive/fallback - only receive is declared, failing, fallback is not declared, sending data': function (browser) {
// don't need to redeploy it, same contract
browser.perform((done) => {
browser.getAddressAtPosition(1, (address) => {
browser.sendLowLevelTx(address, '0', '0xaa')
.pause(1000)
.waitForElementVisible(`#instance${address} label[id="deployAndRunLLTxError"]`)
.assert.containsText(`#instance${address} label[id="deployAndRunLLTxError"]`, `'Fallback' function is not defined`)
.perform(done)
})
})
},
'Use special functions receive/fallback - only fallback declared and is payable, sending wei': function (browser) {
browser.waitForElementVisible('#icon-panel', 10000)
.testContracts('fallbackOnlyPayable.sol', sources[2]['browser/fallbackOnlyPayable.sol'], ['CheckSpecials'])
.clickLaunchIcon('udapp')
.selectContract('CheckSpecials')
.createContract('')
.clickInstance(2)
.perform((done) => {
browser.getAddressAtPosition(2, (address) => {
browser.sendLowLevelTx(address, '1', '')
.pause(1000)
.journalLastChildIncludes('to:CheckSpecials.(fallback)')
.journalLastChildIncludes('value:1 wei')
.journalLastChildIncludes('data:0x')
.perform(done)
})
})
},
'Use special functions receive/fallback - only fallback is diclared and is payable, sending data and wei': function (browser) {
// don't need to redeploy it, same contract
browser.perform((done) => {
browser.getAddressAtPosition(2, (address) => {
browser.sendLowLevelTx(address, '1', '0xaa')
.pause(1000)
.journalLastChildIncludes('to:CheckSpecials.(fallback)')
.journalLastChildIncludes('value:1 wei')
.journalLastChildIncludes('data:0xaa')
.perform(done)
})
})
},
'Use special functions receive/fallback - only fallback is declared, fallback should fail cause not payable, sending wei': function (browser) {
browser.waitForElementVisible('#icon-panel', 10000)
.testContracts('fallbackOnlyNotPayable.sol', sources[3]['browser/fallbackOnlyNotPayable.sol'], ['CheckSpecials'])
.clickLaunchIcon('udapp')
.selectContract('CheckSpecials')
.createContract('')
.clickInstance(3)
.perform((done) => {
browser.getAddressAtPosition(3, (address) => {
browser.sendLowLevelTx(address, '1', '')
.pause(1000)
.waitForElementVisible(`#instance${address} label[id="deployAndRunLLTxError"]`)
.assert.containsText(`#instance${address} label[id="deployAndRunLLTxError"]`, `should have either 'receive' or payable 'fallback'`)
.perform(done)
})
})
},
'Use special functions receive/fallback - receive and fallback are declared, sending data and wei': function (browser) {
browser.waitForElementVisible('#icon-panel', 10000)
.testContracts('receiveAndFallbackBothPayable.sol', sources[4]['browser/receiveAndFallbackBothPayable.sol'], ['CheckSpecials'])
.clickLaunchIcon('udapp')
.selectContract('CheckSpecials')
.waitForElementVisible('#value')
.clearValue('#value')
.setValue('#value', 0)
.createContract('')
.clickInstance(4)
.pause(1000)
.perform((done) => {
browser.getAddressAtPosition(4, (address) => {
browser.sendLowLevelTx(address, '1', '0xaa')
.pause(1000)
.journalLastChildIncludes('to:CheckSpecials.(fallback)')
.journalLastChildIncludes('value:1 wei')
.journalLastChildIncludes('data:0xaa')
.perform(done)
})
})
},
'Use special functions receive/fallback - receive and fallback are declared and payable, sending wei': function (browser) {
browser.perform((done) => {
browser.getAddressAtPosition(4, (address) => {
browser.sendLowLevelTx(address, '1', '')
.pause(1000)
.journalLastChildIncludes('to:CheckSpecials.(receive)')
.journalLastChildIncludes('value:1 wei')
.journalLastChildIncludes('data:0x')
.perform(done)
})
})
},
'Use special functions receive/fallback - receive and fallback are not declared, sending nothing': function (browser) {
browser.waitForElementVisible('#icon-panel', 10000)
.testContracts('notSpecial.sol', sources[5]['browser/notSpecial.sol'], ['CheckSpecials'])
.clickLaunchIcon('udapp')
.selectContract('CheckSpecials')
.waitForElementVisible('#value')
.clearValue('#value')
.setValue('#value', 0)
.createContract('')
.clickInstance(5)
.pause(1000)
.perform((done) => {
browser.getAddressAtPosition(5, (address) => {
browser.sendLowLevelTx(address, '0', '')
.pause(1000)
.waitForElementVisible(`#instance${address} label[id="deployAndRunLLTxError"]`)
.assert.containsText(`#instance${address} label[id="deployAndRunLLTxError"]`, `Both 'receive' and 'fallback' functions are not defined`)
.perform(done)
})
})
.end()
},
tearDown: sauce
}
var sources = [
{
'browser/receiveAndFallback.sol': {
content: `
contract CheckSpecials {
receive() payable external{}
fallback() external {}
}
`
}
},
{
'browser/receiveOnly.sol': {
content: `
contract CheckSpecials {
receive() payable external {}
}
`
}
},
{
'browser/fallbackOnlyPayable.sol': {
content: `
contract CheckSpecials {
fallback() payable external {}
}
`
}
},
{
'browser/fallbackOnlyNotPayable.sol': {
content: `
contract CheckSpecials {
fallback() external {}
}
`
}
},
{
'browser/receiveAndFallbackBothPayable.sol': {
content: `
contract CheckSpecials {
receive() payable external {}
fallback() payable external {}
}
`
}
},
{
'browser/notSpecial.sol': {
content: `
contract CheckSpecials {
function otherFallback() payable external {}
}
`
}
}
]

@ -0,0 +1,17 @@
'use strict'
const init = require('../helpers/init')
const sauce = require('./sauce')
module.exports = {
before: function (browser, done) {
init(browser, done, 'http://127.0.0.1:8080?plugins=solidity,udapp', false)
},
'CheckSolidityActivatedAndUDapp': function (browser) {
browser
.waitForElementVisible('#icon-panel', 10000)
.clickLaunchIcon('solidity')
.clickLaunchIcon('udapp')
.end()
},
tearDown: sauce
}

@ -10,7 +10,7 @@ test('compiler.compile smoke', function (t) {
var noop = function () {}
var fakeImport = function (url, cb) { cb('Not implemented') }
var compiler = new Compiler(fakeImport)
compiler.setCompileJSON(noop)
compiler.compileJSON = noop
compiler.compile({ 'test': '' }, 'test')
t.ok(compiler)
})

Loading…
Cancel
Save