Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EIP 712 #5237

Merged
merged 22 commits into from
Oct 16, 2024
Merged

EIP 712 #5237

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions apps/remix-ide-e2e/src/tests/runAndDeploy_injected.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,36 @@ const tests = {
browser
.executeScriptInTerminal('web3.eth.getAccounts()')
.journalLastChildIncludes('["0x76a3ABb5a12dcd603B52Ed22195dED17ee82708f"]')
},

'Test EIP 712 Signature with Injected Provider (Metamask) #group1 #flaky': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('i[id="remixRunSignMsg"]')
.click('i[id="remixRunSignMsg"]')
.waitForElementVisible('*[data-id="signMessageTextarea"]', 120000)
.click('*[data-id="sign-eip-712"]')
.waitForElementVisible('*[data-id="udappNotify-modal-footer-ok-react"]')
.modalFooterOKClick('udappNotify')
.pause(1000)
.getEditorValue((content) => {
browser.assert.ok(content.indexOf('"primaryType": "AuthRequest",') !== -1, 'EIP 712 data file must be opened')
})
.clickLaunchIcon('filePanel')
.rightClick('li[data-id="treeViewLitreeViewItemEIP-712-data.json"]')
.click('*[data-id="contextMenuItemsignTypedData"]')
.perform((done) => { // call delegate
browser.switchBrowserWindow(extension_url, 'MetaMask', (browser) => {
browser
.hideMetaMaskPopup()
.saveScreenshot('./reports/screenshots/metamask_6.png')
.waitForElementPresent('button[aria-label="Scroll down"]', 60000)
.click('button[aria-label="Scroll down"]') // scroll down
.click('button[data-testid="confirm-footer-button"]') // confirm
.switchBrowserTab(0) // back to remix
.perform(() => done())
})
})
.pause(1000)
.journalChildIncludes('0x8be3a81e17b7e4a40006864a4ff6bfa3fb1e18b292b6f47edec95cd8feaa53275b90f56ca02669d461a297e6bf94ab0ee4b7c89aede3228ed5aedb59c7e007501c')
}
}

Expand Down
19 changes: 18 additions & 1 deletion apps/remix-ide-e2e/src/tests/signingMessage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,24 @@ module.exports = {
})
})
})
.end()
},

'Test EIP 712 Signature': function (browser: NightwatchBrowser) {
browser.waitForElementPresent('i[id="remixRunSignMsg"]')
.click('i[id="remixRunSignMsg"]')
.waitForElementVisible('*[data-id="signMessageTextarea"]', 120000)
.click('*[data-id="sign-eip-712"]')
.waitForElementVisible('*[data-id="udappNotify-modal-footer-ok-react"]')
.modalFooterOKClick('udappNotify')
.pause(1000)
.getEditorValue((content) => {
browser.assert.ok(content.indexOf('"primaryType": "AuthRequest",') !== -1, 'EIP 712 data file must be opened')
})
.clickLaunchIcon('filePanel')
.rightClick('li[data-id="treeViewLitreeViewItemEIP-712-data.json"]')
.click('*[data-id="contextMenuItemsignTypedData"]')
.pause(1000)
.journalChildIncludes('0x8be3a81e17b7e4a40006864a4ff6bfa3fb1e18b292b6f47edec95cd8feaa53275b90f56ca02669d461a297e6bf94ab0ee4b7c89aede3228ed5aedb59c7e007501c')
}
}

