Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions contracts/core/BatchGuardProcessor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/// @title BatchGuardProcessor
/// @notice Batch processing contract using calldata parameters for external functions.
/// @dev All external functions use `calldata` instead of `memory` for reference types
/// to avoid unnecessary memory allocation and calldata-to-memory copying.

error InvalidLength();
error ZeroAddress();
error BatchTooLarge();
error OperationFailed();

contract BatchGuardProcessor {
struct TransferRequest {
address from;
address to;
uint256 amount;
}

struct BatchResult {
bool success;
uint256 gasUsed;
string reason;
}

mapping(address => uint256) public balances;
uint256 public totalProcessed;

event BatchProcessed(uint256 count, uint256 totalGas);
event TransferCompleted(address from, address to, uint256 amount);

uint256 public constant MAX_BATCH_SIZE = 100;

/// @notice Process a batch of transfers using calldata (not memory).
/// @param requests Array of transfer requests stored in calldata.
/// @return results Array of batch results.
function processBatch(
calldata TransferRequest[] requests
) external returns (BatchResult[] memory results) {
if (requests.length == 0) revert InvalidLength();
if (requests.length > MAX_BATCH_SIZE) revert BatchTooLarge();

results = new BatchResult[](requests.length);
uint256 startGas = gasleft();

for (uint256 i = 0; i < requests.length; i++) {
calldata TransferRequest memory req = requests[i];

if (req.from == address(0) || req.to == address(0)) {
results[i] = BatchResult(false, 0, "Zero address");
continue;
}

if (balances[req.from] < req.amount) {
results[i] = BatchResult(false, 0, "Insufficient balance");
continue;
}

uint256 gasBefore = gasleft();
balances[req.from] -= req.amount;
balances[req.to] += req.amount;
uint256 gasAfter = gasleft();

totalProcessed++;
results[i] = BatchResult(true, gasBefore - gasAfter, "");
emit TransferCompleted(req.from, req.to, req.amount);
}

emit BatchProcessed(requests.length, startGas - gasleft());
}

/// @notice Validate a batch of addresses using calldata.
/// @param addresses Array of addresses to validate.
/// @return valid Array of booleans indicating validity.
function validateAddresses(
calldata address[] addresses
) external view returns (bool[] memory valid) {
valid = new bool[](addresses.length);
for (uint256 i = 0; i < addresses.length; i++) {
valid[i] = addresses[i] != address(0);
}
}

/// @notice Compute checksums for a batch of amounts using calldata.
/// @param amounts Array of amounts.
/// @return checksums Array of keccak256 checksums.
function computeChecksums(
calldata uint256[] amounts
) external view returns (bytes32[] memory checksums) {
checksums = new bytes32[](amounts.length);
for (uint256 i = 0; i < amounts.length; i++) {
checksums[i] = keccak256(abi.encode(amounts[i], i));
}
}

/// @notice Deposit funds.
function deposit() external payable {
balances[msg.sender] += msg.value;
}
}
81 changes: 81 additions & 0 deletions contracts/core/GasGuardRouter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/// @title GasGuardRouter
/// @notice Main router contract for GasGuard protocol using custom errors.
/// @dev Replaces all require() strings with custom error selectors for gas savings.

error Unauthorized();
error InvalidAmount();
error ZeroAddress();
error AlreadyInitialized();
error RequestNotFound();
error RequestAlreadyCompleted();
error InsufficientBalance();
error OperationFailed();

contract GasGuardRouter {
address public admin;
bool public initialized;

mapping(address => uint256) public balances;
mapping(bytes32 => bool) public processedRequests;

event Deposit(address indexed user, uint256 amount);
event Withdrawal(address indexed user, uint256 amount);
event RequestProcessed(bytes32 indexed requestId);

modifier onlyAdmin() {
if (msg.sender != admin) revert Unauthorized();
_;
}

/// @notice Initialize the router.
/// @param _admin The admin address.
function initialize(address _admin) external {
if (initialized) revert AlreadyInitialized();
if (_admin == address(0)) revert ZeroAddress();

admin = _admin;
initialized = true;
}

/// @notice Deposit funds.
function deposit() external payable {
if (msg.value == 0) revert InvalidAmount();

balances[msg.sender] += msg.value;
emit Deposit(msg.sender, msg.value);
}

/// @notice Withdraw funds.
/// @param amount Amount to withdraw.
function withdraw(uint256 amount) external {
if (amount == 0) revert InvalidAmount();
if (balances[msg.sender] < amount) revert InsufficientBalance();

balances[msg.sender] -= amount;
(bool success, ) = msg.sender.call{value: amount}("");
if (!success) revert OperationFailed();

emit Withdrawal(msg.sender, amount);
}

/// @notice Process a request with a unique ID.
/// @param requestId Unique request identifier.
/// @param amount Processing amount.
function processRequest(bytes32 requestId, uint256 amount) external onlyAdmin {
if (requestId == bytes32(0)) revert InvalidAmount();
if (processedRequests[requestId]) revert RequestAlreadyCompleted();
if (amount == 0) revert InvalidAmount();

processedRequests[requestId] = true;
emit RequestProcessed(requestId);
}

/// @notice Get balance of a user.
function getBalance(address user) external view returns (uint256) {
if (user == address(0)) revert ZeroAddress();
return balances[user];
}
}
104 changes: 104 additions & 0 deletions contracts/core/GasGuardVault.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/// @title GasGuardVault
/// @notice Vault contract for GasGuard using custom errors throughout.

error Unauthorized();
error InvalidAmount();
error ZeroAddress();
error VaultNotInitialized();
error InsufficientBalance();
error WithdrawalFailed();
error AlreadyPaused();
error NotPaused();

