diff --git a/package.json b/package.json index 65134f7..2c832e4 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "next-themes": "^0.2.1", "react": "18.2.0", "react-dom": "18.2.0", - "viem": "^0.3.37" + "viem": "^1.5.0" }, "devDependencies": { "@tailwindcss/forms": "^0.5.3", @@ -29,7 +29,9 @@ "@types/react-dom": "18.2.3", "@typescript-eslint/eslint-plugin": "^6.1.0", "@typescript-eslint/parser": "^6.1.0", + "abitype": "^0.9.6", "autoprefixer": "10.4.14", + "clipboardy": "^3.0.0", "eslint": "8.39.0", "eslint-config-next": "13.4.0", "eslint-plugin-react": "^7.32.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 39b67ca..2f31b0a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,8 +30,8 @@ dependencies: specifier: 18.2.0 version: 18.2.0(react@18.2.0) viem: - specifier: ^0.3.37 - version: 0.3.37(typescript@5.0.4) + specifier: ^1.5.0 + version: 1.5.0(typescript@5.0.4) devDependencies: '@tailwindcss/forms': @@ -55,9 +55,15 @@ devDependencies: '@typescript-eslint/parser': specifier: ^6.1.0 version: 6.1.0(eslint@8.39.0)(typescript@5.0.4) + abitype: + specifier: ^0.9.6 + version: 0.9.6(typescript@5.0.4) autoprefixer: specifier: 10.4.14 version: 10.4.14(postcss@8.4.23) + clipboardy: + specifier: ^3.0.0 + version: 3.0.0 eslint: specifier: 8.39.0 version: 8.39.0 @@ -765,18 +771,45 @@ packages: typescript: 5.0.4 dev: false - /abitype@0.8.2(typescript@5.0.4): - resolution: {integrity: sha512-B1ViNMGpfx/qjVQi0RTc2HEFHuR9uoCoTEkwELT5Y7pBPtBbctYijz9BK6+Kd0hQ3S70FhYTO2dWWk0QNUEXMA==} + /@wagmi/chains@1.6.0(typescript@5.0.4): + resolution: {integrity: sha512-5FRlVxse5P4ZaHG3GTvxwVANSmYJas1eQrTBHhjxVtqXoorm0aLmCHbhmN8Xo1yu09PaWKlleEvfE98yH4AgIw==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + typescript: 5.0.4 + dev: false + + /abitype@0.9.3(typescript@5.0.4): + resolution: {integrity: sha512-dz4qCQLurx97FQhnb/EIYTk/ldQ+oafEDUqC0VVIeQS1Q48/YWt/9YNfMmp9SLFqN41ktxny3c8aYxHjmFIB/w==} peerDependencies: typescript: '>=5.0.4' zod: ^3 >=3.19.1 peerDependenciesMeta: + typescript: + optional: true zod: optional: true dependencies: typescript: 5.0.4 dev: false + /abitype@0.9.6(typescript@5.0.4): + resolution: {integrity: sha512-bnx3yBJ9BhF5SWlIy+fMRprm2w5VhOrDj5bh0K9Aj40AfgQoICuIolm3GmSIhE+lE8qTqRv9PmTg3C9b25vmag==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3 >=3.19.1 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + dependencies: + typescript: 5.0.4 + dev: true + /acorn-jsx@5.3.2(acorn@8.8.2): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -831,6 +864,10 @@ packages: picomatch: 2.3.1 dev: true + /arch@2.2.0: + resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==} + dev: true + /arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} dev: true @@ -1050,6 +1087,15 @@ packages: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} dev: false + /clipboardy@3.0.0: + resolution: {integrity: sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + arch: 2.2.0 + execa: 5.1.1 + is-wsl: 2.2.0 + dev: true + /color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: @@ -3272,21 +3318,26 @@ packages: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true - /viem@0.3.37(typescript@5.0.4): - resolution: {integrity: sha512-17jycP/1Hy9DsDpHlaaI7bbAHBDYGfVYHN6j0ltE7A/S30RXhPVFe4LAPRfmG+xR2QBq8xSUpjO78cRgDLBjZQ==} + /viem@1.5.0(typescript@5.0.4): + resolution: {integrity: sha512-+SVCVaXJRR+fE9q6Sf6q9droB6TunPGY1JfwB3uTdxwWrkgr3qq/NbXaRdtEk+TyaOEjzZQr7t6vUzjOL0zL0Q==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true dependencies: '@adraffy/ens-normalize': 1.9.0 '@noble/curves': 1.0.0 '@noble/hashes': 1.3.0 '@scure/bip32': 1.3.0 '@scure/bip39': 1.2.0 - '@wagmi/chains': 0.3.1(typescript@5.0.4) - abitype: 0.8.2(typescript@5.0.4) + '@wagmi/chains': 1.6.0(typescript@5.0.4) + abitype: 0.9.3(typescript@5.0.4) isomorphic-ws: 5.0.0(ws@8.12.0) + typescript: 5.0.4 ws: 8.12.0 transitivePeerDependencies: - bufferutil - - typescript - utf-8-validate - zod dev: false diff --git a/script/fetch-abi.ts b/script/fetch-abi.ts new file mode 100644 index 0000000..87e3c39 --- /dev/null +++ b/script/fetch-abi.ts @@ -0,0 +1,97 @@ +// Given an Etherscan URL, fetch the ABI and convert it to viem's human readable format. +// Example usage: +// bun script/fetch-abi.ts https://arbiscan.io/address/0x0000000000000000000000000000000000000064 +import { formatAbi } from 'abitype'; +import clipboardy from 'clipboardy'; +import { Address } from 'viem'; + +type ChainConfig = { + name: string; + urlPart: string; + apiPrefix: string; + apiKey: string; +}; + +const chains: Record = { + arbitrum: { + name: 'Arbitrum', + urlPart: 'arbiscan.io', + apiPrefix: 'api.', + apiKey: process.env.ARBISCAN_API_KEY!, + }, + optimism: { + name: 'Optimism', + urlPart: 'optimistic.etherscan.io', + apiPrefix: 'api-', + apiKey: process.env.OPTIMISM_API_KEY!, + }, +}; + +async function getAbi(apiUrl: string) { + // Fetch the ABI from the API. + const response = await fetch(apiUrl); + const data = await response.json(); + if (data.status !== '1') throw new Error('API request failed: ' + data.message); + + // Return formatted ABI. + const abi = JSON.parse(data.result); + return formatAbi(abi); +} + +function getChain(url: string): ChainConfig { + for (const chainName in chains) { + if (url.includes(chains[chainName].urlPart)) { + const chain = chains[chainName]; + if (!chain.apiKey) throw new Error(`API key not found for chain ${chainName}`); + return chain; + } + } + throw new Error(`Chain not recognized from URL: ${url}`); +} + +function getEtherscanUrl(chain: ChainConfig, address: Address, kind: 'abi' | 'source') { + const query = kind === 'abi' ? 'getabi' : 'getsourcecode'; + const { apiPrefix, apiKey, urlPart } = chain; + return `https://${apiPrefix}${urlPart}/api?module=contract&action=${query}&address=${address}&apikey=${apiKey}`; +} + +async function main() { + // Use the first argument as the URL and get the contract address from it. + const url = process.argv[2]; + if (!url) throw new Error('URL is required'); + + // Get the address and chain from the URL. + const address = url.split('/').pop() as Address; + if (!address) throw new Error('Could not parse address from URL'); + const chain = getChain(url); + + // Get the ABI. + const abiRequestUrl = getEtherscanUrl(chain, address, 'abi'); + const formattedAbi1 = await getAbi(abiRequestUrl); + + // Now we check if it's a proxy contract. + // Note that for some optimism predeploys that are proxies, this does not return the + // implementation address, so it's not guaranteed to work. + const sourceRequestUrl = getEtherscanUrl(chain, address, 'source'); + const sourceResponse = await fetch(sourceRequestUrl); + const data = await sourceResponse.json(); + if (data.status !== '1') throw new Error('API request failed: ' + data.message); + const implementationAddress = data.result[0].Implementation as '' | Address; + + // If it is a proxy contract, fetch the implementation ABI. + let formattedAbi2; + if (implementationAddress) { + const implAbiRequestUrl = getEtherscanUrl(chain, implementationAddress, 'abi'); + formattedAbi2 = await getAbi(implAbiRequestUrl); + } + + // If we have a second ABI, separate the two with a divider so they can be easily distinguished. + const fullAbi = formattedAbi2 ? formattedAbi1.concat('--------', formattedAbi2) : formattedAbi1; + + // Copy the formatted ABI to the clipboard. + clipboardy.writeSync(JSON.stringify(fullAbi)); + const kind = formattedAbi2 ? ' proxy and implementation ' : ' '; + console.log(`✅ Copied${kind}ABI to clipboard for ${chain.name} contract ${address}`); +} + +main().catch(console.error); diff --git a/script/json-abi-to-viem-human-readable.ts b/script/json-abi-to-viem-human-readable.ts new file mode 100644 index 0000000..db196f1 --- /dev/null +++ b/script/json-abi-to-viem-human-readable.ts @@ -0,0 +1,10 @@ +// Given a JSON ABI on the clipboard, replace the clipboard contents with a viem human-readable ABI. +// Example usage: +// bun script/json-abi-to-viem-human-readable.ts +import { formatAbi } from 'abitype'; +import clipboardy from 'clipboardy'; + +const abi = clipboardy.readSync(); +const formattedAbi = formatAbi(JSON.parse(abi)); +clipboardy.writeSync(JSON.stringify(formattedAbi)); +console.log('✅ Copied formatted ABI to clipboard'); diff --git a/script/optimism-difficulty.sh b/script/optimism-difficulty.sh new file mode 100644 index 0000000..0eccc05 --- /dev/null +++ b/script/optimism-difficulty.sh @@ -0,0 +1,5 @@ +for i in {1..100}; do + cast call 0xca11bde05977b3631167028862be2a173976ca11 "aggregate((address,bytes)[])(uint256, bytes[])" "[(0xca11bde05977b3631167028862be2a173976ca11,0x72425d9d)]" --rpc-url $MAINNET_RPC_URL + cast call 0xca11bde05977b3631167028862be2a173976ca11 "aggregate((address,bytes)[])(uint256, bytes[])" "[(0xca11bde05977b3631167028862be2a173976ca11,0x72425d9d)]" --rpc-url $OPTIMISM_RPC_URL + echo "---" +done \ No newline at end of file diff --git a/script/precompile-check.ts b/script/precompile-check.ts new file mode 100644 index 0000000..7a5e9ec --- /dev/null +++ b/script/precompile-check.ts @@ -0,0 +1,83 @@ +// Given an Etherscan URL, fetch the ABI and convert it to viem's human readable format. +// Example usage: +// bun script/precompile-check.ts optimism +import { Address, createPublicClient, getAddress, http } from 'viem'; +import { Chain, arbitrum, optimism } from 'viem/chains'; + +type ChainConfig = { + addresses: Address[]; + chain: Chain; + rpcUrl: string; +}; + +const addressMap: Record = { + arbitrum: { + chain: arbitrum, + rpcUrl: process.env.ARBITRUM_RPC_URL!, + addresses: [ + '0x5288c571Fd7aD117beA99bF60FE0846C4E84F933', + '0x09e9222E96E7B4AE2a407B98d48e330053351EEe', + '0x096760F208390250649E3e8763348E783AEF5562', + '0x6c411aD3E74De3E7Bd422b94A27770f5B86C623B', + '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', + '0xd570aCE65C43af47101fC6250FD6fC63D1c22a86', + '0x0000000000000000000000000000000000000064', + '0x000000000000000000000000000000000000006E', + '0x000000000000000000000000000000000000006C', + '0x0000000000000000000000000000000000000066', + '0x000000000000000000000000000000000000006F', + '0x00000000000000000000000000000000000000C8', + '0x0000000000000000000000000000000000000067', + '0x0000000000000000000000000000000000000065', + '0x0000000000000000000000000000000000000070', + '0x000000000000000000000000000000000000006b', + '0x000000000000000000000000000000000000006D', + '0x0000000000000000000000000000000000000068', + ], + }, + optimism: { + chain: optimism, + rpcUrl: process.env.OPTIMISM_RPC_URL!, + addresses: [ + '0x4200000000000000000000000000000000000000', + '0x4200000000000000000000000000000000000002', + '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000', + '0x4200000000000000000000000000000000000006', + '0x4200000000000000000000000000000000000007', + '0x4200000000000000000000000000000000000010', + '0x4200000000000000000000000000000000000011', + '0x4200000000000000000000000000000000000012', + '0x4200000000000000000000000000000000000013', + '0x420000000000000000000000000000000000000F', + '0x4200000000000000000000000000000000000042', + '0x4200000000000000000000000000000000000015', + '0x4200000000000000000000000000000000000016', + '0x4200000000000000000000000000000000000014', + '0x4200000000000000000000000000000000000017', + '0x4200000000000000000000000000000000000018', + '0x4200000000000000000000000000000000000019', + '0x420000000000000000000000000000000000001a', + ], + }, +}; + +async function main() { + const chainName = process.argv[2]; + const chainData = addressMap[chainName]; + const { chain, rpcUrl, addresses } = chainData; + if (!addresses) throw new Error(`Unknown chain: ${chainName}`); + if (!rpcUrl) throw new Error(`Undefined RPC URL for chain: ${chainName}`); + + const transport = http(rpcUrl, { batch: true }); + const client = createPublicClient({ chain, transport }); + + const promises = addresses.map((address) => client.getBytecode({ address })); + const codes = await Promise.all(promises); + const kinds = codes.map((code) => + code === '0x' || code === '0xfe' ? 'precompile' : 'predeploy' + ); + + addresses.forEach((address, i) => console.log(`${getAddress(address)} ${kinds[i]}`)); +} + +main().catch(console.error); diff --git a/src/chains/arbitrum/index.ts b/src/chains/arbitrum/index.ts index e273524..e91fc8b 100644 --- a/src/chains/arbitrum/index.ts +++ b/src/chains/arbitrum/index.ts @@ -4,10 +4,12 @@ import { Chain } from '@/types'; import { signatureTypes } from './signatureTypes'; import { opcodes } from './vm/opcodes'; import { precompiles } from './vm/precompiles'; +import { predeploys } from './vm/predeploys'; export const arbitrum: Chain = { metadata: arbitrumMetadata, precompiles, + predeploys, signatureTypes: sortedArrayByField(signatureTypes, 'prefixByte'), opcodes: sortedArrayByField(opcodes, 'number'), }; diff --git a/src/chains/arbitrum/vm/opcodes/block/blockhash.ts b/src/chains/arbitrum/vm/opcodes/block/blockhash.ts index ce35456..bc14a6c 100644 --- a/src/chains/arbitrum/vm/opcodes/block/blockhash.ts +++ b/src/chains/arbitrum/vm/opcodes/block/blockhash.ts @@ -6,6 +6,19 @@ export const blockhash: Omit = { ...opcode, description: 'Returns a cryptographically insecure, pseudo-random hash for `x` within the range `block.number - 256 <= x < block.number`. If `x` is outside of this range, `blockhash(x)` will return 0. This includes `blockhash(block.number)`, which always returns 0 just like on Ethereum. The hashes returned do not come from L1.', + outputs: [ + { + name: 'hash', + description: + 'The pseudo-random hash for the input block number, or 0 if the block number is not in the valid range', + }, + ], + examples: [ + { + input: '17813636', + output: '0xfe4f20b10608dbb75f84782733dd434832c50192993f7389386dfa40f6feda4b', + }, + ], references: [ { name: 'Differences between Arbitrum and Ethereum opcodes', diff --git a/src/chains/arbitrum/vm/opcodes/block/coinbase.ts b/src/chains/arbitrum/vm/opcodes/block/coinbase.ts index 006a686..3a4e537 100644 --- a/src/chains/arbitrum/vm/opcodes/block/coinbase.ts +++ b/src/chains/arbitrum/vm/opcodes/block/coinbase.ts @@ -6,6 +6,17 @@ export const coinbase: Omit = { ...opcode, description: 'Returns `0xA4b000000000000000000073657175656e636572`, the address of the L1 Batch Poster.', + outputs: [ + { + name: 'address', + description: "The L1 Batch Poster's address", + }, + ], + examples: [ + { + output: '0xA4b000000000000000000073657175656e636572', + }, + ], references: [ { name: 'Differences between Arbitrum and Ethereum opcodes', diff --git a/src/chains/arbitrum/vm/opcodes/block/difficulty.ts b/src/chains/arbitrum/vm/opcodes/block/difficulty.ts deleted file mode 100644 index e1fae33..0000000 --- a/src/chains/arbitrum/vm/opcodes/block/difficulty.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { difficulty as baseOpcode } from '@/chains/mainnet/vm/opcodes/block/difficulty'; -import { Opcode } from '@/types'; - -const { supportedHardforks: _supportedHardforks, ...opcode } = baseOpcode; -export const difficulty: Omit = { - ...opcode, - description: 'Returns the constant `1`.', - references: [ - { - name: 'Differences between Arbitrum and Ethereum opcodes', - url: 'https://developer.arbitrum.io/solidity-support', - }, - ], -}; diff --git a/src/chains/arbitrum/vm/opcodes/block/number.ts b/src/chains/arbitrum/vm/opcodes/block/number.ts index d17e48b..74687a8 100644 --- a/src/chains/arbitrum/vm/opcodes/block/number.ts +++ b/src/chains/arbitrum/vm/opcodes/block/number.ts @@ -6,6 +6,13 @@ export const number: Omit = { ...opcode, description: 'Returns an estimate of the L1 block number at which the Sequencer received the transaction.', + outputs: [ + { + name: 'blockNumber', + description: + 'Estimate of the L1 block number at which the Sequencer received the transaction.', + }, + ], references: [ { name: 'Differences between Arbitrum and Ethereum', diff --git a/src/chains/arbitrum/vm/opcodes/block/prevrandao.ts b/src/chains/arbitrum/vm/opcodes/block/prevrandao.ts new file mode 100644 index 0000000..dca68a8 --- /dev/null +++ b/src/chains/arbitrum/vm/opcodes/block/prevrandao.ts @@ -0,0 +1,16 @@ +import { prevrandao as baseOpcode } from '@/chains/mainnet/vm/opcodes/block/prevrandao'; +import { Opcode } from '@/types'; + +const { supportedHardforks: _supportedHardforks, notes: _notes, ...opcode } = baseOpcode; +export const prevrandao: Omit = { + ...opcode, + outputs: [{ name: 'constant', description: 'The constant `1`.' }], + examples: [{ output: '1' }], + description: 'Returns the constant `1`.', + references: [ + { + name: 'Differences between Arbitrum and Ethereum opcodes', + url: 'https://developer.arbitrum.io/solidity-support', + }, + ], +}; diff --git a/src/chains/arbitrum/vm/opcodes/index.ts b/src/chains/arbitrum/vm/opcodes/index.ts index 270b426..b268e06 100644 --- a/src/chains/arbitrum/vm/opcodes/index.ts +++ b/src/chains/arbitrum/vm/opcodes/index.ts @@ -2,8 +2,8 @@ import { opcodes as mainnetOpcodes } from '@/chains/mainnet/vm/opcodes'; import { Opcode } from '@/types'; import { blockhash } from './block/blockhash'; import { coinbase } from './block/coinbase'; -import { difficulty } from './block/difficulty'; import { number } from './block/number'; +import { prevrandao } from './block/prevrandao'; import { caller } from './environment/caller'; import { origin } from './environment/origin'; import { push0 } from './stack/push0'; @@ -14,7 +14,7 @@ export const opcodes: Record> = { // block ...{ [blockhash.number]: blockhash }, ...{ [coinbase.number]: coinbase }, - ...{ [difficulty.number]: difficulty }, + ...{ [prevrandao.number]: prevrandao }, ...{ [number.number]: number }, // environment diff --git a/src/chains/arbitrum/vm/precompiles.ts b/src/chains/arbitrum/vm/precompiles.ts index 5506b09..b2b2244 100644 --- a/src/chains/arbitrum/vm/precompiles.ts +++ b/src/chains/arbitrum/vm/precompiles.ts @@ -1,61 +1,31 @@ import { precompiles as mainnetPrecompiles } from '@/chains/mainnet/vm/precompiles'; -import { Precompile, Predeploy } from '@/types'; +import { Precompile } from '@/types'; -// https://developer.arbitrum.io/useful-addresses#arbitrum-precompiles-l2-same-on-all-arb-chains -export const precompiles: (Precompile | Predeploy)[] = [ +// https://developer.arbitrum.io/for-devs/useful-addresses#precompiles +export const precompiles: Precompile[] = [ ...mainnetPrecompiles, - { - address: '0x5288c571Fd7aD117beA99bF60FE0846C4E84F933', - name: 'L2 Gateway Router', - description: - 'Handles withdrawals from Ethereum into Arbitrum. Tokens are routed to their appropriate L2 gateway (Router itself also conforms to the Gateway interface).', - deprecated: false, - references: ['https://developer.arbitrum.io/for-devs/useful-addresses'], - }, - { - address: '0x09e9222E96E7B4AE2a407B98d48e330053351EEe', - name: 'L2 ERC20 Gateway', - description: - "Initiates Arbitrum to Ethereum ERC20 transfers, which are forwarded to the token's L2 Gateway to communicate with its corresponding L1 Gateway.", - deprecated: false, - references: ['https://developer.arbitrum.io/for-devs/useful-addresses'], - }, - { - address: '0x096760F208390250649E3e8763348E783AEF5562', - name: 'L2 Arb-Custom Gateway', - description: - 'Allows to transfer of custom tokens from Arbitrum to Ethereum, which are forwarded to the L2 Gateway to communicate with its corresponding L1 Gateway.', - deprecated: false, - references: ['https://developer.arbitrum.io/for-devs/useful-addresses'], - }, - { - address: '0x6c411aD3E74De3E7Bd422b94A27770f5B86C623B', - name: 'L2 Weth Gateway', - description: - "Handles Arbitrum to Ethereum transfers of WETH by unwrapping the Ether and re-wrapping it on Ethereum, ensuring that all WETH tokens are always fully collateralized on the layer it's transferred to.", - deprecated: false, - references: ['https://developer.arbitrum.io/for-devs/useful-addresses'], - }, - { - address: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', - name: 'L2 Weth', - description: - 'Wrapped Ether contract on Arbitrum, which is an ERC-20 token that represents 1 Ether.', - deprecated: false, - references: ['https://developer.arbitrum.io/for-devs/useful-addresses'], - }, - { - address: '0xd570aCE65C43af47101fC6250FD6fC63D1c22a86', - name: 'L2 Proxy Admin', - description: 'The owner of all of the Arbitrum proxy contracts set at the predeploys.', - deprecated: false, - references: ['https://developer.arbitrum.io/for-devs/useful-addresses'], - }, { address: '0x0000000000000000000000000000000000000064', name: 'ArbSys', description: 'Exposes a variety of system-level functionality for interacting with Ethereum and understanding the call stack.', + logicAbi: [ + 'event L2ToL1Transaction(address caller, address indexed destination, uint256 indexed uniqueId, uint256 indexed batchNumber, uint256 indexInBatch, uint256 arbBlockNum, uint256 ethBlockNum, uint256 timestamp, uint256 callvalue, bytes data)', + 'event L2ToL1Tx(address caller, address indexed destination, uint256 indexed hash, uint256 indexed position, uint256 arbBlockNum, uint256 ethBlockNum, uint256 timestamp, uint256 callvalue, bytes data)', + 'event SendMerkleUpdate(uint256 indexed reserved, bytes32 indexed hash, uint256 indexed position)', + 'function arbBlockHash(uint256 arbBlockNum) view returns (bytes32)', + 'function arbBlockNumber() view returns (uint256)', + 'function arbChainID() view returns (uint256)', + 'function arbOSVersion() view returns (uint256)', + 'function getStorageGasAvailable() view returns (uint256)', + 'function isTopLevelCall() view returns (bool)', + 'function mapL1SenderContractAddressToL2Alias(address sender, address unused) pure returns (address)', + 'function myCallersAddressWithoutAliasing() view returns (address)', + 'function sendMerkleTreeState() view returns (uint256 size, bytes32 root, bytes32[] partials)', + 'function sendTxToL1(address destination, bytes data) payable returns (uint256)', + 'function wasMyCallersAddressAliased() view returns (bool)', + 'function withdrawEth(address destination) payable returns (uint256)', + ], deprecated: false, references: ['https://developer.arbitrum.io/for-devs/useful-addresses'], }, @@ -63,6 +33,23 @@ export const precompiles: (Precompile | Predeploy)[] = [ address: '0x000000000000000000000000000000000000006E', name: 'ArbRetryableTx', description: 'Manages retryable transactions related to data retrieval and interactions.', + logicAbi: [ + 'error NoTicketWithID()', + 'error NotCallable()', + 'event Canceled(bytes32 indexed ticketId)', + 'event LifetimeExtended(bytes32 indexed ticketId, uint256 newTimeout)', + 'event RedeemScheduled(bytes32 indexed ticketId, bytes32 indexed retryTxHash, uint64 indexed sequenceNum, uint64 donatedGas, address gasDonor, uint256 maxRefund, uint256 submissionFeeRefund)', + 'event Redeemed(bytes32 indexed userTxHash)', + 'event TicketCreated(bytes32 indexed ticketId)', + 'function cancel(bytes32 ticketId)', + 'function getBeneficiary(bytes32 ticketId) view returns (address)', + 'function getCurrentRedeemer() view returns (address)', + 'function getLifetime() view returns (uint256)', + 'function getTimeout(bytes32 ticketId) view returns (uint256)', + 'function keepalive(bytes32 ticketId) returns (uint256)', + 'function redeem(bytes32 ticketId) returns (bytes32)', + 'function submitRetryable(bytes32 requestId, uint256 l1BaseFee, uint256 deposit, uint256 callvalue, uint256 gasFeeCap, uint64 gasLimit, uint256 maxSubmissionFee, address feeRefundAddress, address beneficiary, address retryTo, bytes retryData)', + ], deprecated: false, references: ['https://developer.arbitrum.io/for-devs/useful-addresses'], }, @@ -70,6 +57,25 @@ export const precompiles: (Precompile | Predeploy)[] = [ address: '0x000000000000000000000000000000000000006C', name: 'ArbGasInfo', description: 'Provides insight into the costs of using Arbitrum.', + logicAbi: [ + 'function getAmortizedCostCapBips() view returns (uint64)', + 'function getCurrentTxL1GasFees() view returns (uint256)', + 'function getGasAccountingParams() view returns (uint256, uint256, uint256)', + 'function getGasBacklog() view returns (uint64)', + 'function getGasBacklogTolerance() view returns (uint64)', + 'function getL1BaseFeeEstimate() view returns (uint256)', + 'function getL1BaseFeeEstimateInertia() view returns (uint64)', + 'function getL1FeesAvailable() view returns (uint256)', + 'function getL1GasPriceEstimate() view returns (uint256)', + 'function getL1PricingSurplus() view returns (int256)', + 'function getMinimumGasPrice() view returns (uint256)', + 'function getPerBatchGasCharge() view returns (int64)', + 'function getPricesInArbGas() view returns (uint256, uint256, uint256)', + 'function getPricesInArbGasWithAggregator(address aggregator) view returns (uint256, uint256, uint256)', + 'function getPricesInWei() view returns (uint256, uint256, uint256, uint256, uint256, uint256)', + 'function getPricesInWeiWithAggregator(address aggregator) view returns (uint256, uint256, uint256, uint256, uint256, uint256)', + 'function getPricingInertia() view returns (uint64)', + ], deprecated: false, references: ['https://developer.arbitrum.io/for-devs/useful-addresses'], }, @@ -78,6 +84,15 @@ export const precompiles: (Precompile | Predeploy)[] = [ name: 'ArbAddressTable', description: 'Allows registering and retrieving commonly used addresses via indices, saving calldata.', + logicAbi: [ + 'function addressExists(address addr) view returns (bool)', + 'function compress(address addr) returns (bytes)', + 'function decompress(bytes buf, uint256 offset) view returns (address, uint256)', + 'function lookup(address addr) view returns (uint256)', + 'function lookupIndex(uint256 index) view returns (address)', + 'function register(address addr) returns (uint256)', + 'function size() view returns (uint256)', + ], deprecated: false, references: ['https://developer.arbitrum.io/for-devs/useful-addresses'], }, @@ -86,6 +101,9 @@ export const precompiles: (Precompile | Predeploy)[] = [ name: 'ArbStatistics', description: 'Provides statistics about Arbitrum, such as the number of blocks, accounts, transactions, and contracts.', + logicAbi: [ + 'function getStats() view returns (uint256, uint256, uint256, uint256, uint256, uint256)', + ], deprecated: false, references: ['https://developer.arbitrum.io/for-devs/useful-addresses'], }, @@ -94,6 +112,16 @@ export const precompiles: (Precompile | Predeploy)[] = [ name: 'NodeInterface', description: 'Retrieves the revenant data of calls by Arbitrum contracts to execute them in Ethereum via the Outbox contract.', + logicAbi: [ + 'function constructOutboxProof(uint64 size, uint64 leaf) view returns (bytes32 send, bytes32 root, bytes32[] proof)', + 'function estimateRetryableTicket(address sender, uint256 deposit, address to, uint256 l2CallValue, address excessFeeRefundAddress, address callValueRefundAddress, bytes data)', + 'function findBatchContainingBlock(uint64 blockNum) view returns (uint64 batch)', + 'function gasEstimateComponents(address to, bool contractCreation, bytes data) payable returns (uint64 gasEstimate, uint64 gasEstimateForL1, uint256 baseFee, uint256 l1BaseFeeEstimate)', + 'function gasEstimateL1Component(address to, bool contractCreation, bytes data) payable returns (uint64 gasEstimateForL1, uint256 baseFee, uint256 l1BaseFeeEstimate)', + 'function getL1Confirmations(bytes32 blockHash) view returns (uint64 confirmations)', + 'function legacyLookupMessageBatchProof(uint256 batchNum, uint64 index) view returns (bytes32[] proof, uint256 path, address l2Sender, address l1Dest, uint256 l2Block, uint256 l1Block, uint256 timestamp, uint256 amount, bytes calldataForL1)', + 'function nitroGenesisBlock() pure returns (uint256 number)', + ], deprecated: false, references: ['https://developer.arbitrum.io/for-devs/useful-addresses'], }, @@ -101,13 +129,7 @@ export const precompiles: (Precompile | Predeploy)[] = [ address: '0x0000000000000000000000000000000000000067', name: 'ArbBLS', description: 'Provides a registry of BLS public keys for accounts.', - deprecated: false, - references: ['https://developer.arbitrum.io/for-devs/useful-addresses'], - }, - { - address: '0x0000000000000000000000000000000000000065', - name: 'ArbInfo', - description: 'Provides the ability to lookup basic info about accounts and contracts.', + logicAbi: [], // TODO have not found ABI yet. deprecated: false, references: ['https://developer.arbitrum.io/for-devs/useful-addresses'], }, @@ -116,6 +138,33 @@ export const precompiles: (Precompile | Predeploy)[] = [ name: 'ArbOwner', description: 'Provides owners with tools for managing the rollup. All calls to this precompile are authorized by the OwnerPrecompile wrapper, which ensures only a chain owner can access these methods.', + logicAbi: [ + 'event OwnerActs(bytes4 indexed method, address indexed owner, bytes data)', + 'function addChainOwner(address newOwner)', + 'function getAllChainOwners() view returns (address[])', + 'function getInfraFeeAccount() view returns (address)', + 'function getNetworkFeeAccount() view returns (address)', + 'function isChainOwner(address addr) view returns (bool)', + 'function releaseL1PricerSurplusFunds(uint256 maxWeiToRelease) returns (uint256)', + 'function removeChainOwner(address ownerToRemove)', + 'function scheduleArbOSUpgrade(uint64 newVersion, uint64 timestamp)', + 'function setAmortizedCostCapBips(uint64 cap)', + 'function setInfraFeeAccount(address newInfraFeeAccount)', + 'function setL1BaseFeeEstimateInertia(uint64 inertia)', + 'function setL1PricePerUnit(uint256 pricePerUnit)', + 'function setL1PricingEquilibrationUnits(uint256 equilibrationUnits)', + 'function setL1PricingInertia(uint64 inertia)', + 'function setL1PricingRewardRate(uint64 weiPerUnit)', + 'function setL1PricingRewardRecipient(address recipient)', + 'function setL2BaseFee(uint256 priceInWei)', + 'function setL2GasBacklogTolerance(uint64 sec)', + 'function setL2GasPricingInertia(uint64 sec)', + 'function setMaxTxGasLimit(uint64 limit)', + 'function setMinimumL2BaseFee(uint256 priceInWei)', + 'function setNetworkFeeAccount(address newNetworkFeeAccount)', + 'function setPerBatchGasCharge(int64 cost)', + 'function setSpeedLimit(uint64 limit)', + ], deprecated: false, references: [ 'https://developer.arbitrum.io/for-devs/useful-addresses', @@ -127,6 +176,12 @@ export const precompiles: (Precompile | Predeploy)[] = [ name: 'ArbOwnerPublic', description: 'Provides non-owners with info about the current chain owners. The calls to this precompile do not require the sender be a chain owner. For those that are, see `ArbOwner`.', + logicAbi: [ + 'function getAllChainOwners() view returns (address[])', + 'function getInfraFeeAccount() view returns (address)', + 'function getNetworkFeeAccount() view returns (address)', + 'function isChainOwner(address addr) view returns (bool)', + ], deprecated: false, references: [ 'https://developer.arbitrum.io/for-devs/useful-addresses', @@ -138,6 +193,16 @@ export const precompiles: (Precompile | Predeploy)[] = [ name: 'ArbAggregator', description: "Provides aggregators and their users methods for configuring how they participate in Ethereum aggregation. The default aggregator is Arbitrum's Sequencer.", + logicAbi: [ + 'function addBatchPoster(address newBatchPoster)', + 'function getBatchPosters() view returns (address[])', + 'function getDefaultAggregator() view returns (address)', + 'function getFeeCollector(address batchPoster) view returns (address)', + 'function getPreferredAggregator(address addr) view returns (address, bool)', + 'function getTxBaseFee(address aggregator) view returns (uint256)', + 'function setFeeCollector(address batchPoster, address newFeeCollector)', + 'function setTxBaseFee(address aggregator, uint256 feeInL1Gas)', + ], deprecated: false, references: ['https://developer.arbitrum.io/for-devs/useful-addresses'], }, @@ -146,6 +211,11 @@ export const precompiles: (Precompile | Predeploy)[] = [ name: 'ArbFunctionTable', description: 'Allows aggregators to manage function tables for one form of transaction compression.', + logicAbi: [ + 'function get(address addr, uint256 index) view returns (uint256, bool, uint256)', + 'function size(address addr) view returns (uint256)', + 'function upload(bytes buf)', + ], deprecated: false, references: ['https://developer.arbitrum.io/for-devs/useful-addresses'], }, diff --git a/src/chains/arbitrum/vm/predeploys.ts b/src/chains/arbitrum/vm/predeploys.ts new file mode 100644 index 0000000..e44e4e0 --- /dev/null +++ b/src/chains/arbitrum/vm/predeploys.ts @@ -0,0 +1,244 @@ +import { Predeploy } from '@/types'; + +// https://developer.arbitrum.io/useful-addresses#arbitrum-precompiles-l2-same-on-all-arb-chains +export const predeploys: Predeploy[] = [ + { + address: '0x5288c571Fd7aD117beA99bF60FE0846C4E84F933', + name: 'L2 Gateway Router', + description: + 'Handles withdrawals from Ethereum into Arbitrum. Tokens are routed to their appropriate L2 gateway (Router itself also conforms to the Gateway interface).', + proxyAbi: [ + 'constructor(address _logic, address admin_, bytes _data) payable', + 'event AdminChanged(address previousAdmin, address newAdmin)', + 'event Upgraded(address indexed implementation)', + 'fallback()', + 'function admin() returns (address admin_)', + 'function changeAdmin(address newAdmin)', + 'function implementation() returns (address implementation_)', + 'function upgradeTo(address newImplementation)', + 'function upgradeToAndCall(address newImplementation, bytes data) payable', + 'receive() external payable', + ], + logicAbi: [ + 'event DefaultGatewayUpdated(address newDefaultGateway)', + 'event GatewaySet(address indexed l1Token, address indexed gateway)', + 'event TransferRouted(address indexed token, address indexed _userFrom, address indexed _userTo, address gateway)', + 'event TxToL1(address indexed _from, address indexed _to, uint256 indexed _id, bytes _data)', + 'function calculateL2TokenAddress(address l1ERC20) view returns (address)', + 'function counterpartGateway() view returns (address)', + 'function defaultGateway() view returns (address)', + 'function finalizeInboundTransfer(address, address, address, uint256, bytes) payable', + 'function getGateway(address _token) view returns (address gateway)', + 'function getOutboundCalldata(address _token, address _from, address _to, uint256 _amount, bytes _data) view returns (bytes)', + 'function initialize(address _counterpartGateway, address _defaultGateway)', + 'function l1TokenToGateway(address) view returns (address)', + 'function outboundTransfer(address _l1Token, address _to, uint256 _amount, bytes _data) payable returns (bytes)', + 'function outboundTransfer(address _token, address _to, uint256 _amount, uint256 _maxGas, uint256 _gasPriceBid, bytes _data) payable returns (bytes)', + 'function postUpgradeInit()', + 'function router() view returns (address)', + 'function setDefaultGateway(address newL2DefaultGateway)', + 'function setGateway(address[] _l1Token, address[] _gateway)', + ], + logicAddress: '0xe80eb0238029333e368e0bDDB7acDf1b9cb28278', + deprecated: false, + references: ['https://developer.arbitrum.io/for-devs/useful-addresses'], + }, + { + address: '0x09e9222E96E7B4AE2a407B98d48e330053351EEe', + name: 'L2 ERC20 Gateway', + description: + "Initiates Arbitrum to Ethereum ERC20 transfers, which are forwarded to the token's L2 Gateway to communicate with its corresponding L1 Gateway.", + proxyAbi: [ + 'constructor(address _logic, address admin_, bytes _data) payable', + 'event AdminChanged(address previousAdmin, address newAdmin)', + 'event Upgraded(address indexed implementation)', + 'fallback()', + 'function admin() returns (address admin_)', + 'function changeAdmin(address newAdmin)', + 'function implementation() returns (address implementation_)', + 'function upgradeTo(address newImplementation)', + 'function upgradeToAndCall(address newImplementation, bytes data) payable', + 'receive() external payable', + ], + logicAbi: [ + 'event DepositFinalized(address indexed l1Token, address indexed _from, address indexed _to, uint256 _amount)', + 'event TxToL1(address indexed _from, address indexed _to, uint256 indexed _id, bytes _data)', + 'event WithdrawalInitiated(address l1Token, address indexed _from, address indexed _to, uint256 indexed _l2ToL1Id, uint256 _exitNum, uint256 _amount)', + 'function beaconProxyFactory() view returns (address)', + 'function calculateL2TokenAddress(address l1ERC20) view returns (address)', + 'function cloneableProxyHash() view returns (bytes32)', + 'function counterpartGateway() view returns (address)', + 'function exitNum() view returns (uint256)', + 'function finalizeInboundTransfer(address _token, address _from, address _to, uint256 _amount, bytes _data) payable', + 'function getOutboundCalldata(address _token, address _from, address _to, uint256 _amount, bytes _data) view returns (bytes outboundCalldata)', + 'function getUserSalt(address l1ERC20) pure returns (bytes32)', + 'function initialize(address _l1Counterpart, address _router, address _beaconProxyFactory)', + 'function outboundTransfer(address _l1Token, address _to, uint256 _amount, bytes _data) payable returns (bytes)', + 'function outboundTransfer(address _l1Token, address _to, uint256 _amount, uint256, uint256, bytes _data) payable returns (bytes res)', + 'function postUpgradeInit()', + 'function router() view returns (address)', + ], + logicAddress: '0x1DCf7D03574fbC7C205F41f2e116eE094a652e93', + deprecated: false, + references: ['https://developer.arbitrum.io/for-devs/useful-addresses'], + }, + { + address: '0x096760F208390250649E3e8763348E783AEF5562', + name: 'L2 Arb-Custom Gateway', + description: + 'Allows to transfer of custom tokens from Arbitrum to Ethereum, which are forwarded to the L2 Gateway to communicate with its corresponding L1 Gateway.', + proxyAbi: [ + 'constructor(address _logic, address admin_, bytes _data) payable', + 'event AdminChanged(address previousAdmin, address newAdmin)', + 'event Upgraded(address indexed implementation)', + 'fallback()', + 'function admin() returns (address admin_)', + 'function changeAdmin(address newAdmin)', + 'function implementation() returns (address implementation_)', + 'function upgradeTo(address newImplementation)', + 'function upgradeToAndCall(address newImplementation, bytes data) payable', + 'receive() external payable', + ], + logicAbi: [ + 'event DepositFinalized(address indexed l1Token, address indexed _from, address indexed _to, uint256 _amount)', + 'event TokenSet(address indexed l1Address, address indexed l2Address)', + 'event TxToL1(address indexed _from, address indexed _to, uint256 indexed _id, bytes _data)', + 'event WithdrawalInitiated(address l1Token, address indexed _from, address indexed _to, uint256 indexed _l2ToL1Id, uint256 _exitNum, uint256 _amount)', + 'function calculateL2TokenAddress(address l1ERC20) view returns (address)', + 'function counterpartGateway() view returns (address)', + 'function exitNum() view returns (uint256)', + 'function finalizeInboundTransfer(address _token, address _from, address _to, uint256 _amount, bytes _data) payable', + 'function getOutboundCalldata(address _token, address _from, address _to, uint256 _amount, bytes _data) view returns (bytes outboundCalldata)', + 'function initialize(address _l1Counterpart, address _router)', + 'function l1ToL2Token(address) view returns (address)', + 'function outboundTransfer(address _l1Token, address _to, uint256 _amount, bytes _data) payable returns (bytes)', + 'function outboundTransfer(address _l1Token, address _to, uint256 _amount, uint256, uint256, bytes _data) payable returns (bytes res)', + 'function postUpgradeInit()', + 'function registerTokenFromL1(address[] l1Address, address[] l2Address)', + 'function router() view returns (address)', + ], + logicAddress: '0x190274fEa8f30e3f48CE43aDCBd9a74110118284', + deprecated: false, + references: ['https://developer.arbitrum.io/for-devs/useful-addresses'], + }, + { + address: '0x6c411aD3E74De3E7Bd422b94A27770f5B86C623B', + name: 'L2 Weth Gateway', + description: + "Handles Arbitrum to Ethereum transfers of WETH by unwrapping the Ether and re-wrapping it on Ethereum, ensuring that all WETH tokens are always fully collateralized on the layer it's transferred to.", + proxyAbi: [ + 'constructor(address _logic, address admin_, bytes _data) payable', + 'event AdminChanged(address previousAdmin, address newAdmin)', + 'event Upgraded(address indexed implementation)', + 'fallback()', + 'function admin() returns (address admin_)', + 'function changeAdmin(address newAdmin)', + 'function implementation() returns (address implementation_)', + 'function upgradeTo(address newImplementation)', + 'function upgradeToAndCall(address newImplementation, bytes data) payable', + 'receive() external payable', + ], + logicAbi: [ + 'event DepositFinalized(address indexed l1Token, address indexed _from, address indexed _to, uint256 _amount)', + 'event TxToL1(address indexed _from, address indexed _to, uint256 indexed _id, bytes _data)', + 'event WithdrawalInitiated(address l1Token, address indexed _from, address indexed _to, uint256 indexed _l2ToL1Id, uint256 _exitNum, uint256 _amount)', + 'function calculateL2TokenAddress(address l1ERC20) view returns (address)', + 'function counterpartGateway() view returns (address)', + 'function exitNum() view returns (uint256)', + 'function finalizeInboundTransfer(address _token, address _from, address _to, uint256 _amount, bytes _data) payable', + 'function getOutboundCalldata(address _token, address _from, address _to, uint256 _amount, bytes _data) view returns (bytes outboundCalldata)', + 'function initialize(address _l1Counterpart, address _router, address _l1Weth, address _l2Weth)', + 'function l1Weth() view returns (address)', + 'function l2Weth() view returns (address)', + 'function outboundTransfer(address _l1Token, address _to, uint256 _amount, bytes _data) payable returns (bytes)', + 'function outboundTransfer(address _l1Token, address _to, uint256 _amount, uint256, uint256, bytes _data) payable returns (bytes res)', + 'function postUpgradeInit()', + 'function router() view returns (address)', + 'receive() external payable', + ], + logicAddress: '0x806421D09cDb253aa9d128a658e60c0B95eFFA01', + deprecated: false, + references: ['https://developer.arbitrum.io/for-devs/useful-addresses'], + }, + { + address: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', + name: 'L2 Weth', + description: + 'Wrapped Ether contract on Arbitrum, which is an ERC-20 token that represents 1 Ether.', + proxyAbi: [ + 'constructor(address _logic, address admin_, bytes _data) payable', + 'event AdminChanged(address previousAdmin, address newAdmin)', + 'event Upgraded(address indexed implementation)', + 'fallback()', + 'function admin() returns (address admin_)', + 'function changeAdmin(address newAdmin)', + 'function implementation() returns (address implementation_)', + 'function upgradeTo(address newImplementation)', + 'function upgradeToAndCall(address newImplementation, bytes data) payable', + 'receive() external payable', + ], + logicAbi: [ + 'event Approval(address indexed owner, address indexed spender, uint256 value)', + 'event Transfer(address indexed from, address indexed to, uint256 value, bytes data)', + 'event Transfer(address indexed from, address indexed to, uint256 value)', + 'function DOMAIN_SEPARATOR() view returns (bytes32)', + 'function allowance(address owner, address spender) view returns (uint256)', + 'function approve(address spender, uint256 amount) returns (bool)', + 'function balanceOf(address account) view returns (uint256)', + 'function bridgeBurn(address account, uint256 amount)', + 'function bridgeMint(address account, uint256 amount)', + 'function decimals() view returns (uint8)', + 'function decreaseAllowance(address spender, uint256 subtractedValue) returns (bool)', + 'function deposit() payable', + 'function depositTo(address account) payable', + 'function increaseAllowance(address spender, uint256 addedValue) returns (bool)', + 'function initialize(string _name, string _symbol, uint8 _decimals, address _l2Gateway, address _l1Address)', + 'function l1Address() view returns (address)', + 'function l2Gateway() view returns (address)', + 'function name() view returns (string)', + 'function nonces(address owner) view returns (uint256)', + 'function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)', + 'function symbol() view returns (string)', + 'function totalSupply() view returns (uint256)', + 'function transfer(address recipient, uint256 amount) returns (bool)', + 'function transferAndCall(address _to, uint256 _value, bytes _data) returns (bool success)', + 'function transferFrom(address sender, address recipient, uint256 amount) returns (bool)', + 'function withdraw(uint256 amount)', + 'function withdrawTo(address account, uint256 amount)', + 'receive() external payable', + ], + logicAddress: '0x8b194bEae1d3e0788A1a35173978001ACDFba668', + deprecated: false, + references: ['https://developer.arbitrum.io/for-devs/useful-addresses'], + }, + { + address: '0xd570aCE65C43af47101fC6250FD6fC63D1c22a86', + name: 'L2 Proxy Admin', + description: 'The owner of all of the Arbitrum proxy contracts set at the predeploys.', + logicAbi: [ + 'event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)', + 'function changeProxyAdmin(address proxy, address newAdmin)', + 'function getProxyAdmin(address proxy) view returns (address)', + 'function getProxyImplementation(address proxy) view returns (address)', + 'function owner() view returns (address)', + 'function renounceOwnership()', + 'function transferOwnership(address newOwner)', + 'function upgrade(address proxy, address implementation)', + 'function upgradeAndCall(address proxy, address implementation, bytes data) payable', + ], + deprecated: false, + references: ['https://developer.arbitrum.io/for-devs/useful-addresses'], + }, + + { + address: '0x0000000000000000000000000000000000000065', + name: 'ArbInfo', + description: 'Provides the ability to lookup basic info about accounts and contracts.', + logicAbi: [ + 'function getBalance(address account) view returns (uint256)', + 'function getCode(address account) view returns (bytes)', + ], + deprecated: false, + references: ['https://developer.arbitrum.io/for-devs/useful-addresses'], + }, +]; diff --git a/src/chains/mainnet/index.ts b/src/chains/mainnet/index.ts index b997cdf..ba456bb 100644 --- a/src/chains/mainnet/index.ts +++ b/src/chains/mainnet/index.ts @@ -4,10 +4,12 @@ import { Chain } from '@/types'; import { signatureTypes } from './signatureTypes'; import { opcodes } from './vm/opcodes'; import { precompiles } from './vm/precompiles'; +import { predeploys } from './vm/predeploys'; export const mainnet: Chain = { metadata: mainnetMetadata, precompiles, + predeploys, signatureTypes: sortedArrayByField(signatureTypes, 'prefixByte'), opcodes: sortedArrayByField(opcodes, 'number'), }; diff --git a/src/chains/mainnet/vm/opcodes/block/blockhash.ts b/src/chains/mainnet/vm/opcodes/block/blockhash.ts index fa0dd44..6e47fca 100644 --- a/src/chains/mainnet/vm/opcodes/block/blockhash.ts +++ b/src/chains/mainnet/vm/opcodes/block/blockhash.ts @@ -5,7 +5,7 @@ import { Opcode } from '@/types'; export const blockhash: Opcode = { number: 0x40, name: 'blockhash', - description: 'Get the hash of one of the 256 most recent complete blocks', + description: 'Get the hash of one of the 256 most recent complete blocks.', minGas: 20, inputs: [ { @@ -23,8 +23,8 @@ export const blockhash: Opcode = { ], examples: [ { - input: '599423545', - output: '0x29045A592007D0C246EF02C2223570DA9522D0CF0F73282C79A1BC8F0BB2C238', + input: '17813636', + output: '0x3204feac1c276343f84e44df04f8fcddbb80eee246ee0533026511e4c4bbf4b6', }, ], errorCases: ['Not enough gas', 'Not enough values on the stack'], diff --git a/src/chains/mainnet/vm/opcodes/block/difficulty.ts b/src/chains/mainnet/vm/opcodes/block/difficulty.ts deleted file mode 100644 index 0445b8e..0000000 --- a/src/chains/mainnet/vm/opcodes/block/difficulty.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { MainnetHardfork, getHardforksFromTo } from '@/chains/mainnet/hardforks'; -import { OpcodeGroup, ethSpecsOpcodeSrc, evmCodesOpcodesLink } from '@/lib/opcodes'; -import { Opcode } from '@/types'; - -export const difficulty: Opcode = { - number: 0x44, - name: 'difficulty', - description: "Get the block's difficulty", - minGas: 2, - outputs: [ - { - name: 'difficulty', - description: 'The current block difficulty', - }, - ], - examples: [ - { - output: '10995000000000000', - }, - ], - errorCases: ['Not enough gas', 'Stack overflow'], - references: [ - { - name: 'evm.codes', - url: evmCodesOpcodesLink(0x44), - }, - { - name: 'execution-specs', - url: ethSpecsOpcodeSrc(MainnetHardfork.GrayGlacier, OpcodeGroup.Block, 22), - }, - ], - supportedHardforks: getHardforksFromTo(MainnetHardfork.Frontier, MainnetHardfork.GrayGlacier), -}; diff --git a/src/chains/mainnet/vm/opcodes/block/index.ts b/src/chains/mainnet/vm/opcodes/block/index.ts index 18e72d8..915c164 100644 --- a/src/chains/mainnet/vm/opcodes/block/index.ts +++ b/src/chains/mainnet/vm/opcodes/block/index.ts @@ -1,8 +1,6 @@ -import { opcodeId } from '@/lib/opcodes'; import { Opcode } from '@/types'; import { blockhash } from './blockhash'; import { coinbase } from './coinbase'; -import { difficulty } from './difficulty'; import { gaslimit } from './gaslimit'; import { number } from './number'; import { prevrandao } from './prevrandao'; @@ -11,9 +9,7 @@ import { timestamp } from './timestamp'; export const opcodes: Record = { [blockhash.number]: blockhash, [coinbase.number]: coinbase, - // `difficulty` and `prevrandao` have the same opcode numbers so we use `id()` to generate unique keys. - [opcodeId(difficulty)]: difficulty, - [opcodeId(prevrandao)]: prevrandao, + [prevrandao.number]: prevrandao, [gaslimit.number]: gaslimit, [number.number]: number, [timestamp.number]: timestamp, diff --git a/src/chains/mainnet/vm/opcodes/block/prevrandao.ts b/src/chains/mainnet/vm/opcodes/block/prevrandao.ts index 97a2c45..36e1cb2 100644 --- a/src/chains/mainnet/vm/opcodes/block/prevrandao.ts +++ b/src/chains/mainnet/vm/opcodes/block/prevrandao.ts @@ -6,7 +6,7 @@ export const prevrandao: Opcode = { number: 0x44, name: 'prevrandao', description: - "Get the random output of the beacon chain's randomness oracle for the previous block", + "Get the random output of the beacon chain's randomness oracle for the previous block.", minGas: 2, outputs: [ { @@ -16,9 +16,12 @@ export const prevrandao: Opcode = { ], examples: [ { - output: '10995000000000000', + output: '0xce124dee50136f3f93f19667fb4198c6b94eecbacfa300469e5280012757be94', }, ], + notes: [ + "Prior to the Paris hardfork, this opcode was named `difficulty` and returned the block's current difficulty. From Paris onwards, the `difficulty` opcode became `prevrandao`.", + ], // TODO: add the evm.codes playground link once available errorCases: ['Not enough gas.', 'Stack overflow.'], references: [ @@ -30,6 +33,10 @@ export const prevrandao: Opcode = { name: 'execution-specs', url: ethSpecsOpcodeSrc(MainnetHardfork.Shanghai, OpcodeGroup.Block, 158), }, + { + name: 'EIP-4399: Supplant DIFFICULTY opcode with PREVRANDAO', + url: 'https://eips.ethereum.org/EIPS/eip-4399', + }, ], - supportedHardforks: getHardforksFrom(MainnetHardfork.Paris), + supportedHardforks: getHardforksFrom(MainnetHardfork.Frontier), }; diff --git a/src/chains/mainnet/vm/opcodes/environment/caller.ts b/src/chains/mainnet/vm/opcodes/environment/caller.ts index cbdbef9..b881acf 100644 --- a/src/chains/mainnet/vm/opcodes/environment/caller.ts +++ b/src/chains/mainnet/vm/opcodes/environment/caller.ts @@ -16,7 +16,7 @@ export const caller: Opcode = { { name: 'address', description: - 'The 20-byte address of the caller account. This is the account that did the last call (except delegate call).', + "The 20-byte address of the caller's account. This is the account that did the last call (except delegate call).", }, ], examples: [ diff --git a/src/chains/mainnet/vm/precompiles.ts b/src/chains/mainnet/vm/precompiles.ts index d73e3d9..546cd4f 100644 --- a/src/chains/mainnet/vm/precompiles.ts +++ b/src/chains/mainnet/vm/precompiles.ts @@ -40,13 +40,14 @@ export const precompiles: Precompile[] = [ description: 'The recovered 20-byte address right aligned to 32 bytes', }, ], + deprecated: false, references: [ 'https://github.com/ethereum/execution-specs/blob/6f8614566e7117afa327ad054c3f4bfe19694d73/src/ethereum/shanghai/vm/precompiled_contracts/ecrecover.py', 'https://github.com/ethereum/execution-specs/blob/6f8614566e7117afa327ad054c3f4bfe19694d73/src/ethereum/shanghai/vm/gas.py#L50', 'https://github.com/ethereum/execution-specs/blob/6f8614566e7117afa327ad054c3f4bfe19694d73/src/ethereum/shanghai/vm/precompiled_contracts/__init__.py#L30', ], notes: [ - "If an address cannot be recovered, or not enough gas was given, then there is no return data. Note that the return data is the address that issued the signature but it won't verify the signature.", + "If an address cannot be recovered or not enough gas was given, then there is no return data, indicating a precompile contract error. Note that the return data is the address that issued the signature but it won't verify the signature.", ], }, { @@ -70,12 +71,15 @@ export const precompiles: Precompile[] = [ description: 'The SHA-256 hash of the input data', }, ], + deprecated: false, references: [ 'https://github.com/ethereum/execution-specs/blob/6f8614566e7117afa327ad054c3f4bfe19694d73/src/ethereum/shanghai/vm/precompiled_contracts/sha256.py', 'https://github.com/ethereum/execution-specs/blob/6f8614566e7117afa327ad054c3f4bfe19694d73/src/ethereum/shanghai/vm/gas.py#L51', 'https://github.com/ethereum/execution-specs/blob/6f8614566e7117afa327ad054c3f4bfe19694d73/src/ethereum/shanghai/vm/precompiled_contracts/__init__.py#L31', ], - notes: ['If not enough gas was given, then there is no return data.'], + notes: [ + 'If not enough gas was given, then there is no return data, indicating a precompile contract error.', + ], }, { address: '0x0000000000000000000000000000000000000003', @@ -98,12 +102,15 @@ export const precompiles: Precompile[] = [ description: 'The resulting 20-byte hash right aligned to 32 bytes', }, ], + deprecated: false, references: [ 'https://github.com/ethereum/execution-specs/blob/6f8614566e7117afa327ad054c3f4bfe19694d73/src/ethereum/shanghai/vm/precompiled_contracts/ripemd160.py', 'https://github.com/ethereum/execution-specs/blob/6f8614566e7117afa327ad054c3f4bfe19694d73/src/ethereum/shanghai/vm/gas.py#L53', 'https://github.com/ethereum/execution-specs/blob/6f8614566e7117afa327ad054c3f4bfe19694d73/src/ethereum/shanghai/vm/precompiled_contracts/__init__.py#L32', ], - notes: ['If not enough gas was given, then there is no return data.'], + notes: [ + 'If not enough gas was given, then there is no return data, indicating a precompile contract error.', + ], }, { address: '0x0000000000000000000000000000000000000004', @@ -126,13 +133,14 @@ export const precompiles: Precompile[] = [ description: 'Data from input', }, ], + deprecated: false, references: [ 'https://github.com/ethereum/execution-specs/blob/6f8614566e7117afa327ad054c3f4bfe19694d73/src/ethereum/shanghai/vm/precompiled_contracts/identity.py', 'https://github.com/ethereum/execution-specs/blob/6f8614566e7117afa327ad054c3f4bfe19694d73/src/ethereum/shanghai/vm/gas.py#L55', 'https://github.com/ethereum/execution-specs/blob/6f8614566e7117afa327ad054c3f4bfe19694d73/src/ethereum/shanghai/vm/precompiled_contracts/__init__.py#L33', ], notes: [ - 'If not enough gas was given, then there is no return data.', + 'If not enough gas was given, then there is no return data, indicating a precompile contract error.', 'The identity function is typically used to copy a chunk of memory.', ], }, @@ -187,12 +195,15 @@ export const precompiles: Precompile[] = [ description: 'Result of the computation, with the same number of bytes as M', }, ], + deprecated: false, references: [ 'https://github.com/ethereum/execution-specs/blob/6f8614566e7117afa327ad054c3f4bfe19694d73/src/ethereum/shanghai/vm/precompiled_contracts/modexp.py', 'https://github.com/ethereum/execution-specs/blob/6f8614566e7117afa327ad054c3f4bfe19694d73/src/ethereum/shanghai/vm/precompiled_contracts/modexp.py#L167', 'https://github.com/ethereum/execution-specs/blob/6f8614566e7117afa327ad054c3f4bfe19694d73/src/ethereum/shanghai/vm/precompiled_contracts/__init__.py#L34', ], - notes: ['If not enough gas was given, then there is no return data.'], + notes: [ + 'If not enough gas was given, then there is no return data, indicating a precompile contract error.', + ], }, { address: '0x0000000000000000000000000000000000000006', @@ -239,13 +250,15 @@ export const precompiles: Precompile[] = [ description: "y-coordinate of the result point on the elliptic curve 'alt_bn128'", }, ], + deprecated: false, references: [ 'https://github.com/ethereum/execution-specs/blob/6f8614566e7117afa327ad054c3f4bfe19694d73/src/ethereum/shanghai/vm/precompiled_contracts/alt_bn128.py#L33', 'https://github.com/ethereum/execution-specs/blob/6f8614566e7117afa327ad054c3f4bfe19694d73/src/ethereum/shanghai/vm/precompiled_contracts/alt_bn128.py#L45', 'https://github.com/ethereum/execution-specs/blob/6f8614566e7117afa327ad054c3f4bfe19694d73/src/ethereum/shanghai/vm/precompiled_contracts/__init__.py#L35', ], notes: [ - 'If the input is not valid, or if not enough gas was given, then there is no return data.', + 'If the input is not valid, all gas provided is consumed and there is no return data, indicating a precompile contract error.', + 'If not enough gas was given, there is no return data.', 'The gas cost is fixed at 150. However, if the input does not allow to compute a valid result, all the gas sent is consumed.', 'The point at infinity is encoded with both field x and y at 0.', ], @@ -289,13 +302,15 @@ export const precompiles: Precompile[] = [ description: "y-coordinate of the result point on the elliptic curve 'alt_bn128'", }, ], + deprecated: false, references: [ 'https://github.com/ethereum/execution-specs/blob/6f8614566e7117afa327ad054c3f4bfe19694d73/src/ethereum/shanghai/vm/precompiled_contracts/alt_bn128.py#L72', 'https://github.com/ethereum/execution-specs/blob/6f8614566e7117afa327ad054c3f4bfe19694d73/src/ethereum/shanghai/vm/precompiled_contracts/alt_bn128.py#L84', 'https://github.com/ethereum/execution-specs/blob/6f8614566e7117afa327ad054c3f4bfe19694d73/src/ethereum/shanghai/vm/precompiled_contracts/__init__.py#L36', ], notes: [ - 'If the input is not valid, or if not enough gas was given, then there is no return data.', + 'If the input is not valid, all gas provided is consumed and there is no return data, indicating a precompile contract error.', + 'If not enough gas was given, there is no return data.', 'The gas cost is fixed at 6000. However, if the input does not allow to compute a valid result, all the gas sent is consumed.', 'The point at infinity is encoded with both field x and y at 0.', ], @@ -353,14 +368,16 @@ export const precompiles: Precompile[] = [ description: '1 if the pairing was a success, 0 otherwise', }, ], + deprecated: false, references: [ 'https://github.com/ethereum/execution-specs/blob/6f8614566e7117afa327ad054c3f4bfe19694d73/src/ethereum/shanghai/vm/precompiled_contracts/alt_bn128.py#L107', 'https://github.com/ethereum/execution-specs/blob/6f8614566e7117afa327ad054c3f4bfe19694d73/src/ethereum/shanghai/vm/precompiled_contracts/alt_bn128.py#L119', 'https://github.com/ethereum/execution-specs/blob/6f8614566e7117afa327ad054c3f4bfe19694d73/src/ethereum/shanghai/vm/precompiled_contracts/__init__.py#L37', ], notes: [ + 'If the input is not valid, all gas provided is consumed and there is no return data, indicating a precompile contract error.', + 'If not enough gas was given, there is no return data.', 'The input must always be a multiple of 6 32-byte values. 0 inputs is valid and returns 1.', - 'If the input is not valid, or if not enough gas was given, then there is no return data.', 'The point at infinity is encoded with both field x and y at 0.', ], }, @@ -410,9 +427,10 @@ export const precompiles: Precompile[] = [ }, ], notes: [ - 'If the input is not valid, or if not enough gas was given, then there is no return data.', - 'Of the input does not allow to compute a valid result, all the gas sent is consumed.', + 'If the input is not valid or does not allow a valid result to be computed, all gas provided is consumed and there is no return data, indicating a precompile contract error.', + 'If not enough gas was given, there is no return data.', ], + deprecated: false, references: [ 'https://github.com/ethereum/execution-specs/blob/6f8614566e7117afa327ad054c3f4bfe19694d73/src/ethereum/shanghai/vm/precompiled_contracts/blake2f.py', 'https://github.com/ethereum/execution-specs/blob/6f8614566e7117afa327ad054c3f4bfe19694d73/src/ethereum/shanghai/vm/gas.py#L59', diff --git a/src/chains/mainnet/vm/predeploys.ts b/src/chains/mainnet/vm/predeploys.ts new file mode 100644 index 0000000..e003601 --- /dev/null +++ b/src/chains/mainnet/vm/predeploys.ts @@ -0,0 +1,3 @@ +import { Predeploy } from '@/types'; + +export const predeploys: Predeploy[] = []; diff --git a/src/chains/optimism/index.ts b/src/chains/optimism/index.ts index 421e92d..2b8d343 100644 --- a/src/chains/optimism/index.ts +++ b/src/chains/optimism/index.ts @@ -4,10 +4,12 @@ import { Chain } from '@/types'; import { signatureTypes } from './signatureTypes'; import { opcodes } from './vm/opcodes'; import { precompiles } from './vm/precompiles'; +import { predeploys } from './vm/predeploys'; export const optimism: Chain = { metadata: optimismMetadata, precompiles, + predeploys, signatureTypes: sortedArrayByField(signatureTypes, 'prefixByte'), opcodes: sortedArrayByField(opcodes, 'number'), }; diff --git a/src/chains/optimism/vm/opcodes/block/coinbase.ts b/src/chains/optimism/vm/opcodes/block/coinbase.ts index f0132a6..6657b13 100644 --- a/src/chains/optimism/vm/opcodes/block/coinbase.ts +++ b/src/chains/optimism/vm/opcodes/block/coinbase.ts @@ -6,6 +6,17 @@ export const coinbase: Omit = { ...opcode, description: 'Returns `0x4200000000000000000000000000000000000011`, the address of the Sequencer Fee Vault.', + outputs: [ + { + name: 'address', + description: 'The address of the Sequencer Fee Vault.', + }, + ], + examples: [ + { + output: '0x4200000000000000000000000000000000000011', + }, + ], references: [ { name: 'Differences between Optimism and Ethereum opcodes', diff --git a/src/chains/optimism/vm/opcodes/block/difficulty.ts b/src/chains/optimism/vm/opcodes/block/difficulty.ts deleted file mode 100644 index 72166b2..0000000 --- a/src/chains/optimism/vm/opcodes/block/difficulty.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { difficulty as baseOpcode } from '@/chains/mainnet/vm/opcodes/block/difficulty'; -import { Opcode } from '@/types'; - -const { supportedHardforks: _supportedHardforks, ...opcode } = baseOpcode; -export const difficulty: Omit = { - ...opcode, - description: - 'Returns a random value. As this value is set by the sequencer, it is not as reliably random as the L1 equivalent. You can use an oracle for randomness.', - references: [ - { - name: 'Differences between Optimism and Ethereum opcodes', - url: 'https://community.optimism.io/docs/developers/build/differences/#opcode-differences', - }, - ], -}; diff --git a/src/chains/optimism/vm/opcodes/block/number.ts b/src/chains/optimism/vm/opcodes/block/number.ts deleted file mode 100644 index f6d6051..0000000 --- a/src/chains/optimism/vm/opcodes/block/number.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { number as baseOpcode } from '@/chains/mainnet/vm/opcodes/block/number'; -import { Opcode } from '@/types'; - -const { supportedHardforks: _supportedHardforks, ...opcode } = baseOpcode; -export const number: Omit = { - ...opcode, - references: [ - { - name: 'Differences between Optimism and Ethereum', - url: 'https://community.optimism.io/docs/developers/build/differences/#opcode-differences', - }, - ], -}; diff --git a/src/chains/optimism/vm/opcodes/block/prevrandao.ts b/src/chains/optimism/vm/opcodes/block/prevrandao.ts new file mode 100644 index 0000000..1207b97 --- /dev/null +++ b/src/chains/optimism/vm/opcodes/block/prevrandao.ts @@ -0,0 +1,26 @@ +import { prevrandao as baseOpcode } from '@/chains/mainnet/vm/opcodes/block/prevrandao'; +import { Opcode } from '@/types'; + +const { supportedHardforks: _supportedHardforks, notes: _notes, ...opcode } = baseOpcode; +export const prevrandao: Omit = { + ...opcode, + outputs: [ + { + name: 'random', + description: + "The random output of the L1 beacon chain's oracle from approximately 5 L1 blocks ago.", + }, + ], + description: + "Returns the random output of the L1 beacon chain's randomness oracle. This value lags behind the L1 block's prevrandao value by approximately 5 L1 blocks, and is updated when the `L1BlockInfo` predeploy is updated.", + references: [ + { + name: 'Deriving the Transaction List', + url: 'https://github.com/ethereum-optimism/optimism/blob/develop/specs/derivation.md#building-individual-payload-attributes', + }, + { + name: 'EVM Diff Issue #21', + url: 'https://github.com/mds1/evm-diff/issues/21', + }, + ], +}; diff --git a/src/chains/optimism/vm/opcodes/environment/caller.ts b/src/chains/optimism/vm/opcodes/environment/caller.ts index a771369..ca16607 100644 --- a/src/chains/optimism/vm/opcodes/environment/caller.ts +++ b/src/chains/optimism/vm/opcodes/environment/caller.ts @@ -6,6 +6,13 @@ export const caller: Omit = { ...opcode, description: 'If the transaction is an L1 ⇒ L2 transaction, and this is the initial call (rather than an internal transaction from one contract to another), then `msg.sender` is set to the aliased address of the address that triggered the L1 ⇒ L2 transaction. Otherwise, this opcode behaves normally.', + outputs: [ + { + name: 'address', + description: + "The 20-byte address of the caller's account, or the aliased address for L1 ⇒ L2 transactions.. This is the account that did the last call (except delegate call).", + }, + ], references: [ { name: 'Differences between Optimism and Ethereum', diff --git a/src/chains/optimism/vm/opcodes/environment/origin.ts b/src/chains/optimism/vm/opcodes/environment/origin.ts index 3b7bbf2..f733cc8 100644 --- a/src/chains/optimism/vm/opcodes/environment/origin.ts +++ b/src/chains/optimism/vm/opcodes/environment/origin.ts @@ -6,6 +6,13 @@ export const origin: Omit = { ...opcode, description: 'If the transaction is an L1 ⇒ L2 transaction, then `tx.origin` is set to the aliased address of the address that triggered the L1 ⇒ L2 transaction. Otherwise, this opcode behaves normally.', + outputs: [ + { + name: 'address', + description: + 'The 20-byte address of the sender of the transaction, or the aliased address for L1 ⇒ L2 transactions. It can only be an account without code.', + }, + ], references: [ { name: 'Differences between Optimism and Ethereum', diff --git a/src/chains/optimism/vm/opcodes/index.ts b/src/chains/optimism/vm/opcodes/index.ts index aa53db8..1afc134 100644 --- a/src/chains/optimism/vm/opcodes/index.ts +++ b/src/chains/optimism/vm/opcodes/index.ts @@ -1,8 +1,7 @@ import { opcodes as mainnetOpcodes } from '@/chains/mainnet/vm/opcodes'; import { Opcode } from '@/types'; import { coinbase } from './block/coinbase'; -import { difficulty } from './block/difficulty'; -import { number } from './block/number'; +import { prevrandao } from './block/prevrandao'; import { caller } from './environment/caller'; import { origin } from './environment/origin'; import { push0 } from './stack/push0'; @@ -12,8 +11,7 @@ export const opcodes: Record> = { // Block. ...{ [coinbase.number]: coinbase }, - ...{ [difficulty.number]: difficulty }, - ...{ [number.number]: number }, + ...{ [prevrandao.number]: prevrandao }, // Environment. ...{ [caller.number]: caller }, diff --git a/src/chains/optimism/vm/opcodes/stack/push0.ts b/src/chains/optimism/vm/opcodes/stack/push0.ts index 3ec8a69..0d4437e 100644 --- a/src/chains/optimism/vm/opcodes/stack/push0.ts +++ b/src/chains/optimism/vm/opcodes/stack/push0.ts @@ -5,7 +5,7 @@ const { supportedHardforks: _supportedHardforks, ...opcode } = baseOpcode; export const push0: Omit = { ...opcode, description: - 'The opcode not supported yet, but will be added in a future hardfork. This means you cannot yet use Solidity 0.8.20 or later with an `evm_version` of Shanghai.', + 'The opcode is not supported yet, but will be added in a future hardfork. This means you cannot yet use Solidity 0.8.20 or later with an `evm_version` of Shanghai.', references: [ { name: 'Differences between Optimism and Ethereum opcodes', diff --git a/src/chains/optimism/vm/precompiles.ts b/src/chains/optimism/vm/precompiles.ts index c5733d7..e998fa4 100644 --- a/src/chains/optimism/vm/precompiles.ts +++ b/src/chains/optimism/vm/precompiles.ts @@ -1,145 +1,4 @@ import { precompiles as mainnetPrecompiles } from '@/chains/mainnet/vm/precompiles'; -import { Precompile, Predeploy } from '@/types'; +import { Precompile } from '@/types'; -// https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md -export const precompiles: (Precompile | Predeploy)[] = [ - ...mainnetPrecompiles, - { - address: '0x4200000000000000000000000000000000000000', - name: 'LegacyMessagePasser', - description: 'Stores commitments to withdrawal transactions before the Bedrock upgrade.', - deprecated: true, - references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], - }, - { - address: '0x4200000000000000000000000000000000000002', - name: 'DeployerWhitelist', - description: - 'Defined a list of accounts that were allowed to deploy contracts during the initial phases of Optimism.', - deprecated: true, - references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], - }, - { - address: '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000', - name: 'LegacyERC20ETH', - description: 'Represents all Ether in the system before Bedrock.', - deprecated: true, - references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], - }, - { - address: '0x4200000000000000000000000000000000000006', - name: 'WETH9', - description: "Wrapped Ether contract, behaves identically to mainnet's canonical WETH.", - deprecated: false, - references: [ - 'https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md', - 'https://help.optimism.io/hc/en-us/articles/4417948883611-What-is-ETH-WETH-How-do-they-interact-', - ], - }, - { - address: '0x4200000000000000000000000000000000000007', - name: 'L2CrossDomainMessenger', - description: - 'Provides a higher level API for sending cross-domain messages, compared to directly calling L2ToL1MessagePasser.', - deprecated: false, - references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], - }, - { - address: '0x4200000000000000000000000000000000000010', - name: 'L2StandardBridge', - description: - 'Higher level API built on top of the L2CrossDomainMessenger that gives a standard interface for sending ETH or ERC20 tokens across domains.', - deprecated: false, - references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], - }, - { - address: '0x4200000000000000000000000000000000000011', - name: 'SequencerFeeVault', - description: - 'Accumulates any transaction priority fees and is the value of block.coinbase. When enough fees accumulate in this account, they can be withdrawn to an immutable L1 address.', - deprecated: false, - references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], - }, - { - address: '0x4200000000000000000000000000000000000012', - name: 'OptimismMintableERC20Factory', - description: - 'Responsible for creating ERC20 contracts on L2 that can be used for depositing native L1 tokens into.', - deprecated: false, - references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], - }, - { - address: '0x4200000000000000000000000000000000000013', - name: 'L1BlockNumber', - description: 'Returns the last known L1 block number.', - deprecated: true, - references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], - }, - { - address: '0x420000000000000000000000000000000000000F', - name: 'GasPriceOracle', - description: 'Provides an API to return the L1 portion of the fee for a transaction.', - deprecated: false, - references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], - }, - { - address: '0x4200000000000000000000000000000000000042', - name: 'GovernanceToken', - description: 'The Optimism (OP) token contract.', - deprecated: false, - references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], - }, - { - address: '0x4200000000000000000000000000000000000015', - name: 'L1Block', - description: 'Allows for L1 state to be accessed in L2.', - deprecated: false, - references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], - }, - { - address: '0x4200000000000000000000000000000000000016', - name: 'L2ToL1MessagePasser', - description: 'Stores commitments to withdrawal transactions.', - deprecated: false, - references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], - }, - { - address: '0x4200000000000000000000000000000000000014', - name: 'L2ERC721Bridge', - description: - 'Works together with the L1 ERC721 bridge to enable transfers of ERC721 tokens from Ethereum to Optimism.', - deprecated: false, - references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], - }, - { - address: '0x4200000000000000000000000000000000000017', - name: 'OptimismMintableERC721Factory', - description: - 'Responsible for creating ERC721 contracts on L2 that can be used for depositing native L1 NFTs into.', - deprecated: false, - references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], - }, - { - address: '0x4200000000000000000000000000000000000018', - name: 'ProxyAdmin', - description: 'The owner of all of the proxy contracts set at the predeploys.', - deprecated: false, - references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], - }, - { - address: '0x4200000000000000000000000000000000000019', - name: 'BaseFeeVault', - description: - 'Receives the base fees on L2, since the basefee is not burnt on L2 like it is on L1.', - deprecated: false, - references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], - }, - { - address: '0x420000000000000000000000000000000000001a', - name: 'L1FeeVault', - description: - 'Receives the L1 portion of the transaction fees. Once the contract has received a certain amount of fees, the ETH can be withdrawn to an immutable address on L1.', - deprecated: false, - references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], - }, -]; +export const precompiles: Precompile[] = mainnetPrecompiles; diff --git a/src/chains/optimism/vm/predeploys.ts b/src/chains/optimism/vm/predeploys.ts new file mode 100644 index 0000000..b95137d --- /dev/null +++ b/src/chains/optimism/vm/predeploys.ts @@ -0,0 +1,602 @@ +import { Predeploy } from '@/types'; + +// https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md +export const predeploys: Predeploy[] = [ + { + address: '0x4200000000000000000000000000000000000000', + name: 'LegacyMessagePasser', + description: 'Stores commitments to withdrawal transactions before the Bedrock upgrade.', + proxyAbi: [ + 'constructor(address _admin)', + 'event AdminChanged(address previousAdmin, address newAdmin)', + 'event Upgraded(address indexed implementation)', + 'fallback()', + 'function admin() returns (address)', + 'function changeAdmin(address _admin)', + 'function implementation() returns (address)', + 'function upgradeTo(address _implementation)', + 'function upgradeToAndCall(address _implementation, bytes _data) payable returns (bytes)', + 'receive() external payable', + ], + logicAbi: [ + 'constructor()', + 'function passMessageToL1(bytes _message)', + 'function sentMessages(bytes32) view returns (bool)', + 'function version() view returns (string)', + ], + logicAddress: '0xc0D3C0d3C0d3C0D3c0d3C0d3c0D3C0d3c0d30000', + deprecated: true, + references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], + }, + { + address: '0x4200000000000000000000000000000000000002', + name: 'DeployerWhitelist', + description: + 'Defined a list of accounts that were allowed to deploy contracts during the initial phases of Optimism.', + proxyAbi: [ + 'constructor(address _admin)', + 'event AdminChanged(address previousAdmin, address newAdmin)', + 'event Upgraded(address indexed implementation)', + 'fallback()', + 'function admin() returns (address)', + 'function changeAdmin(address _admin)', + 'function implementation() returns (address)', + 'function upgradeTo(address _implementation)', + 'function upgradeToAndCall(address _implementation, bytes _data) payable returns (bytes)', + 'receive() external payable', + ], + logicAbi: [ + 'constructor()', + 'event OwnerChanged(address oldOwner, address newOwner)', + 'event WhitelistDisabled(address oldOwner)', + 'event WhitelistStatusChanged(address deployer, bool whitelisted)', + 'function enableArbitraryContractDeployment()', + 'function isDeployerAllowed(address _deployer) view returns (bool)', + 'function owner() view returns (address)', + 'function setOwner(address _owner)', + 'function setWhitelistedDeployer(address _deployer, bool _isWhitelisted)', + 'function version() view returns (string)', + 'function whitelist(address) view returns (bool)', + ], + logicAddress: '0xc0d3c0d3C0d3c0D3c0d3C0D3c0d3C0d3c0D30002', + deprecated: true, + references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], + }, + { + address: '0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000', + name: 'LegacyERC20ETH', + description: 'Represents all Ether in the system before Bedrock.', + logicAbi: [ + 'constructor()', + 'event Approval(address indexed owner, address indexed spender, uint256 value)', + 'event Burn(address indexed account, uint256 amount)', + 'event Mint(address indexed account, uint256 amount)', + 'event Transfer(address indexed from, address indexed to, uint256 value)', + 'function BRIDGE() view returns (address)', + 'function REMOTE_TOKEN() view returns (address)', + 'function allowance(address owner, address spender) view returns (uint256)', + 'function approve(address, uint256) returns (bool)', + 'function balanceOf(address _who) view returns (uint256)', + 'function bridge() view returns (address)', + 'function burn(address, uint256)', + 'function decimals() view returns (uint8)', + 'function decreaseAllowance(address, uint256) returns (bool)', + 'function increaseAllowance(address, uint256) returns (bool)', + 'function l1Token() view returns (address)', + 'function l2Bridge() view returns (address)', + 'function mint(address, uint256)', + 'function name() view returns (string)', + 'function remoteToken() view returns (address)', + 'function supportsInterface(bytes4 _interfaceId) pure returns (bool)', + 'function symbol() view returns (string)', + 'function totalSupply() view returns (uint256)', + 'function transfer(address, uint256) returns (bool)', + 'function transferFrom(address, address, uint256) returns (bool)', + ], + deprecated: true, + references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], + }, + { + address: '0x4200000000000000000000000000000000000006', + name: 'WETH9', + description: "Wrapped Ether contract, behaves identically to mainnet's canonical WETH.", + logicAbi: [ + 'event Approval(address indexed src, address indexed guy, uint256 wad)', + 'event Deposit(address indexed dst, uint256 wad)', + 'event Transfer(address indexed src, address indexed dst, uint256 wad)', + 'event Withdrawal(address indexed src, uint256 wad)', + 'fallback()', + 'function allowance(address, address) view returns (uint256)', + 'function approve(address guy, uint256 wad) returns (bool)', + 'function balanceOf(address) view returns (uint256)', + 'function decimals() view returns (uint8)', + 'function deposit() payable', + 'function name() view returns (string)', + 'function symbol() view returns (string)', + 'function totalSupply() view returns (uint256)', + 'function transfer(address dst, uint256 wad) returns (bool)', + 'function transferFrom(address src, address dst, uint256 wad) returns (bool)', + 'function withdraw(uint256 wad)', + ], + deprecated: false, + references: [ + 'https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md', + 'https://help.optimism.io/hc/en-us/articles/4417948883611-What-is-ETH-WETH-How-do-they-interact-', + ], + }, + { + address: '0x4200000000000000000000000000000000000007', + name: 'L2CrossDomainMessenger', + description: + 'Provides a higher level API for sending cross-domain messages, compared to directly calling L2ToL1MessagePasser.', + proxyAbi: [ + 'constructor(address _admin)', + 'event AdminChanged(address previousAdmin, address newAdmin)', + 'event Upgraded(address indexed implementation)', + 'fallback()', + 'function admin() returns (address)', + 'function changeAdmin(address _admin)', + 'function implementation() returns (address)', + 'function upgradeTo(address _implementation)', + 'function upgradeToAndCall(address _implementation, bytes _data) payable returns (bytes)', + 'receive() external payable', + ], + logicAbi: [ + 'constructor(address _l1CrossDomainMessenger)', + 'event FailedRelayedMessage(bytes32 indexed msgHash)', + 'event Initialized(uint8 version)', + 'event RelayedMessage(bytes32 indexed msgHash)', + 'event SentMessage(address indexed target, address sender, bytes message, uint256 messageNonce, uint256 gasLimit)', + 'event SentMessageExtension1(address indexed sender, uint256 value)', + 'function MESSAGE_VERSION() view returns (uint16)', + 'function MIN_GAS_CALLDATA_OVERHEAD() view returns (uint64)', + 'function MIN_GAS_DYNAMIC_OVERHEAD_DENOMINATOR() view returns (uint64)', + 'function MIN_GAS_DYNAMIC_OVERHEAD_NUMERATOR() view returns (uint64)', + 'function OTHER_MESSENGER() view returns (address)', + 'function RELAY_CALL_OVERHEAD() view returns (uint64)', + 'function RELAY_CONSTANT_OVERHEAD() view returns (uint64)', + 'function RELAY_GAS_CHECK_BUFFER() view returns (uint64)', + 'function RELAY_RESERVED_GAS() view returns (uint64)', + 'function baseGas(bytes _message, uint32 _minGasLimit) pure returns (uint64)', + 'function failedMessages(bytes32) view returns (bool)', + 'function initialize()', + 'function l1CrossDomainMessenger() view returns (address)', + 'function messageNonce() view returns (uint256)', + 'function relayMessage(uint256 _nonce, address _sender, address _target, uint256 _value, uint256 _minGasLimit, bytes _message) payable', + 'function sendMessage(address _target, bytes _message, uint32 _minGasLimit) payable', + 'function successfulMessages(bytes32) view returns (bool)', + 'function version() view returns (string)', + 'function xDomainMessageSender() view returns (address)', + ], + logicAddress: '0xC0d3c0d3c0D3c0D3C0d3C0D3C0D3c0d3c0d30007', + deprecated: false, + references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], + }, + { + address: '0x4200000000000000000000000000000000000010', + name: 'L2StandardBridge', + description: + 'Higher level API built on top of the L2CrossDomainMessenger that gives a standard interface for sending ETH or ERC20 tokens across domains.', + proxyAbi: [ + 'constructor(address _admin)', + 'event AdminChanged(address previousAdmin, address newAdmin)', + 'event Upgraded(address indexed implementation)', + 'fallback()', + 'function admin() returns (address)', + 'function changeAdmin(address _admin)', + 'function implementation() returns (address)', + 'function upgradeTo(address _implementation)', + 'function upgradeToAndCall(address _implementation, bytes _data) payable returns (bytes)', + 'receive() external payable', + ], + logicAbi: [ + 'constructor(address _otherBridge)', + 'event DepositFinalized(address indexed l1Token, address indexed l2Token, address indexed from, address to, uint256 amount, bytes extraData)', + 'event ERC20BridgeFinalized(address indexed localToken, address indexed remoteToken, address indexed from, address to, uint256 amount, bytes extraData)', + 'event ERC20BridgeInitiated(address indexed localToken, address indexed remoteToken, address indexed from, address to, uint256 amount, bytes extraData)', + 'event ETHBridgeFinalized(address indexed from, address indexed to, uint256 amount, bytes extraData)', + 'event ETHBridgeInitiated(address indexed from, address indexed to, uint256 amount, bytes extraData)', + 'event WithdrawalInitiated(address indexed l1Token, address indexed l2Token, address indexed from, address to, uint256 amount, bytes extraData)', + 'function MESSENGER() view returns (address)', + 'function OTHER_BRIDGE() view returns (address)', + 'function bridgeERC20(address _localToken, address _remoteToken, uint256 _amount, uint32 _minGasLimit, bytes _extraData)', + 'function bridgeERC20To(address _localToken, address _remoteToken, address _to, uint256 _amount, uint32 _minGasLimit, bytes _extraData)', + 'function bridgeETH(uint32 _minGasLimit, bytes _extraData) payable', + 'function bridgeETHTo(address _to, uint32 _minGasLimit, bytes _extraData) payable', + 'function deposits(address, address) view returns (uint256)', + 'function finalizeBridgeERC20(address _localToken, address _remoteToken, address _from, address _to, uint256 _amount, bytes _extraData)', + 'function finalizeBridgeETH(address _from, address _to, uint256 _amount, bytes _extraData) payable', + 'function finalizeDeposit(address _l1Token, address _l2Token, address _from, address _to, uint256 _amount, bytes _extraData) payable', + 'function l1TokenBridge() view returns (address)', + 'function messenger() view returns (address)', + 'function version() view returns (string)', + 'function withdraw(address _l2Token, uint256 _amount, uint32 _minGasLimit, bytes _extraData) payable', + 'function withdrawTo(address _l2Token, address _to, uint256 _amount, uint32 _minGasLimit, bytes _extraData) payable', + 'receive() external payable', + ], + logicAddress: '0xC0d3c0d3c0D3c0d3C0D3c0D3C0d3C0D3C0D30010', + deprecated: false, + references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], + }, + { + address: '0x4200000000000000000000000000000000000011', + name: 'SequencerFeeVault', + description: + 'Accumulates any transaction priority fees and is the value of block.coinbase. When enough fees accumulate in this account, they can be withdrawn to an immutable L1 address.', + logicAbi: [ + 'constructor(address _l1FeeWallet)', + 'function MIN_WITHDRAWAL_AMOUNT() view returns (uint256)', + 'function l1FeeWallet() view returns (address)', + 'function withdraw()', + 'receive() external payable', + ], + deprecated: false, + references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], + }, + { + address: '0x4200000000000000000000000000000000000012', + name: 'OptimismMintableERC20Factory', + description: + 'Responsible for creating ERC20 contracts on L2 that can be used for depositing native L1 tokens into.', + proxyAbi: [ + 'constructor(address _admin)', + 'event AdminChanged(address previousAdmin, address newAdmin)', + 'event Upgraded(address indexed implementation)', + 'fallback()', + 'function admin() returns (address)', + 'function changeAdmin(address _admin)', + 'function implementation() returns (address)', + 'function upgradeTo(address _implementation)', + 'function upgradeToAndCall(address _implementation, bytes _data) payable returns (bytes)', + 'receive() external payable', + ], + logicAbi: [ + 'constructor(address _bridge)', + 'event OptimismMintableERC20Created(address indexed localToken, address indexed remoteToken, address deployer)', + 'event StandardL2TokenCreated(address indexed remoteToken, address indexed localToken)', + 'function BRIDGE() view returns (address)', + 'function createOptimismMintableERC20(address _remoteToken, string _name, string _symbol) returns (address)', + 'function createStandardL2Token(address _remoteToken, string _name, string _symbol) returns (address)', + 'function version() view returns (string)', + ], + logicAddress: '0xc0D3c0d3C0d3c0d3c0D3c0d3c0D3c0D3c0D30012', + deprecated: false, + references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], + }, + { + address: '0x4200000000000000000000000000000000000013', + name: 'L1BlockNumber', + description: 'Returns the last known L1 block number.', + proxyAbi: [ + 'constructor(address _admin)', + 'event AdminChanged(address previousAdmin, address newAdmin)', + 'event Upgraded(address indexed implementation)', + 'fallback()', + 'function admin() returns (address)', + 'function changeAdmin(address _admin)', + 'function implementation() returns (address)', + 'function upgradeTo(address _implementation)', + 'function upgradeToAndCall(address _implementation, bytes _data) payable returns (bytes)', + 'receive() external payable', + ], + logicAbi: [ + 'constructor()', + 'fallback()', + 'function getL1BlockNumber() view returns (uint256)', + 'function version() view returns (string)', + 'receive() external payable', + ], + logicAddress: '0xC0D3C0d3C0D3c0D3C0d3c0D3C0d3c0d3C0d30013', + deprecated: true, + references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], + }, + { + address: '0x420000000000000000000000000000000000000F', + name: 'GasPriceOracle', + description: 'Provides an API to return the L1 portion of the fee for a transaction.', + proxyAbi: [ + 'constructor(address _admin)', + 'event AdminChanged(address previousAdmin, address newAdmin)', + 'event Upgraded(address indexed implementation)', + 'fallback()', + 'function admin() returns (address)', + 'function changeAdmin(address _admin)', + 'function implementation() returns (address)', + 'function upgradeTo(address _implementation)', + 'function upgradeToAndCall(address _implementation, bytes _data) payable returns (bytes)', + 'receive() external payable', + ], + logicAbi: [ + 'constructor()', + 'function DECIMALS() view returns (uint256)', + 'function baseFee() view returns (uint256)', + 'function decimals() pure returns (uint256)', + 'function gasPrice() view returns (uint256)', + 'function getL1Fee(bytes _data) view returns (uint256)', + 'function getL1GasUsed(bytes _data) view returns (uint256)', + 'function l1BaseFee() view returns (uint256)', + 'function overhead() view returns (uint256)', + 'function scalar() view returns (uint256)', + 'function version() view returns (string)', + ], + logicAddress: '0xc0d3C0d3C0d3c0D3C0D3C0d3C0d3C0D3C0D3000f', + deprecated: false, + references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], + }, + { + address: '0x4200000000000000000000000000000000000042', + name: 'GovernanceToken', + description: 'The Optimism (OP) token contract.', + logicAbi: [ + 'constructor()', + 'event Approval(address indexed owner, address indexed spender, uint256 value)', + 'event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate)', + 'event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance)', + 'event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)', + 'event Transfer(address indexed from, address indexed to, uint256 value)', + 'function DOMAIN_SEPARATOR() view returns (bytes32)', + 'function allowance(address owner, address spender) view returns (uint256)', + 'function approve(address spender, uint256 amount) returns (bool)', + 'function balanceOf(address account) view returns (uint256)', + 'function burn(uint256 amount)', + 'function burnFrom(address account, uint256 amount)', + 'function checkpoints(address account, uint32 pos) view returns ((uint32 fromBlock, uint224 votes))', + 'function decimals() view returns (uint8)', + 'function decreaseAllowance(address spender, uint256 subtractedValue) returns (bool)', + 'function delegate(address delegatee)', + 'function delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s)', + 'function delegates(address account) view returns (address)', + 'function getPastTotalSupply(uint256 blockNumber) view returns (uint256)', + 'function getPastVotes(address account, uint256 blockNumber) view returns (uint256)', + 'function getVotes(address account) view returns (uint256)', + 'function increaseAllowance(address spender, uint256 addedValue) returns (bool)', + 'function mint(address _account, uint256 _amount)', + 'function name() view returns (string)', + 'function nonces(address owner) view returns (uint256)', + 'function numCheckpoints(address account) view returns (uint32)', + 'function owner() view returns (address)', + 'function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)', + 'function renounceOwnership()', + 'function symbol() view returns (string)', + 'function totalSupply() view returns (uint256)', + 'function transfer(address to, uint256 amount) returns (bool)', + 'function transferFrom(address from, address to, uint256 amount) returns (bool)', + 'function transferOwnership(address newOwner)', + ], + deprecated: false, + references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], + }, + { + address: '0x4200000000000000000000000000000000000015', + name: 'L1Block', + description: 'Allows for L1 state to be accessed in L2.', + proxyAbi: [ + 'constructor(address _admin)', + 'event AdminChanged(address previousAdmin, address newAdmin)', + 'event Upgraded(address indexed implementation)', + 'fallback()', + 'function admin() returns (address)', + 'function changeAdmin(address _admin)', + 'function implementation() returns (address)', + 'function upgradeTo(address _implementation)', + 'function upgradeToAndCall(address _implementation, bytes _data) payable returns (bytes)', + 'receive() external payable', + ], + logicAbi: [ + 'constructor()', + 'function DEPOSITOR_ACCOUNT() view returns (address)', + 'function basefee() view returns (uint256)', + 'function batcherHash() view returns (bytes32)', + 'function hash() view returns (bytes32)', + 'function l1FeeOverhead() view returns (uint256)', + 'function l1FeeScalar() view returns (uint256)', + 'function number() view returns (uint64)', + 'function sequenceNumber() view returns (uint64)', + 'function setL1BlockValues(uint64 _number, uint64 _timestamp, uint256 _basefee, bytes32 _hash, uint64 _sequenceNumber, bytes32 _batcherHash, uint256 _l1FeeOverhead, uint256 _l1FeeScalar)', + 'function timestamp() view returns (uint64)', + 'function version() view returns (string)', + ], + logicAddress: '0xc0d3C0D3C0D3c0D3C0D3C0d3C0D3c0D3c0d30015', + deprecated: false, + references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], + }, + { + address: '0x4200000000000000000000000000000000000016', + name: 'L2ToL1MessagePasser', + description: 'Stores commitments to withdrawal transactions.', + proxyAbi: [ + 'constructor(address _admin)', + 'event AdminChanged(address previousAdmin, address newAdmin)', + 'event Upgraded(address indexed implementation)', + 'fallback()', + 'function admin() returns (address)', + 'function changeAdmin(address _admin)', + 'function implementation() returns (address)', + 'function upgradeTo(address _implementation)', + 'function upgradeToAndCall(address _implementation, bytes _data) payable returns (bytes)', + 'receive() external payable', + ], + logicAbi: [ + 'constructor()', + 'event MessagePassed(uint256 indexed nonce, address indexed sender, address indexed target, uint256 value, uint256 gasLimit, bytes data, bytes32 withdrawalHash)', + 'event WithdrawerBalanceBurnt(uint256 indexed amount)', + 'function MESSAGE_VERSION() view returns (uint16)', + 'function burn()', + 'function initiateWithdrawal(address _target, uint256 _gasLimit, bytes _data) payable', + 'function messageNonce() view returns (uint256)', + 'function sentMessages(bytes32) view returns (bool)', + 'function version() view returns (string)', + 'receive() external payable', + ], + logicAddress: '0xC0D3C0d3C0d3c0d3C0d3C0D3c0D3c0d3c0D30016', + deprecated: false, + references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], + }, + { + address: '0x4200000000000000000000000000000000000014', + name: 'L2ERC721Bridge', + description: + 'Works together with the L1 ERC721 bridge to enable transfers of ERC721 tokens from Ethereum to Optimism.', + proxyAbi: [ + 'constructor(address _admin)', + 'event AdminChanged(address previousAdmin, address newAdmin)', + 'event Upgraded(address indexed implementation)', + 'fallback()', + 'function admin() returns (address)', + 'function changeAdmin(address _admin)', + 'function implementation() returns (address)', + 'function upgradeTo(address _implementation)', + 'function upgradeToAndCall(address _implementation, bytes _data) payable returns (bytes)', + 'receive() external payable', + ], + logicAbi: [ + 'constructor(address _messenger, address _otherBridge)', + 'event ERC721BridgeFinalized(address indexed localToken, address indexed remoteToken, address indexed from, address to, uint256 tokenId, bytes extraData)', + 'event ERC721BridgeInitiated(address indexed localToken, address indexed remoteToken, address indexed from, address to, uint256 tokenId, bytes extraData)', + 'function MESSENGER() view returns (address)', + 'function OTHER_BRIDGE() view returns (address)', + 'function bridgeERC721(address _localToken, address _remoteToken, uint256 _tokenId, uint32 _minGasLimit, bytes _extraData)', + 'function bridgeERC721To(address _localToken, address _remoteToken, address _to, uint256 _tokenId, uint32 _minGasLimit, bytes _extraData)', + 'function finalizeBridgeERC721(address _localToken, address _remoteToken, address _from, address _to, uint256 _tokenId, bytes _extraData)', + 'function messenger() view returns (address)', + 'function otherBridge() view returns (address)', + 'function version() view returns (string)', + ], + logicAddress: '0xC0D3c0d3c0d3c0d3c0D3C0d3C0D3C0D3c0d30014', + deprecated: false, + references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], + }, + { + address: '0x4200000000000000000000000000000000000017', + name: 'OptimismMintableERC721Factory', + description: + 'Responsible for creating ERC721 contracts on L2 that can be used for depositing native L1 NFTs into.', + proxyAbi: [ + 'constructor(address _admin)', + 'event AdminChanged(address previousAdmin, address newAdmin)', + 'event Upgraded(address indexed implementation)', + 'fallback()', + 'function admin() returns (address)', + 'function changeAdmin(address _admin)', + 'function implementation() returns (address)', + 'function upgradeTo(address _implementation)', + 'function upgradeToAndCall(address _implementation, bytes _data) payable returns (bytes)', + 'receive() external payable', + ], + logicAbi: [ + 'constructor(address _bridge, uint256 _remoteChainId)', + 'event OptimismMintableERC721Created(address indexed localToken, address indexed remoteToken, address deployer)', + 'function BRIDGE() view returns (address)', + 'function REMOTE_CHAIN_ID() view returns (uint256)', + 'function createOptimismMintableERC721(address _remoteToken, string _name, string _symbol) returns (address)', + 'function isOptimismMintableERC721(address) view returns (bool)', + 'function version() view returns (string)', + ], + logicAddress: '0xc0d3C0d3C0d3C0d3C0d3c0d3C0D3C0d3C0D30017', + deprecated: false, + references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], + }, + { + address: '0x4200000000000000000000000000000000000018', + name: 'ProxyAdmin', + description: 'The owner of all of the proxy contracts set at the predeploys.', + proxyAbi: [ + 'constructor(address _admin)', + 'event AdminChanged(address previousAdmin, address newAdmin)', + 'event Upgraded(address indexed implementation)', + 'fallback()', + 'function admin() returns (address)', + 'function changeAdmin(address _admin)', + 'function implementation() returns (address)', + 'function upgradeTo(address _implementation)', + 'function upgradeToAndCall(address _implementation, bytes _data) payable returns (bytes)', + 'receive() external payable', + ], + logicAbi: [ + 'constructor(address _owner)', + 'event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)', + 'function addressManager() view returns (address)', + 'function changeProxyAdmin(address _proxy, address _newAdmin)', + 'function getProxyAdmin(address _proxy) view returns (address)', + 'function getProxyImplementation(address _proxy) view returns (address)', + 'function implementationName(address) view returns (string)', + 'function isUpgrading() view returns (bool)', + 'function owner() view returns (address)', + 'function proxyType(address) view returns (uint8)', + 'function renounceOwnership()', + 'function setAddress(string _name, address _address)', + 'function setAddressManager(address _address)', + 'function setImplementationName(address _address, string _name)', + 'function setProxyType(address _address, uint8 _type)', + 'function setUpgrading(bool _upgrading)', + 'function transferOwnership(address newOwner)', + 'function upgrade(address _proxy, address _implementation)', + 'function upgradeAndCall(address _proxy, address _implementation, bytes _data) payable', + ], + logicAddress: '0xC0d3C0D3c0d3C0d3c0d3c0D3C0D3C0d3C0D30018', + deprecated: false, + references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], + }, + { + address: '0x4200000000000000000000000000000000000019', + name: 'BaseFeeVault', + description: + 'Receives the base fees on L2, since the basefee is not burnt on L2 like it is on L1.', + proxyAbi: [ + 'constructor(address _admin)', + 'event AdminChanged(address previousAdmin, address newAdmin)', + 'event Upgraded(address indexed implementation)', + 'fallback()', + 'function admin() returns (address)', + 'function changeAdmin(address _admin)', + 'function implementation() returns (address)', + 'function upgradeTo(address _implementation)', + 'function upgradeToAndCall(address _implementation, bytes _data) payable returns (bytes)', + 'receive() external payable', + ], + logicAbi: [ + 'constructor(address _recipient)', + 'event Withdrawal(uint256 value, address to, address from)', + 'function MIN_WITHDRAWAL_AMOUNT() view returns (uint256)', + 'function RECIPIENT() view returns (address)', + 'function totalProcessed() view returns (uint256)', + 'function version() view returns (string)', + 'function withdraw()', + 'receive() external payable', + ], + logicAddress: '0xC0d3c0D3c0d3C0D3C0D3C0d3c0D3C0D3c0d30019', + deprecated: false, + references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], + }, + { + address: '0x420000000000000000000000000000000000001a', + name: 'L1FeeVault', + description: + 'Receives the L1 portion of the transaction fees. Once the contract has received a certain amount of fees, the ETH can be withdrawn to an immutable address on L1.', + proxyAbi: [ + 'constructor(address _admin)', + 'event AdminChanged(address previousAdmin, address newAdmin)', + 'event Upgraded(address indexed implementation)', + 'fallback()', + 'function admin() returns (address)', + 'function changeAdmin(address _admin)', + 'function implementation() returns (address)', + 'function upgradeTo(address _implementation)', + 'function upgradeToAndCall(address _implementation, bytes _data) payable returns (bytes)', + 'receive() external payable', + ], + logicAbi: [ + 'constructor(address _recipient)', + 'event Withdrawal(uint256 value, address to, address from)', + 'function MIN_WITHDRAWAL_AMOUNT() view returns (uint256)', + 'function RECIPIENT() view returns (address)', + 'function totalProcessed() view returns (uint256)', + 'function version() view returns (string)', + 'function withdraw()', + 'receive() external payable', + ], + logicAddress: '0xc0D3c0D3C0d3c0d3c0d3C0d3c0d3C0d3C0D3001A', + deprecated: false, + references: ['https://github.com/ethereum-optimism/optimism/blob/develop/specs/predeploys.md'], + }, +]; diff --git a/src/components/diff/DiffOpcodes.tsx b/src/components/diff/DiffOpcodes.tsx index bd719fe..ecd7eb3 100644 --- a/src/components/diff/DiffOpcodes.tsx +++ b/src/components/diff/DiffOpcodes.tsx @@ -1,6 +1,5 @@ import { Copyable } from '@/components/ui/Copyable'; import { CURRENT_MAINNET_HARDFORK } from '@/lib/constants'; -import { opcodeId } from '@/lib/opcodes'; import { classNames, formatPrefixByte } from '@/lib/utils'; import { toUppercase } from '@/lib/utils'; import { Example, Opcode, Reference, Variable } from '@/types'; @@ -56,13 +55,13 @@ const formatVariables = (title: string, array?: Variable[]): JSX.Element => { const formatVariable = (v: Variable): JSX.Element => { return ( - <> +

{v.name}: {v.description.toLowerCase()}

{v.expression && ( <> -

+

Sub-variables ({v.name})
{v.name} = {v.expression} {v.variables && ( @@ -70,11 +69,11 @@ const formatVariable = (v: Variable): JSX.Element => {
    {v.variables.map((subvariables) => formatVariable(subvariables))}
)} -

+

)} - +
); }; @@ -258,17 +257,17 @@ export const DiffOpcodes = ({ base, target, onlyShowDiff }: Props): JSX.Element if (!Array.isArray(base) || !Array.isArray(target)) return <>; // Generate a sorted list of all opcode numbers from both base and target. - const sortedOpcodeIds = [ - ...base.map((opcode) => opcodeId(opcode)), - ...target.map((opcode) => opcodeId(opcode)), - ].sort((a, b) => a.localeCompare(b)); - const opcodeIds = [...new Set(sortedOpcodeIds)]; + const sortedOpcodeNumbers = [ + ...base.map((opcode) => opcode.number), + ...target.map((opcode) => opcode.number), + ].sort((a, b) => a - b); + const opcodeNumbers = [...new Set(sortedOpcodeNumbers)]; return ( <> - {opcodeIds.map((id) => { - const baseOpcode = base.find((opcode) => opcodeId(opcode) === id); - const targetOpcode = target.find((opcode) => opcodeId(opcode) === id); + {opcodeNumbers.map((number) => { + const baseOpcode = base.find((opcode) => opcode.number === number); + const targetOpcode = target.find((opcode) => opcode.number === number); if (!baseOpcode || !targetOpcode) { return <>; } @@ -281,7 +280,7 @@ export const DiffOpcodes = ({ base, target, onlyShowDiff }: Props): JSX.Element return ( showOpcode && (
diff --git a/src/components/diff/DiffPrecompiles.tsx b/src/components/diff/DiffPrecompiles.tsx index 260174a..2400a9a 100644 --- a/src/components/diff/DiffPrecompiles.tsx +++ b/src/components/diff/DiffPrecompiles.tsx @@ -1,14 +1,14 @@ import { Address, getAddress } from 'viem'; import { Copyable } from '@/components/ui/Copyable'; -import { Precompile, Predeploy } from '@/types'; +import { Precompile } from '@/types'; type Props = { - base: (Precompile | Predeploy)[]; - target: (Precompile | Predeploy)[]; + base: Precompile[]; + target: Precompile[]; onlyShowDiff: boolean; }; -const formatPrecompile = (contents: Precompile | Predeploy | undefined) => { +const formatPrecompile = (contents: Precompile | undefined) => { if (!contents) return

Not present

; return ( <> @@ -18,6 +18,7 @@ const formatPrecompile = (contents: Precompile | Predeploy | undefined) => { ); }; +// TODO Dedupe this helper method const formatAddress = (addr: Address) => { addr = getAddress(addr); return {`${addr.slice(0, 6)}...${addr.slice(-4)}`}; diff --git a/src/components/diff/DiffPredeploys.tsx b/src/components/diff/DiffPredeploys.tsx new file mode 100644 index 0000000..c643711 --- /dev/null +++ b/src/components/diff/DiffPredeploys.tsx @@ -0,0 +1,58 @@ +import { Address, getAddress } from 'viem'; +import { Copyable } from '@/components/ui/Copyable'; +import { Predeploy } from '@/types'; + +type Props = { + base: Predeploy[]; + target: Predeploy[]; + onlyShowDiff: boolean; +}; + +const formatPredeploy = (contents: Predeploy | undefined) => { + if (!contents) return

Not present

; + return ( + <> +

{contents.name}

+

{contents.description}

+ + ); +}; + +const formatAddress = (addr: Address) => { + addr = getAddress(addr); + return {`${addr.slice(0, 6)}...${addr.slice(-4)}`}; +}; + +export const DiffPredeploys = ({ base, target, onlyShowDiff }: Props) => { + // Generate a sorted list of all predeploys from both base and target. + const sortedAddrs = [ + ...base.map((p) => getAddress(p.address)), + ...target.map((p) => getAddress(p.address)), + ].sort((a, b) => a.localeCompare(b)); + const predeployAddrs = [...new Set(sortedAddrs)]; + + return ( + <> + {predeployAddrs.map((addr) => { + const basePredeploy = base.find((p) => getAddress(p.address) === addr); + const targetPredeploy = target.find((p) => getAddress(p.address) === addr); + + const isEqual = JSON.stringify(basePredeploy) === JSON.stringify(targetPredeploy); + const showPredeploy = !isEqual || !onlyShowDiff; + + return ( + showPredeploy && ( +
+ +
{formatPredeploy(basePredeploy)}
+
{formatPredeploy(targetPredeploy)}
+
+ ) + ); + })} + + ); +}; diff --git a/src/lib/opcodes.ts b/src/lib/opcodes.ts index a6365c7..9bdc308 100644 --- a/src/lib/opcodes.ts +++ b/src/lib/opcodes.ts @@ -1,5 +1,4 @@ import { MainnetHardfork } from '@/chains/mainnet/hardforks'; -import { Opcode } from '@/types'; import { ETHEREUM_EXECUTION_SPECS_COMMIT_ID, ETHEREUM_EXECUTION_SPECS_URL, @@ -24,12 +23,6 @@ export enum OpcodeGroup { // Returns a hex string (without the '0x' prefix) padded to 2 characters. const formatOpcodeNumber = (n: number): string => n.toString(16).padStart(2, '0'); -// Returns a unique ID for each opcode (the opcode number is not sufficient because opcodes can have -// the same value such as `block.difficulty` and `block.prevrandao`). We format the opcode number -// to ensure the sorted order is correct. -export const opcodeId = (opcode: Opcode): string => - `${formatOpcodeNumber(opcode.number)}-${opcode.name}`; - // Returns a link to the Ethereum execution specs for the given hardfork, opcode, and line number. export const ethSpecsOpcodeSrc = ( hardfork: MainnetHardfork, diff --git a/src/pages/diff.tsx b/src/pages/diff.tsx index 0fcddc7..3359685 100644 --- a/src/pages/diff.tsx +++ b/src/pages/diff.tsx @@ -6,6 +6,7 @@ import { ChainDiffSelector } from '@/components/ChainDiffSelector'; import { DiffMetadata } from '@/components/diff/DiffMetadata'; import { DiffOpcodes } from '@/components/diff/DiffOpcodes'; import { DiffPrecompiles } from '@/components/diff/DiffPrecompiles'; +import { DiffPredeploys } from '@/components/diff/DiffPredeploys'; import { DiffSignatureTypes } from '@/components/diff/DiffSignatureTypes'; import { Copyable } from '@/components/ui/Copyable'; import { Toggle } from '@/components/ui/Toggle'; @@ -27,7 +28,8 @@ interface Section { const SECTION_MAP: Record = { metadata: { title: 'Metadata', component: DiffMetadata }, - precompiles: { title: 'Precompiles and Predeploys', component: DiffPrecompiles }, + precompiles: { title: 'Precompiles', component: DiffPrecompiles }, + predeploys: { title: 'Predeploys', component: DiffPredeploys }, signatureTypes: { title: 'Transaction and Signature Types', component: DiffSignatureTypes }, opcodes: { title: 'Opcodes', component: DiffOpcodes }, }; @@ -108,6 +110,7 @@ const Diff = () => { const target = targetChain[section as keyof Chain]; return (
+ {/* Header */} { )} /> - + {/* Diff */} +
+ +
+ + {/* If diff is empty, convey that to user */} + {document.getElementById(`diff-${section}`)?.innerHTML === '' && ( +
No differences found.
+ )}
); })} diff --git a/src/types/chain.ts b/src/types/chain.ts index a69570f..6daf320 100644 --- a/src/types/chain.ts +++ b/src/types/chain.ts @@ -1,11 +1,13 @@ import { Chain as Metadata } from '@wagmi/chains'; import { Opcode } from './opcode'; -import { Precompile, Predeploy } from './precompile'; +import { Precompile } from './precompile'; +import { Predeploy } from './predeploy'; import { SignatureType } from './signatureType'; export type Chain = { metadata: Metadata; - precompiles: (Precompile | Predeploy)[]; + precompiles: Precompile[]; + predeploys: Predeploy[]; signatureTypes: SignatureType[]; opcodes: Partial[]; }; diff --git a/src/types/index.ts b/src/types/index.ts index 0a180df..0515263 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,4 +1,5 @@ export type { Chain } from './chain'; export type { Variable, Example, Reference, Opcode } from './opcode'; -export type { Precompile, Predeploy } from './precompile'; +export type { Precompile } from './precompile'; +export type { Predeploy } from './predeploy'; export type { SignatureType } from './signatureType'; diff --git a/src/types/precompile.ts b/src/types/precompile.ts index cd805b7..7e4ca66 100644 --- a/src/types/precompile.ts +++ b/src/types/precompile.ts @@ -7,20 +7,23 @@ type PrecompileParam = { description: string; }; -export type Predeploy = { +export type PrecompileBase = { address: Address; name: string; description: string; + minGas?: number; deprecated: boolean; + references: string[]; + notes?: string[]; }; -export type Precompile = { - address: Address; - name: string; - description: string; - minGas: number; +export type PrecompileInputOutput = PrecompileBase & { input: PrecompileParam[]; output: PrecompileParam[]; - references: string[]; - notes?: string[]; }; + +export type PrecompileAbi = PrecompileBase & { + logicAbi: string[]; +}; + +export type Precompile = PrecompileInputOutput | PrecompileAbi; diff --git a/src/types/predeploy.ts b/src/types/predeploy.ts new file mode 100644 index 0000000..7f65b55 --- /dev/null +++ b/src/types/predeploy.ts @@ -0,0 +1,21 @@ +import { Address } from 'viem'; + +type PredeployBase = { + address: Address; + name: string; + description: string; + deprecated: boolean; + references: string[]; +}; + +type StandardPredeploy = PredeployBase & { + logicAbi: string[]; +}; + +type ProxiedPredeploy = PredeployBase & { + proxyAbi: string[]; + logicAbi: string[]; + logicAddress: Address; +}; + +export type Predeploy = StandardPredeploy | ProxiedPredeploy;