Files
smom-dbis-138/contracts/mirror/TransactionMirror.sol
defiQUG 1fb7266469 Add Oracle Aggregator and CCIP Integration
- Introduced Aggregator.sol for Chainlink-compatible oracle functionality, including round-based updates and access control.
- Added OracleWithCCIP.sol to extend Aggregator with CCIP cross-chain messaging capabilities.
- Created .gitmodules to include OpenZeppelin contracts as a submodule.
- Developed a comprehensive deployment guide in NEXT_STEPS_COMPLETE_GUIDE.md for Phase 2 and smart contract deployment.
- Implemented Vite configuration for the orchestration portal, supporting both Vue and React frameworks.
- Added server-side logic for the Multi-Cloud Orchestration Portal, including API endpoints for environment management and monitoring.
- Created scripts for resource import and usage validation across non-US regions.
- Added tests for CCIP error handling and integration to ensure robust functionality.
- Included various new files and directories for the orchestration portal and deployment scripts.
2025-12-12 14:57:48 -08:00

274 lines
8.3 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/**
* @title TransactionMirror
* @notice Mirrors Chain-138 transactions to Ethereum Mainnet for Etherscan visibility
* @dev Logs all Chain-138 transactions as events on Mainnet, making them searchable
* and viewable on Etherscan. This provides transparency and auditability.
*/
contract TransactionMirror {
address public admin;
bool public paused;
// Chain-138 chain ID
uint64 public constant CHAIN_138 = 138;
// Maximum batch size to prevent gas limit issues
uint256 public constant MAX_BATCH_SIZE = 100;
// Transaction structure
struct MirroredTransaction {
bytes32 txHash; // Chain-138 transaction hash
address from; // Sender address
address to; // Recipient address (or contract)
uint256 value; // Value transferred
uint256 blockNumber; // Chain-138 block number
uint256 blockTimestamp; // Block timestamp
uint256 gasUsed; // Gas used
bool success; // Transaction success status
bytes data; // Transaction data (if any)
bytes32 indexedHash; // Indexed hash for searchability
}
// Mapping: txHash => MirroredTransaction
mapping(bytes32 => MirroredTransaction) public transactions;
// Array of all mirrored transaction hashes
bytes32[] public mirroredTxHashes;
// Mapping: txHash => bool (replay protection)
mapping(bytes32 => bool) public processed;
// Events (indexed for Etherscan searchability)
event TransactionMirrored(
bytes32 indexed txHash,
address indexed from,
address indexed to,
uint256 value,
uint256 blockNumber,
uint256 blockTimestamp,
uint256 gasUsed,
bool success
);
event BatchTransactionsMirrored(
uint256 count,
uint256 startBlock,
uint256 endBlock
);
event AdminChanged(address indexed newAdmin);
event Paused();
event Unpaused();
modifier onlyAdmin() {
require(msg.sender == admin, "only admin");
_;
}
modifier whenNotPaused() {
require(!paused, "paused");
_;
}
constructor(address _admin) {
require(_admin != address(0), "zero admin");
admin = _admin;
}
/**
* @notice Mirror a single Chain-138 transaction to Mainnet
* @param txHash Chain-138 transaction hash
* @param from Sender address
* @param to Recipient address
* @param value Value transferred
* @param blockNumber Chain-138 block number
* @param blockTimestamp Block timestamp
* @param gasUsed Gas used
* @param success Transaction success status
* @param data Transaction data (optional)
*/
function mirrorTransaction(
bytes32 txHash,
address from,
address to,
uint256 value,
uint256 blockNumber,
uint256 blockTimestamp,
uint256 gasUsed,
bool success,
bytes calldata data
) external onlyAdmin whenNotPaused {
require(txHash != bytes32(0), "invalid hash");
require(!processed[txHash], "already mirrored");
bytes32 indexedHash = keccak256(abi.encodePacked(CHAIN_138, txHash));
transactions[txHash] = MirroredTransaction({
txHash: txHash,
from: from,
to: to,
value: value,
blockNumber: blockNumber,
blockTimestamp: blockTimestamp,
gasUsed: gasUsed,
success: success,
data: data,
indexedHash: indexedHash
});
mirroredTxHashes.push(txHash);
processed[txHash] = true;
emit TransactionMirrored(
txHash,
from,
to,
value,
blockNumber,
blockTimestamp,
gasUsed,
success
);
}
/**
* @notice Mirror multiple Chain-138 transactions in a batch
* @param txHashes Array of transaction hashes
* @param froms Array of sender addresses
* @param tos Array of recipient addresses
* @param values Array of values
* @param blockNumbers Array of block numbers
* @param blockTimestamps Array of timestamps
* @param gasUseds Array of gas used
* @param successes Array of success statuses
* @param datas Array of transaction data
*/
function mirrorBatchTransactions(
bytes32[] calldata txHashes,
address[] calldata froms,
address[] calldata tos,
uint256[] calldata values,
uint256[] calldata blockNumbers,
uint256[] calldata blockTimestamps,
uint256[] calldata gasUseds,
bool[] calldata successes,
bytes[] calldata datas
) external onlyAdmin whenNotPaused {
uint256 count = txHashes.length;
require(count > 0, "empty batch");
require(count <= MAX_BATCH_SIZE, "batch too large");
require(
count == froms.length &&
count == tos.length &&
count == values.length &&
count == blockNumbers.length &&
count == blockTimestamps.length &&
count == gasUseds.length &&
count == successes.length &&
count == datas.length,
"array length mismatch"
);
uint256 startBlock = blockNumbers[0];
uint256 endBlock = blockNumbers[count - 1];
// Process transactions in batches to avoid stack too deep
for (uint256 i = 0; i < count; i++) {
bytes32 txHash = txHashes[i];
require(txHash != bytes32(0), "invalid hash");
require(!processed[txHash], "already mirrored");
bytes32 indexedHash = keccak256(abi.encodePacked(CHAIN_138, txHash));
transactions[txHash] = MirroredTransaction({
txHash: txHash,
from: froms[i],
to: tos[i],
value: values[i],
blockNumber: blockNumbers[i],
blockTimestamp: blockTimestamps[i],
gasUsed: gasUseds[i],
success: successes[i],
data: datas[i],
indexedHash: indexedHash
});
mirroredTxHashes.push(txHash);
processed[txHash] = true;
emit TransactionMirrored(
txHash,
froms[i],
tos[i],
values[i],
blockNumbers[i],
blockTimestamps[i],
gasUseds[i],
successes[i]
);
}
emit BatchTransactionsMirrored(count, startBlock, endBlock);
}
/**
* @notice Get mirrored transaction details
* @param txHash Chain-138 transaction hash
* @return tx Mirrored transaction structure
*/
function getTransaction(bytes32 txHash) external view returns (MirroredTransaction memory tx) {
require(processed[txHash], "not mirrored");
return transactions[txHash];
}
/**
* @notice Check if a transaction is mirrored
* @param txHash Chain-138 transaction hash
* @return true if mirrored
*/
function isMirrored(bytes32 txHash) external view returns (bool) {
return processed[txHash];
}
/**
* @notice Get total number of mirrored transactions
* @return count Number of mirrored transactions
*/
function getMirroredTransactionCount() external view returns (uint256) {
return mirroredTxHashes.length;
}
/**
* @notice Get mirrored transaction hash at index
* @param index Index in mirroredTxHashes array
* @return txHash Transaction hash
*/
function getMirroredTransaction(uint256 index) external view returns (bytes32) {
require(index < mirroredTxHashes.length, "out of bounds");
return mirroredTxHashes[index];
}
/**
* @notice Admin functions
*/
function setAdmin(address newAdmin) external onlyAdmin {
require(newAdmin != address(0), "zero admin");
admin = newAdmin;
emit AdminChanged(newAdmin);
}
function pause() external onlyAdmin {
paused = true;
emit Paused();
}
function unpause() external onlyAdmin {
paused = false;
emit Unpaused();
}
}