contract GasGuardVault {
address public admin;
address public feeRecipient;
uint256 public feeBps;
bool public initialized;
bool public paused;

mapping(address => uint256) public deposits;
uint256 public totalDeposits;

event Deposited(address indexed user, uint256 amount);
event Withdrawn(address indexed user, uint256 amount, uint256 fee);
event FeesUpdated(uint256 newFeeBps);
event Paused();
event Unpaused();

modifier onlyAdmin() {
if (msg.sender != admin) revert Unauthorized();
_;
}

modifier whenNotPaused() {
if (paused) revert AlreadyPaused();
_;
}

modifier whenPaused() {
if (!paused) revert NotPaused();
_;
}

function initialize(address _admin, address _feeRecipient, uint256 _feeBps) external {
if (initialized) revert AlreadyPaused();
if (_admin == address(0) || _feeRecipient == address(0)) revert ZeroAddress();
if (_feeBps > 10000) revert InvalidAmount();

admin = _admin;
feeRecipient = _feeRecipient;
feeBps = _feeBps;
initialized = true;
}

function deposit() external payable whenNotPaused {
if (msg.value == 0) revert InvalidAmount();

deposits[msg.sender] += msg.value;
totalDeposits += msg.value;
emit Deposited(msg.sender, msg.value);
}

function withdraw(uint256 amount) external whenNotPaused {
if (amount == 0) revert InvalidAmount();
if (deposits[msg.sender] < amount) revert InsufficientBalance();

uint256 fee = amount * feeBps / 10000;
uint256 netAmount = amount - fee;

deposits[msg.sender] -= amount;
totalDeposits -= amount;

(bool success, ) = msg.sender.call{value: netAmount}("");
if (!success) revert WithdrawalFailed();

if (fee > 0) {
(bool feeSuccess, ) = feeRecipient.call{value: fee}("");
if (!feeSuccess) revert WithdrawalFailed();
}

emit Withdrawn(msg.sender, netAmount, fee);
}

function pause() external onlyAdmin {
if (paused) revert AlreadyPaused();
paused = true;
emit Paused();
}

function unpause() external onlyAdmin {
if (!paused) revert NotPaused();
paused = false;
emit Unpaused();
}

function updateFee(uint256 _feeBps) external onlyAdmin {
if (_feeBps > 10000) revert InvalidAmount();
feeBps = _feeBps;
emit FeesUpdated(_feeBps);
}
}
108 changes: 108 additions & 0 deletions contracts/dispatcher/HighVolumeDispatcher.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/// @title HighVolumeDispatcher
/// @notice Optimized function dispatcher with high-frequency selectors at the top.
/// @dev Demonstrates how function selector ordering affects dispatch gas cost.
/// Solidity sorts selectors alphabetically; this contract manually orders
/// the dispatch tree so the most-called functions are checked first.
///
/// Selector computation:
/// process(address,uint256) → bytes4(keccak256("process(address,uint256)"))
/// getStatus(bytes32) → bytes4(keccak256("getStatus(bytes32)"))
/// deposit() → bytes4(keccak256("deposit()"))
///
/// In a manually-optimized dispatcher, we'd arrange checks so the most frequent
/// function is compared first, saving ~10 gas per call on average.

error InvalidSelector();
error Unauthorized();
error InvalidAmount();
error NotProcessed();

contract HighVolumeDispatcher {
address public admin;
mapping(address => uint256) public balances;
mapping(bytes32 => bool) public processed;
mapping(bytes32 => uint256) public results;

event Processed(bytes32 indexed id, address indexed user, uint256 amount);
event Deposited(address indexed user, uint256 amount);

// Function selectors for reference (computed at compile time):
// deposit() = 0xd0e30db0
// process(address,uint256) = 0x9c4e469e (example)
// getStatus(bytes32) = 0x... (example)
// batchProcess(address[],uint256[]) = 0x... (example)

modifier onlyAdmin() {
if (msg.sender != admin) revert Unauthorized();
_;
}

constructor() {
admin = msg.sender;
}

/// @notice High-frequency: deposit XLM.
/// This should be the first selector checked in an optimized dispatcher.
function deposit() external payable {
if (msg.value == 0) revert InvalidAmount();
balances[msg.sender] += msg.value;
emit Deposited(msg.sender, msg.value);
}

/// @notice High-frequency: process a request.
/// This should be the second selector checked.
function process(
address user,
uint256 amount
) external onlyAdmin {
if (user == address(0)) revert Unauthorized();
if (amount == 0) revert InvalidAmount();

balances[user] += amount;
bytes32 id = keccak256(abi.encode(user, amount, block.timestamp));
processed[id] = true;
results[id] = amount;

emit Processed(id, user, amount);
}

/// @notice Medium-frequency: get status of a request.
function getStatus(bytes32 requestId) external view returns (uint256) {
if (!processed[requestId]) revert NotProcessed();
return results[requestId];
}

/// @notice Low-frequency: batch process multiple requests.
function batchProcess(
address[] calldata users,
uint256[] calldata amounts
) external onlyAdmin {
require(users.length == amounts.length, "Length mismatch");
for (uint256 i = 0; i < users.length; i++) {
if (users[i] != address(0) && amounts[i] > 0) {
balances[users[i]] += amounts[i];
bytes32 id = keccak256(abi.encode(users[i], amounts[i], block.timestamp, i));
processed[id] = true;
results[id] = amounts[i];
emit Processed(id, users[i], amounts[i]);
}
}
}

/// @notice Get balance.
function getBalance(address user) external view returns (uint256) {
return balances[user];
}

/// @dev Fallback to revert with invalid selector.
fallback() external {
revert InvalidSelector();
}

receive() external payable {
balances[msg.sender] += msg.value;
}
}
Loading
Loading