commit
4eb9957eae
@ -0,0 +1,122 @@ |
||||
'use strict' |
||||
|
||||
import { NightwatchBrowser } from 'nightwatch' |
||||
import init from '../helpers/init' |
||||
|
||||
module.exports = { |
||||
|
||||
before: function (browser: NightwatchBrowser, done: VoidFunction) { |
||||
init(browser, done, 'http://127.0.0.1:8080', true) |
||||
}, |
||||
'Should find text': function (browser: NightwatchBrowser) { |
||||
browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]') |
||||
.click('*[plugin="search"]').waitForElementVisible('*[id="search_input"]') |
||||
.setValue('*[id="search_input"]', 'read').pause(1000) |
||||
.waitForElementContainsText('*[data-id="search_results"]', '3_BALLOT.SOL', 60000) |
||||
.waitForElementContainsText('*[data-id="search_results"]', 'contracts', 60000) |
||||
.waitForElementContainsText('*[data-id="search_results"]', 'README.TXT', 60000) |
||||
.waitForElementContainsText('*[data-id="search_results"]', 'file must') |
||||
.waitForElementContainsText('*[data-id="search_results"]', 'be compiled') |
||||
.waitForElementContainsText('*[data-id="search_results"]', 'that person al') |
||||
.waitForElementContainsText('*[data-id="search_results"]', 'sender.voted') |
||||
.waitForElementContainsText('*[data-id="search_results"]', 'read') |
||||
.elements('css selector','.search_plugin_search_line', (res) => { |
||||
Array.isArray(res.value) && browser.assert.equal(res.value.length, 6) |
||||
}) |
||||
}, |
||||
'Should find regex': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="search_use_regex"]').click('*[data-id="search_use_regex"]') |
||||
.waitForElementVisible('*[id="search_input"]') |
||||
.clearValue('*[id="search_input"]') |
||||
.setValue('*[id="search_input"]', '^contract').pause(1000) |
||||
.waitForElementContainsText('*[data-id="search_results"]', '3_BALLOT.SOL', 60000) |
||||
.waitForElementContainsText('*[data-id="search_results"]', '2_OWNER.SOL', 60000) |
||||
.waitForElementContainsText('*[data-id="search_results"]', '1_STORAGE.SOL', 60000) |
||||
.waitForElementContainsText('*[data-id="search_results"]', '4_BALLOT_TEST.SOL', 60000) |
||||
.waitForElementContainsText('*[data-id="search_results"]', 'tests', 60000) |
||||
.elements('css selector','.search_plugin_search_line', (res) => { |
||||
Array.isArray(res.value) && browser.assert.equal(res.value.length, 4) |
||||
}) |
||||
}, |
||||
'Should find matchcase': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="search_use_regex"]').click('*[data-id="search_use_regex"]') |
||||
.waitForElementVisible('*[data-id="search_case_sensitive"]').click('*[data-id="search_case_sensitive"]') |
||||
.elements('css selector','.search_plugin_search_line', (res) => { |
||||
Array.isArray(res.value) && browser.assert.equal(res.value.length, 0) |
||||
})
|
||||
.clearValue('*[id="search_input"]') |
||||
.setValue('*[id="search_input"]', 'Contract').pause(1000) |
||||
.elements('css selector','.search_plugin_search_line', (res) => { |
||||
Array.isArray(res.value) && browser.assert.equal(res.value.length, 6) |
||||
}) |
||||
.waitForElementContainsText('*[data-id="search_results"]', 'DEPLOY_ETHERS.JS', 60000) |
||||
.waitForElementContainsText('*[data-id="search_results"]', 'DEPLOY_WEB3.JS', 60000)
|
||||
.waitForElementContainsText('*[data-id="search_results"]', 'scripts', 60000)
|
||||
}, |
||||
'Should find matchword': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[data-id="search_case_sensitive"]').click('*[data-id="search_case_sensitive"]') |
||||
.waitForElementVisible('*[data-id="search_whole_word"]').click('*[data-id="search_whole_word"]') |
||||
.clearValue('*[id="search_input"]') |
||||
.setValue('*[id="search_input"]', 'contract').pause(1000) |
||||
.elements('css selector','.search_plugin_search_line', (res) => { |
||||
Array.isArray(res.value) && browser.assert.equal(res.value.length, 27) |
||||
}) |
||||
}, |
||||
'Should replace text': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.setValue('*[id="search_replace"]', 'replacing').pause(1000) |
||||
.waitForElementVisible('*[data-id="contracts/2_Owner.sol-30-71"]') |
||||
.moveToElement('*[data-id="contracts/2_Owner.sol-30-71"]', 10, 10) |
||||
.waitForElementVisible('*[data-id="replace-contracts/2_Owner.sol-30-71"]') |
||||
.click('*[data-id="replace-contracts/2_Owner.sol-30-71"]').pause(2000). |
||||
modalFooterOKClick('confirmreplace').pause(2000). |
||||
getEditorValue((content) => { |
||||
browser.assert.ok(content.includes('replacing deployer for a constructor'), 'should replace text ok') |
||||
}) |
||||
}, |
||||
'Should replace text without confirmation': function (browser: NightwatchBrowser) { |
||||
browser.click('*[data-id="confirm_replace_label"]').pause(500) |
||||
.clearValue('*[id="search_input"]') |
||||
.setValue('*[id="search_input"]', 'replacing').pause(1000) |
||||
.setValue('*[id="search_replace"]', '2').pause(1000) |
||||
.waitForElementVisible('*[data-id="contracts/2_Owner.sol-30-71"]') |
||||
.moveToElement('*[data-id="contracts/2_Owner.sol-30-71"]', 10, 10) |
||||
.waitForElementVisible('*[data-id="replace-contracts/2_Owner.sol-30-71"]') |
||||
.click('*[data-id="replace-contracts/2_Owner.sol-30-71"]').pause(2000). |
||||
getEditorValue((content) => { |
||||
browser.assert.ok(content.includes('replacing2 deployer for a constructor'), 'should replace text ok') |
||||
}) |
||||
}, |
||||
'Should find text with include': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clearValue('*[id="search_input"]') |
||||
.setValue('*[id="search_input"]', 'contract').pause(1000) |
||||
.setValue('*[id="search_include"]', 'contracts/**').pause(2000) |
||||
.elements('css selector','.search_plugin_search_line', (res) => { |
||||
Array.isArray(res.value) && browser.assert.equal(res.value.length, 4) |
||||
}) |
||||
}, |
||||
'Should find text with exclude': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.clearValue('*[id="search_include"]').pause(2000) |
||||
.setValue('*[id="search_include"]', '**').pause(2000) |
||||
.elements('css selector','.search_plugin_search_line', (res) => { |
||||
Array.isArray(res.value) && browser.assert.equal(res.value.length, 26) |
||||
}) |
||||
.setValue('*[id="search_exclude"]', ',contracts/**').pause(2000) |
||||
.elements('css selector','.search_plugin_search_line', (res) => { |
||||
Array.isArray(res.value) && browser.assert.equal(res.value.length, 22) |
||||
}) |
||||
}, |
||||
'should clear search': function (browser: NightwatchBrowser) { |
||||
browser |
||||
.waitForElementVisible('*[id="search_input"]') |
||||
.setValue('*[id="search_input"]', 'nodata').pause(1000) |
||||
.elements('css selector','.search_plugin_search_line', (res) => { |
||||
Array.isArray(res.value) && browser.assert.equal(res.value.length, 0) |
||||
}) |
||||
} |
||||
} |
@ -0,0 +1,195 @@ |
||||
'use strict' |
||||
|
||||
import { NightwatchBrowser } from 'nightwatch' |
||||
import init from '../helpers/init' |
||||
|
||||
module.exports = { |
||||
|
||||
before: function (browser: NightwatchBrowser, done: VoidFunction) { |
||||
init(browser, done) |
||||
}, |
||||
|
||||
'Should create 10 files, reload, and check if the files are saved': function (browser: NightwatchBrowser) { |
||||
const contents = {} |
||||
const checkContent = function (i, done) { |
||||
const name = 'test_' + i + '.sol' |
||||
browser |
||||
.openFile(name) |
||||
.pause(500) |
||||
.getEditorValue((content) => { |
||||
browser.assert.ok(content === contents[i]) |
||||
done() |
||||
}) |
||||
} |
||||
browser.clickLaunchIcon('filePanel').perform((done) => { |
||||
let contentEditSet = content.slice() |
||||
for (let i = 0; i < 10; i++) { |
||||
contentEditSet += contentEditSet |
||||
contents[i] = contentEditSet |
||||
const name = 'test_' + i + '.sol' |
||||
browser.click('[data-id="fileExplorerNewFilecreateNewFile"]') |
||||
.waitForElementContainsText('*[data-id$="/blank"]', '', 60000) |
||||
.sendKeys('*[data-id$="/blank"] .remixui_items', name) |
||||
.sendKeys('*[data-id$="/blank"] .remixui_items', browser.Keys.ENTER) |
||||
.waitForElementVisible(`li[data-id="treeViewLitreeViewItem${name}"]`, 60000) |
||||
.setEditorValue(contentEditSet) |
||||
} |
||||
done() |
||||
}).pause(10000).refresh() |
||||
.perform(done => checkContent(0, done)) |
||||
.perform(done => checkContent(1, done)) |
||||
.perform(done => checkContent(2, done)) |
||||
.perform(done => checkContent(3, done)) |
||||
.perform(done => checkContent(4, done)) |
||||
.perform(done => checkContent(5, done)) |
||||
.perform(done => checkContent(6, done)) |
||||
.perform(done => checkContent(7, done)) |
||||
.perform(done => checkContent(8, done)) |
||||
.perform(done => checkContent(9, done)) |
||||
.end() |
||||
} |
||||
} |
||||
|
||||
const content = ` |
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
|
||||
pragma solidity >=0.7.0 <0.9.0; |
||||
|
||||
/** |
||||
* @title Ballot |
||||
* @dev Implements voting process along with vote delegation| |
||||
*/ |
||||
contract Ballot { |
||||
|
||||
struct Voter { |
||||
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 { |
||||
// 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 public chairperson; |
||||
|
||||
mapping(address => Voter) public voters; |
||||
|
||||
Proposal[] public proposals; |
||||
|
||||
function () test { |
||||
|
||||
/** |
||||
* @dev Create a new ballot to choose one of 'proposalNames'. |
||||
* @param proposalNames names of proposals |
||||
*/ |
||||
constructor(bytes32[] memory proposalNames) { |
||||
chairperson = msg.sender; |
||||
voters[chairperson].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; |
||||
} |
||||
|
||||
/** |
||||
* @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]; |
||||
require(!sender.voted, "You already voted."); |
||||
require(to != msg.sender, "Self-delegation is disallowed."); |
||||
|
||||
while (voters[to].delegate != address(0)) { |
||||
to = voters[to].delegate; |
||||
|
||||
// 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 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; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @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]; |
||||
require(sender.weight != 0, "Has no right to vote"); |
||||
require(!sender.voted, "Already voted."); |
||||
sender.voted = true; |
||||
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; |
||||
} |
||||
|
||||
/** |
||||
* @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; |
||||
} |
||||
} |
||||
|
||||
` |
@ -0,0 +1,130 @@ |
||||
import { RemixApp } from '@remix-ui/app' |
||||
import React, { useEffect, useRef, useState } from 'react' |
||||
import { render } from 'react-dom' |
||||
import * as packageJson from '../../../../../package.json' |
||||
import { fileSystem, fileSystems } from '../files/fileSystem' |
||||
import { indexedDBFileSystem } from '../files/filesystems/indexedDB' |
||||
import { localStorageFS } from '../files/filesystems/localStorage' |
||||
import { fileSystemUtility, migrationTestData } from '../files/filesystems/fileSystemUtility' |
||||
import './styles/preload.css' |
||||
const _paq = window._paq = window._paq || [] |
||||
|
||||
export const Preload = () => { |
||||
|
||||
const [supported, setSupported] = useState<boolean>(true) |
||||
const [error, setError] = useState<boolean>(false) |
||||
const [showDownloader, setShowDownloader] = useState<boolean>(false) |
||||
const remixFileSystems = useRef<fileSystems>(new fileSystems()) |
||||
const remixIndexedDB = useRef<fileSystem>(new indexedDBFileSystem()) |
||||
const localStorageFileSystem = useRef<fileSystem>(new localStorageFS()) |
||||
// url parameters to e2e test the fallbacks and error warnings
|
||||
const testmigrationFallback = useRef<boolean>(window.location.hash.includes('e2e_testmigration_fallback=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:') |
||||
const testmigrationResult = useRef<boolean>(window.location.hash.includes('e2e_testmigration=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:') |
||||
const testBlockStorage = useRef<boolean>(window.location.hash.includes('e2e_testblock_storage=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:') |
||||
|
||||
function loadAppComponent() { |
||||
import('../../app').then((AppComponent) => { |
||||
const appComponent = new AppComponent.default() |
||||
appComponent.run().then(() => { |
||||
render( |
||||
<> |
||||
<RemixApp app={appComponent} /> |
||||
</>, |
||||
document.getElementById('root') |
||||
) |
||||
}) |
||||
}).catch(err => { |
||||
_paq.push(['trackEvent', 'Preload', 'error', err && err.message]) |
||||
console.log('Error loading Remix:', err) |
||||
setError(true) |
||||
}) |
||||
} |
||||
|
||||
const downloadBackup = async () => { |
||||
setShowDownloader(false) |
||||
const fsUtility = new fileSystemUtility() |
||||
await fsUtility.downloadBackup(remixFileSystems.current.fileSystems['localstorage']) |
||||
await migrateAndLoad() |
||||
} |
||||
|
||||
const migrateAndLoad = async () => { |
||||
setShowDownloader(false) |
||||
const fsUtility = new fileSystemUtility() |
||||
const migrationResult = await fsUtility.migrate(localStorageFileSystem.current, remixIndexedDB.current) |
||||
_paq.push(['trackEvent', 'Migrate', 'result', migrationResult?'success' : 'fail']) |
||||
await setFileSystems() |
||||
} |
||||
|
||||
const setFileSystems = async() => { |
||||
const fsLoaded = await remixFileSystems.current.setFileSystem([(testmigrationFallback.current || testBlockStorage.current)? null: remixIndexedDB.current, testBlockStorage.current? null:localStorageFileSystem.current]) |
||||
if (fsLoaded) { |
||||
console.log(fsLoaded.name + ' activated') |
||||
_paq.push(['trackEvent', 'Storage', 'activate', fsLoaded.name]) |
||||
loadAppComponent() |
||||
} else { |
||||
_paq.push(['trackEvent', 'Storage', 'error', 'no supported storage']) |
||||
setSupported(false) |
||||
} |
||||
} |
||||
|
||||
const testmigration = async() => {
|
||||
if (testmigrationResult.current) { |
||||
const fsUtility = new fileSystemUtility() |
||||
fsUtility.populateWorkspace(migrationTestData, remixFileSystems.current.fileSystems['localstorage'].fs) |
||||
} |
||||
} |
||||
|
||||
useEffect(() => { |
||||
async function loadStorage() { |
||||
await remixFileSystems.current.addFileSystem(remixIndexedDB.current) || _paq.push(['trackEvent', 'Storage', 'error', 'indexedDB not supported']) |
||||
await remixFileSystems.current.addFileSystem(localStorageFileSystem.current) || _paq.push(['trackEvent', 'Storage', 'error', 'localstorage not supported']) |
||||
await testmigration() |
||||
remixIndexedDB.current.loaded && await remixIndexedDB.current.checkWorkspaces() |
||||
localStorageFileSystem.current.loaded && await localStorageFileSystem.current.checkWorkspaces() |
||||
remixIndexedDB.current.loaded && ( (remixIndexedDB.current.hasWorkSpaces || !localStorageFileSystem.current.hasWorkSpaces)? await setFileSystems():setShowDownloader(true)) |
||||
!remixIndexedDB.current.loaded && await setFileSystems() |
||||
} |
||||
loadStorage() |
||||
}, []) |
||||
|
||||
return <> |
||||
<div className='preload-container'> |
||||
<div className='preload-logo pb-4'> |
||||
{logo} |
||||
<div className="info-secondary splash"> |
||||
REMIX IDE |
||||
<br /> |
||||
<span className='version'> v{packageJson.version}</span> |
||||
</div> |
||||
</div> |
||||
{!supported ? |
||||
<div className='preload-info-container alert alert-warning'> |
||||
Your browser does not support any of the filesytems required by Remix. |
||||
Either change the settings in your browser or use a supported browser. |
||||
</div> : null} |
||||
{error ? |
||||
<div className='preload-info-container alert alert-danger'> |
||||
An unknown error has occured loading the application. |
||||
</div> : null} |
||||
{showDownloader ? |
||||
<div className='preload-info-container alert alert-info'> |
||||
This app will be updated now. Please download a backup of your files now to make sure you don't lose your work. |
||||
<br></br> |
||||
You don't need to do anything else, your files will be available when the app loads. |
||||
<div onClick={async () => { await downloadBackup() }} data-id='downloadbackup-btn' className='btn btn-primary mt-1'>download backup</div> |
||||
<div onClick={async () => { await migrateAndLoad() }} data-id='skipbackup-btn' className='btn btn-primary mt-1'>skip backup</div> |
||||
</div> : null} |
||||
{(supported && !error && !showDownloader) ? |
||||
<div> |
||||
<i className="fas fa-spinner fa-spin fa-2x"></i> |
||||
</div> : null} |
||||
</div> |
||||
</> |
||||
} |
||||
|
||||
|
||||
const logo = <svg id="Ebene_2" data-name="Ebene 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 105 100"> |
||||
<path d="M91.84,35a.09.09,0,0,1-.1-.07,41,41,0,0,0-79.48,0,.09.09,0,0,1-.1.07C9.45,35,1,35.35,1,42.53c0,8.56,1,16,6,20.32,2.16,1.85,5.81,2.3,9.27,2.22a44.4,44.4,0,0,0,6.45-.68.09.09,0,0,0,.06-.15A34.81,34.81,0,0,1,17,45c0-.1,0-.21,0-.31a35,35,0,0,1,70,0c0,.1,0,.21,0,.31a34.81,34.81,0,0,1-5.78,19.24.09.09,0,0,0,.06.15,44.4,44.4,0,0,0,6.45.68c3.46.08,7.11-.37,9.27-2.22,5-4.27,6-11.76,6-20.32C103,35.35,94.55,35,91.84,35Z" /> |
||||
<path d="M52,74,25.4,65.13a.1.1,0,0,0-.1.17L51.93,91.93a.1.1,0,0,0,.14,0L78.7,65.3a.1.1,0,0,0-.1-.17L52,74A.06.06,0,0,1,52,74Z" /> |
||||
<path d="M75.68,46.9,82,45a.09.09,0,0,0,.08-.09,29.91,29.91,0,0,0-.87-6.94.11.11,0,0,0-.09-.08l-6.43-.58a.1.1,0,0,1-.06-.18l4.78-4.18a.13.13,0,0,0,0-.12,30.19,30.19,0,0,0-3.65-6.07.09.09,0,0,0-.11,0l-5.91,2a.1.1,0,0,1-.12-.14L72.19,23a.11.11,0,0,0,0-.12,29.86,29.86,0,0,0-5.84-4.13.09.09,0,0,0-.11,0l-4.47,4.13a.1.1,0,0,1-.17-.07l.09-6a.1.1,0,0,0-.07-.1,30.54,30.54,0,0,0-7-1.47.1.1,0,0,0-.1.07l-2.38,5.54a.1.1,0,0,1-.18,0l-2.37-5.54a.11.11,0,0,0-.11-.06,30,30,0,0,0-7,1.48.12.12,0,0,0-.07.1l.08,6.05a.09.09,0,0,1-.16.07L37.8,18.76a.11.11,0,0,0-.12,0,29.75,29.75,0,0,0-5.83,4.13.11.11,0,0,0,0,.12l2.59,5.6a.11.11,0,0,1-.13.14l-5.9-2a.11.11,0,0,0-.12,0,30.23,30.23,0,0,0-3.62,6.08.11.11,0,0,0,0,.12l4.79,4.19a.1.1,0,0,1-.06.17L23,37.91a.1.1,0,0,0-.09.07A29.9,29.9,0,0,0,22,44.92a.1.1,0,0,0,.07.1L28.4,47a.1.1,0,0,1,0,.18l-5.84,3.26a.16.16,0,0,0,0,.11,30.17,30.17,0,0,0,2.1,6.76c.32.71.67,1.4,1,2.08a.1.1,0,0,0,.06,0L52,68.16H52l26.34-8.78a.1.1,0,0,0,.06-.05,30.48,30.48,0,0,0,3.11-8.88.1.1,0,0,0-.05-.11l-5.83-3.26A.1.1,0,0,1,75.68,46.9Z" /> |
||||
</svg> |
@ -0,0 +1,23 @@ |
||||
.preload-container { |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: center; |
||||
align-items: center; |
||||
height: 100vh; |
||||
} |
||||
|
||||
.preload-info-container { |
||||
display: flex; |
||||
flex-direction: column; |
||||
text-align: center; |
||||
max-width: 400px; |
||||
} |
||||
|
||||
.preload-info-container .btn { |
||||
cursor: pointer; |
||||
} |
||||
|
||||
.preload-logo { |
||||
min-width: 200px; |
||||
padding-bottom: 1.5rem !important; |
||||
} |
@ -0,0 +1,72 @@ |
||||
export class fileSystem { |
||||
name: string |
||||
enabled: boolean |
||||
available: boolean |
||||
fs: any |
||||
fsCallBack: any; |
||||
hasWorkSpaces: boolean |
||||
loaded: boolean |
||||
load: () => Promise<unknown> |
||||
test: () => Promise<unknown> |
||||
|
||||
constructor() { |
||||
this.available = false |
||||
this.enabled = false |
||||
this.hasWorkSpaces = false |
||||
this.loaded = false |
||||
} |
||||
|
||||
checkWorkspaces = async () => { |
||||
try { |
||||
await this.fs.stat('.workspaces') |
||||
this.hasWorkSpaces = true |
||||
} catch (e) { |
||||
|
||||
} |
||||
} |
||||
|
||||
set = async () => { |
||||
const w = (window as any) |
||||
if (!this.loaded) return false |
||||
w.remixFileSystem = this.fs |
||||
w.remixFileSystem.name = this.name |
||||
w.remixFileSystemCallback = this.fsCallBack |
||||
return true |
||||
} |
||||
} |
||||
|
||||
export class fileSystems { |
||||
fileSystems: Record<string, fileSystem> |
||||
constructor() { |
||||
this.fileSystems = {} |
||||
} |
||||
|
||||
addFileSystem = async (fs: fileSystem): Promise<boolean> => { |
||||
try { |
||||
this.fileSystems[fs.name] = fs |
||||
await fs.test() && await fs.load() |
||||
console.log(fs.name + ' is loaded...') |
||||
return true |
||||
} catch (e) { |
||||
console.log(fs.name + ' not available...') |
||||
return false |
||||
} |
||||
} |
||||
/** |
||||
* sets filesystem using list as fallback |
||||
* @param {string[]} names |
||||
* @returns {Promise} |
||||
*/ |
||||
setFileSystem = async (filesystems?: fileSystem[]): Promise<fileSystem> => { |
||||
for (const fs of filesystems) { |
||||
if (fs && this.fileSystems[fs.name]) { |
||||
const result = await this.fileSystems[fs.name].set() |
||||
if (result) return this.fileSystems[fs.name] |
||||
} |
||||
} |
||||
return null |
||||
} |
||||
|
||||
|
||||
} |
||||
|
@ -0,0 +1,190 @@ |
||||
import { hashMessage } from "ethers/lib/utils" |
||||
import JSZip from "jszip" |
||||
import { fileSystem } from "../fileSystem" |
||||
const _paq = window._paq = window._paq || [] |
||||
export class fileSystemUtility { |
||||
migrate = async (fsFrom: fileSystem, fsTo: fileSystem) => { |
||||
try { |
||||
await fsFrom.checkWorkspaces() |
||||
await fsTo.checkWorkspaces() |
||||
|
||||
if (fsTo.hasWorkSpaces) { |
||||
console.log(`${fsTo.name} already has files`) |
||||
return true |
||||
} |
||||
|
||||
if (!fsFrom.hasWorkSpaces) { |
||||
console.log('no files to migrate') |
||||
return true |
||||
} |
||||
|
||||
const fromFiles = await this.copyFolderToJson('/', null, null, fsFrom.fs) |
||||
await this.populateWorkspace(fromFiles, fsTo.fs) |
||||
const toFiles = await this.copyFolderToJson('/', null, null, fsTo.fs) |
||||
|
||||
if (hashMessage(JSON.stringify(toFiles)) === hashMessage(JSON.stringify(fromFiles))) { |
||||
console.log('file migration successful') |
||||
return true |
||||
} else { |
||||
_paq.push(['trackEvent', 'Migrate', 'error', 'hash mismatch']) |
||||
console.log('file migration failed falling back to ' + fsFrom.name) |
||||
fsTo.loaded = false |
||||
return false |
||||
} |
||||
} catch (err) { |
||||
console.log(err) |
||||
_paq.push(['trackEvent', 'Migrate', 'error', err && err.message]) |
||||
console.log('file migration failed falling back to ' + fsFrom.name) |
||||
fsTo.loaded = false |
||||
return false |
||||
} |
||||
} |
||||
|
||||
downloadBackup = async (fs: fileSystem) => { |
||||
try { |
||||
const zip = new JSZip() |
||||
await fs.checkWorkspaces() |
||||
await this.copyFolderToJson('/', null, null, fs.fs, ({ path, content }) => { |
||||
zip.file(path, content) |
||||
}) |
||||
const blob = await zip.generateAsync({ type: 'blob' }) |
||||
const today = new Date() |
||||
const date = today.getFullYear() + '-' + (today.getMonth() + 1) + '-' + today.getDate() |
||||
const time = today.getHours() + 'h' + today.getMinutes() + 'min' |
||||
this.saveAs(blob, `remix-backup-at-${time}-${date}.zip`) |
||||
_paq.push(['trackEvent','Backup','download','preload']) |
||||
} catch (err) { |
||||
_paq.push(['trackEvent','Backup','error',err && err.message]) |
||||
console.log(err) |
||||
} |
||||
} |
||||
|
||||
populateWorkspace = async (json, fs) => { |
||||
for (const item in json) { |
||||
const isFolder = json[item].content === undefined |
||||
if (isFolder) { |
||||
await this.createDir(item, fs) |
||||
await this.populateWorkspace(json[item].children, fs) |
||||
} else { |
||||
await fs.writeFile(item, json[item].content, 'utf8') |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* copy the folder recursively |
||||
* @param {string} path is the folder to be copied over |
||||
* @param {Function} visitFile is a function called for each visited files |
||||
* @param {Function} visitFolder is a function called for each visited folders |
||||
*/ |
||||
copyFolderToJson = async (path, visitFile, visitFolder, fs, cb = null) => { |
||||
visitFile = visitFile || (() => { }) |
||||
visitFolder = visitFolder || (() => { }) |
||||
return await this._copyFolderToJsonInternal(path, visitFile, visitFolder, fs, cb) |
||||
} |
||||
|
||||
/** |
||||
* copy the folder recursively (internal use) |
||||
* @param {string} path is the folder to be copied over |
||||
* @param {Function} visitFile is a function called for each visited files |
||||
* @param {Function} visitFolder is a function called for each visited folders |
||||
*/ |
||||
async _copyFolderToJsonInternal(path, visitFile, visitFolder, fs, cb) { |
||||
visitFile = visitFile || function () { /* do nothing. */ } |
||||
visitFolder = visitFolder || function () { /* do nothing. */ } |
||||
|
||||
const json = {} |
||||
// path = this.removePrefix(path)
|
||||
if (await fs.exists(path)) { |
||||
const items = await fs.readdir(path) |
||||
visitFolder({ path }) |
||||
if (items.length !== 0) { |
||||
for (const item of items) { |
||||
const file: any = {} |
||||
const curPath = `${path}${path.endsWith('/') ? '' : '/'}${item}` |
||||
if ((await fs.stat(curPath)).isDirectory()) { |
||||
file.children = await this._copyFolderToJsonInternal(curPath, visitFile, visitFolder, fs, cb) |
||||
} else { |
||||
file.content = await fs.readFile(curPath, 'utf8') |
||||
if (cb) cb({ path: curPath, content: file.content }) |
||||
visitFile({ path: curPath, content: file.content }) |
||||
|
||||
} |
||||
json[curPath] = file |
||||
} |
||||
} |
||||
} |
||||
return json |
||||
} |
||||
|
||||
createDir = async (path, fs) => { |
||||
const paths = path.split('/') |
||||
if (paths.length && paths[0] === '') paths.shift() |
||||
let currentCheck = '' |
||||
for (const value of paths) { |
||||
currentCheck = currentCheck + (currentCheck ? '/' : '') + value |
||||
if (!await fs.exists(currentCheck)) { |
||||
await fs.mkdir(currentCheck) |
||||
} |
||||
} |
||||
} |
||||
|
||||
saveAs = (blob, name) => { |
||||
const node = document.createElement('a') |
||||
node.download = name |
||||
node.rel = 'noopener' |
||||
node.href = URL.createObjectURL(blob) |
||||
setTimeout(function () { URL.revokeObjectURL(node.href) }, 4E4) // 40s
|
||||
setTimeout(function () { |
||||
try { |
||||
node.dispatchEvent(new MouseEvent('click')) |
||||
} catch (e) { |
||||
const evt = document.createEvent('MouseEvents') |
||||
evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, |
||||
20, false, false, false, false, 0, null) |
||||
node.dispatchEvent(evt) |
||||
} |
||||
}, 0) // 40s
|
||||
} |
||||
} |
||||
|
||||
|
||||
/* eslint-disable no-template-curly-in-string */ |
||||
export const migrationTestData = { |
||||
'.workspaces': { |
||||
children: { |
||||
'.workspaces/default_workspace': { |
||||
children: { |
||||
'.workspaces/default_workspace/README.txt': { |
||||
content: 'TEST README' |
||||
} |
||||
} |
||||
}, |
||||
'.workspaces/emptyspace': { |
||||
|
||||
}, |
||||
'.workspaces/workspace_test': { |
||||
children: { |
||||
'.workspaces/workspace_test/TEST_README.txt': { |
||||
content: 'TEST README' |
||||
}, |
||||
'.workspaces/workspace_test/test_contracts': { |
||||
children: { |
||||
'.workspaces/workspace_test/test_contracts/1_Storage.sol': { |
||||
content: 'testing' |
||||
}, |
||||
'.workspaces/workspace_test/test_contracts/artifacts': { |
||||
children: { |
||||
'.workspaces/workspace_test/test_contracts/artifacts/Storage_metadata.json': { |
||||
content: '{ "test": "data" }' |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,91 @@ |
||||
import LightningFS from "@isomorphic-git/lightning-fs" |
||||
import { fileSystem } from "../fileSystem" |
||||
|
||||
export class IndexedDBStorage extends LightningFS { |
||||
base: LightningFS.PromisifedFS |
||||
addSlash: (file: string) => string |
||||
extended: { exists: (path: string) => Promise<unknown>; rmdir: (path: any) => Promise<void>; readdir: (path: any) => Promise<string[]>; unlink: (path: any) => Promise<void>; mkdir: (path: any) => Promise<void>; readFile: (path: any, options: any) => Promise<Uint8Array>; rename: (from: any, to: any) => Promise<void>; writeFile: (path: any, content: any, options: any) => Promise<void>; stat: (path: any) => Promise<import("fs").Stats>; init(name: string, opt?: LightningFS.FSConstructorOptions): void; activate(): Promise<void>; deactivate(): Promise<void>; lstat(filePath: string): Promise<import("fs").Stats>; readlink(filePath: string): Promise<string>; symlink(target: string, filePath: string): Promise<void> } |
||||
constructor(name: string) { |
||||
super(name) |
||||
this.addSlash = (file) => { |
||||
if (!file.startsWith('/')) file = '/' + file |
||||
return file |
||||
} |
||||
this.base = this.promises |
||||
this.extended = { |
||||
...this.promises, |
||||
exists: async (path: string) => { |
||||
return new Promise((resolve) => { |
||||
this.base.stat(this.addSlash(path)).then(() => resolve(true)).catch(() => resolve(false)) |
||||
}) |
||||
}, |
||||
rmdir: async (path) => { |
||||
return this.base.rmdir(this.addSlash(path)) |
||||
}, |
||||
readdir: async (path) => { |
||||
return this.base.readdir(this.addSlash(path)) |
||||
}, |
||||
unlink: async (path) => { |
||||
return this.base.unlink(this.addSlash(path)) |
||||
}, |
||||
mkdir: async (path) => { |
||||
return this.base.mkdir(this.addSlash(path)) |
||||
}, |
||||
readFile: async (path, options) => { |
||||
return this.base.readFile(this.addSlash(path), options) |
||||
}, |
||||
rename: async (from, to) => { |
||||
return this.base.rename(this.addSlash(from), this.addSlash(to)) |
||||
}, |
||||
writeFile: async (path, content, options) => { |
||||
return this.base.writeFile(this.addSlash(path), content, options) |
||||
}, |
||||
stat: async (path) => { |
||||
return this.base.stat(this.addSlash(path)) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
export class indexedDBFileSystem extends fileSystem { |
||||
constructor() { |
||||
super() |
||||
this.name = 'indexedDB' |
||||
} |
||||
|
||||
load = async () => { |
||||
return new Promise((resolve, reject) => { |
||||
try { |
||||
const fs = new IndexedDBStorage('RemixFileSystem') |
||||
fs.init('RemixFileSystem') |
||||
this.fs = fs.extended |
||||
this.fsCallBack = fs |
||||
this.loaded = true |
||||
resolve(true) |
||||
} catch (e) { |
||||
reject(e) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
test = async () => { |
||||
return new Promise((resolve, reject) => { |
||||
if (!window.indexedDB) { |
||||
this.available = false |
||||
reject('No indexedDB on window') |
||||
} |
||||
const request = window.indexedDB.open("RemixTestDataBase"); |
||||
request.onerror = () => { |
||||
this.available = false |
||||
reject('Error creating test database') |
||||
}; |
||||
request.onsuccess = () => { |
||||
window.indexedDB.deleteDatabase("RemixTestDataBase"); |
||||
this.available = true |
||||
resolve(true) |
||||
}; |
||||
}) |
||||
} |
||||
|
||||
} |
@ -0,0 +1,57 @@ |
||||
import { fileSystem } from "../fileSystem"; |
||||
|
||||
export class localStorageFS extends fileSystem { |
||||
|
||||
constructor() { |
||||
super() |
||||
this.name = 'localstorage' |
||||
} |
||||
load = async () => { |
||||
const me = this |
||||
return new Promise((resolve, reject) => { |
||||
try { |
||||
const w = window as any |
||||
w.BrowserFS.install(window) |
||||
w.BrowserFS.configure({ |
||||
fs: 'LocalStorage' |
||||
}, async function (e) { |
||||
if (e) { |
||||
console.log('BrowserFS Error: ' + e) |
||||
reject(e) |
||||
} else { |
||||
me.fs = { ...window.require('fs') } |
||||
me.fsCallBack = window.require('fs') |
||||
me.fs.readdir = me.fs.readdirSync |
||||
me.fs.readFile = me.fs.readFileSync |
||||
me.fs.writeFile = me.fs.writeFileSync |
||||
me.fs.stat = me.fs.statSync |
||||
me.fs.unlink = me.fs.unlinkSync |
||||
me.fs.rmdir = me.fs.rmdirSync |
||||
me.fs.mkdir = me.fs.mkdirSync |
||||
me.fs.rename = me.fs.renameSync |
||||
me.fs.exists = me.fs.existsSync |
||||
me.loaded = true |
||||
resolve(true) |
||||
} |
||||
}) |
||||
} catch (e) { |
||||
console.log('BrowserFS is not ready!') |
||||
reject(e) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
test = async () => { |
||||
return new Promise((resolve, reject) => { |
||||
const test = 'test'; |
||||
try { |
||||
localStorage.setItem(test, test); |
||||
localStorage.removeItem(test); |
||||
resolve(true) |
||||
} catch(e) { |
||||
reject(e) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
} |
@ -0,0 +1,32 @@ |
||||
import { ViewPlugin } from '@remixproject/engine-web' |
||||
import * as packageJson from '../../../../../package.json' |
||||
import React from 'react' // eslint-disable-line
|
||||
import { SearchTab } from '@remix-ui/search' |
||||
const profile = { |
||||
name: 'search', |
||||
displayName: 'Search', |
||||
methods: [''], |
||||
events: [], |
||||
icon: 'assets/img/Search_Icon.svg', |
||||
description: '', |
||||
kind: '', |
||||
location: 'sidePanel', |
||||
documentation: '', |
||||
version: packageJson.version |
||||
} |
||||
|
||||
export class SearchPlugin extends ViewPlugin { |
||||
|
||||
constructor () { |
||||
super(profile) |
||||
} |
||||
|
||||
render() {
|
||||
return ( |
||||
<div id='searchTab'> |
||||
<SearchTab plugin={this}></SearchTab> |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
} |
After Width: | Height: | Size: 2.6 KiB |
File diff suppressed because one or more lines are too long
@ -1,139 +0,0 @@ |
||||
// eslint-disable-next-line no-unused-vars
|
||||
async function migrateFilesFromLocalStorage (cb) { |
||||
let testmigration = false // migration loads test data into localstorage with browserfs
|
||||
// indexeddb will be empty by this point, so there is no danger but do a check for the origin to load test data so it runs only locally
|
||||
testmigration = window.location.hash.includes('e2e_testmigration=true') && window.location.host === '127.0.0.1:8080' && window.location.protocol === 'http:' |
||||
// eslint-disable-next-line no-undef
|
||||
BrowserFS.install(window) |
||||
// eslint-disable-next-line no-undef
|
||||
BrowserFS.configure({ |
||||
fs: 'LocalStorage' |
||||
}, async function (e) { |
||||
if (e) console.log(e) |
||||
|
||||
const browserFS = window.require('fs') |
||||
|
||||
/** |
||||
* copy the folder recursively (internal use) |
||||
* @param {string} path is the folder to be copied over |
||||
* @param {Function} visitFile is a function called for each visited files |
||||
* @param {Function} visitFolder is a function called for each visited folders |
||||
*/ |
||||
async function _copyFolderToJsonInternal (path, visitFile, visitFolder, fs) { |
||||
visitFile = visitFile || (() => { }) |
||||
visitFolder = visitFolder || (() => { }) |
||||
return new Promise((resolve, reject) => { |
||||
const json = {} |
||||
if (fs.existsSync(path)) { |
||||
try { |
||||
const items = fs.readdirSync(path) |
||||
visitFolder({ path }) |
||||
if (items.length !== 0) { |
||||
items.forEach(async (item, index) => { |
||||
const file = {} |
||||
const curPath = `${path}${path.endsWith('/') ? '' : '/'}${item}` |
||||
if (fs.statSync(curPath).isDirectory()) { |
||||
file.children = await _copyFolderToJsonInternal(curPath, visitFile, visitFolder, fs) |
||||
} else { |
||||
file.content = fs.readFileSync(curPath, 'utf8') |
||||
visitFile({ path: curPath, content: file.content }) |
||||
} |
||||
json[curPath] = file |
||||
}) |
||||
} |
||||
} catch (e) { |
||||
console.log(e) |
||||
return reject(e) |
||||
} |
||||
} |
||||
return resolve(json) |
||||
}) |
||||
} |
||||
|
||||
/** |
||||
* copy the folder recursively |
||||
* @param {string} path is the folder to be copied over |
||||
* @param {Function} visitFile is a function called for each visited files |
||||
* @param {Function} visitFolder is a function called for each visited folders |
||||
*/ |
||||
async function copyFolderToJson (path, visitFile, visitFolder, fs) { |
||||
visitFile = visitFile || (() => { }) |
||||
visitFolder = visitFolder || (() => { }) |
||||
return _copyFolderToJsonInternal(path, visitFile, visitFolder, fs) |
||||
} |
||||
|
||||
const populateWorkspace = async (json, fs) => { |
||||
for (const item in json) { |
||||
const isFolder = json[item].content === undefined |
||||
if (isFolder) { |
||||
await createDir(item, fs) |
||||
await populateWorkspace(json[item].children, fs) |
||||
} else { |
||||
try { |
||||
await fs.writeFile(item, json[item].content, 'utf8') |
||||
} catch (error) { |
||||
console.log(error) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
const createDir = async (path, fs) => { |
||||
const paths = path.split('/') |
||||
if (paths.length && paths[0] === '') paths.shift() |
||||
let currentCheck = '' |
||||
for (const value of paths) { |
||||
currentCheck = currentCheck + (currentCheck ? '/' : '') + value |
||||
if (!await fs.exists(currentCheck)) { |
||||
try { |
||||
await fs.mkdir(currentCheck) |
||||
} catch (error) { |
||||
console.log(error) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
//
|
||||
if (testmigration) await populateWorkspace(testData, browserFS) |
||||
const files = await copyFolderToJson('/', null, null, browserFS) |
||||
await populateWorkspace(files, window.remixFileSystem) |
||||
// eslint-disable-next-line no-undef
|
||||
if (cb) cb() |
||||
}) |
||||
} |
||||
|
||||
/* eslint-disable no-template-curly-in-string */ |
||||
const testData = { |
||||
'.workspaces': { |
||||
children: { |
||||
'.workspaces/default_workspace': { |
||||
children: { |
||||
'.workspaces/default_workspace/README.txt': { |
||||
content: 'TEST README' |
||||
} |
||||
} |
||||
}, |
||||
'.workspaces/workspace_test': { |
||||
children: { |
||||
'.workspaces/workspace_test/TEST_README.txt': { |
||||
content: 'TEST README' |
||||
}, |
||||
'.workspaces/workspace_test/test_contracts': { |
||||
children: { |
||||
'.workspaces/workspace_test/test_contracts/1_Storage.sol': { |
||||
content: 'testing' |
||||
}, |
||||
'.workspaces/workspace_test/test_contracts/artifacts': { |
||||
children: { |
||||
'.workspaces/workspace_test/test_contracts/artifacts/Storage_metadata.json': { |
||||
content: '{ "test": "data" }' |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,57 +0,0 @@ |
||||
const allPrograms = [ |
||||
{ ethers: 'The ethers.js library is a compact and complete JavaScript library for Ethereum.' }, |
||||
{ remix: 'Ethereum IDE and tools for the web.' }, |
||||
{ web3: 'The web3.js library is a collection of modules which contain specific functionality for the ethereum ecosystem.' }, |
||||
{ swarmgw: 'This library can be used to upload/download files to Swarm via https://swarm-gateways.net/.' } |
||||
] |
||||
|
||||
const allCommands = [ |
||||
{ 'remix.execute(filepath)': 'Run the script specified by file path. If filepath is empty, script currently displayed in the editor is executed.' }, |
||||
{ 'remix.exeCurrent()': 'Run the script currently displayed in the editor.' }, |
||||
{ 'remix.help()': 'Display this help message.' }, |
||||
{ 'remix.loadgist(id)': 'Load a gist in the file explorer.' }, |
||||
{ 'remix.loadurl(url)': 'Load the given url in the file explorer. The url can be of type github, swarm or ipfs.' }, |
||||
|
||||
{ 'swarmgw.get(url, cb)': 'Download files from Swarm via https://swarm-gateways.net/' }, |
||||
{ 'swarmgw.put(content, cb)': 'Upload files to Swarm via https://swarm-gateways.net/' }, |
||||
|
||||
{ 'ethers.Contract': 'This API provides a graceful connection to a contract deployed on the blockchain, simplifying calling and querying its functions and handling all the binary protocol and conversion as necessarily.' }, |
||||
{ 'ethers.HDNode': 'A Hierarchical Deterministic Wallet represents a large tree of private keys which can reliably be reproduced from an initial seed.' }, |
||||
{ 'ethers.Interface': 'The Interface Object is a meta-class that accepts a Solidity (or compatible) Application Binary Interface (ABI) and populates functions to deal with encoding and decoding the parameters to pass in and results returned.' }, |
||||
{ 'ethers.providers': 'A Provider abstracts a connection to the Ethereum blockchain, for issuing queries and sending state changing transactions.' }, |
||||
{ 'ethers.SigningKey': 'The SigningKey interface provides an abstraction around the secp256k1 elliptic curve cryptography library.' }, |
||||
{ 'ethers.utils': 'The utility functions exposed in both the ethers umbrella package and the ethers-utils.' }, |
||||
{ 'ethers.utils.AbiCoder': 'Create a new ABI Coder object' }, |
||||
{ 'ethers.utils.RLP': 'This encoding method is used internally for several aspects of Ethereum, such as encoding transactions and determining contract addresses.' }, |
||||
{ 'ethers.Wallet': 'A wallet manages a private/public key pair which is used to cryptographically sign transactions and prove ownership on the Ethereum network.' }, |
||||
{ 'ethers.version': 'Contains the version of the ethers container object.' }, |
||||
|
||||
{ 'web3.eth': 'Eth module for interacting with the Ethereum network.' }, |
||||
{ 'web3.eth.accounts': 'The web3.eth.accounts contains functions to generate Ethereum accounts and sign transactions and data.' }, |
||||
{ 'web3.eth.abi': 'The web3.eth.abi functions let you de- and encode parameters to ABI (Application Binary Interface) for function calls to the EVM (Ethereum Virtual Machine).' }, |
||||
{ 'web3.eth.ens': 'The web3.eth.ens functions let you interacting with ENS.' }, |
||||
{ 'web3.eth.Iban': 'The web3.eth.Iban function lets convert Ethereum addresses from and to IBAN and BBAN.' }, |
||||
{ 'web3.eth.net': 'Net module for interacting with network properties.' }, |
||||
{ 'web3.eth.personal': 'Personal module for interacting with the Ethereum accounts.' }, |
||||
{ 'web3.eth.subscribe': 'The web3.eth.subscribe function lets you subscribe to specific events in the blockchain.' }, |
||||
{ 'web3.givenProvider': 'When using web3.js in an Ethereum compatible browser, it will set with the current native provider by that browser. Will return the given provider by the (browser) environment, otherwise null.' }, |
||||
{ 'web3.modules': 'Contains the version of the web3 container object.' }, |
||||
{ 'web3.providers': 'Contains the current available providers.' }, |
||||
{ 'web3.shh': 'Shh module for interacting with the whisper protocol' }, |
||||
{ 'web3.utils': 'This package provides utility functions for Ethereum dapps and other web3.js packages.' }, |
||||
{ 'web3.version': 'Contains the version of the web3 container object.' }, |
||||
|
||||
{ 'web3.eth.clearSubscriptions();': 'Resets subscriptions.' }, |
||||
{ 'web3.eth.Contract(jsonInterface[, address][, options])': 'The web3.eth.Contract object makes it easy to interact with smart contracts on the ethereum blockchain.' }, |
||||
{ 'web3.eth.accounts.create([entropy]);': 'The web3.eth.accounts contains functions to generate Ethereum accounts and sign transactions and data.' }, |
||||
{ 'web3.eth.getAccounts();': 'Retrieve the list of accounts' }, |
||||
{ 'web3.eth.accounts.privateKeyToAccount(privateKey [, ignoreLength ]);': 'Get the account from the private key' }, |
||||
{ 'web3.eth.accounts.signTransaction(tx, privateKey [, callback]);': 'Sign Transaction' }, |
||||
{ 'web3.eth.accounts.recoverTransaction(rawTransaction);': 'Sign Transaction' }, |
||||
{ 'web3.eth.accounts.hashMessage(message);': 'Hash message' } |
||||
] |
||||
|
||||
module.exports = { |
||||
allPrograms, |
||||
allCommands |
||||
} |
@ -0,0 +1,59 @@ |
||||
export const fetchContractFromEtherscan = async (plugin, network, contractAddress, targetPath) => { |
||||
let data |
||||
const compilationTargets = {} |
||||
|
||||
const etherscanKey = await plugin.call('config', 'getAppParameter', 'etherscan-access-token') |
||||
if (etherscanKey) { |
||||
const endpoint = network.id == 1 ? 'api.etherscan.io' : 'api-' + network.name + '.etherscan.io' |
||||
data = await fetch('https://' + endpoint + '/api?module=contract&action=getsourcecode&address=' + contractAddress + '&apikey=' + etherscanKey) |
||||
data = await data.json() |
||||
// etherscan api doc https://docs.etherscan.io/api-endpoints/contracts
|
||||
if (data.message === 'OK' && data.status === "1") { |
||||
if (data.result.length) { |
||||
if (data.result[0].SourceCode === '') throw new Error('contract not verified') |
||||
if (data.result[0].SourceCode.startsWith('{')) { |
||||
data.result[0].SourceCode = JSON.parse(data.result[0].SourceCode.replace(/(?:\r\n|\r|\n)/g, '').replace(/^{{/,'{').replace(/}}$/,'}')) |
||||
} |
||||
}
|
||||
} else throw new Error('unable to retrieve contract data ' + data.message) |
||||
} else throw new Error('unable to try fetching the source code from etherscan: etherscan access token not found. please go to the Remix settings page and provide an access token.') |
||||
|
||||
if (!data || !data.result) { |
||||
return null |
||||
} |
||||
|
||||
if (typeof data.result[0].SourceCode === 'string') { |
||||
const fileName = `${targetPath}/${network.id}/${contractAddress}/${data.result[0].ContractName}.sol` |
||||
await plugin.call('fileManager', 'setFile', fileName , data.result[0].SourceCode) |
||||
compilationTargets[fileName] = { content: data.result[0].SourceCode } |
||||
} else if (data.result[0].SourceCode && typeof data.result[0].SourceCode == 'object') { |
||||
const sources = data.result[0].SourceCode.sources |
||||
for (let [file, source] of Object.entries(sources)) { // eslint-disable-line
|
||||
file = file.replace('browser/', '') // should be fixed in the remix IDE end.
|
||||
file = file.replace(/^\//g, '') // remove first slash.
|
||||
if (await plugin.call('contentImport', 'isExternalUrl', file)) { |
||||
// nothing to do, the compiler callback will handle those
|
||||
} else {
|
||||
const path = `${targetPath}/${network.id}/${contractAddress}/${file}` |
||||
const content = (source as any).content |
||||
await plugin.call('fileManager', 'setFile', path, content) |
||||
compilationTargets[path] = { content } |
||||
} |
||||
} |
||||
} |
||||
let runs = 0 |
||||
try { |
||||
runs = parseInt(data.result[0].Runs) |
||||
} catch (e) {} |
||||
const settings = { |
||||
version: data.result[0].CompilerVersion.replace(/^v/, ''), |
||||
language: 'Solidity', |
||||
evmVersion: data.result[0].EVMVersion.toLowerCase(), |
||||
optimize: data.result[0].OptimizationUsed === '1', |
||||
runs |
||||
} |
||||
return { |
||||
settings, |
||||
compilationTargets |
||||
} |
||||
} |
@ -0,0 +1,46 @@ |
||||
export const fetchContractFromSourcify = async (plugin, network, contractAddress, targetPath) => { |
||||
let data |
||||
const compilationTargets = {} |
||||
|
||||
try { |
||||
data = await plugin.call('sourcify', 'fetchByNetwork', contractAddress, network.id) |
||||
} catch (e) { |
||||
console.log(e) |
||||
} |
||||
|
||||
if (!data || !data.metadata) { |
||||
return null |
||||
} |
||||
|
||||
// set the solidity contract code using metadata
|
||||
await plugin.call('fileManager', 'setFile', `${targetPath}/${network.id}/${contractAddress}/metadata.json`, JSON.stringify(data.metadata, null, '\t')) |
||||
for (let file in data.metadata.sources) { |
||||
const urls = data.metadata.sources[file].urls |
||||
for (const url of urls) { |
||||
if (url.includes('ipfs')) { |
||||
const stdUrl = `ipfs://${url.split('/')[2]}` |
||||
const source = await plugin.call('contentImport', 'resolve', stdUrl) |
||||
file = file.replace('browser/', '') // should be fixed in the remix IDE end.
|
||||
if (await plugin.call('contentImport', 'isExternalUrl', file)) { |
||||
// nothing to do, the compiler callback will handle those
|
||||
} else { |
||||
const path = `${targetPath}/${network.id}/${contractAddress}/${file}` |
||||
await plugin.call('fileManager', 'setFile', path, source.content) |
||||
compilationTargets[path] = { content: source.content } |
||||
} |
||||
break |
||||
} |
||||
} |
||||
} |
||||
const settings = { |
||||
version: data.metadata.compiler.version, |
||||
language: data.metadata.language, |
||||
evmVersion: data.metadata.settings.evmVersion, |
||||
optimize: data.metadata.settings.optimizer.enabled, |
||||
runs: data.metadata.settings.optimizer.runs |
||||
} |
||||
return { |
||||
settings, |
||||
compilationTargets |
||||
} |
||||
} |
@ -0,0 +1,12 @@ |
||||
'use strict' |
||||
import { ValueType } from './ValueType' |
||||
|
||||
export class FunctionType extends ValueType { |
||||
constructor (type, stateDefinitions, contractName, location) { |
||||
super(1, 8, 'function') |
||||
} |
||||
|
||||
decodeValue (value) { |
||||
return 'at program counter ' + value |
||||
} |
||||
} |
@ -1,55 +0,0 @@ |
||||
export class DummyProvider { |
||||
eth |
||||
debug |
||||
providers |
||||
currentProvider |
||||
|
||||
constructor () { |
||||
this.eth = {} |
||||
this.debug = {} |
||||
this.eth.getCode = (address, cb) => { return this.getCode(address, cb) } |
||||
this.eth.getTransaction = (hash, cb) => { return this.getTransaction(hash, cb) } |
||||
this.eth.getTransactionFromBlock = (blockNumber, txIndex, cb) => { return this.getTransactionFromBlock(blockNumber, txIndex, cb) } |
||||
this.eth.getBlockNumber = (cb) => { return this.getBlockNumber(cb) } |
||||
this.debug.traceTransaction = (hash, options, cb) => { return this.traceTransaction(hash, options, cb) } |
||||
this.debug.storageRangeAt = (blockNumber, txIndex, address, start, end, maxLength, cb) => { return this.storageRangeAt(blockNumber, txIndex, address, start, end, maxLength, cb) } |
||||
this.providers = { HttpProvider: function (url) {} } |
||||
this.currentProvider = { host: '' } |
||||
} |
||||
|
||||
getCode (address, cb) { |
||||
cb(null, '') |
||||
} |
||||
|
||||
setProvider (provider) {} |
||||
|
||||
traceTransaction (txHash, options, cb) { |
||||
if (cb) { |
||||
cb(null, {}) |
||||
} |
||||
return {} |
||||
} |
||||
|
||||
storageRangeAt (blockNumber, txIndex, address, start, end, maxLength, cb) { |
||||
if (cb) { |
||||
cb(null, {}) |
||||
} |
||||
return {} |
||||
} |
||||
|
||||
getBlockNumber (cb) { cb(null, '') } |
||||
|
||||
getTransaction (txHash, cb) { |
||||
if (cb) { |
||||
cb(null, {}) |
||||
} |
||||
return {} |
||||
} |
||||
|
||||
getTransactionFromBlock (blockNumber, txIndex, cb) { |
||||
if (cb) { |
||||
cb(null, {}) |
||||
} |
||||
return {} |
||||
} |
||||
} |
@ -1,38 +0,0 @@ |
||||
import { Web3VmProvider } from './web3VmProvider' |
||||
import { loadWeb3, extendWeb3 } from '../init' |
||||
|
||||
export class Web3Providers { |
||||
modes |
||||
constructor () { |
||||
this.modes = {} |
||||
} |
||||
|
||||
addProvider (type, obj) { |
||||
if (type === 'INTERNAL') { |
||||
const web3 = loadWeb3() |
||||
this.addWeb3(type, web3) |
||||
} else if (type === 'vm') { |
||||
this.addVM(type, obj) |
||||
} else { |
||||
extendWeb3(obj) |
||||
this.addWeb3(type, obj) |
||||
} |
||||
} |
||||
|
||||
get (type, cb) { |
||||
if (this.modes[type]) { |
||||
return cb(null, this.modes[type]) |
||||
} |
||||
cb('error: this provider has not been setup (' + type + ')', null) |
||||
} |
||||
|
||||
addWeb3 (type, web3) { |
||||
this.modes[type] = web3 |
||||
} |
||||
|
||||
addVM (type, vm) { |
||||
const vmProvider = new Web3VmProvider() |
||||
vmProvider.setVM(vm) |
||||
this.modes[type] = vmProvider |
||||
} |
||||
} |
@ -1,6 +1,6 @@ |
||||
export { default as RemixApp } from './lib/remix-app/remix-app' |
||||
export { dispatchModalContext, AppContext } from './lib/remix-app/context/context' |
||||
export { ModalProvider } from './lib/remix-app/context/provider' |
||||
export { ModalProvider, useDialogDispatchers } from './lib/remix-app/context/provider' |
||||
export { AppModal } from './lib/remix-app/interface/index' |
||||
export { AlertModal } from './lib/remix-app/interface/index' |
||||
export { ModalTypes } from './lib/remix-app/types/index' |
||||
|
@ -1,2 +1,3 @@ |
||||
export * from './lib/remix-ui-helper' |
||||
export * from './lib/helper-components' |
||||
export * from './lib/components/PluginViewWrapper' |
@ -0,0 +1,24 @@ |
||||
import React from "react" |
||||
import { useEffect, useState } from "react" |
||||
|
||||
interface IPluginViewWrapperProps { |
||||
plugin: any |
||||
} |
||||
|
||||
export const PluginViewWrapper = (props: IPluginViewWrapperProps) => { |
||||
|
||||
const [state, setState] = useState<any>(null) |
||||
|
||||
useEffect(() => { |
||||
if(props.plugin.setDispatch){ |
||||
props.plugin.setDispatch(setState) |
||||
} |
||||
}, []) |
||||
|
||||
return ( |
||||
<>{state?
|
||||
<>{props.plugin.updateComponent(state)}</> |
||||
:<></> |
||||
}</> |
||||
) |
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue