Skip to content

Commit d97c1d8

Browse files
committed
Merge remote-tracking branch 'origin/master' into eip_7702
2 parents 32897f0 + 5033474 commit d97c1d8

File tree

8 files changed

+87
-58
lines changed

8 files changed

+87
-58
lines changed

apps/remix-ide-e2e/src/tests/eip7702.test.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,27 @@ module.exports = {
1111
'Should activate delegation and make a transaction to the authority address #group1': function (browser: NightwatchBrowser) {
1212
let addressDelegate
1313
browser
14-
.pinGrid('vm-pectra', true)
1514
.clickLaunchIcon('udapp')
15+
.pinGrid('vm-pectra', true)
1616
.switchEnvironment('vm-pectra')
1717
.addFile('delegate.sol', { content: delegate })
1818
.clickLaunchIcon('solidity')
19-
.setSolidityCompilerVersion('soljson-v0.8.24+commit.e11b9ed9.js')
19+
.setSolidityCompilerVersion('soljson-v0.8.28+commit.7893614a.js')
2020
.clickLaunchIcon('solidity')
2121
.verifyContracts(['TestDelegate'])
2222
.clickLaunchIcon('udapp')
2323
.selectContract('Simple7702Account')
2424
.createContract('')
2525
.perform((done) => {
2626
browser.getAddressAtPosition(0, (address) => {
27-
addressDelegate = delegate
27+
addressDelegate = address
28+
done()
2829
})
2930
})
3031
.click('*[data-id="create-delegation-authorization"]')
32+
.waitForElementVisible('`*[data-id="udappNotify-modal-footer-ok-react"]`')
3133
.setValue('*[data-id="create-delegation-authorization-input"]', addressDelegate)
32-
.modalFooterOKClick('udapp')
34+
.modalFooterOKClick('udappNotify')
3335
.waitForElementContainsText('*[data-id="terminalJournal"]', 'This account will be running the code located at')
3436
.clickInstance(1)
3537
.clickFunction('entryPoint - call (not payable)')

libs/remix-lib/src/execution/txRunnerWeb3.ts

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { Transaction as InternalTransaction } from './txRunner'
44
import { Web3 } from 'web3'
55
import { BrowserProvider } from 'ethers'
66
import { normalizeHexAddress } from '../helpers/uiHelper'
7+
import { aaSupportedNetworks, aaLocalStorageKey, getPimlicoBundlerURL, aaDeterminiticProxyAddress } from '../helpers/aaConstants'
78
import { toBigInt, toHex, toChecksumAddress } from 'web3-utils'
89
import { randomBytes } from 'crypto'
910
import "viem/window"
@@ -110,7 +111,7 @@ export class TxRunnerWeb3 {
110111
} else {
111112
try {
112113
if (tx.fromSmartAccount) {
113-
const { txHash, contractAddress } = await this.sendUserOp(tx)
114+
const { txHash, contractAddress } = await this.sendUserOp(tx, network.id)
114115
cb(null, txHash, isCreation, true, contractAddress)
115116
} else {
116117
const res = await this.getWeb3().eth.sendTransaction(tx, null, { checkRevertBeforeSending: false, ignoreGasPricing: true })
@@ -201,8 +202,11 @@ export class TxRunnerWeb3 {
201202
callback(new Error('Gas estimation failed because of an unknown internal error. This may indicated that the transaction will fail.'))
202203
return
203204
}
204-
if (tx.fromSmartAccount && tx.value === "0" && err && err.error && err.error.indexOf('insufficient funds for transfer') !== -1) {
205-
// Do not show dialog for insufficient funds as smart account may be using paymaster
205+
if (tx.fromSmartAccount && tx.value === "0" &&
206+
err && err.message && err.message.includes('missing revert data')
207+
) {
208+
// Do not show dialog for 'missing revert data'
209+
// tx fees can be managed by paymaster in case of smart account tx
206210
// @todo If paymaster is used, check if balance/credits are available
207211
err = null
208212
}
@@ -225,16 +229,13 @@ export class TxRunnerWeb3 {
225229
})
226230
}
227231

228-
async sendUserOp (tx) {
229-
const localStorageKey = 'smartAccounts'
230-
const PUBLIC_NODE_URL = "https://go.getblock.io/ee42d0a88f314707be11dd799b122cb9"
231-
const determiniticProxyAddress = "0x4e59b44847b379578588920cA78FbF26c0B4956C"
232-
const network = 'sepolia'
233-
const chain = chains[network]
234-
const BUNDLER_URL = `https://pimlico.remixproject.org/api/proxy/${chain.id}`
232+
async sendUserOp (tx, chainId) {
233+
const chain = chains[aaSupportedNetworks[chainId].name]
234+
const PUBLIC_NODE_URL = aaSupportedNetworks[chainId].publicNodeUrl
235+
const BUNDLER_URL = getPimlicoBundlerURL(chainId)
235236

236237
// Check that saOwner is there in MM addresses
237-
let smartAccountsObj = localStorage.getItem(localStorageKey)
238+
let smartAccountsObj = localStorage.getItem(aaLocalStorageKey)
238239
smartAccountsObj = JSON.parse(smartAccountsObj)
239240
const saDetails = smartAccountsObj[chain.id][tx.from]
240241
const saOwner = saDetails['ownerEOA']
@@ -248,7 +249,7 @@ export class TxRunnerWeb3 {
248249

249250
const publicClient = createPublicClient({
250251
chain,
251-
transport: http(PUBLIC_NODE_URL) // choose any provider here
252+
transport: http(PUBLIC_NODE_URL)
252253
})
253254

254255
const safeAccount = await toSafeSmartAccount({
@@ -284,15 +285,15 @@ export class TxRunnerWeb3 {
284285

285286
const expectedDeploymentAddress = getContractAddress({
286287
bytecode,
287-
from: determiniticProxyAddress,
288+
from: aaDeterminiticProxyAddress,
288289
opcode: 'CREATE2',
289290
salt
290291
})
291292
let txHash, contractAddress
292293
if (!tx.to) {
293294
// contract deployment transaction
294295
txHash = await saClient.sendTransaction({
295-
to: determiniticProxyAddress,
296+
to: aaDeterminiticProxyAddress,
296297
data: encodePacked(["bytes32", "bytes"], [salt, bytecode])
297298
})
298299
// check if code is deployed to expectedDeployment Address
@@ -303,7 +304,7 @@ export class TxRunnerWeb3 {
303304
contractAddress = expectedDeploymentAddress
304305
} else {
305306
contractAddress = undefined
306-
console.error('Error in contract deployment')
307+
console.error('Error in contract deployment using smart account')
307308
}
308309
} else {
309310
// contract interaction transaction
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { ZeroAddress } from 'ethers'
2+
3+
// AA02: Add network name and public URL to support contract transactions using smart account
4+
export const aaSupportedNetworks = {
5+
"11155111": {
6+
name: "sepolia",
7+
publicNodeUrl: "https://go.getblock.io/ee42d0a88f314707be11dd799b122cb9"
8+
},
9+
// "10200": {
10+
// name: "gnosisChiado",
11+
// publicNodeUrl: "https://rpc.chiadochain.net/"
12+
// }
13+
}
14+
15+
export const getPimlicoBundlerURL = (chainId) => {
16+
return `https://pimlico.remixproject.org/api/proxy/${chainId}`
17+
}
18+
19+
export const aaLocalStorageKey = 'smartAccounts'
20+
21+
// AA03: Check if this address is valid for newly added network
22+
// This determiniticProxyAddress is used for replay protection during contract deployment
23+
// See: https://github.com/safe-global/safe-smart-account?tab=readme-ov-file#replay-protection-eip-155
24+
export const aaDeterminiticProxyAddress = "0x4e59b44847b379578588920cA78FbF26c0B4956C"
25+
26+
export const toAddress = ZeroAddress // A dummy zero value tx is made to this zero address to create existence of smart account

libs/remix-lib/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { TxRunnerVM } from './execution/txRunnerVM'
1717
import { TxRunnerWeb3 } from './execution/txRunnerWeb3'
1818
import * as txResultHelper from './helpers/txResultHelper'
1919
export { ConsoleLogs } from './helpers/hhconsoleSigs'
20+
export { aaSupportedNetworks, aaLocalStorageKey, getPimlicoBundlerURL, aaDeterminiticProxyAddress, toAddress } from './helpers/aaConstants'
2021
export { ICompilerApi, ConfigurationSettings, iSolJsonBinData, iSolJsonBinDataBuild } from './types/ICompilerApi'
2122
export { QueryParams } from './query-params'
2223
export { VMexecutionResult } from './execution/txRunnerVM'

libs/remix-simulator/src/methods/blocks.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export class Blocks {
7070
}
7171
})
7272
}
73-
73+
7474
const b = {
7575
baseFeePerGas: '0x01',
7676
number: bigIntToHex(block.header.number),
@@ -132,7 +132,7 @@ export class Blocks {
132132
}
133133
})
134134
}
135-
135+
136136
const b = {
137137
baseFeePerGas: '0x01',
138138
number: bigIntToHex(block.header.number),

libs/remix-ui/run-tab/src/lib/actions/account.ts

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { RunTab } from "../types/run-tab"
33
import { clearInstances, setAccount, setExecEnv } from "./actions"
44
import { displayNotification, fetchAccountsListFailed, fetchAccountsListRequest, fetchAccountsListSuccess, setMatchPassphrase, setPassphrase } from "./payload"
55
import { toChecksumAddress, bytesToHex } from '@ethereumjs/util'
6+
import { aaSupportedNetworks, aaLocalStorageKey, getPimlicoBundlerURL, toAddress } from '@remix-project/remix-lib'
67
import { SmartAccount } from "../types"
78
import { BrowserProvider, BaseWallet, SigningKey } from "ethers"
89
import "viem/window"
@@ -171,14 +172,13 @@ export const delegationAuthorization = async (contractAddress: string, plugin: R
171172
}
172173

173174
export const createSmartAccount = async (plugin: RunTab, dispatch: React.Dispatch<any>) => {
174-
const localStorageKey = 'smartAccounts'
175-
const PUBLIC_NODE_URL = "https://go.getblock.io/ee42d0a88f314707be11dd799b122cb9"
176-
const toAddress = "0xAFdAC33F6F134D46bAbE74d9125F3bf8e8AB3a44" // A dummy zero value tx is made to this address to create existence of smart account
177-
const safeAddresses: string[] = Object.keys(plugin.REACT_API.smartAccounts)
178-
const network = 'sepolia'
179-
const chain = chains[network]
180-
const BUNDLER_URL = `https://pimlico.remixproject.org/api/proxy/${chain.id}`
181175

176+
const { chainId } = plugin.REACT_API
177+
const chain = chains[aaSupportedNetworks[chainId].name]
178+
const PUBLIC_NODE_URL = aaSupportedNetworks[chainId].publicNodeUrl
179+
const BUNDLER_URL = getPimlicoBundlerURL(chainId)
180+
181+
const safeAddresses: string[] = Object.keys(plugin.REACT_API.smartAccounts)
182182
let salt
183183

184184
// @ts-ignore
@@ -230,6 +230,7 @@ export const createSmartAccount = async (plugin: RunTab, dispatch: React.Dispatc
230230
estimateFeesPerGas: async () => (await paymasterClient.getUserOperationGasPrice()).fast,
231231
}
232232
})
233+
233234
// Make a dummy tx to force smart account deployment
234235
const useropHash = await saClient.sendUserOperation({
235236
calls: [{
@@ -239,9 +240,8 @@ export const createSmartAccount = async (plugin: RunTab, dispatch: React.Dispatc
239240
})
240241
await saClient.waitForUserOperationReceipt({ hash: useropHash })
241242

242-
// TO verify creation, check if there is a contract code at this address
243+
// To verify creation, check if there is a contract code at this address
243244
const safeAddress = safeAccount.address
244-
245245
const sAccount: SmartAccount = {
246246
address : safeAccount.address,
247247
salt,
@@ -250,10 +250,10 @@ export const createSmartAccount = async (plugin: RunTab, dispatch: React.Dispatc
250250
}
251251
plugin.REACT_API.smartAccounts[safeAddress] = sAccount
252252
// Save smart accounts in local storage
253-
const smartAccountsStr = localStorage.getItem(localStorageKey)
253+
const smartAccountsStr = localStorage.getItem(aaLocalStorageKey)
254254
const smartAccountsObj = JSON.parse(smartAccountsStr)
255-
smartAccountsObj[plugin.REACT_API.chainId] = plugin.REACT_API.smartAccounts
256-
localStorage.setItem(localStorageKey, JSON.stringify(smartAccountsObj))
255+
smartAccountsObj[chainId] = plugin.REACT_API.smartAccounts
256+
localStorage.setItem(aaLocalStorageKey, JSON.stringify(smartAccountsObj))
257257

258258
return plugin.call('notification', 'toast', `Safe account ${safeAccount.address} created for owner ${account}`)
259259
} catch (error) {
@@ -264,21 +264,19 @@ export const createSmartAccount = async (plugin: RunTab, dispatch: React.Dispatc
264264

265265
export const loadSmartAccounts = async (plugin) => {
266266
const { chainId } = plugin.REACT_API
267-
const localStorageKey = 'smartAccounts'
268-
269-
const smartAccountsStr = localStorage.getItem(localStorageKey)
267+
const smartAccountsStr = localStorage.getItem(aaLocalStorageKey)
270268
if (smartAccountsStr) {
271269
const smartAccountsObj = JSON.parse(smartAccountsStr)
272270
if (smartAccountsObj[chainId]) {
273271
plugin.REACT_API.smartAccounts = smartAccountsObj[chainId]
274272
} else {
275273
smartAccountsObj[chainId] = {}
276-
localStorage.setItem(localStorageKey, JSON.stringify(smartAccountsObj))
274+
localStorage.setItem(aaLocalStorageKey, JSON.stringify(smartAccountsObj))
277275
}
278276
} else {
279277
const objToStore = {}
280278
objToStore[chainId] = {}
281-
localStorage.setItem(localStorageKey, JSON.stringify(objToStore))
279+
localStorage.setItem(aaLocalStorageKey, JSON.stringify(objToStore))
282280
}
283281
}
284282

libs/remix-ui/run-tab/src/lib/components/account.tsx

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,32 +25,33 @@ export function AccountUI(props: AccountProps) {
2525
const ownerEOA = useRef(null)
2626

2727
const intl = useIntl()
28-
const smartAccounts: string[] = networkName.includes('Sepolia') ? Object.keys(props.runTabPlugin.REACT_API.smartAccounts) : []
28+
const aaSupportedChainIds = ["11155111"] // AA01: Add chain id here to show 'Create Smart Account' button in Udapp
29+
const smartAccounts: string[] = aaSupportedChainIds.some(e => networkName.includes(e)) ? Object.keys(props.runTabPlugin.REACT_API.smartAccounts) : []
2930

3031
useEffect(() => {
3132
if (accounts.length > 0 && !accounts.includes(selectedAccount)) {
3233
props.setAccount(accounts[0])
3334
}
3435
}, [accounts, selectedAccount])
3536

36-
// Uncomment this when we want to show 'Create Smart Account' button
37-
// useEffect(() => {
38-
// if (networkName.includes('Sepolia')) {
39-
// if (smartAccounts.length > 0 && smartAccounts.includes(selectedAccount)) {
40-
// setSmartAccountSelected(true)
41-
// setEnableCSM(false)
42-
// ownerEOA.current = props.runTabPlugin.REACT_API.smartAccounts[selectedAccount].ownerEOA
43-
// }
44-
// else {
45-
// setSmartAccountSelected(false)
46-
// setEnableCSM(true)
47-
// ownerEOA.current = null
48-
// }
49-
// } else {
50-
// setEnableCSM(false)
51-
// setSmartAccountSelected(false)
52-
// }
53-
// }, [selectedAccount])
37+
// Comment this when not to show 'Create Smart Account' button
38+
useEffect(() => {
39+
if (aaSupportedChainIds.some(e => networkName.includes(e))) {
40+
if (smartAccounts.length > 0 && smartAccounts.includes(selectedAccount)) {
41+
setSmartAccountSelected(true)
42+
setEnableCSM(false)
43+
ownerEOA.current = props.runTabPlugin.REACT_API.smartAccounts[selectedAccount].ownerEOA
44+
}
45+
else {
46+
setSmartAccountSelected(false)
47+
setEnableCSM(true)
48+
ownerEOA.current = null
49+
}
50+
} else {
51+
setEnableCSM(false)
52+
setSmartAccountSelected(false)
53+
}
54+
}, [selectedAccount])
5455

5556
useEffect(() => {
5657
const run = async () => {

libs/remixd/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"@remixproject/plugin-api": "0.3.33",
3333
"@remixproject/plugin-utils": "0.3.33",
3434
"@remixproject/plugin-ws": "0.3.33",
35-
"axios": "1.6.0",
35+
"axios": "1.8.2",
3636
"chokidar": "^2.1.8",
3737
"commander": "^9.4.1",
3838
"fs-extra": "^3.0.1",

0 commit comments

Comments
 (0)