This page showcases advanced Solidity concepts for managing and executing permanent executable payloads on the Ethereum blockchain. Each section includes an explanation and the corresponding Solidity code.
Concept: This feature allows for versioning of payloads, so that different versions of logic can be stored, executed, or rolled back if needed. It ensures that changes can be tracked and different versions of logic can coexist.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
contract VersionedLogicStorage is AccessControl, ReentrancyGuard {
using ECDSA for bytes32;
bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
struct Payload {
bytes data;
bool executed;
}
mapping(uint256 => mapping(uint256 => Payload)) public versionedPayloads;
mapping(uint256 => uint256) public latestVersion;
uint256 public payloadCount;
event LogicStored(address indexed executor, uint256 payloadIndex, uint256 version);
event LogicExecuted(address indexed executor, uint256 payloadIndex, uint256 version);
constructor() {
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
_setupRole(EXECUTOR_ROLE, msg.sender);
}
function storeLogic(bytes memory payload, uint256 version) public onlyExecutor nonReentrant {
require(version > latestVersion[payloadCount], "Version must be greater than the latest");
versionedPayloads[payloadCount][version] = Payload({
data: payload,
executed: false
});
latestVersion[payloadCount] = version;
emit LogicStored(msg.sender, payloadCount, version);
payloadCount++;
}
function executeLogic(uint256 index, uint256 version, bytes memory signature) public nonReentrant {
Payload storage payload = versionedPayloads[index][version];
require(!payload.executed, "Payload already executed");
// Verify the payload integrity using the signature
bytes32 messageHash = keccak256(abi.encodePacked(payload.data));
address signer = messageHash.toEthSignedMessageHash().recover(signature);
require(hasRole(EXECUTOR_ROLE, signer), "Invalid signature");
// Execute the payload
(bool success, ) = address(this).delegatecall(payload.data);
require(success, "Execution failed");
// Mark as executed
payload.executed = true;
emit LogicExecuted(msg.sender, index, version);
}
function getPayload(uint256 index, uint256 version) public view returns (bytes memory) {
return versionedPayloads[index][version].data;
}
}
Concept: Different roles can have varying permissions for injecting, executing, or managing payloads. This adds an additional layer of access control, ensuring that only authorized users can perform specific actions.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
contract RoleBasedLogicStorage is AccessControl, ReentrancyGuard {
using ECDSA for bytes32;
bytes32 public constant INJECTOR_ROLE = keccak256("INJECTOR_ROLE");
bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
struct Payload {
bytes data;
bool executed;
}
mapping(uint256 => Payload) public payloads;
uint256 public payloadCount;
event LogicStored(address indexed injector, uint256 payloadIndex);
event LogicExecuted(address indexed executor, uint256 payloadIndex);
constructor() {
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
_setupRole(INJECTOR_ROLE, msg.sender);
_setupRole(EXECUTOR_ROLE, msg.sender);
}
function storeLogic(bytes memory payload) public onlyRole(INJECTOR_ROLE) nonReentrant {
payloads[payloadCount] = Payload({
data: payload,
executed: false
});
emit LogicStored(msg.sender, payloadCount);
payloadCount++;
}
function executeLogic(uint256 index, bytes memory signature) public onlyRole(EXECUTOR_ROLE) nonReentrant {
Payload storage payload = payloads[index];
require(!payload.executed, "Payload already executed");
// Verify the payload integrity using the signature
bytes32 messageHash = keccak256(abi.encodePacked(payload.data));
address signer = messageHash.toEthSignedMessageHash().recover(signature);
require(hasRole(EXECUTOR_ROLE, signer), "Invalid signature");
// Execute the payload
(bool success, ) = address(this).delegatecall(payload.data);
require(success, "Execution failed");
// Mark as executed
payload.executed = true;
emit LogicExecuted(msg.sender, index);
}
function getPayload(uint256 index) public view returns (bytes memory) {
return payloads[index].data;
}
}
Concept: Integrate oracles to fetch off-chain data to trigger or validate the execution of payloads. This allows dynamic interaction with real-world events and conditions.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
contract OracleIntegratedLogicStorage is AccessControl, ReentrancyGuard {
AggregatorV3Interface internal priceFeed;
bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
mapping(uint256 => bytes) public payloads;
uint256 public payloadCount;
event LogicStored(address indexed executor, bytes payload);
event LogicExecuted(address indexed executor, uint256 payloadIndex);
constructor(address _priceFeed) {
priceFeed = AggregatorV3Interface(_priceFeed);
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
_setupRole(EXECUTOR_ROLE, msg.sender);
}
function storeLogic(bytes memory payload) public onlyRole(EXECUTOR_ROLE) nonReentrant {
payloads[payloadCount] = payload;
emit LogicStored(msg.sender, payload);
payloadCount++;
}
function executeLogic(uint256 index) public onlyRole(EXECUTOR_ROLE) nonReentrant {
require(payloadCount > index, "Invalid index");
// Fetch the current price
(,int256 price,,,) = priceFeed.latestRoundData();
// Condition: only execute if the price is above a certain threshold
require(price > 2000 * 10 ** 8, "Price condition not met");
// Execute the payload
(bool success, ) = address(this).delegatecall(payloads[index]);
require(success, "Execution failed");
emit LogicExecuted(msg.sender, index);
}
function getPayload(uint256 index) public view returns (bytes memory) {
return payloads[index];
}
}
Concept: Require multiple signatures from different addresses before executing a payload. This increases security by distributing trust and ensuring consensus.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
contract MultiSigLogicStorage is AccessControl, ReentrancyGuard {
using ECDSA for bytes32;
bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
struct Payload {
bytes data;
uint256 requiredSignatures;
mapping(address => bool) signatures;
uint256 signatureCount;
bool executed;
}
mapping(uint256 => Payload) public payloads;
uint256 public payloadCount;
event LogicStored(address indexed executor, uint256 payloadIndex);
event SignatureAdded(address indexed signer, uint256 payloadIndex);
event LogicExecuted(address indexed executor, uint256 payloadIndex);
constructor() {
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
_setupRole(EXECUTOR_ROLE, msg.sender);
}
function storeLogic(bytes memory payload, uint256 requiredSignatures) public onlyRole(EXECUTOR_ROLE) nonReentrant {
payloads[payloadCount].data = payload;
payloads[payloadCount].requiredSignatures = requiredSignatures;
emit LogicStored(msg.sender, payloadCount);
payloadCount++;
}
function addSignature(uint256 index, bytes memory signature) public onlyRole(EXECUTOR_ROLE) nonReentrant {
Payload storage payload = payloads[index];
require(!payload.signatures[msg.sender], "Signature already added");
// Verify the signature
bytes32 messageHash = keccak256(abi.encodePacked(payload.data));
address signer = messageHash.toEthSignedMessageHash().recover(signature);
require(hasRole(EXECUTOR_ROLE, signer), "Invalid signature");
payload.signatures[msg.sender] = true;
payload.signatureCount++;
emit SignatureAdded(msg.sender, index);
}
function executeLogic(uint256 index) public onlyRole(EXECUTOR_ROLE) nonReentrant {
Payload storage payload = payloads[index];
require(payload.signatureCount >= payload.requiredSignatures, "Not enough signatures");
require(!payload.executed, "Payload already executed");
// Execute the payload
(bool success, ) = address(this).delegatecall(payload.data);
require(success, "Execution failed");
payload.executed = true;
emit LogicExecuted(msg.sender, index);
}
function getPayload(uint256 index) public view returns (bytes memory) {
return payloads[index].data;
}
}
Concept: Introduce a time-lock mechanism where payloads can only be executed after a specific block number or timestamp. This ensures that certain actions are delayed until a predefined time.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract TimeLockedLogicStorage is AccessControl, ReentrancyGuard {
bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
struct Payload {
bytes data;
uint256 unlockTime;
bool executed;
}
mapping(uint256 => Payload) public payloads;
uint256 public payloadCount;
event LogicStored(address indexed executor, uint256 payloadIndex, uint256 unlockTime);
event LogicExecuted(address indexed executor, uint256 payloadIndex);
constructor() {
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
_setupRole(EXECUTOR_ROLE, msg.sender);
}
function storeLogic(bytes memory payload, uint256 unlockTime) public onlyRole(EXECUTOR_ROLE) nonReentrant {
require(unlockTime > block.timestamp, "Unlock time must be in the future");
payloads[payloadCount] = Payload({
data: payload,
unlockTime: unlockTime,
executed: false
});
emit LogicStored(msg.sender, payloadCount, unlockTime);
payloadCount++;
}
function executeLogic(uint256 index) public onlyRole(EXECUTOR_ROLE) nonReentrant {
Payload storage payload = payloads[index];
require(block.timestamp >= payload.unlockTime, "Unlock time not reached");
require(!payload.executed, "Payload already executed");
// Execute the payload
(bool success, ) = address(this).delegatecall(payload.data);
require(success, "Execution failed");
payload.executed = true;
emit LogicExecuted(msg.sender, index);
}
function getPayload(uint256 index) public view returns (bytes memory) {
return payloads[index].data;
}
}