Expand Down
2 changes: 1 addition & 1 deletion apps/remix-ide/src/app/files/fileManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ class FileManager extends Plugin {
} else {
const ret = await this.setFileContent(path, data)
this.emit('fileAdded', path)
return { newContent: ret, newpath: path }
return { newContent: ret, newPath: path }
}
} catch (e) {
throw new Error(e)
Expand Down
2 changes: 1 addition & 1 deletion apps/remix-ide/src/app/providers/abstract-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export abstract class AbstractProvider extends Plugin implements IProvider {
const result = await this.provider.send(data.method, data.params)
resolve({ jsonrpc: '2.0', result, id: data.id })
} catch (error) {
if (error && error.message && error.message.includes('net_version') && error.message.includes('SERVER_ERROR')) {
if (error && error.message && error.message.includes('SERVER_ERROR')) {
this.switchAway(true)
}
error.code = -32603
Expand Down
4 changes: 3 additions & 1 deletion apps/remix-ide/src/app/tabs/locales/en/filePanel.json
Original file line number Diff line number Diff line change
Expand Up @@ -150,5 +150,7 @@
"filePanel.saveCodeSample": "This code-sample workspace will not be persisted. Click here to save it.",
"filePanel.logInGithub": "Sign in to GitHub.",
"filePanel.gitHubLoggedAs": "Signed in as {githubuser}",
"filePanel.updateSubmodules": "Update all submodules of repository. Click to pull dependencies."
"filePanel.updateSubmodules": "Update all submodules of repository. Click to pull dependencies.",
"filePanel.signTypedData": "Sign Typed Data",
"filePanel.signTypedDataError": "Error while signing this typed data."
}
9 changes: 7 additions & 2 deletions apps/remix-ide/src/app/tabs/locales/en/udapp.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"udapp._comment_account.tsx": "libs/remix-ui/run-tab/src/lib/components/account.tsx",
"udapp.account": "Account",
"udapp.signAMessage": "Sign a message",
"udapp.enterAMessageToSign": "Enter a message to sign",
"udapp.enterAMessageToSign": "Enter a message to sign and click `Sign`",
"udapp.hash": "hash",
"udapp.signature": "signature",
"udapp.injectedTitle": "Unfortunately it's not possible to create an account using injected provider. Please create the account directly from your provider (i.e metamask or other of the same type).",
Expand Down Expand Up @@ -161,5 +161,10 @@
"udapp.ganacheProviderText1": "Note: To run Ganache on your system, run:",
"udapp.ganacheProviderText2": "For more info, visit: <a>Ganache Documentation</a>",
"udapp.hardhatProviderText1": "Note: To run Hardhat network node on your system, go to hardhat project folder and run command:",
"udapp.hardhatProviderText2": "For more info, visit: <a>Hardhat Documentation</a>"
"udapp.hardhatProviderText2": "For more info, visit: <a>Hardhat Documentation</a>",
"udapp.EIP712-2": "Please follow <a>this link</a> to get more information.",
"udapp.EIP712-3": "In Remix, signing typed data is possible by right clicking (right click / Sign Typed Data) on a JSON file whose content is EIP-712 compatible.",
"udapp.EIP712-create-template": "Create a JSON compliant with EIP-712",
"udapp.EIP712-close": "Close",
"udapp.sign": "Sign"
}
4 changes: 1 addition & 3 deletions apps/remix-ide/src/app/tabs/runTab/model/recorder.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const profile = {
/**
* Record transaction as long as the user create them.
*/
class Recorder extends Plugin {
export class Recorder extends Plugin {
constructor (blockchain) {
super(profile)
this.event = new EventManager()
Expand Down Expand Up @@ -328,5 +328,3 @@ class Recorder extends Plugin {
})
}
}

module.exports = Recorder
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import React from 'react' // eslint-disable-line
import {RunTabUI} from '@remix-ui/run-tab'
import {ViewPlugin} from '@remixproject/engine-web'
import { RunTabUI } from '@remix-ui/run-tab'
import { ViewPlugin } from '@remixproject/engine-web'
import isElectron from 'is-electron'
import {addressToString} from '@remix-ui/helper'
import {InjectedProviderDefault} from '../providers/injected-provider-default'
import {InjectedCustomProvider} from '../providers/injected-custom-provider'
import { addressToString } from '@remix-ui/helper'
import { InjectedProviderDefault } from '../providers/injected-provider-default'
import { InjectedCustomProvider } from '../providers/injected-custom-provider'
import * as packageJson from '../../../../../package.json'

const EventManager = require('../../lib/events')
const Recorder = require('../tabs/runTab/model/recorder.js')
import { EventManager } from '@remix-project/remix-lib'
import type { Blockchain } from '../../blockchain/blockchain'
import type { CompilerArtefacts } from '@remix-project/core-plugin'
// import type { NetworkModule } from '../tabs/network-module'
// import type FileProvider from '../files/fileProvider'
import { Recorder } from '../tabs/runTab/model/recorder'
const _paq = (window._paq = window._paq || [])

const profile = {
Expand Down Expand Up @@ -37,7 +40,20 @@ const profile = {
}

export class RunTab extends ViewPlugin {
constructor(blockchain, config, fileManager, editor, filePanel, compilersArtefacts, networkModule, fileProvider, engine) {
event: EventManager
engine: any
config: any
blockchain: Blockchain
fileManager: any
editor: any
filePanel: any
compilersArtefacts: CompilerArtefacts
networkModule: any
fileProvider: any
recorder: any
REACT_API: any
el: any
constructor(blockchain: Blockchain, config: any, fileManager: any, editor: any, filePanel: any, compilersArtefacts: CompilerArtefacts, networkModule: any, fileProvider: any, engine: any) {
super(profile)
this.event = new EventManager()
this.engine = engine
Expand Down Expand Up @@ -74,7 +90,7 @@ export class RunTab extends ViewPlugin {
async setEnvironmentMode(env) {
const canCall = await this.askUserPermission('setEnvironmentMode', 'change the environment used')
if (canCall) {
env = typeof env === 'string' ? {context: env} : env
env = typeof env === 'string' ? { context: env } : env
this.emit('setEnvironmentModeReducer', env, this.currentRequest.from)
}
}
Expand All @@ -83,7 +99,7 @@ export class RunTab extends ViewPlugin {
this.emit('clearAllInstancesReducer')
}

addInstance(address, abi, name, contractData) {
addInstance(address, abi, name, contractData?) {
this.emit('addInstanceReducer', address, abi, name, contractData)
}

Expand Down Expand Up @@ -153,7 +169,7 @@ export class RunTab extends ViewPlugin {
'injected-Brave Wallet': ['assets/img/brave.png'],
'injected-Trust Wallet': ['assets/img/trust-wallet.png'],
'hardhat-provider': ['assets/img/hardhat.png'],
'walletconnect': ['assets/img/Walletconnect-logo.png'],
'walletconnect': ['assets/img/Walletconnect-logo.png'],
'foundry-provider': ['assets/img/foundry.png']
}

Expand Down Expand Up @@ -181,31 +197,42 @@ export class RunTab extends ViewPlugin {
provider: {
sendAsync (payload) {
return udapp.call(name, 'sendAsync', payload)
},
async request (payload) {
try {
const requestResult = await udapp.call(name, 'sendAsync', payload)
if (requestResult.error) {
throw new Error(requestResult.error.message)
}
return requestResult.result
} catch (err) {
throw new Error(err.message)
}
}
}
})
}

const addCustomInjectedProvider = async (position, event, name, displayName, networkId, urls, nativeCurrency) => {
const addCustomInjectedProvider = async (position, event, name, displayName, networkId, urls, nativeCurrency?) => {
// name = `${name} through ${event.detail.info.name}`
await this.engine.register([new InjectedCustomProvider(event.detail.provider, name, networkId, urls, nativeCurrency)])
await addProvider(position, name, displayName, true, false, false)
await addProvider(position, name, displayName, true, false)
}
const registerInjectedProvider = async (event) => {
const name = 'injected-' + event.detail.info.name
const displayName = 'Injected Provider - ' + event.detail.info.name
await this.engine.register([new InjectedProviderDefault(event.detail.provider, name)])
await addProvider(0, name, displayName, true, false, false)
await addProvider(0, name, displayName, true, false)

if (event.detail.info.name === 'MetaMask') {
await addCustomInjectedProvider(7, event, 'injected-metamask-optimism', 'L2 - Optimism - ' + event.detail.info.name, '0xa', ['https://mainnet.optimism.io'])
await addCustomInjectedProvider(8, event, 'injected-metamask-arbitrum', 'L2 - Arbitrum - ' + event.detail.info.name, '0xa4b1', ['https://arb1.arbitrum.io/rpc'])
await addCustomInjectedProvider(8, event, 'injected-metamask-arbitrum', 'L2 - Arbitrum - ' + event.detail.info.name, '0xa4b1', ['https://arb1.arbitrum.io/rpc'])
await addCustomInjectedProvider(5, event, 'injected-metamask-sepolia', 'Sepolia Testnet - ' + event.detail.info.name, '0xaa36a7', [],
{
"name": "Sepolia ETH",
"symbol": "ETH",
"decimals": 18
})
})
await addCustomInjectedProvider(9, event, 'injected-metamask-ephemery', 'Ephemery Testnet - ' + event.detail.info.name, '', ['https://otter.bordel.wtf/erigon', 'https://eth.ephemeral.zeus.fyi'],
{
"name": "Ephemery ETH",
Expand All @@ -218,7 +245,6 @@ export class RunTab extends ViewPlugin {
"symbol": "XDAI",
"decimals": 18
})

/*
await addCustomInjectedProvider(9, event, 'SKALE Chaos Testnet', '0x50877ed6', ['https://staging-v3.skalenodes.com/v1/staging-fast-active-bellatrix'],
{
Expand All @@ -227,10 +253,10 @@ export class RunTab extends ViewPlugin {
"decimals": 18
})
*/
}
}
}

// VM
// VM
const titleVM = 'Execution environment is local to Remix. Data is only saved to browser memory and will vanish upon reload.'
await addProvider(1, 'vm-cancun', 'Remix VM (Cancun)', false, true, 'cancun', 'settingsVMCancunMode', titleVM)
await addProvider(50, 'vm-shanghai', 'Remix VM (Shanghai)', false, true, 'shanghai', 'settingsVMShanghaiMode', titleVM)
Expand All @@ -251,7 +277,7 @@ export class RunTab extends ViewPlugin {
await addProvider(22, 'foundry-provider', 'Dev - Foundry Provider', false, false)

// register injected providers

window.addEventListener(
"eip6963:announceProvider",
(event) => {
Expand Down
6 changes: 3 additions & 3 deletions apps/remix-ide/src/blockchain/providers/injected.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ export class InjectedProvider {
}

signMessage (message, account, _passphrase, cb) {
message = isHexString(message) ? message : Web3.utils.utf8ToHex(message)
const messageHash = hashPersonalMessage(Buffer.from(message))
try {
message = isHexString(message) ? message : Web3.utils.utf8ToHex(message)
this.executionContext.web3().eth.personal.sign(message, account).then((error, signedData) => {
cb(error, bytesToHex(messageHash), signedData)
this.executionContext.web3().eth.sign(messageHash, account).then((signedData) => {
cb(null, bytesToHex(messageHash), signedData)
}).catch((error => cb(error)))
} catch (e) {
cb(e.message)
Expand Down
21 changes: 18 additions & 3 deletions apps/remix-ide/src/blockchain/providers/vm.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Web3, FMT_BYTES, FMT_NUMBER, LegacySendAsyncProvider } from 'web3'
import { Web3, FMT_BYTES, FMT_NUMBER, LegacySendAsyncProvider, LegacyRequestProvider } from 'web3'
import { fromWei, toBigInt } from 'web3-utils'
import { privateToAddress, hashPersonalMessage, isHexString, bytesToHex } from '@ethereumjs/util'
import { extend, JSONRPCRequestPayload, JSONRPCResponseCallback } from '@remix-project/remix-simulator'
Expand All @@ -10,6 +10,7 @@ export class VMProvider {
worker: Worker
provider: {
sendAsync: (query: JSONRPCRequestPayload, callback: JSONRPCResponseCallback) => void
request: (query: JSONRPCRequestPayload) => Promise<any>
}
newAccountCallback: {[stamp: number]: (error: Error, address: string) => void}
constructor (executionContext: ExecutionContext) {
Expand Down Expand Up @@ -37,7 +38,13 @@ export class VMProvider {

return new Promise((resolve, reject) => {
this.worker.addEventListener('message', (msg) => {
if (msg.data.cmd === 'sendAsyncResult' && stamps[msg.data.stamp]) {
if (msg.data.cmd === 'requestResult' && stamps[msg.data.stamp]) {
if (msg.data.error) {
stamps[msg.data.stamp].reject(msg.data.error)
} else {
stamps[msg.data.stamp].resolve(msg.data.result)
}
} else if (msg.data.cmd === 'sendAsyncResult' && stamps[msg.data.stamp]) {
if (stamps[msg.data.stamp].callback) {
stamps[msg.data.stamp].callback(msg.data.error, msg.data.result)
return
Expand All @@ -57,9 +64,17 @@ export class VMProvider {
stamps[stamp] = { callback, resolve, reject }
this.worker.postMessage({ cmd: 'sendAsync', query, stamp })
})
},
request: (query) => {
return new Promise((resolve, reject) => {
const stamp = Date.now() + incr
incr++
stamps[stamp] = { resolve, reject }
this.worker.postMessage({ cmd: 'request', query, stamp })
})
}
}
this.web3 = new Web3(this.provider as LegacySendAsyncProvider)
this.web3 = new Web3(this.provider as (LegacySendAsyncProvider | LegacyRequestProvider))
this.web3.setConfig({ defaultTransactionType: '0x0' })
extend(this.web3)
this.executionContext.setWeb3(this.executionContext.getProvider(), this.web3)
Expand Down
31 changes: 31 additions & 0 deletions apps/remix-ide/src/blockchain/providers/worker-vm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,37 @@ self.onmessage = (e: MessageEvent) => {

break
}
case 'request':
{
(function (data) {
const stamp = data.stamp
if (provider) {
provider.request(data.query).then((result) => {
self.postMessage({
cmd: 'requestResult',
error: null,
result: result,
stamp: stamp
})
}).catch((error) => {
self.postMessage({
cmd: 'requestResult',
error: error,
result: null,
stamp: stamp
})
})
} else {
self.postMessage({
cmd: 'requestResult',
error: 'Provider not instantiated',
result: null,
stamp: stamp
})
}
})(data)
break
}
case 'addAccount':
{
if (provider) {
Expand Down
3 changes: 2 additions & 1 deletion libs/remix-lib/src/execution/txRunnerWeb3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,10 @@ export class TxRunnerWeb3 {
}, callback)
})
.catch(err => {
if (err && err.message.indexOf('Invalid JSON RPC response') !== -1) {
if (err && err.error && err.error.indexOf('Invalid JSON RPC response') !== -1) {
// // @todo(#378) this should be removed when https://github.com/WalletConnect/walletconnect-monorepo/issues/334 is fixed
callback(new Error('Gas estimation failed because of an unknown internal error. This may indicated that the transaction will fail.'))
return
}
err = network.name === 'VM' ? null : err // just send the tx if "VM"
gasEstimationForceSend(err, () => {
Expand Down
Loading
Loading