diff --git a/src/GovV3Playground.sol b/src/GovV3Playground.sol new file mode 100644 index 000000000..898a59212 --- /dev/null +++ b/src/GovV3Playground.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {WithChainIdValidation, EthereumScript} from './ScriptUtils.sol'; +import {GovV3Helpers, IPayloadsControllerCore} from './GovV3Helpers.sol'; + +contract LetMeJustHaveSome { + string public name = 'some'; +} + +abstract contract WithChainIdValidationAndPayloads is WithChainIdValidation { + function getPayloads() public view virtual returns (bytes[] memory); +} + +abstract contract DeployPayloads is WithChainIdValidationAndPayloads { + function run() external broadcast { + bytes[] memory payloadsCode = getPayloads(); + + // deploy payloads + address[] memory payloadsAddresses = new address[](payloadsCode.length); + for (uint256 i = 0; i < payloadsCode.length; i++) { + payloadsAddresses[i] = GovV3Helpers.deployDeterministic(payloadsCode[i]); + } + + // compose action + IPayloadsControllerCore.ExecutionAction[] + memory actions = new IPayloadsControllerCore.ExecutionAction[](payloadsCode.length); + for (uint256 i = 0; i < payloadsCode.length; i++) { + actions[i] = GovV3Helpers.buildAction(payloadsAddresses[i]); + } + + // register action at payloadsController + GovV3Helpers.createPayload(actions); + } +} + +abstract contract DeployPayloadSimple is DeployPayloads { + bytes public payloadCode; + + constructor(bytes memory code) { + payloadCode = code; + } + + function getPayloads() public view override returns (bytes[] memory) { + bytes[] memory payloadsCode = new bytes[](1); + payloadsCode[0] = payloadCode; + return payloadsCode; + } +} + +contract DeploySomeSimple is + DeployPayloadSimple(type(LetMeJustHaveSome).creationCode), + EthereumScript +{} + +contract DeploySomeMany is DeployPayloads, EthereumScript { + function getPayloads() public view override returns (bytes[] memory) { + bytes[] memory payloadsCode = new bytes[](2); + payloadsCode[0] = type(LetMeJustHaveSome).creationCode; + payloadsCode[1] = type(LetMeJustHaveSome).creationCode; + return payloadsCode; + } +} diff --git a/src/GovV3PlaygroundAdvanced.sol b/src/GovV3PlaygroundAdvanced.sol new file mode 100644 index 000000000..817215f0d --- /dev/null +++ b/src/GovV3PlaygroundAdvanced.sol @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Script} from 'forge-std/Script.sol'; +import {GovV3Helpers, IPayloadsControllerCore, PayloadsControllerUtils} from './GovV3Helpers.sol'; + +abstract contract WithPayloads { + struct ActionsPerChain { + string chainName; + bytes[] actionCode; + } + + function getActions() public view virtual returns (ActionsPerChain[] memory); +} + +abstract contract DeployPayloads is WithPayloads, Script { + function isChainSupported(string memory chain) public view virtual returns (bool); + + function run() external { + ActionsPerChain[] memory actionsPerChain = getActions(); + + for (uint256 i = 0; i < actionsPerChain.length; i++) { + ActionsPerChain memory rawActions = actionsPerChain[i]; + + // if actions belongs to the network we should not deploy, skip + if (!isChainSupported(rawActions.chainName)) continue; + require(rawActions.actionCode.length != 0, 'should be at least one payload action per chain'); + + vm.rpcUrl(rawActions.chainName); + // TODO: after rpc switch we should be checking that chainId is the one we expect, just in case + vm.startBroadcast(); + + // compose actions + IPayloadsControllerCore.ExecutionAction[] + memory composedActions = new IPayloadsControllerCore.ExecutionAction[]( + rawActions.actionCode.length + ); + // deploy payloads + for (uint256 j = 0; j < rawActions.actionCode.length; j++) { + composedActions[j] = GovV3Helpers.buildAction( + GovV3Helpers.deployDeterministic(rawActions.actionCode[j]) + ); + } + + // register actions at payloadsController + GovV3Helpers.createPayload(composedActions); + vm.stopBroadcast(); + } + } +} + +// not so applicable atm, because requires solid multiChan support, but for the good future +abstract contract DeployPayloadsMultiChain is DeployPayloads { + mapping(bytes32 => bool) internal _supportedChain; + + constructor(string[] memory supportedChains) { + for (uint256 i = 0; i < supportedChains.length; i++) { + _supportedChain[keccak256(bytes(supportedChains[i]))] = true; + } + } + + function isChainSupported(string memory chain) public view override returns (bool) { + return _supportedChain[keccak256(bytes(chain))]; + } +} + +abstract contract DeployPayloadsSingleChain is DeployPayloads { + string public supportedChain; + + constructor(string memory chainName) { + supportedChain = chainName; + } + + function isChainSupported(string memory chain) public view override returns (bool) { + return keccak256(bytes(chain)) == keccak256(bytes(supportedChain)); + } +} + +abstract contract DeployPayloadsEthereum is DeployPayloadsSingleChain('ethereum') {} + +abstract contract DeployPayloadsPolygon is DeployPayloadsSingleChain('polygon') {} + +abstract contract CreateProposal is WithPayloads, Script { + string internal _ipfsFilePath; + + // TODO: I would make it more human readable with params: date, name, isMulti(?) and generation of the actual string + constructor(string memory ipfsFilePath) { + _ipfsFilePath = ipfsFilePath; + } + + function run() external { + ActionsPerChain[] memory actionsPerChain = getActions(); + + // create payloads + PayloadsControllerUtils.Payload[] memory payloadsPinned = new PayloadsControllerUtils.Payload[]( + actionsPerChain.length + ); + + for (uint256 i = 0; i < actionsPerChain.length; i++) { + ActionsPerChain memory rawActions = actionsPerChain[i]; + vm.rpcUrl(rawActions.chainName); + + IPayloadsControllerCore.ExecutionAction[] + memory actions = new IPayloadsControllerCore.ExecutionAction[]( + rawActions.actionCode.length + ); + + for (uint256 j = 0; j < rawActions.actionCode.length; j++) { + actions[j] = GovV3Helpers.buildAction(rawActions.actionCode[j]); + } + payloadsPinned[i] = GovV3Helpers._buildPayload(vm, block.chainid, actions); + } + + // create proposal + vm.rpcUrl('ethereum'); + // TODO: after rpc switch we should be checking that chainId is the one we expect, just in case + vm.startBroadcast(); + GovV3Helpers.createProposal(vm, payloadsPinned, GovV3Helpers.ipfsHashFile(vm, _ipfsFilePath)); + vm.stopBroadcast(); + } +} diff --git a/src/GovV3PlaygroundAdvancedExamples.sol b/src/GovV3PlaygroundAdvancedExamples.sol new file mode 100644 index 000000000..5b11bc799 --- /dev/null +++ b/src/GovV3PlaygroundAdvancedExamples.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import './GovV3PlaygroundAdvanced.sol'; + +contract LetMeJustHaveSome { + string public name = 'some'; +} + +contract LetMeJustHaveAnother { + string public name = 'another'; +} + +abstract contract MyPayloads is WithPayloads { + function getActions() public pure override returns (ActionsPerChain[] memory) { + ActionsPerChain[] memory payloads = new ActionsPerChain[](2); + + payloads[0].chainName = 'ethereum'; + payloads[0].actionCode = new bytes[](1); + payloads[0].actionCode[0] = type(LetMeJustHaveSome).creationCode; + + payloads[1].chainName = 'polygon'; + payloads[1].actionCode = new bytes[](1); + payloads[1].actionCode[0] = type(LetMeJustHaveAnother).creationCode; + + return payloads; + } +} + +contract DeploymentComplexEthereum is MyPayloads, DeployPayloadsEthereum {} + +contract DeploymentComplexPolygon is MyPayloads, DeployPayloadsPolygon {} + +// depends on what will be better for generator +contract DeploymentComplexPoly is MyPayloads, DeployPayloadsSingleChain('polygon') { + +} + +// I think it will look way better +// contract ProposalCreationComplex is +// MyPayloads, +// CreateProposal('20240121', 'UpdateStETHAndWETHRiskParamsOnAaveV3EthereumOptimismAndArbitrum') +// {} +contract ProposalCreationComplex is + MyPayloads, + CreateProposal( + 'src/20240121_Multi_UpdateStETHAndWETHRiskParamsOnAaveV3EthereumOptimismAndArbitrum/UpdateStETHAndWETHRiskParamsOnAaveV3EthereumOptimismAndArbitrum.md' + ) +{ + +}