diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/ExampleSolidityTest.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/ExampleSolidityTest.java new file mode 100644 index 0000000000..301ad0fe37 --- /dev/null +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/ExampleSolidityTest.java @@ -0,0 +1,227 @@ +/* + * Copyright Consensys Software Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package net.consensys.linea.zktracer; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.math.BigInteger; +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; + +import net.consensys.linea.testing.SolidityUtils; +import net.consensys.linea.testing.ToyAccount; +import net.consensys.linea.testing.ToyExecutionEnvironmentV2; +import net.consensys.linea.testing.ToyTransaction; +import net.consensys.linea.testing.generated.FrameworkEntrypoint; +import net.consensys.linea.testing.generated.TestSnippet_Events; +import net.consensys.linea.testing.generated.TestStorage; +import org.apache.tuweni.bytes.Bytes; +import org.hyperledger.besu.crypto.KeyPair; +import org.hyperledger.besu.crypto.SECP256K1; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; +import org.hyperledger.besu.evm.log.Log; +import org.junit.jupiter.api.Test; +import org.web3j.abi.EventEncoder; +import org.web3j.abi.FunctionEncoder; +import org.web3j.abi.datatypes.DynamicArray; +import org.web3j.abi.datatypes.Function; +import org.web3j.abi.datatypes.generated.Uint256; + +public class ExampleSolidityTest { + + @Test + void testWithFrameworkEntrypoint() { + KeyPair keyPair = new SECP256K1().generateKeyPair(); + Address senderAddress = Address.extract(Hash.hash(keyPair.getPublicKey().getEncodedBytes())); + + ToyAccount senderAccount = + ToyAccount.builder().balance(Wei.fromEth(1)).nonce(5).address(senderAddress).build(); + + ToyAccount frameworkEntrypointAccount = + ToyAccount.builder() + .address(Address.fromHexString("0x22222")) + .balance(Wei.ONE) + .nonce(5) + .code(SolidityUtils.getContractByteCode(FrameworkEntrypoint.class)) + .build(); + + ToyAccount snippetAccount = + ToyAccount.builder() + .address(Address.fromHexString("0x11111")) + .balance(Wei.ONE) + .nonce(6) + .code(SolidityUtils.getContractByteCode(TestSnippet_Events.class)) + .build(); + + Function snippetFunction = + new Function( + TestSnippet_Events.FUNC_EMITDATANOINDEXES, + List.of(new Uint256(BigInteger.valueOf(123456))), + Collections.emptyList()); + + FrameworkEntrypoint.ContractCall snippetContractCall = + new FrameworkEntrypoint.ContractCall( + /*Address*/ snippetAccount.getAddress().toHexString(), + /*calldata*/ Bytes.fromHexStringLenient(FunctionEncoder.encode(snippetFunction)) + .toArray(), + /*gasLimit*/ BigInteger.ZERO, + /*value*/ BigInteger.ZERO, + /*callType*/ BigInteger.ZERO); + + List contractCalls = List.of(snippetContractCall); + + Function frameworkEntryPointFunction = + new Function( + FrameworkEntrypoint.FUNC_EXECUTECALLS, + List.of(new DynamicArray<>(FrameworkEntrypoint.ContractCall.class, contractCalls)), + Collections.emptyList()); + Bytes txPayload = + Bytes.fromHexStringLenient(FunctionEncoder.encode(frameworkEntryPointFunction)); + + Transaction tx = + ToyTransaction.builder() + .sender(senderAccount) + .to(frameworkEntrypointAccount) + .payload(txPayload) + .keyPair(keyPair) + .build(); + + Consumer resultValidator = + (TransactionProcessingResult result) -> { + // One event from the snippet + // One event from the framework entrypoint about contract call + assertEquals(result.getLogs().size(), 2); + for (Log log : result.getLogs()) { + String logTopic = log.getTopics().getFirst().toHexString(); + if (EventEncoder.encode(TestSnippet_Events.DATANOINDEXES_EVENT).equals(logTopic)) { + TestSnippet_Events.DataNoIndexesEventResponse response = + TestSnippet_Events.getDataNoIndexesEventFromLog(SolidityUtils.fromBesuLog(log)); + assertEquals(response.singleInt, BigInteger.valueOf(123456)); + } else if (EventEncoder.encode(FrameworkEntrypoint.CALLEXECUTED_EVENT) + .equals(logTopic)) { + FrameworkEntrypoint.CallExecutedEventResponse response = + FrameworkEntrypoint.getCallExecutedEventFromLog(SolidityUtils.fromBesuLog(log)); + assertTrue(response.isSuccess); + assertEquals(response.destination, snippetAccount.getAddress().toHexString()); + } else { + fail(); + } + } + }; + + ToyExecutionEnvironmentV2.builder() + .accounts(List.of(senderAccount, frameworkEntrypointAccount, snippetAccount)) + .transaction(tx) + .testValidator(resultValidator) + .build() + .run(); + } + + @Test + void testSnippetIndependently() { + KeyPair keyPair = new SECP256K1().generateKeyPair(); + Address senderAddress = Address.extract(Hash.hash(keyPair.getPublicKey().getEncodedBytes())); + + ToyAccount senderAccount = + ToyAccount.builder().balance(Wei.fromEth(1)).nonce(5).address(senderAddress).build(); + + ToyAccount contractAccount = + ToyAccount.builder() + .address(Address.fromHexString("0x11111")) + .balance(Wei.ONE) + .nonce(6) + .code(SolidityUtils.getContractByteCode(TestSnippet_Events.class)) + .build(); + + Function function = + new Function( + TestSnippet_Events.FUNC_EMITDATANOINDEXES, + List.of(new Uint256(BigInteger.valueOf(123456))), + Collections.emptyList()); + String encodedFunction = FunctionEncoder.encode(function); + Bytes txPayload = Bytes.fromHexStringLenient(encodedFunction); + + Transaction tx = + ToyTransaction.builder() + .sender(senderAccount) + .to(contractAccount) + .payload(txPayload) + .keyPair(keyPair) + .build(); + + Consumer resultValidator = + (TransactionProcessingResult result) -> { + assertEquals(result.getLogs().size(), 1); + TestSnippet_Events.DataNoIndexesEventResponse response = + TestSnippet_Events.getDataNoIndexesEventFromLog( + SolidityUtils.fromBesuLog(result.getLogs().getFirst())); + assertEquals(response.singleInt, BigInteger.valueOf(123456)); + }; + + ToyExecutionEnvironmentV2.builder() + .accounts(List.of(senderAccount, contractAccount)) + .transaction(tx) + .testValidator(resultValidator) + .build() + .run(); + } + + @Test + void testContractNotRelatedToTestingFramework() { + KeyPair senderkeyPair = new SECP256K1().generateKeyPair(); + Address senderAddress = + Address.extract(Hash.hash(senderkeyPair.getPublicKey().getEncodedBytes())); + + ToyAccount senderAccount = + ToyAccount.builder().balance(Wei.fromEth(1)).nonce(5).address(senderAddress).build(); + + ToyAccount contractAccount = + ToyAccount.builder() + .address(Address.fromHexString("0x11111")) + .balance(Wei.ONE) + .nonce(6) + .code(SolidityUtils.getContractByteCode(TestStorage.class)) + .build(); + + Function function = + new Function( + TestStorage.FUNC_STORE, + List.of(new Uint256(BigInteger.valueOf(3))), + Collections.emptyList()); + String encodedFunction = FunctionEncoder.encode(function); + Bytes txPayload = Bytes.fromHexStringLenient(encodedFunction); + Transaction tx = + ToyTransaction.builder() + .sender(senderAccount) + .to(contractAccount) + .payload(txPayload) + .keyPair(senderkeyPair) + .build(); + + ToyExecutionEnvironmentV2.builder() + .accounts(List.of(senderAccount, contractAccount)) + .transaction(tx) + .build() + .run(); + } +} diff --git a/testing/build.gradle b/testing/build.gradle index 0a9983ebd5..e583448658 100644 --- a/testing/build.gradle +++ b/testing/build.gradle @@ -1,5 +1,9 @@ +import org.web3j.solidity.gradle.plugin.EVMVersion +import org.web3j.solidity.gradle.plugin.OutputComponent + plugins { id "common-plugins" + id "org.web3j" version "4.12.2" } apply from: rootProject.file("gradle/corset.gradle") @@ -34,3 +38,19 @@ dependencies { implementation 'org.mockito:mockito-junit-jupiter' implementation 'org.assertj:assertj-core' } + +nodeSetup { + mustRunAfter(spotlessBash, spotlessGroovy, spotlessSol) +} + + + +solidity { + optimize = false + prettyJson = true + evmVersion = EVMVersion.LONDON +} + +web3j { + generatedPackageName = "net.consensys.linea.testing.generated" +} diff --git a/testing/src/main/java/net/consensys/linea/testing/ReplayExecutionEnvironment.java b/testing/src/main/java/net/consensys/linea/testing/ReplayExecutionEnvironment.java index ffdae20b32..bd54af5a07 100644 --- a/testing/src/main/java/net/consensys/linea/testing/ReplayExecutionEnvironment.java +++ b/testing/src/main/java/net/consensys/linea/testing/ReplayExecutionEnvironment.java @@ -85,6 +85,11 @@ public class ReplayExecutionEnvironment { */ private final boolean txResultChecking; + /** + * Setting this to true will disable the clique consensus protocol for parsing coinbase address + * from block header. This is needed for manual tests like the Multi Block tests which do not have + * encoded coinbase address in the block header. + */ @Builder.Default private final boolean useCoinbaseAddressFromBlockHeader = false; private final ZkTracer zkTracer = new ZkTracer(); diff --git a/testing/src/main/java/net/consensys/linea/testing/SolidityUtils.java b/testing/src/main/java/net/consensys/linea/testing/SolidityUtils.java new file mode 100644 index 0000000000..3cf36815aa --- /dev/null +++ b/testing/src/main/java/net/consensys/linea/testing/SolidityUtils.java @@ -0,0 +1,81 @@ +/* + * Copyright Consensys Software Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package net.consensys.linea.testing; + +import java.net.URL; +import java.util.Iterator; +import java.util.Map; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.tuweni.bytes.Bytes; +import org.hyperledger.besu.evm.log.LogTopic; +import org.web3j.protocol.core.methods.response.Log; +import org.web3j.tx.Contract; + +public class SolidityUtils { + + private static final ObjectMapper objectMapper = new ObjectMapper(); + private static final ClassLoader classLoader = SolidityUtils.class.getClassLoader(); + + /** + * The solidity contracts under testing/src/main/solidity are compiled using the solidity plugin + * and then the web3j plugin generates java wrapper classes for each solidity contract. This + * method reads the output of the solidity compiler output file and returns the deployed or + * runtime bytecode for solidity contract. + * + * @param contractClass: The web3j wrapper class that is autogenerated from the solidity contract + * @return The depolyed or runtime bytecode for the solidity contract + */ + public static Bytes getContractByteCode(Class contractClass) { + String contractResourcePath = "solidity/%s.json".formatted(contractClass.getSimpleName()); + URL contractResourceURL = classLoader.getResource(contractResourcePath); + try { + JsonNode jsonRoot = objectMapper.readTree(contractResourceURL); + Iterator> contracts = jsonRoot.get("contracts").fields(); + while (contracts.hasNext()) { + Map.Entry contract = contracts.next(); + if (contract.getKey().contains(contractClass.getSimpleName())) { + return Bytes.fromHexStringLenient(contract.getValue().get("bin-runtime").asText()); + } + } + } catch (Exception e) { + throw new RuntimeException(e); + } + throw new RuntimeException("Could not find contract bytecode"); + } + + /** + * Creates a web3j Log object from the Besu Log object. Web3j Log object can use used which web3j + * APIs to parse events in the log. + * + * @param log The besu log object + * @return The web3j log object + */ + public static Log fromBesuLog(org.hyperledger.besu.evm.log.Log log) { + return new Log( + false, + "", + "", + "", + "", + "", + log.getLogger().toHexString(), + log.getData().toHexString(), + "", + log.getTopics().stream().map(LogTopic::toHexString).toList()); + } +} diff --git a/testing/src/main/solidity/DynamicBytecode.yul b/testing/src/main/solidity/DynamicBytecode.yul new file mode 100644 index 0000000000..4cae19dfc3 --- /dev/null +++ b/testing/src/main/solidity/DynamicBytecode.yul @@ -0,0 +1,61 @@ +object "DynamicBytecode" { + code { + // return the bytecode of the contract + datacopy(0x00, dataoffset("runtime"), datasize("runtime")) + return(0x00, datasize("runtime")) + } + + object "runtime" { + code { + switch selector() + case 0xa770741d // Write() + { + write() + } + + case 0x97deb47b // Read() + { + // store the ID to memory at 0x00 + mstore(0x00, sload(0x00)) + + return (0x00,0x20) + } + + default { + // if the function signature sent does not match any + // of the contract functions, revert + revert(0, 0) + } + + + function write() { + // take no inputs, push the 1234...EF 32 byte integer onto the stack + // add 0 as the slot key to the stack + // store the key/value + verbatim_0i_0o(hex"7f0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF600055") + } + + // Return the function selector: the first 4 bytes of the call data + function selector() -> s { + s := div(calldataload(0), 0x100000000000000000000000000000000000000000000000000000000) + } + + // Implementation of the require statement from Solidity + function require(condition) { + if iszero(condition) { revert(0, 0) } + } + + // Check if the calldata has the correct number of params + function checkParamLength(len) { + require(eq(calldatasize(), add(4, mul(32, len)))) + } + + // Transfer ether to the caller address + function transfer(amount) { + if iszero(call(gas(), caller(), amount, 0, 0, 0, 0)) { + revert(0,0) + } + } + } + } +} \ No newline at end of file diff --git a/testing/src/main/solidity/ExecuteStepsFromContractCode.sol b/testing/src/main/solidity/ExecuteStepsFromContractCode.sol new file mode 100644 index 0000000000..292cc9a7f6 --- /dev/null +++ b/testing/src/main/solidity/ExecuteStepsFromContractCode.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {TestingFrameworkStorageLayout} from "./TestingFrameworkStorageLayout.sol"; +import {TestingBase} from "./TestingBase.sol"; + +/** + * @notice Sample contract that reads data from a contract's code as bytes, converts to external calls and executes them. + */ +contract ExecuteStepsFromContractCode is + TestingFrameworkStorageLayout, + TestingBase +{ + // Compile steps into ContractCall array + // Call encodeCallsToContract with the array getting the address back for storage + // In a function or externally you can call getContractBasedStepsAndExecute with that address +} diff --git a/testing/src/main/solidity/FrameworkEntrypoint.sol b/testing/src/main/solidity/FrameworkEntrypoint.sol new file mode 100644 index 0000000000..74df96bf16 --- /dev/null +++ b/testing/src/main/solidity/FrameworkEntrypoint.sol @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {TestingFrameworkStorageLayout} from "./TestingFrameworkStorageLayout.sol"; +import {TestingBase} from "./TestingBase.sol"; +import {TestingFrameworkEvents} from "./TestingFrameworkEvents.sol"; + +/** + * @notice Framework Entrypoint that inherits storage layout and functions for execution. + */ +contract FrameworkEntrypoint is TestingFrameworkStorageLayout, TestingBase { + /// @dev - this is inherited function executeCalls(ContractCall[] memory _contractCalls) public payable virtual; +} + +/* + Scenario: + - Deploy Solidity Framework entrypoint ( Addr_0 ) + - Deploy Yul based contract with firstInt storage manipulation ( Addr_1 ) - snippet demonstrating verbatim bytecode + [Addr_1,0xa770741d,0,0,0] + ["0xE2A247187aFB847fC36070DccF2c76bA8237d378",0xa770741d,0,0,0] + - Deploy Solidity contract with secondInt storage manipulation ( Addr_2 ) - demonstrating code snippets - delegateable storage adjustments + [Addr_2,0x2792dcc80000000000000000000000000000000000000000000000000db4da5f7ef412b1,0,0,0] + ["0xfD4331c96496A0FD6De3cb18d7CA52BDf0A6EE88",0x2792dcc80000000000000000000000000000000000000000000000000db4da5f7ef412b1,0,0,0] + + - Deploy Solidity Contract (Encode and write commands) to set first int and set second int ( Addr_3 ) - delegatecalls - demonstrating the ability to put function commands in contract code location for later use to simplify outer call requests + [[Addr_1,0xa770741d,0,0,0],[Addr_2,0x2792dcc80000000000000000000000000000000000000000000000000db4da5f7ef412b1,0,0,0]] + [["0xE2A247187aFB847fC36070DccF2c76bA8237d378",0xa770741d,0,0,0], ["0xfD4331c96496A0FD6De3cb18d7CA52BDf0A6EE88",0x2792dcc80000000000000000000000000000000000000000000000000db4da5f7ef412b1,0,0,0]] + Addr_3 = 0xE9F535514219F04198880bAf8c02BEEb4688c5B9 + + - Get a predetermined address for a Create2 contract (SelfDestructingDeeperCalls) that has a self destruct mechanism and a function on itself that can be called (Deployer = Framework Addr_0, but could be anything) + - deployer = 0x81cf3A4AF9895d551b728Dc62af5B51117bCdA49 + - bytecode = 0x6080604052611813806100136000396000f3fe6080604052600436106100c65760003560e01c8063aae16a701161007f578063c4992dd511610059578063c4992dd514610256578063c564c63014610293578063e4f2355a146102be578063fc14f287146102da576100cd565b8063aae16a70146101b1578063aed9777e146101dc578063c0243a2814610219576100cd565b8063348b2afb146100cf5780633f5a0bdd146100ff5780634300409b1461012857806346a7d021146101535780638507a8f01461017e578063a30d978a14610195576100cd565b366100cd57005b005b6100e960048036038101906100e49190610a54565b610317565b6040516100f69190610af1565b60405180910390f35b34801561010b57600080fd5b5061012660048036038101906101219190610b4a565b61036d565b005b34801561013457600080fd5b5061013d6103bd565b60405161014a9190610b90565b60405180910390f35b34801561015f57600080fd5b506101686103c3565b6040516101759190610bc6565b60405180910390f35b34801561018a57600080fd5b506101936103d6565b005b6101af60048036038101906101aa9190610c0d565b6103f3565b005b3480156101bd57600080fd5b506101c6610422565b6040516101d39190610af1565b60405180910390f35b3480156101e857600080fd5b5061020360048036038101906101fe9190610c3a565b610448565b6040516102109190610af1565b60405180910390f35b34801561022557600080fd5b50610240600480360381019061023b9190610ca9565b61048e565b60405161024d9190610af1565b60405180910390f35b34801561026257600080fd5b5061027d60048036038101906102789190610d1e565b6104d5565b60405161028a9190610ddd565b60405180910390f35b34801561029f57600080fd5b506102a861050d565b6040516102b59190610b90565b60405180910390f35b6102d860048036038101906102d39190610fbc565b610513565b005b3480156102e657600080fd5b5061030160048036038101906102fc9190611060565b61069b565b60405161030e9190610af1565b60405180910390f35b6000348383516020850183f591508161032f57600080fd5b507fcf78cf0d6f3d8371e1075c69c492ab4ec5d8cf23a1a239b6a51a1d00be7ca3128160405161035f9190610af1565b60405180910390a192915050565b7f3ab1d7915d663a46c292b8f01ac13567c748cff5213cb3652695882b5f9b2e0f3060405161039c9190610af1565b60405180910390a18073ffffffffffffffffffffffffffffffffffffffff16ff5b60005481565b600260009054906101000a900460ff1681565b6001600260006101000a81548160ff021916908315150217905550565b60006104008260006104d5565b80602001905181019061041391906112b9565b905061041e81610513565b5050565b600260019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600060ff60f81b8483858051906020012060405160200161046c94939291906113b8565b6040516020818303038152906040528051906020012060001c90509392505050565b600081518060401b6bfe61000180600a3d393df3000161fffe8211840152600b8101601584016000f09150816104cc5763301164256000526004601cfd5b80835250919050565b6060604051905064ffffffffff6001843b03166021810183601f8401863c808310838203028252815160400182016040525092915050565b60015481565b60005b81518110156106975760008083838151811061053557610534611406565b5b6020026020010151604001511461056a5782828151811061055957610558611406565b5b60200260200101516040015161056c565b5a5b9050600061060084848151811061058657610585611406565b5b6020026020010151600001518585815181106105a5576105a4611406565b5b602002602001015160200151848787815181106105c5576105c4611406565b5b6020026020010151606001516105f88989815181106105e7576105e6611406565b5b6020026020010151608001516106cf565b63ffffffff16565b90503073ffffffffffffffffffffffffffffffffffffffff1684848151811061062c5761062b611406565b5b60200260200101516000015173ffffffffffffffffffffffffffffffffffffffff168215157f74501cd9bf7c785863dd834ae290e0772ddbc338f2c12ac0ce5ff3b5563492c2866040516106809190610b90565b60405180910390a482806001019350505050610516565b5050565b60006106c783836040516020016106b3929190611737565b60405160208183030381529060405261048e565b905092915050565b6108ba600060028111156106e6576106e5611563565b5b8260028111156106f9576106f8611563565b5b03610708576107499050610744565b6001600281111561071c5761071b611563565b5b82600281111561072f5761072e611563565b5b0361073e576107c39050610744565b61084090505b919050565b6000808573ffffffffffffffffffffffffffffffffffffffff1684866040516107729190611797565b6000604051808303818686f4925050503d80600081146107ae576040519150601f19603f3d011682016040523d82523d6000602084013e6107b3565b606091505b5050905080915050949350505050565b6000808573ffffffffffffffffffffffffffffffffffffffff168484876040516107ed9190611797565b600060405180830381858888f193505050503d806000811461082b576040519150601f19603f3d011682016040523d82523d6000602084013e610830565b606091505b5050905080915050949350505050565b6000808573ffffffffffffffffffffffffffffffffffffffff1684866040516108699190611797565b6000604051808303818686fa925050503d80600081146108a5576040519150601f19603f3d011682016040523d82523d6000602084013e6108aa565b606091505b5050905080915050949350505050565b6108c26117ae565b565b6000604051905090565b600080fd5b600080fd5b6000819050919050565b6108eb816108d8565b81146108f657600080fd5b50565b600081359050610908816108e2565b92915050565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61096182610918565b810181811067ffffffffffffffff821117156109805761097f610929565b5b80604052505050565b60006109936108c4565b905061099f8282610958565b919050565b600067ffffffffffffffff8211156109bf576109be610929565b5b6109c882610918565b9050602081019050919050565b82818337600083830152505050565b60006109f76109f2846109a4565b610989565b905082815260208101848484011115610a1357610a12610913565b5b610a1e8482856109d5565b509392505050565b600082601f830112610a3b57610a3a61090e565b5b8135610a4b8482602086016109e4565b91505092915050565b60008060408385031215610a6b57610a6a6108ce565b5b6000610a79858286016108f9565b925050602083013567ffffffffffffffff811115610a9a57610a996108d3565b5b610aa685828601610a26565b9150509250929050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610adb82610ab0565b9050919050565b610aeb81610ad0565b82525050565b6000602082019050610b066000830184610ae2565b92915050565b6000610b1782610ab0565b9050919050565b610b2781610b0c565b8114610b3257600080fd5b50565b600081359050610b4481610b1e565b92915050565b600060208284031215610b6057610b5f6108ce565b5b6000610b6e84828501610b35565b91505092915050565b6000819050919050565b610b8a81610b77565b82525050565b6000602082019050610ba56000830184610b81565b92915050565b60008115159050919050565b610bc081610bab565b82525050565b6000602082019050610bdb6000830184610bb7565b92915050565b610bea81610ad0565b8114610bf557600080fd5b50565b600081359050610c0781610be1565b92915050565b600060208284031215610c2357610c226108ce565b5b6000610c3184828501610bf8565b91505092915050565b600080600060608486031215610c5357610c526108ce565b5b6000610c6186828701610bf8565b935050602084013567ffffffffffffffff811115610c8257610c816108d3565b5b610c8e86828701610a26565b9250506040610c9f868287016108f9565b9150509250925092565b600060208284031215610cbf57610cbe6108ce565b5b600082013567ffffffffffffffff811115610cdd57610cdc6108d3565b5b610ce984828501610a26565b91505092915050565b610cfb81610b77565b8114610d0657600080fd5b50565b600081359050610d1881610cf2565b92915050565b60008060408385031215610d3557610d346108ce565b5b6000610d4385828601610bf8565b9250506020610d5485828601610d09565b9150509250929050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610d98578082015181840152602081019050610d7d565b60008484015250505050565b6000610daf82610d5e565b610db98185610d69565b9350610dc9818560208601610d7a565b610dd281610918565b840191505092915050565b60006020820190508181036000830152610df78184610da4565b905092915050565b600067ffffffffffffffff821115610e1a57610e19610929565b5b602082029050602081019050919050565b600080fd5b600080fd5b600080fd5b60038110610e4757600080fd5b50565b600081359050610e5981610e3a565b92915050565b600060a08284031215610e7557610e74610e30565b5b610e7f60a0610989565b90506000610e8f84828501610bf8565b600083015250602082013567ffffffffffffffff811115610eb357610eb2610e35565b5b610ebf84828501610a26565b6020830152506040610ed384828501610d09565b6040830152506060610ee784828501610d09565b6060830152506080610efb84828501610e4a565b60808301525092915050565b6000610f1a610f1584610dff565b610989565b90508083825260208201905060208402830185811115610f3d57610f3c610e2b565b5b835b81811015610f8457803567ffffffffffffffff811115610f6257610f6161090e565b5b808601610f6f8982610e5f565b85526020850194505050602081019050610f3f565b5050509392505050565b600082601f830112610fa357610fa261090e565b5b8135610fb3848260208601610f07565b91505092915050565b600060208284031215610fd257610fd16108ce565b5b600082013567ffffffffffffffff811115610ff057610fef6108d3565b5b610ffc84828501610f8e565b91505092915050565b600080fd5b60008083601f8401126110205761101f61090e565b5b8235905067ffffffffffffffff81111561103d5761103c611005565b5b60208301915083602082028301111561105957611058610e2b565b5b9250929050565b60008060208385031215611077576110766108ce565b5b600083013567ffffffffffffffff811115611095576110946108d3565b5b6110a18582860161100a565b92509250509250929050565b6000815190506110bc81610be1565b92915050565b60006110d56110d0846109a4565b610989565b9050828152602081018484840111156110f1576110f0610913565b5b6110fc848285610d7a565b509392505050565b600082601f8301126111195761111861090e565b5b81516111298482602086016110c2565b91505092915050565b60008151905061114181610cf2565b92915050565b60008151905061115681610e3a565b92915050565b600060a0828403121561117257611171610e30565b5b61117c60a0610989565b9050600061118c848285016110ad565b600083015250602082015167ffffffffffffffff8111156111b0576111af610e35565b5b6111bc84828501611104565b60208301525060406111d084828501611132565b60408301525060606111e484828501611132565b60608301525060806111f884828501611147565b60808301525092915050565b600061121761121284610dff565b610989565b9050808382526020820190506020840283018581111561123a57611239610e2b565b5b835b8181101561128157805167ffffffffffffffff81111561125f5761125e61090e565b5b80860161126c898261115c565b8552602085019450505060208101905061123c565b5050509392505050565b600082601f8301126112a05761129f61090e565b5b81516112b0848260208601611204565b91505092915050565b6000602082840312156112cf576112ce6108ce565b5b600082015167ffffffffffffffff8111156112ed576112ec6108d3565b5b6112f98482850161128b565b91505092915050565b60007fff0000000000000000000000000000000000000000000000000000000000000082169050919050565b6000819050919050565b61134961134482611302565b61132e565b82525050565b60008160601b9050919050565b60006113678261134f565b9050919050565b60006113798261135c565b9050919050565b61139161138c82610ad0565b61136e565b82525050565b6000819050919050565b6113b26113ad826108d8565b611397565b82525050565b60006113c48287611338565b6001820191506113d48286611380565b6014820191506113e482856113a1565b6020820191506113f482846113a1565b60208201915081905095945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082825260208201905092915050565b6000819050919050565b600061145f6020840184610bf8565b905092915050565b61147081610ad0565b82525050565b600080fd5b600080fd5b600080fd5b600080833560016020038436030381126114a2576114a1611480565b5b83810192508235915060208301925067ffffffffffffffff8211156114ca576114c9611476565b5b6001820236038313156114e0576114df61147b565b5b509250929050565b600082825260208201905092915050565b600061150583856114e8565b93506115128385846109d5565b61151b83610918565b840190509392505050565b60006115356020840184610d09565b905092915050565b61154681610b77565b82525050565b600061155b6020840184610e4a565b905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600381106115a3576115a2611563565b5b50565b60008190506115b482611592565b919050565b60006115c4826115a6565b9050919050565b6115d4816115b9565b82525050565b600060a083016115ed6000840184611450565b6115fa6000860182611467565b506116086020840184611485565b858303602087015261161b8382846114f9565b9250505061162c6040840184611526565b611639604086018261153d565b506116476060840184611526565b611654606086018261153d565b50611662608084018461154c565b61166f60808601826115cb565b508091505092915050565b600061168683836115da565b905092915050565b60008235600160a0038336030381126116aa576116a9611480565b5b82810191505092915050565b6000602082019050919050565b60006116cf8385611435565b9350836020840285016116e184611446565b8060005b878110156117255784840389526116fc828461168e565b611706858261167a565b9450611711836116b6565b925060208a019950506001810190506116e5565b50829750879450505050509392505050565b600060208201905081810360008301526117528184866116c3565b90509392505050565b600081905092915050565b600061177182610d5e565b61177b818561175b565b935061178b818560208601610d7a565b80840191505092915050565b60006117a38284611766565b915081905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052605160045260246000fdfea26469706673582212200f3fa68781f69e5bd49d185d0bf7eb81cc25814462ba7c8723d8a711a3bbc69064736f6c63430008130033 + + salt = 0x0000000000000000000000000000000000000000000000000000000000000001 + + expected_address = 0xA8865E8852437aF01B557a5f2B5A430E24462b85 + + - Deploy Solidity Create2 create contract ( Addr_4 ) - same data as previous step to demonstrate repeatable address usage + - address = 0xA8865E8852437aF01B557a5f2B5A430E24462b85 (confirm) + + - Execute the following calls on the framework (replace addresses and combine into one) + - Call Addr_4 to call Addr_2's commands - delegatecall (sets firstInt and secondInt) + [Addr_4,0xa30d978a000000000000000000000000E9F535514219F04198880bAf8c02BEEb4688c5B9,0,0,0] + // replace 0xA8865E8852437aF01B557a5f2B5A430E24462b85 + ["0xA8865E8852437aF01B557a5f2B5A430E24462b85", 0xa30d978a000000000000000000000000E9F535514219F04198880bAf8c02BEEb4688c5B9,0,0,0] + + - Call Addr_4 to executeCalls with a call to self + // replace 0xA8865E8852437aF01B557a5f2B5A430E24462b85 - including the bytes + ["0xA8865E8852437aF01B557a5f2B5A430E24462b85",0xe4f2355a000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000A8865E8852437aF01B557a5f2B5A430E24462b8500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000048507a8f000000000000000000000000000000000000000000000000000000000,0,0,0] + + - Call Addr_4 to selfDestruct - call + [Addr_4,0x3f5a0bdd0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4,0,0,1] + // replace 0xA8865E8852437aF01B557a5f2B5A430E24462b85 + ["0xA8865E8852437aF01B557a5f2B5A430E24462b85", 0x3f5a0bdd0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4,0,0,1] - CALL VS. DELEGATECALL + + COMBINED: + [["0xA8865E8852437aF01B557a5f2B5A430E24462b85", 0xa30d978a000000000000000000000000E9F535514219F04198880bAf8c02BEEb4688c5B9,0,0,0],["0xA8865E8852437aF01B557a5f2B5A430E24462b85",0xe4f2355a000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000A8865E8852437aF01B557a5f2B5A430E24462b8500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000048507a8f000000000000000000000000000000000000000000000000000000000,0,0,0],["0xA8865E8852437aF01B557a5f2B5A430E24462b85", 0x3f5a0bdd0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4,0,0,1]] + + // EXPECT: + - SET firstInt + - SET secontInt + - SET selfReferenceCallExecuted + - Self destruction + + prove recreation. + */ diff --git a/testing/src/main/solidity/SelfDestructingDeeperCalls.sol b/testing/src/main/solidity/SelfDestructingDeeperCalls.sol new file mode 100644 index 0000000000..4d3f16a46c --- /dev/null +++ b/testing/src/main/solidity/SelfDestructingDeeperCalls.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {TestingFrameworkStorageLayout} from "./TestingFrameworkStorageLayout.sol"; +import {TestingBase} from "./TestingBase.sol"; + +/** + * @notice Contract to test self referencing and multi-level calls. + */ +contract SelfDestructingDeeperCalls is + TestingFrameworkStorageLayout, + TestingBase +{ + constructor() payable {} + + function setSelfReferencedCallExecuted() public { + selfReferencedCallExecuted = true; + } + + fallback() external payable {} + + receive() external payable {} +} diff --git a/testing/src/main/solidity/TestSnippet_Create2.sol b/testing/src/main/solidity/TestSnippet_Create2.sol new file mode 100644 index 0000000000..5accaaa7b6 --- /dev/null +++ b/testing/src/main/solidity/TestSnippet_Create2.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {TestingFrameworkStorageLayout} from "./TestingFrameworkStorageLayout.sol"; +import {TestingBase} from "./TestingBase.sol"; + +/** + * @notice Contract to test Create2 if needed. + */ +contract TestSnippet_Create2 is TestingFrameworkStorageLayout, TestingBase {} diff --git a/testing/src/main/solidity/TestSnippet_Events.sol b/testing/src/main/solidity/TestSnippet_Events.sol new file mode 100644 index 0000000000..705848d3f0 --- /dev/null +++ b/testing/src/main/solidity/TestSnippet_Events.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {TestingFrameworkEvents} from "./TestingFrameworkEvents.sol"; + +/** + * @notice Event emitting contract for purely testing events. + */ +contract TestSnippet_Events is TestingFrameworkEvents { + // 0xb0bc1f76 emitNoData() + function emitNoData() public { + emit NoData(); + } + + // 0xbc5b9381000000000000000000000000000000000000000000000000000000000001e240 emitDataNoIndexes(123456) + function emitDataNoIndexes(uint256 _singleInt) public { + emit DataNoIndexes(_singleInt); + } + + // 0xbd6f0b2f000000000000000000000000000000000000000000000000000000002f091180 emitOneIndex(789123456) + function emitOneIndex(uint256 singleInt) public { + emit OneIndex(singleInt); + } + + // 0x0f1087cc000000000000000000000000000000000000000000000000000000002f09118000000000000000000000000000000000000000000000000000000000040bf02d emitTwoIndexes(789123456,67891245) + function emitTwoIndexes(uint256 _firstInt, uint256 _secondInt) public { + emit TwoIndexes(_firstInt, _secondInt); + } + + // 0x4497a35e000000000000000000000000000000000000000000000000000000002f09118000000000000000000000000000000000000000000000000000000000040bf02d000000000000000000000000000000000000000000000000000000003ade68b1 emitThreeIndexes(789123456,67891245,987654321) + function emitThreeIndexes( + uint256 _firstInt, + uint256 _secondInt, + uint256 _thirdInt + ) public { + emit ThreeIndexes(_firstInt, _secondInt, _thirdInt); + } + + fallback() external payable {} + + receive() external payable {} +} diff --git a/testing/src/main/solidity/TestSnippet_StorageSetting.sol b/testing/src/main/solidity/TestSnippet_StorageSetting.sol new file mode 100644 index 0000000000..ab04d591ff --- /dev/null +++ b/testing/src/main/solidity/TestSnippet_StorageSetting.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import {TestingFrameworkStorageLayout} from "./TestingFrameworkStorageLayout.sol"; +import {TestingBase} from "./TestingBase.sol"; + +contract TestSnippet_StorageSetting is TestingFrameworkStorageLayout { + // use 0xc35824a8000000000000000000000000000000000000000000000000000000000001e240 on the framework executeCalls to set `188992` as a test + function setSecondInt(uint256 _val) public { + secondInt = _val; + } + + // use 0x2792dcc80000000000000000000000000000000000000000000000000db4da5f7ef412b1 setSecondIntBasedOnFirstInt(987654321987654321) + function setSecondIntBasedOnFirstInt(uint256 _secondInt) public { + if (firstInt == 0) { + revert("notSet"); + } + + secondInt = _secondInt; + } + + fallback() external payable {} + + receive() external payable {} +} diff --git a/testing/src/main/solidity/TestStorage.sol b/testing/src/main/solidity/TestStorage.sol new file mode 100644 index 0000000000..fd7bb1eee1 --- /dev/null +++ b/testing/src/main/solidity/TestStorage.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.8.2 <0.9.0; + +/** + * @title TestStorage + * @dev Store & retrieve value in a variable + * @custom:dev-run-script ./scripts/deploy_with_ethers.ts + */ +contract TestStorage { + + uint256 number; + + /** + * @dev Store value in variable + * @param num value to store + */ + function store(uint256 num) public { + number = num; + } + + /** + * @dev Return value + * @return value of 'number' + */ + function retrieve() public view returns (uint256){ + return number; + } +} \ No newline at end of file diff --git a/testing/src/main/solidity/TestingBase.sol b/testing/src/main/solidity/TestingBase.sol new file mode 100644 index 0000000000..7068a45c57 --- /dev/null +++ b/testing/src/main/solidity/TestingBase.sol @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +/** + * @notice Shared base contract for functions and events relating to call execution and contract creation. + */ +abstract contract TestingBase { + /// @dev The event indicating call status, the index in the collection and the address being called. + /// @param isSuccess The success bool. + /// @param callIndex The index of the call in the collection. + /// @param destination The destination contract address where the call is made to. + event CallExecuted( + bool indexed isSuccess, + uint256 callIndex, + address indexed destination, + address indexed executedFrom + ); + + /// @dev The contract created event; + /// @param contractAddress The contract address. + event ContractCreated(address contractAddress); + + /// @dev The contract destroyed event; + /// @param contractAddress The contract address. + event ContractDestroyed(address contractAddress); + + /// @dev The Call types for external calls. + /// @dev Default is delegate call (value==0), so be aware. + enum CallType { + DELEGATE_CALL, + CALL, + STATIC_CALL + } + + /** + * @notice Struct describing an external call to execute. + * _contract The contract address to call. + * _calldata The function signature and parameters encoded to send to the _contract address. + * _gasLimit The gas to send with the call - 0 sends all. + * _callType The call type to use - call, static or delegatecall. + */ + struct ContractCall { + address _contract; + bytes _calldata; + uint256 _gasLimit; + uint256 _value; + CallType _callType; + } + + /** + * @notice Iterates over and executes `ContractCall`s. + * @dev CallExecuted is emitting indicating success or failure of the particular call. + * @dev If the gas limit is zero gasleft() will be used to send all the gas. + * @param _contractCalls The calls to encode and store. + */ + function executeCalls( + ContractCall[] memory _contractCalls + ) public payable virtual { + for (uint256 i; i < _contractCalls.length; ) { + uint256 gasToUse = _contractCalls[i]._gasLimit == 0 + ? gasleft() + : _contractCalls[i]._gasLimit; + + /// Handle failures by emitting events. + /// @dev function is retrieved and executed - some params aren't used on some of the functions. + bool success = getCallFunction(_contractCalls[i]._callType)( + _contractCalls[i]._contract, + _contractCalls[i]._calldata, + gasToUse, + _contractCalls[i]._value + ); + + emit CallExecuted( + success, + i, + _contractCalls[i]._contract, + address(this) + ); + + unchecked { + i++; + } + } + } + + /** + * @notice Reads data from a contract's code as bytes, converts to external calls and executes them. + * @param _contractWithSteps The address of the contract containing data to read. + */ + function getContractBasedStepsAndExecute( + address _contractWithSteps + ) public payable { + ContractCall[] memory steps = abi.decode( + readContractAsData(_contractWithSteps, 0), + (ContractCall[]) + ); + + executeCalls(steps); + } + + /** + * @notice Determines call function type based on enum value. + * @param _callType Enum value for the different external call types. + * @return function to execute. + */ + function getCallFunction( + CallType _callType + ) + internal + pure + returns ( + function(address, bytes memory, uint256, uint256) returns (bool) + ) + { + if (_callType == CallType.DELEGATE_CALL) { + return doDelegateCall; + } + + if (_callType == CallType.CALL) { + return doCall; + } + + return doStaticCall; + } + + /** + * @notice Executes a delegatecall. + * @param _address The address to call. + * @param _calldata The calldata to pass for function selection and parameters. + * @param _gas The gas to use. + * @return The success or failure boolean. + */ + function doDelegateCall( + address _address, + bytes memory _calldata, + uint256 _gas, + uint256 + ) internal returns (bool) { + (bool success, ) = _address.delegatecall{gas: _gas}(_calldata); + return success; + } + + /** + * @notice Executes a normal external call. + * @param _address The address to call. + * @param _calldata The calldata to pass for function selection and parameters. + * @param _gas The gas to use. + * @param _value The value to pass to the contract. + * @return The success or failure boolean. + */ + function doCall( + address _address, + bytes memory _calldata, + uint256 _gas, + uint256 _value + ) internal returns (bool) { + (bool success, ) = _address.call{gas: _gas, value: _value}(_calldata); + return success; + } + + /** + * @notice Executes a normal static call. + * @param _address The address to call. + * @param _calldata The calldata to pass for function selection and parameters. + * @param _gas The gas to use. + * @return The success or failure boolean. + */ + function doStaticCall( + address _address, + bytes memory _calldata, + uint256 _gas, + uint256 + ) internal view returns (bool) { + (bool success, ) = _address.staticcall{gas: _gas}(_calldata); + return success; + } + + /** + * @notice Encodes `ContractCall`s and writes `data` into the bytecode of a storage contract and returns its address. + * @param _contractCalls The calls to encode and store. + * @return contractAddress The address of the newly deployed contract containing the data. + */ + function encodeCallsToContract( + ContractCall[] calldata _contractCalls + ) public returns (address contractAddress) { + contractAddress = writeDataAsContract(abi.encode(_contractCalls)); + } + + /** + * @notice Writes `data` into the bytecode of a storage contract and returns its address. + * @dev sourced from Solady (https://github.com/vectorized/solady/blob/main/src/utils/SSTORE2.sol) + * @param _data The data to store. + * @return pointer The address of the newly deployed contract containing the data. + */ + function writeDataAsContract( + bytes memory _data + ) public virtual returns (address pointer) { + /// @solidity memory-safe-assembly + assembly { + let n := mload(_data) // Let `l` be `n + 1`. +1 as we prefix a STOP opcode. + /** + * ---------------------------------------------------+ + * Opcode | Mnemonic | Stack | Memory | + * ---------------------------------------------------| + * 61 l | PUSH2 l | l | | + * 80 | DUP1 | l l | | + * 60 0xa | PUSH1 0xa | 0xa l l | | + * 3D | RETURNDATASIZE | 0 0xa l l | | + * 39 | CODECOPY | l | [0..l): code | + * 3D | RETURNDATASIZE | 0 l | [0..l): code | + * F3 | RETURN | | [0..l): code | + * 00 | STOP | | | + * ---------------------------------------------------+ + * @dev Prefix the bytecode with a STOP opcode to ensure it cannot be called. + * Also PUSH2 is used since max contract size cap is 24,576 bytes which is less than 2 ** 16. + */ + // Do a out-of-gas revert if `n + 1` is more than 2 bytes. + mstore( + add(_data, gt(n, 0xfffe)), + add(0xfe61000180600a3d393df300, shl(0x40, n)) + ) + // Deploy a new contract with the generated creation code. + pointer := create(0, add(_data, 0x15), add(n, 0xb)) + if iszero(pointer) { + mstore(0x00, 0x30116425) // `DeploymentFailed()`. + revert(0x1c, 0x04) + } + mstore(_data, n) // Restore the length of `data`. + } + } + + /** + * @notice Reads data from a contract's code as bytes memory. + * @dev The start parameter for the most part is 0 if the entire data is required. + * @dev sourced from Solady (https://github.com/vectorized/solady/blob/main/src/utils/SSTORE2.sol) + * @param _pointer The address of the contract containing data to read. + * @param _start The offset of the data to read. + */ + function readContractAsData( + address _pointer, + uint256 _start + ) public view virtual returns (bytes memory data) { + /// @solidity memory-safe-assembly + assembly { + data := mload(0x40) + let n := and(sub(extcodesize(_pointer), 0x01), 0xffffffffff) + extcodecopy(_pointer, add(data, 0x1f), _start, add(n, 0x21)) + mstore(data, mul(sub(n, _start), lt(_start, n))) // Store the length. + mstore(0x40, add(data, add(0x40, mload(data)))) // Allocate memory. + } + } + + /** + * @notice Deploys a contract with create 2. + * @param _salt The salt for creating the contract. + * @param _bytecode The bytecode to use in creation. + * @return addr The new contract address. + */ + function deployWithCreate2( + bytes32 _salt, + bytes memory _bytecode + ) public payable returns (address addr) { + assembly { + let value := callvalue() + addr := create2( + value, + add(_bytecode, 0x20), + mload(_bytecode), + _salt + ) + if iszero(addr) { + revert(0, 0) + } + } + + emit ContractCreated(addr); + } + + /** + * @notice Predetermines a Create2 address. + * @param _deployerAddress The deploying contract's address. + * @param _bytecode The bytecode to use in creation. + * @param _salt The salt being used. + * @return addr The expected contract address. + */ + function calculateCreate2Address( + address _deployerAddress, + bytes memory _bytecode, + bytes32 _salt + ) public pure returns (address addr) { + addr = address( + uint160( + uint256( + keccak256( + abi.encodePacked( + bytes1(0xff), + _deployerAddress, + _salt, + keccak256(_bytecode) + ) + ) + ) + ) + ); + } + + /** + * @notice Selfdestructs and sends remaining ETH to a payable address. + * @dev Keep in mind you need to compile and target London EVM version - this doesn't work for repeat addresses on Cancun etc. + * @param _fundAddress The deploying contract's address. + */ + // 0x3f5a0bdd0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4 (replace 5b38da6a701c568545dcfcb03fcb875f56beddc4) + function selfDestruct(address payable _fundAddress) public { + emit ContractDestroyed(address(this)); + selfdestruct(_fundAddress); + } +} diff --git a/testing/src/main/solidity/TestingFrameworkEvents.sol b/testing/src/main/solidity/TestingFrameworkEvents.sol new file mode 100644 index 0000000000..c97e4ea2db --- /dev/null +++ b/testing/src/main/solidity/TestingFrameworkEvents.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +/** + * @notice Shared contract containing framework events. + */ +abstract contract TestingFrameworkEvents { + event NoData(); + event DataNoIndexes(uint256 singleInt); + event OneIndex(uint256 indexed singleInt); + event TwoIndexes(uint256 indexed firstInt, uint256 indexed secondInt); + event ThreeIndexes( + uint256 indexed firstInt, + uint256 indexed secondInt, + uint256 indexed thirdInt + ); +} diff --git a/testing/src/main/solidity/TestingFrameworkStorageLayout.sol b/testing/src/main/solidity/TestingFrameworkStorageLayout.sol new file mode 100644 index 0000000000..d7dc0a2459 --- /dev/null +++ b/testing/src/main/solidity/TestingFrameworkStorageLayout.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +/** + * @notice Shared storage layout for all contracts being delegated to. + * @dev Customize this to your liking for scenarios. + */ +abstract contract TestingFrameworkStorageLayout { + uint256 public firstInt; + uint256 public secondInt; + bool public selfReferencedCallExecuted; + address public createdContract; +}