diff --git a/packages/Unknown.pdf b/packages/Unknown.pdf new file mode 100644 index 00000000000..39fa9b1ef66 Binary files /dev/null and b/packages/Unknown.pdf differ diff --git a/packages/protocol/contracts/L1/TaikoData.sol b/packages/protocol/contracts/L1/TaikoData.sol index 8a50cd168f7..b46ba0553de 100644 --- a/packages/protocol/contracts/L1/TaikoData.sol +++ b/packages/protocol/contracts/L1/TaikoData.sol @@ -69,25 +69,20 @@ library TaikoData { bytes data; } - struct ProverAssignment { - address prover; - address feeToken; - TierFee[] tierFees; - uint64 expiry; - uint64 maxBlockId; - uint64 maxProposedIn; - bytes32 metaHash; - bytes signature; + struct HookCall { + address hook; + bytes data; } struct BlockParams { - ProverAssignment assignment; + address assignedProver; bytes32 extraData; bytes32 blobHash; uint24 txListByteOffset; uint24 txListByteSize; bool cacheBlobForReuse; bytes32 parentMetaHash; + HookCall[] hookCalls; } /// @dev Struct containing data only required for proving a block diff --git a/packages/protocol/contracts/L1/TaikoErrors.sol b/packages/protocol/contracts/L1/TaikoErrors.sol index d9e82323950..58c155dc4f2 100644 --- a/packages/protocol/contracts/L1/TaikoErrors.sol +++ b/packages/protocol/contracts/L1/TaikoErrors.sol @@ -15,10 +15,6 @@ abstract contract TaikoErrors { // `L1/libs/*.sol`. error L1_ALREADY_CONTESTED(); error L1_ALREADY_PROVED(); - error L1_ASSIGNMENT_EXPIRED(); - error L1_ASSIGNMENT_INVALID_SIG(); - error L1_ASSIGNMENT_INVALID_PARAMS(); - error L1_ASSIGNMENT_INSUFFICIENT_FEE(); error L1_ASSIGNED_PROVER_NOT_ALLOWED(); error L1_BLOB_FOR_DA_DISABLED(); error L1_BLOB_NOT_FOUND(); @@ -33,13 +29,16 @@ abstract contract TaikoErrors { error L1_INVALID_PARAM(); error L1_INVALID_PAUSE_STATUS(); error L1_INVALID_PROOF(); + error L1_INVALID_PROVER(); error L1_INVALID_TIER(); error L1_INVALID_TRANSITION(); + error L1_LIVENESS_BOND_NOT_RECEIVED(); error L1_NOT_ASSIGNED_PROVER(); error L1_PROPOSER_NOT_EOA(); error L1_PROVING_PAUSED(); - error L1_TIER_NOT_FOUND(); + error L1_RECEIVE_DISABLED(); error L1_TOO_MANY_BLOCKS(); + error L1_TOO_MANY_TIERS(); error L1_TRANSITION_ID_ZERO(); error L1_TRANSITION_NOT_FOUND(); error L1_TXLIST_OFFSET_SIZE(); diff --git a/packages/protocol/contracts/L1/TaikoEvents.sol b/packages/protocol/contracts/L1/TaikoEvents.sol index fa2a7e5818e..bad23df0b40 100644 --- a/packages/protocol/contracts/L1/TaikoEvents.sol +++ b/packages/protocol/contracts/L1/TaikoEvents.sol @@ -19,7 +19,6 @@ abstract contract TaikoEvents { /// @param blockId The ID of the proposed block. /// @param assignedProver The block's assigned prover. /// @param livenessBond The bond in Taiko token from the assigned prover. - /// @param proverFee The fee paid to the assigned prover. /// @param meta The block metadata containing information about the proposed /// block. /// @param depositsProcessed Ether deposits processed. @@ -27,7 +26,6 @@ abstract contract TaikoEvents { uint256 indexed blockId, address indexed assignedProver, uint96 livenessBond, - uint256 proverFee, TaikoData.BlockMetadata meta, TaikoData.EthDeposit[] depositsProcessed ); diff --git a/packages/protocol/contracts/L1/TaikoL1.sol b/packages/protocol/contracts/L1/TaikoL1.sol index 1438e580b3b..a1db1f116f1 100644 --- a/packages/protocol/contracts/L1/TaikoL1.sol +++ b/packages/protocol/contracts/L1/TaikoL1.sol @@ -6,20 +6,13 @@ pragma solidity ^0.8.20; -import "../common/AddressResolver.sol"; import "../common/EssentialContract.sol"; -import "../common/ICrossChainSync.sol"; -import "../common/Proxied.sol"; import "./libs/LibDepositing.sol"; import "./libs/LibProposing.sol"; import "./libs/LibProving.sol"; -import "./libs/LibTaikoToken.sol"; -import "./libs/LibUtils.sol"; import "./libs/LibVerifying.sol"; -import "./TaikoData.sol"; import "./TaikoErrors.sol"; import "./TaikoEvents.sol"; -import "./tiers/ITierProvider.sol"; /// @title TaikoL1 /// @dev Labeled in AddressResolver as "taiko" @@ -29,15 +22,14 @@ import "./tiers/ITierProvider.sol"; /// deployed on L1, it can also be deployed on L2s to create L3s ("inception /// layers"). The contract also handles the deposit and withdrawal of Taiko /// tokens and Ether. +/// This contract doesn't hold any Ether. Ether deposited to L2 are held by the Bridge contract. contract TaikoL1 is EssentialContract, ICrossChainSync, ITierProvider, TaikoEvents, TaikoErrors { TaikoData.State public state; uint256[100] private __gap; - error L1_TOO_MANY_TIERS(); - - /// @dev Fallback function to receive Ether and deposit to Layer 2. + /// @dev Fallback function to receive Ether from Hooks receive() external payable { - depositEtherToL2(address(0)); + if (!_inNonReentrant()) revert L1_RECEIVE_DISABLED(); } /// @notice Initializes the rollup. @@ -67,8 +59,10 @@ contract TaikoL1 is EssentialContract, ICrossChainSync, ITierProvider, TaikoEven ) { TaikoData.Config memory config = getConfig(); + (meta, depositsProcessed) = LibProposing.proposeBlock(state, config, AddressResolver(this), params, txList); + if (!state.slotB.provingPaused && config.maxBlocksToVerifyPerProposal > 0) { LibVerifying.verifyBlocks( state, config, AddressResolver(this), config.maxBlocksToVerifyPerProposal @@ -82,6 +76,8 @@ contract TaikoL1 is EssentialContract, ICrossChainSync, ITierProvider, TaikoEven /// @param input An abi-encoded (BlockMetadata, Transition, TierProof) /// tuple. function proveBlock(uint64 blockId, bytes calldata input) external nonReentrant whenNotPaused { + if (state.slotB.provingPaused) revert L1_PROVING_PAUSED(); + ( TaikoData.BlockMetadata memory meta, TaikoData.Transition memory tran, @@ -91,8 +87,10 @@ contract TaikoL1 is EssentialContract, ICrossChainSync, ITierProvider, TaikoEven if (blockId != meta.id) revert L1_INVALID_BLOCK_ID(); TaikoData.Config memory config = getConfig(); + uint8 maxBlocksToVerify = LibProving.proveBlock(state, config, AddressResolver(this), meta, tran, proof); + if (maxBlocksToVerify > 0) { LibVerifying.verifyBlocks(state, config, AddressResolver(this), maxBlocksToVerify); } @@ -113,22 +111,10 @@ contract TaikoL1 is EssentialContract, ICrossChainSync, ITierProvider, TaikoEven LibProving.pauseProving(state, pause); } - /// @notice Deposit Taiko token to this contract - /// @param amount Amount of Taiko token to deposit. - function depositTaikoToken(uint256 amount) external whenNotPaused { - LibTaikoToken.depositTaikoToken(state, AddressResolver(this), amount); - } - - /// @notice Withdraw Taiko token from this contract - /// @param amount Amount of Taiko token to withdraw. - function withdrawTaikoToken(uint256 amount) external whenNotPaused { - LibTaikoToken.withdrawTaikoToken(state, AddressResolver(this), amount); - } - /// @notice Deposits Ether to Layer 2. /// @param recipient Address of the recipient for the deposited Ether on /// Layer 2. - function depositEtherToL2(address recipient) public payable whenNotPaused { + function depositEtherToL2(address recipient) external payable whenNotPaused { LibDepositing.depositEtherToL2(state, getConfig(), AddressResolver(this), recipient); } diff --git a/packages/protocol/contracts/L1/TaikoToken.sol b/packages/protocol/contracts/L1/TaikoToken.sol index 0a13aacdb16..92148356dea 100644 --- a/packages/protocol/contracts/L1/TaikoToken.sol +++ b/packages/protocol/contracts/L1/TaikoToken.sol @@ -6,13 +6,12 @@ pragma solidity ^0.8.20; +import "lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol"; import "lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/ERC20SnapshotUpgradeable.sol"; -import "lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol"; import "lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/ERC20VotesUpgradeable.sol"; import "../common/EssentialContract.sol"; -import "../common/Proxied.sol"; /// @title TaikoToken /// @dev Labeled in AddressResolver as "taiko_token" diff --git a/packages/protocol/contracts/L1/gov/TaikoGovernor.sol b/packages/protocol/contracts/L1/gov/TaikoGovernor.sol index b4e80e9939b..626ba9a6f38 100644 --- a/packages/protocol/contracts/L1/gov/TaikoGovernor.sol +++ b/packages/protocol/contracts/L1/gov/TaikoGovernor.sol @@ -19,7 +19,7 @@ contract TaikoGovernor is IVotes _token, TimelockController _timelock ) - Governor("MyGovernor") + Governor("TaikoGovernor") GovernorVotes(_token) GovernorVotesQuorumFraction(4) GovernorTimelockControl(_timelock) diff --git a/packages/protocol/contracts/L1/hooks/AssignmentHook.sol b/packages/protocol/contracts/L1/hooks/AssignmentHook.sol new file mode 100644 index 00000000000..b3e312c158f --- /dev/null +++ b/packages/protocol/contracts/L1/hooks/AssignmentHook.sol @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: MIT +// _____ _ _ _ _ +// |_ _|_ _(_) |_____ | | __ _| |__ ___ +// | |/ _` | | / / _ \ | |__/ _` | '_ (_-< +// |_|\__,_|_|_\_\___/ |____\__,_|_.__/__/ + +pragma solidity ^0.8.20; + +import "lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol"; +import "../../common/EssentialContract.sol"; +import "../../libs/LibAddress.sol"; +import "../TaikoData.sol"; +import "../TaikoToken.sol"; +import "./IHook.sol"; + +/// @title AssignmentHook +/// A hook that handles prover assignment varification and fee processing. +contract AssignmentHook is EssentialContract, IHook { + using LibAddress for address; + + struct ProverAssignment { + address feeToken; + uint64 expiry; + uint64 maxBlockId; + uint64 maxProposedIn; + bytes32 metaHash; + TaikoData.TierFee[] tierFees; + bytes signature; + } + + struct Input { + ProverAssignment assignment; + uint256 tip; + } + + // Max gas paying the prover. This should be large enough to prevent the + // worst cases, usually block proposer shall be aware the risks and only + // choose provers that cannot consume too much gas when receiving Ether. + uint256 public constant MAX_GAS_PAYING_PROVER = 200_000; + + event BlockAssigned( + address indexed assignedProver, TaikoData.BlockMetadata meta, ProverAssignment assignment + ); + + error HOOK_ASSIGNMENT_EXPIRED(); + error HOOK_ASSIGNMENT_INVALID_SIG(); + error HOOK_ASSIGNMENT_INSUFFICIENT_FEE(); + error HOOK_TIER_NOT_FOUND(); + + function init(address _addressManager) external initializer { + EssentialContract._init(_addressManager); + } + + function onBlockProposed( + TaikoData.Block memory blk, + TaikoData.BlockMetadata memory meta, + bytes memory data + ) + external + payable + nonReentrant + onlyFromNamed("taiko") + { + Input memory input = abi.decode(data, (Input)); + ProverAssignment memory assignment = input.assignment; + + // Check assignment validity + if ( + block.timestamp > assignment.expiry + || assignment.metaHash != 0 && blk.metaHash != assignment.metaHash + || assignment.maxBlockId != 0 && meta.id > assignment.maxBlockId + || assignment.maxProposedIn != 0 && block.number > assignment.maxProposedIn + ) { + revert HOOK_ASSIGNMENT_EXPIRED(); + } + + // Hash the assignment with the blobHash, this hash will be signed by + // the prover, therefore, we add a string as a prefix. + bytes32 hash = hashAssignment(assignment, msg.sender, meta.blobHash); + + if (!blk.assignedProver.isValidSignature(hash, assignment.signature)) { + revert HOOK_ASSIGNMENT_INVALID_SIG(); + } + + // Send the liveness bond to the Taiko contract + TaikoToken tko = TaikoToken(resolve("taiko_token", false)); + tko.transferFrom(blk.assignedProver, msg.sender, blk.livenessBond); + + // Find the prover fee using the minimal tier + uint256 proverFee = _getProverFee(assignment.tierFees, meta.minTier); + + // The proposer irrevocably pays a fee to the assigned prover, either in + // Ether or ERC20 tokens. + uint256 refund; + if (assignment.feeToken == address(0)) { + if (msg.value < proverFee + input.tip) { + revert HOOK_ASSIGNMENT_INSUFFICIENT_FEE(); + } + + unchecked { + refund = msg.value - proverFee - input.tip; + } + + // Paying Ether + blk.assignedProver.sendEther(proverFee, MAX_GAS_PAYING_PROVER); + } else { + if (msg.value < input.tip) { + revert HOOK_ASSIGNMENT_INSUFFICIENT_FEE(); + } + unchecked { + refund = msg.value - input.tip; + } + // Paying ERC20 tokens + ERC20Upgradeable(assignment.feeToken).transferFrom( + msg.sender, blk.assignedProver, proverFee + ); + } + + // block.coinbase can be address(0) in tests + if (input.tip != 0 && block.coinbase != address(0)) { + address(block.coinbase).sendEther(input.tip); + } + + if (refund != 0) { + msg.sender.sendEther(refund); + } + + emit BlockAssigned(blk.assignedProver, meta, assignment); + } + + function hashAssignment( + ProverAssignment memory assignment, + address taikoAddress, + bytes32 blobHash + ) + public + pure + returns (bytes32) + { + return keccak256( + abi.encode( + "PROVER_ASSIGNMENT", + taikoAddress, + blobHash, + assignment.feeToken, + assignment.expiry, + assignment.maxBlockId, + assignment.maxProposedIn, + assignment.tierFees + ) + ); + } + + function _getProverFee( + TaikoData.TierFee[] memory tierFees, + uint16 tierId + ) + private + pure + returns (uint256) + { + for (uint256 i; i < tierFees.length; ++i) { + if (tierFees[i].tier == tierId) return tierFees[i].fee; + } + revert HOOK_TIER_NOT_FOUND(); + } +} + +/// @title ProxiedAssignmentHook +/// @notice Proxied version of the parent contract. +contract ProxiedAssignmentHook is Proxied, AssignmentHook { } diff --git a/packages/protocol/contracts/L1/hooks/IHook.sol b/packages/protocol/contracts/L1/hooks/IHook.sol new file mode 100644 index 00000000000..abe4539c6a2 --- /dev/null +++ b/packages/protocol/contracts/L1/hooks/IHook.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +// _____ _ _ _ _ +// |_ _|_ _(_) |_____ | | __ _| |__ ___ +// | |/ _` | | / / _ \ | |__/ _` | '_ (_-< +// |_|\__,_|_|_\_\___/ |____\__,_|_.__/__/ + +pragma solidity ^0.8.20; + +import "../TaikoData.sol"; + +/// @title IHook Interface +interface IHook { + function onBlockProposed( + TaikoData.Block memory blk, + TaikoData.BlockMetadata memory meta, + bytes memory data + ) + external + payable; +} diff --git a/packages/protocol/contracts/L1/libs/LibDepositing.sol b/packages/protocol/contracts/L1/libs/LibDepositing.sol index 487fb9b9c01..4eb8b4f4cef 100644 --- a/packages/protocol/contracts/L1/libs/LibDepositing.sol +++ b/packages/protocol/contracts/L1/libs/LibDepositing.sol @@ -9,8 +9,8 @@ pragma solidity ^0.8.20; import "../../common/AddressResolver.sol"; import "../../libs/LibAddress.sol"; import "../../libs/LibMath.sol"; - import "../TaikoData.sol"; +import "../TaikoToken.sol"; /// @title LibDepositing /// @notice A library for handling Ether deposits in the Taiko protocol. diff --git a/packages/protocol/contracts/L1/libs/LibProposing.sol b/packages/protocol/contracts/L1/libs/LibProposing.sol index 8875f29fda0..d187e7d8f0c 100644 --- a/packages/protocol/contracts/L1/libs/LibProposing.sol +++ b/packages/protocol/contracts/L1/libs/LibProposing.sol @@ -7,13 +7,13 @@ pragma solidity ^0.8.20; import "lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol"; -import "../../common/AddressResolver.sol"; import "../../4844/IBlobHashReader.sol"; +import "../../common/AddressResolver.sol"; import "../../libs/LibAddress.sol"; +import "../hooks/IHook.sol"; import "../tiers/ITierProvider.sol"; import "../TaikoData.sol"; import "./LibDepositing.sol"; -import "./LibTaikoToken.sol"; /// @title LibProposing /// @notice A library for handling block proposals in the Taiko protocol. @@ -24,17 +24,11 @@ library LibProposing { // field element has 32 bytes. uint256 public constant MAX_BYTES_PER_BLOB = 4096 * 32; - // Max gas paying the prover. This should be large enough to prevent the - // worst cases, usually block proposer shall be aware the risks and only - // choose provers that cannot consume too much gas when receiving Ether. - uint256 public constant MAX_GAS_PAYING_PROVER = 200_000; - // Warning: Any events defined here must also be defined in TaikoEvents.sol. event BlockProposed( uint256 indexed blockId, address indexed assignedProver, uint96 livenessBond, - uint256 proverFee, TaikoData.BlockMetadata meta, TaikoData.EthDeposit[] depositsProcessed ); @@ -42,16 +36,13 @@ library LibProposing { event BlobCached(bytes32 blobHash); // Warning: Any errors defined here must also be defined in TaikoErrors.sol. - error L1_ASSIGNMENT_EXPIRED(); - error L1_ASSIGNMENT_INVALID_SIG(); - error L1_ASSIGNMENT_INVALID_PARAMS(); - error L1_ASSIGNMENT_INSUFFICIENT_FEE(); error L1_BLOB_FOR_DA_DISABLED(); error L1_BLOB_NOT_FOUND(); error L1_BLOB_NOT_REUSEABLE(); error L1_INVALID_PARAM(); + error L1_INVALID_PROVER(); + error L1_LIVENESS_BOND_NOT_RECEIVED(); error L1_PROPOSER_NOT_EOA(); - error L1_TIER_NOT_FOUND(); error L1_TOO_MANY_BLOCKS(); error L1_TXLIST_OFFSET(); error L1_TXLIST_SIZE(); @@ -74,6 +65,10 @@ library LibProposing { { TaikoData.BlockParams memory params = abi.decode(data, (TaikoData.BlockParams)); + if (params.assignedProver == address(0)) { + revert L1_INVALID_PROVER(); + } + // Taiko, as a Based Rollup, enables permissionless block proposals. // However, if the "proposer" address is set to a non-zero value, we // ensure that only that specific address has the authority to propose @@ -229,32 +224,45 @@ library LibProposing { // verification. // Prover can charge ERC20/NFT as fees; msg.value can be zero. Taiko // doesn't mandate Ether as the only proofing fee. - blk.assignedProver = params.assignment.prover; - - // The assigned prover burns Taiko tokens, referred to as the - // "liveness bond." This bond remains non-refundable to the - // assigned prover under two conditions: if the block's verification - // transition is not the initial one or if it was generated and - // validated by different provers. Instead, a portion of the assignment - // bond serves as a reward for the actual prover. - LibTaikoToken.debitTaikoToken(state, resolver, blk.assignedProver, config.livenessBond); + blk.assignedProver = params.assignedProver; // Increment the counter (cursor) by 1. unchecked { ++state.slotB.numBlocks; } - // Validate the prover assignment, then charge Ether or ERC20 as the - // prover fee based on the block's minTier. - uint256 proverFee = _payProverFeeAndTip( - meta.minTier, meta.blobHash, blk.blockId, blk.metaHash, params.assignment - ); + { + TaikoToken tko = TaikoToken(resolver.resolve("taiko_token", false)); + uint256 tkoBalance = tko.balanceOf(address(this)); + + // Run all hooks. + // Note that address(this).balance has been updated with msg.value, + // prior to any code in this function has been executed. + for (uint256 i; i < params.hookCalls.length; ++i) { + // When a hook is called, all ether in this contract will be send to the hook. + // If the ether sent to the hook is not used entirely, the hook shall send the Ether + // back to this contract for the next hook to use. + // Proposers shall choose use extra hooks wisely. + IHook(params.hookCalls[i].hook).onBlockProposed{ value: address(this).balance }( + blk, meta, params.hookCalls[i].data + ); + } + // Refund Ether + if (address(this).balance != 0) { + msg.sender.sendEther(address(this).balance); + } + + // Check that after hooks, the Taiko Token balance of this contract + // have increased by at least config.livenessBond + if (tko.balanceOf(address(this)) < tkoBalance + config.livenessBond) { + revert L1_LIVENESS_BOND_NOT_RECEIVED(); + } + } emit BlockProposed({ blockId: blk.blockId, assignedProver: blk.assignedProver, livenessBond: config.livenessBond, - proverFee: proverFee, meta: meta, depositsProcessed: depositsProcessed }); @@ -272,91 +280,6 @@ library LibProposing { return state.reusableBlobs[blobHash] + config.blobExpiry > block.timestamp; } - function hashAssignment( - TaikoData.ProverAssignment memory assignment, - address taikoAddress, - bytes32 blobHash - ) - internal - pure - returns (bytes32) - { - return keccak256( - abi.encode( - "PROVER_ASSIGNMENT", - taikoAddress, - blobHash, - assignment.feeToken, - assignment.expiry, - assignment.maxBlockId, - assignment.maxProposedIn, - assignment.tierFees - ) - ); - } - - function _payProverFeeAndTip( - uint16 minTier, - bytes32 blobHash, - uint64 blockId, - bytes32 metaHash, - TaikoData.ProverAssignment memory assignment - ) - private - returns (uint256 proverFee) - { - if (blobHash == 0 || assignment.prover == address(0)) { - revert L1_ASSIGNMENT_INVALID_PARAMS(); - } - - // Check assignment validity - if ( - block.timestamp > assignment.expiry - || assignment.metaHash != 0 && metaHash != assignment.metaHash - || assignment.maxBlockId != 0 && blockId > assignment.maxBlockId - || assignment.maxProposedIn != 0 && block.number > assignment.maxProposedIn - ) { - revert L1_ASSIGNMENT_EXPIRED(); - } - - // Hash the assignment with the blobHash, this hash will be signed by - // the prover, therefore, we add a string as a prefix. - bytes32 hash = hashAssignment(assignment, address(this), blobHash); - - if (!assignment.prover.isValidSignature(hash, assignment.signature)) { - revert L1_ASSIGNMENT_INVALID_SIG(); - } - - // Find the prover fee using the minimal tier - proverFee = _getProverFee(assignment.tierFees, minTier); - - // The proposer irrevocably pays a fee to the assigned prover, either in - // Ether or ERC20 tokens. - uint256 tip; - if (assignment.feeToken == address(0)) { - if (msg.value < proverFee) revert L1_ASSIGNMENT_INSUFFICIENT_FEE(); - - unchecked { - tip = msg.value - proverFee; - } - - // Paying Ether - assignment.prover.sendEther(proverFee, MAX_GAS_PAYING_PROVER); - } else { - tip = msg.value; - - // Paying ERC20 tokens - ERC20Upgradeable(assignment.feeToken).transferFrom( - msg.sender, assignment.prover, proverFee - ); - } - - // block.coinbase can be address(0) in tests - if (tip != 0 && block.coinbase != address(0)) { - address(block.coinbase).sendEther(tip); - } - } - function _isProposerPermitted( TaikoData.SlotB memory slotB, AddressResolver resolver @@ -376,18 +299,4 @@ library LibProposing { address proposer = resolver.resolve("proposer", true); return proposer == address(0) || msg.sender == proposer; } - - function _getProverFee( - TaikoData.TierFee[] memory tierFees, - uint16 tierId - ) - private - pure - returns (uint256) - { - for (uint256 i; i < tierFees.length; ++i) { - if (tierFees[i].tier == tierId) return tierFees[i].fee; - } - revert L1_TIER_NOT_FOUND(); - } } diff --git a/packages/protocol/contracts/L1/libs/LibProving.sol b/packages/protocol/contracts/L1/libs/LibProving.sol index 5e3ad4d23e6..0326c8332b3 100644 --- a/packages/protocol/contracts/L1/libs/LibProving.sol +++ b/packages/protocol/contracts/L1/libs/LibProving.sol @@ -10,7 +10,7 @@ import "../../common/AddressResolver.sol"; import "../tiers/ITierProvider.sol"; import "../verifiers/IVerifier.sol"; import "../TaikoData.sol"; -import "./LibTaikoToken.sol"; +import "../TaikoToken.sol"; import "./LibUtils.sol"; /// @title LibProving @@ -206,6 +206,8 @@ library LibProving { } } + TaikoToken tko = TaikoToken(resolver.resolve("taiko_token", false)); + if (tier.contestBond == 0) { assert(tier.validityBond == 0); // When contestBond is zero for the current tier, it signifies @@ -227,7 +229,7 @@ library LibProving { blk.livenessBond > 0 && proof.data.length == 32 && bytes32(proof.data) == RETURN_LIVENESS_BOND ) { - LibTaikoToken.creditTaikoToken(state, blk.assignedProver, blk.livenessBond); + tko.transfer(blk.assignedProver, blk.livenessBond); blk.livenessBond = 0; } @@ -237,9 +239,7 @@ library LibProving { if (ts.contester != address(0)) { // At this point we know that the contester was right - LibTaikoToken.creditTaikoToken( - state, ts.contester, ts.validityBond / 4 + ts.contestBond - ); + tko.transfer(ts.contester, ts.validityBond / 4 + ts.contestBond); ts.contester = address(0); ts.validityBond = 0; } @@ -278,7 +278,7 @@ library LibProving { if (ts.contester != address(0)) revert L1_ALREADY_CONTESTED(); // Burn the contest bond from the prover. - LibTaikoToken.debitTaikoToken(state, resolver, msg.sender, tier.contestBond); + tko.transferFrom(msg.sender, address(this), tier.contestBond); // We retain the contest bond within the transition, just in // case this configuration is altered to a different value @@ -367,7 +367,7 @@ library LibProving { // Mint the reward and the validity bond and return it to // the previous prover. - LibTaikoToken.creditTaikoToken(state, ts.prover, reward + ts.validityBond); + tko.transfer(ts.prover, reward + ts.validityBond); } else { // In the event that the contester is the winner, half of // the validity bond is designated as the reward, to be @@ -378,11 +378,11 @@ library LibProving { // for the tier-0 transition. Consequently, we only grant a // reward to the contester if it is not a zero-address. if (ts.contester != address(0)) { - LibTaikoToken.creditTaikoToken(state, ts.contester, reward + ts.contestBond); + tko.transfer(ts.contester, reward + ts.contestBond); } else { // The prover is also the contester, so the reward is // sent to him. - LibTaikoToken.creditTaikoToken(state, msg.sender, reward); + tko.transfer(msg.sender, reward); } // Given that the contester emerges as the winner, the @@ -396,11 +396,11 @@ library LibProving { // Reward this prover. // In theory, the reward can also be zero for certain tiers if // their validity bonds are set to zero. - LibTaikoToken.creditTaikoToken(state, msg.sender, reward); + tko.transfer(msg.sender, reward); } // Burn the validity bond from the prover. - LibTaikoToken.debitTaikoToken(state, resolver, msg.sender, tier.validityBond); + tko.transferFrom(msg.sender, address(this), tier.validityBond); // Regardless of whether the previous prover or the contester // emerges as the winner, we consistently erase the contest history @@ -423,9 +423,8 @@ library LibProving { } function pauseProving(TaikoData.State storage state, bool pause) internal { - if (state.slotB.provingPaused == pause) { - revert L1_INVALID_PAUSE_STATUS(); - } + if (state.slotB.provingPaused == pause) revert L1_INVALID_PAUSE_STATUS(); + state.slotB.provingPaused = pause; emit ProvingPaused(pause); } diff --git a/packages/protocol/contracts/L1/libs/LibTaikoToken.sol b/packages/protocol/contracts/L1/libs/LibTaikoToken.sol deleted file mode 100644 index 1bc96856664..00000000000 --- a/packages/protocol/contracts/L1/libs/LibTaikoToken.sol +++ /dev/null @@ -1,89 +0,0 @@ -// SPDX-License-Identifier: MIT -// _____ _ _ _ _ -// |_ _|_ _(_) |_____ | | __ _| |__ ___ -// | |/ _` | | / / _ \ | |__/ _` | '_ (_-< -// |_|\__,_|_|_\_\___/ |____\__,_|_.__/__/ - -pragma solidity ^0.8.20; - -import "../../common/AddressResolver.sol"; -import "../TaikoData.sol"; -import "../TaikoToken.sol"; - -library LibTaikoToken { - event TokenDeposited(uint256 amount); - event TokenWithdrawn(uint256 amount); - event TokenCredited(address to, uint256 amount); - event TokenDebited(address from, uint256 amount); - - error L1_INSUFFICIENT_TOKEN(); - error L1_INVALID_ADDRESS(); - error L1_INVALID_AMOUNT(); - - function depositTaikoToken( - TaikoData.State storage state, - AddressResolver resolver, - uint256 amount - ) - external - { - if (amount == 0) revert L1_INVALID_AMOUNT(); - TaikoToken(resolver.resolve("taiko_token", false)).transferFrom( - msg.sender, address(this), amount - ); - unchecked { - state.tokenBalances[msg.sender] += amount; - } - emit TokenDeposited(amount); - } - - function withdrawTaikoToken( - TaikoData.State storage state, - AddressResolver resolver, - uint256 amount - ) - external - { - if (amount == 0) revert L1_INVALID_AMOUNT(); - if (state.tokenBalances[msg.sender] < amount) { - revert L1_INSUFFICIENT_TOKEN(); - } - // Unchecked is safe per above check - unchecked { - state.tokenBalances[msg.sender] -= amount; - } - - TaikoToken(resolver.resolve("taiko_token", false)).transfer(msg.sender, amount); - - emit TokenWithdrawn(amount); - } - - function creditTaikoToken(TaikoData.State storage state, address to, uint256 amount) internal { - if (amount == 0 || to == address(0)) return; - unchecked { - state.tokenBalances[to] += amount; - } - emit TokenCredited(to, amount); - } - - function debitTaikoToken( - TaikoData.State storage state, - AddressResolver resolver, - address from, - uint256 amount - ) - internal - { - if (amount == 0) return; - if (state.tokenBalances[from] < amount) { - TaikoToken(resolver.resolve("taiko_token", false)).transferFrom( - from, address(this), amount - ); - } else { - unchecked { - state.tokenBalances[from] -= amount; - } - } - emit TokenDebited(from, amount); - } -} diff --git a/packages/protocol/contracts/L1/libs/LibVerifying.sol b/packages/protocol/contracts/L1/libs/LibVerifying.sol index 97769f3db21..516b1047d30 100644 --- a/packages/protocol/contracts/L1/libs/LibVerifying.sol +++ b/packages/protocol/contracts/L1/libs/LibVerifying.sol @@ -10,7 +10,7 @@ import "../../common/AddressResolver.sol"; import "../../signal/ISignalService.sol"; import "../tiers/ITierProvider.sol"; import "../TaikoData.sol"; -import "./LibTaikoToken.sol"; +import "../TaikoToken.sol"; import "./LibUtils.sol"; /// @title LibVerifying @@ -105,8 +105,6 @@ library LibVerifying { // Retrieve the latest verified block and the associated transition used // for its verification. TaikoData.SlotB memory b = state.slotB; - if (b.provingPaused) return; - uint64 blockId = b.lastVerifiedBlockId; uint64 slot = blockId % config.blockRingBufferSize; @@ -195,7 +193,8 @@ library LibVerifying { bondToReturn -= blk.livenessBond / 2; } - LibTaikoToken.creditTaikoToken(state, ts.prover, bondToReturn); + TaikoToken tko = TaikoToken(resolver.resolve("taiko_token", false)); + tko.transfer(ts.prover, bondToReturn); // Note: We exclusively address the bonds linked to the // transition used for verification. While there may exist diff --git a/packages/protocol/contracts/L1/provers/GuardianProver.sol b/packages/protocol/contracts/L1/provers/GuardianProver.sol index 9d55317fb75..f0ed23b4d04 100644 --- a/packages/protocol/contracts/L1/provers/GuardianProver.sol +++ b/packages/protocol/contracts/L1/provers/GuardianProver.sol @@ -7,7 +7,6 @@ pragma solidity ^0.8.20; import "../../common/EssentialContract.sol"; -import "../../common/Proxied.sol"; import "../tiers/ITierProvider.sol"; import "../TaikoData.sol"; diff --git a/packages/protocol/contracts/L1/verifiers/GuardianVerifier.sol b/packages/protocol/contracts/L1/verifiers/GuardianVerifier.sol index 7e5370e474b..1ecdbf3d820 100644 --- a/packages/protocol/contracts/L1/verifiers/GuardianVerifier.sol +++ b/packages/protocol/contracts/L1/verifiers/GuardianVerifier.sol @@ -7,7 +7,6 @@ pragma solidity ^0.8.20; import "../../common/EssentialContract.sol"; -import "../../common/Proxied.sol"; import "../TaikoData.sol"; import "./IVerifier.sol"; diff --git a/packages/protocol/contracts/L1/verifiers/PseZkVerifier.sol b/packages/protocol/contracts/L1/verifiers/PseZkVerifier.sol index bf92c784c79..10fb1a2bbd5 100644 --- a/packages/protocol/contracts/L1/verifiers/PseZkVerifier.sol +++ b/packages/protocol/contracts/L1/verifiers/PseZkVerifier.sol @@ -6,10 +6,8 @@ pragma solidity ^0.8.20; -import "../../common/EssentialContract.sol"; import "../../4844/Lib4844.sol"; -import "../../common/Proxied.sol"; -import "../../common/Proxied.sol"; +import "../../common/EssentialContract.sol"; import "../../thirdparty/LibBytesUtils.sol"; import "../TaikoData.sol"; import "./IVerifier.sol"; diff --git a/packages/protocol/contracts/L1/verifiers/SgxAndZkVerifier.sol b/packages/protocol/contracts/L1/verifiers/SgxAndZkVerifier.sol index 3a1c6f16e3b..9972a3f5a29 100644 --- a/packages/protocol/contracts/L1/verifiers/SgxAndZkVerifier.sol +++ b/packages/protocol/contracts/L1/verifiers/SgxAndZkVerifier.sol @@ -8,7 +8,6 @@ pragma solidity ^0.8.20; import "../../common/EssentialContract.sol"; import "../../thirdparty/LibBytesUtils.sol"; -import "../../common/Proxied.sol"; import "../TaikoData.sol"; import "./IVerifier.sol"; diff --git a/packages/protocol/contracts/L1/verifiers/SgxVerifier.sol b/packages/protocol/contracts/L1/verifiers/SgxVerifier.sol index a47afbf9fee..a21334d6b17 100644 --- a/packages/protocol/contracts/L1/verifiers/SgxVerifier.sol +++ b/packages/protocol/contracts/L1/verifiers/SgxVerifier.sol @@ -8,7 +8,6 @@ pragma solidity ^0.8.20; import "lib/openzeppelin-contracts-upgradeable/contracts/utils/cryptography/ECDSAUpgradeable.sol"; import "../../common/EssentialContract.sol"; -import "../../common/Proxied.sol"; import "../../thirdparty/LibBytesUtils.sol"; import "../TaikoData.sol"; import "./IVerifier.sol"; diff --git a/packages/protocol/contracts/L2/TaikoL2.sol b/packages/protocol/contracts/L2/TaikoL2.sol index d4536463b5d..f441a3f9138 100644 --- a/packages/protocol/contracts/L2/TaikoL2.sol +++ b/packages/protocol/contracts/L2/TaikoL2.sol @@ -7,10 +7,10 @@ pragma solidity ^0.8.20; import "lib/openzeppelin-contracts-upgradeable/contracts/access/Ownable2StepUpgradeable.sol"; +import "../common/EssentialContract.sol"; import "../common/ICrossChainSync.sol"; -import "../signal/ISignalService.sol"; -import "../common/Proxied.sol"; import "../libs/LibMath.sol"; +import "../signal/ISignalService.sol"; import "./Lib1559Math.sol"; import "./TaikoL2Signer.sol"; @@ -20,7 +20,7 @@ import "./TaikoL2Signer.sol"; /// It is used to anchor the latest L1 block details to L2 for cross-layer /// communication, manage EIP-1559 parameters for gas pricing, and store /// verified L1 block information. -contract TaikoL2 is Ownable2StepUpgradeable, TaikoL2Signer, ICrossChainSync { +contract TaikoL2 is EssentialContract, TaikoL2Signer, ICrossChainSync { using LibMath for uint256; struct Config { @@ -55,7 +55,7 @@ contract TaikoL2 is Ownable2StepUpgradeable, TaikoL2Signer, ICrossChainSync { /// @param _signalService Address of the {ISignalService} contract. /// @param _gasExcess The initial gasExcess. function init(address _signalService, uint64 _gasExcess) external initializer { - Ownable2StepUpgradeable.__Ownable2Step_init(); + EssentialContract._init(address(0)); if (_signalService == address(0)) revert L2_INVALID_PARAM(); signalService = _signalService; diff --git a/packages/protocol/contracts/L2/TaikoL2EIP1559Configurable.sol b/packages/protocol/contracts/L2/TaikoL2EIP1559Configurable.sol index b9b7ae6c2db..3eec0d12fcb 100644 --- a/packages/protocol/contracts/L2/TaikoL2EIP1559Configurable.sol +++ b/packages/protocol/contracts/L2/TaikoL2EIP1559Configurable.sol @@ -7,7 +7,6 @@ pragma solidity ^0.8.20; import "./TaikoL2.sol"; -import "../common/Proxied.sol"; /// @title TaikoL2EIP1559Configurable /// @notice Taiko L2 with a setter to change EIP-1559 configurations and states. diff --git a/packages/protocol/contracts/bridge/Bridge.sol b/packages/protocol/contracts/bridge/Bridge.sol index 2b933ec3f88..75c51bb85d1 100644 --- a/packages/protocol/contracts/bridge/Bridge.sol +++ b/packages/protocol/contracts/bridge/Bridge.sol @@ -6,9 +6,8 @@ pragma solidity ^0.8.20; import "../common/EssentialContract.sol"; -import "../common/Proxied.sol"; -import "../signal/ISignalService.sol"; import "../libs/LibAddress.sol"; +import "../signal/ISignalService.sol"; import "./IBridge.sol"; /// @title Bridge diff --git a/packages/protocol/contracts/common/AddressManager.sol b/packages/protocol/contracts/common/AddressManager.sol index 0601489d4ae..647ca57e35b 100644 --- a/packages/protocol/contracts/common/AddressManager.sol +++ b/packages/protocol/contracts/common/AddressManager.sol @@ -7,7 +7,6 @@ pragma solidity ^0.8.20; import "lib/openzeppelin-contracts-upgradeable/contracts/access/Ownable2StepUpgradeable.sol"; -import "./Proxied.sol"; /// @title IAddressManager /// @notice Specifies methods to manage address mappings for given chainId-name @@ -63,4 +62,8 @@ contract AddressManager is Ownable2StepUpgradeable, IAddressManager { /// @title ProxiedAddressManager /// @notice Proxied version of the parent contract. -contract ProxiedAddressManager is Proxied, AddressManager { } +contract ProxiedAddressManager is AddressManager { + constructor() { + _disableInitializers(); + } +} diff --git a/packages/protocol/contracts/common/EssentialContract.sol b/packages/protocol/contracts/common/EssentialContract.sol index 42199349b9c..6f5f84d3223 100644 --- a/packages/protocol/contracts/common/EssentialContract.sol +++ b/packages/protocol/contracts/common/EssentialContract.sol @@ -6,6 +6,7 @@ pragma solidity ^0.8.20; +import "lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol"; import "lib/openzeppelin-contracts-upgradeable/contracts/access/Ownable2StepUpgradeable.sol"; import "./AddressResolver.sol"; @@ -67,4 +68,22 @@ abstract contract EssentialContract is Ownable2StepUpgradeable, AddressResolver _reentry = _FALSE; _paused = _FALSE; } + + function _inNonReentrant() internal view returns (bool) { + return _reentry == _TRUE; + } +} + +/// @title Proxied +/// @dev Extends OpenZeppelin's Initializable for upgradeable contracts. +/// Intended as the base class for contracts used with +/// TransparentUpgradeableProxy. +/// +/// @dev For each chain, deploy Proxied contracts with unique deployers to +/// ensure distinct contract addresses. +abstract contract Proxied is Initializable { + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } } diff --git a/packages/protocol/contracts/common/Proxied.sol b/packages/protocol/contracts/common/Proxied.sol deleted file mode 100644 index a09d3ea5e0c..00000000000 --- a/packages/protocol/contracts/common/Proxied.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: MIT -// _____ _ _ _ _ -// |_ _|_ _(_) |_____ | | __ _| |__ ___ -// | |/ _` | | / / _ \ | |__/ _` | '_ (_-< -// |_|\__,_|_|_\_\___/ |____\__,_|_.__/__/ - -pragma solidity ^0.8.20; - -import "lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol"; - -/// @title Proxied -/// @dev Extends OpenZeppelin's Initializable for upgradeable contracts. -/// Intended as the base class for contracts used with -/// TransparentUpgradeableProxy. -/// -/// @dev For each chain, deploy Proxied contracts with unique deployers to -/// ensure distinct contract addresses. -abstract contract Proxied is Initializable { - /// @custom:oz-upgrades-unsafe-allow constructor - constructor() { - _disableInitializers(); - } -} diff --git a/packages/protocol/contracts/libs/LibAddress.sol b/packages/protocol/contracts/libs/LibAddress.sol index e71e1e9946c..e89d9dd269a 100644 --- a/packages/protocol/contracts/libs/LibAddress.sol +++ b/packages/protocol/contracts/libs/LibAddress.sol @@ -6,7 +6,7 @@ pragma solidity ^0.8.20; -import "lib/openzeppelin-contracts-upgradeable/contracts/utils/AddressUpgradeable.sol"; +import "lib/openzeppelin-contracts/contracts/utils/Address.sol"; import "lib/openzeppelin-contracts-upgradeable/contracts/utils/cryptography/ECDSAUpgradeable.sol"; import "lib/openzeppelin-contracts-upgradeable/contracts/utils/introspection/IERC165Upgradeable.sol"; import "lib/openzeppelin-contracts-upgradeable/contracts/interfaces/IERC1271Upgradeable.sol"; @@ -51,7 +51,7 @@ library LibAddress { view returns (bool result) { - if (!AddressUpgradeable.isContract(addr)) return false; + if (!Address.isContract(addr)) return false; try IERC165Upgradeable(addr).supportsInterface(interfaceId) returns (bool _result) { result = _result; @@ -67,7 +67,7 @@ library LibAddress { view returns (bool valid) { - if (AddressUpgradeable.isContract(addr)) { + if (Address.isContract(addr)) { return IERC1271Upgradeable(addr).isValidSignature(hash, sig) == EIP1271_MAGICVALUE; } else { return ECDSAUpgradeable.recover(hash, sig) == addr; diff --git a/packages/protocol/contracts/signal/SignalService.sol b/packages/protocol/contracts/signal/SignalService.sol index 5750bbf5e66..f812459d794 100644 --- a/packages/protocol/contracts/signal/SignalService.sol +++ b/packages/protocol/contracts/signal/SignalService.sol @@ -6,13 +6,10 @@ pragma solidity ^0.8.20; -import "lib/openzeppelin-contracts-upgradeable/contracts/utils/math/SafeCastUpgradeable.sol"; - +import "lib/openzeppelin-contracts/contracts/utils/math/SafeCast.sol"; import "../common/AuthorizableContract.sol"; import "../common/ICrossChainSync.sol"; -import "../common/Proxied.sol"; import "../thirdparty/LibSecureMerkleTrie.sol"; - import "./ISignalService.sol"; /// @title SignalService @@ -26,7 +23,7 @@ import "./ISignalService.sol"; /// Note: SignalService should not authorize Bridges or other Bridgable /// applications. contract SignalService is AuthorizableContract, ISignalService { - using SafeCastUpgradeable for uint256; + using SafeCast for uint256; // storageProof represents ABI-encoded tuple of (key, value, and proof) // returned from the eth_getProof() API. diff --git a/packages/protocol/contracts/team/TimeLockTokenPool.sol b/packages/protocol/contracts/team/TimeLockTokenPool.sol index 0e30f41a5a3..1bfdec7747d 100644 --- a/packages/protocol/contracts/team/TimeLockTokenPool.sol +++ b/packages/protocol/contracts/team/TimeLockTokenPool.sol @@ -10,7 +10,7 @@ import "lib/openzeppelin-contracts-upgradeable/contracts/access/Ownable2StepUpgr import "lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol"; import "lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/utils/SafeERC20Upgradeable.sol"; import "lib/openzeppelin-contracts-upgradeable/contracts/utils/cryptography/ECDSAUpgradeable.sol"; -import "../common/Proxied.sol"; +import "../common/EssentialContract.sol"; /// @title TimeLockTokenPool /// Contract for managing Taiko tokens allocated to different roles and @@ -27,7 +27,7 @@ import "../common/Proxied.sol"; /// - investors /// - team members, advisors, etc. /// - grant program grantees -contract TimeLockTokenPool is Ownable2StepUpgradeable { +contract TimeLockTokenPool is EssentialContract { using SafeERC20Upgradeable for ERC20Upgradeable; struct Grant { @@ -78,7 +78,7 @@ contract TimeLockTokenPool is Ownable2StepUpgradeable { error TOO_MANY(); function init(address _taikoToken, address _sharedVault) external initializer { - Ownable2StepUpgradeable.__Ownable2Step_init(); + EssentialContract._init(address(0)); if (_taikoToken == address(0)) revert INVALID_PARAM(); taikoToken = _taikoToken; diff --git a/packages/protocol/contracts/tokenvault/BaseVault.sol b/packages/protocol/contracts/tokenvault/BaseVault.sol index eb7486d95b1..9f762a02766 100644 --- a/packages/protocol/contracts/tokenvault/BaseVault.sol +++ b/packages/protocol/contracts/tokenvault/BaseVault.sol @@ -8,6 +8,7 @@ pragma solidity ^0.8.20; import "lib/openzeppelin-contracts-upgradeable/contracts/utils/introspection/IERC165Upgradeable.sol"; import "../bridge/IBridge.sol"; import "../common/EssentialContract.sol"; +import "../libs/LibAddress.sol"; abstract contract BaseVault is EssentialContract, IRecallableSender, IERC165Upgradeable { error VAULT_PERMISSION_DENIED(); diff --git a/packages/protocol/contracts/tokenvault/BridgedERC1155.sol b/packages/protocol/contracts/tokenvault/BridgedERC1155.sol index 0ba5f55d229..3de84eb5f57 100644 --- a/packages/protocol/contracts/tokenvault/BridgedERC1155.sol +++ b/packages/protocol/contracts/tokenvault/BridgedERC1155.sol @@ -12,7 +12,6 @@ import "lib/openzeppelin-contracts-upgradeable/contracts/token/ERC1155/extensions/IERC1155MetadataURIUpgradeable.sol"; import "lib/openzeppelin-contracts-upgradeable/contracts/token/ERC1155/IERC1155Upgradeable.sol"; import "../common/EssentialContract.sol"; -import "../common/Proxied.sol"; /// @title BridgedERC1155 /// @notice Contract for bridging ERC1155 tokens across different chains. diff --git a/packages/protocol/contracts/tokenvault/BridgedERC20.sol b/packages/protocol/contracts/tokenvault/BridgedERC20.sol index c692bbe1745..88aa50461b3 100644 --- a/packages/protocol/contracts/tokenvault/BridgedERC20.sol +++ b/packages/protocol/contracts/tokenvault/BridgedERC20.sol @@ -11,7 +11,6 @@ import "lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/IERC20MetadataUpgradeable.sol"; import "lib/openzeppelin-contracts/contracts/utils/Strings.sol"; import "../common/EssentialContract.sol"; -import "../common/Proxied.sol"; import "./IMintableERC20.sol"; /// @title BridgedERC20 diff --git a/packages/protocol/contracts/tokenvault/BridgedERC721.sol b/packages/protocol/contracts/tokenvault/BridgedERC721.sol index 20287b97c6d..82ad8670987 100644 --- a/packages/protocol/contracts/tokenvault/BridgedERC721.sol +++ b/packages/protocol/contracts/tokenvault/BridgedERC721.sol @@ -9,7 +9,6 @@ pragma solidity ^0.8.20; import "lib/openzeppelin-contracts-upgradeable/contracts/token/ERC721/ERC721Upgradeable.sol"; import "lib/openzeppelin-contracts/contracts/utils/Strings.sol"; import "../common/EssentialContract.sol"; -import "../common/Proxied.sol"; /// @title BridgedERC721 /// @notice Contract for bridging ERC721 tokens across different chains. diff --git a/packages/protocol/contracts/tokenvault/ERC1155Vault.sol b/packages/protocol/contracts/tokenvault/ERC1155Vault.sol index 3bb71c3922f..a2e3b996371 100644 --- a/packages/protocol/contracts/tokenvault/ERC1155Vault.sol +++ b/packages/protocol/contracts/tokenvault/ERC1155Vault.sol @@ -7,15 +7,11 @@ pragma solidity ^0.8.20; import "lib/openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; - import "lib/openzeppelin-contracts-upgradeable/contracts/token/ERC1155/utils/ERC1155ReceiverUpgradeable.sol"; import "lib/openzeppelin-contracts-upgradeable/contracts/token/ERC1155/ERC1155Upgradeable.sol"; - -import "../common/Proxied.sol"; import "../bridge/IBridge.sol"; import "../libs/LibAddress.sol"; - import "./BaseNFTVault.sol"; import "./BridgedERC1155.sol"; diff --git a/packages/protocol/contracts/tokenvault/ERC20Vault.sol b/packages/protocol/contracts/tokenvault/ERC20Vault.sol index e05958504c3..e4db98439a1 100644 --- a/packages/protocol/contracts/tokenvault/ERC20Vault.sol +++ b/packages/protocol/contracts/tokenvault/ERC20Vault.sol @@ -7,12 +7,9 @@ pragma solidity ^0.8.20; import "lib/openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; - import "lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol"; import "lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/utils/SafeERC20Upgradeable.sol"; -import "../common/Proxied.sol"; import "../bridge/IBridge.sol"; -import "../libs/LibAddress.sol"; import "./BridgedERC20.sol"; import "./IMintableERC20.sol"; import "./BaseVault.sol"; diff --git a/packages/protocol/contracts/tokenvault/ERC721Vault.sol b/packages/protocol/contracts/tokenvault/ERC721Vault.sol index 07f326f4f5b..8eefa3e9f79 100644 --- a/packages/protocol/contracts/tokenvault/ERC721Vault.sol +++ b/packages/protocol/contracts/tokenvault/ERC721Vault.sol @@ -7,13 +7,10 @@ pragma solidity ^0.8.20; import "lib/openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; - import "lib/openzeppelin-contracts-upgradeable/contracts/token/ERC721/ERC721Upgradeable.sol"; import "lib/openzeppelin-contracts-upgradeable/contracts/token/ERC721/IERC721ReceiverUpgradeable.sol"; import "../bridge/IBridge.sol"; -import "../libs/LibAddress.sol"; -import "../common/Proxied.sol"; import "./BaseNFTVault.sol"; import "./BridgedERC721.sol"; diff --git a/packages/protocol/genesis/GenerateGenesis.g.sol b/packages/protocol/genesis/GenerateGenesis.g.sol index 49aed95bde1..ff503f41f3f 100644 --- a/packages/protocol/genesis/GenerateGenesis.g.sol +++ b/packages/protocol/genesis/GenerateGenesis.g.sol @@ -1,12 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; +import "lib/openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import "forge-std/console2.sol"; import "forge-std/StdJson.sol"; import "forge-std/Test.sol"; - -import "lib/openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; - import "../contracts/common/AddressManager.sol"; import "../contracts/common/AddressResolver.sol"; import "../contracts/common/EssentialContract.sol"; diff --git a/packages/protocol/test/L1/TaikoL1LibProvingWithTiers.t.sol b/packages/protocol/test/L1/TaikoL1LibProvingWithTiers.t.sol index fa562e73375..901dfe7bbcd 100644 --- a/packages/protocol/test/L1/TaikoL1LibProvingWithTiers.t.sol +++ b/packages/protocol/test/L1/TaikoL1LibProvingWithTiers.t.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.20; import "forge-std/Test.sol"; import "forge-std/console2.sol"; - import "../../contracts/common/AddressManager.sol"; import "../../contracts/L1/libs/LibUtils.sol"; import "../../contracts/L1/libs/LibProposing.sol"; @@ -762,7 +761,7 @@ contract TaikoL1LibProvingWithTiers is TaikoL1TestBase { // This is a very weird test (code?) issue here. // If this line is uncommented, // Alice/Bob has no balance.. (Causing reverts !!!) - // Current investigations are onsgoing with foundry team + // Current investigations are ongoing with foundry team giveEthAndTko(Bob, 1e7 ether, 100 ether); console2.log("Bob balance:", tko.balanceOf(Bob)); // Bob @@ -794,6 +793,8 @@ contract TaikoL1LibProvingWithTiers is TaikoL1TestBase { proof.tier = LibTiers.TIER_GUARDIAN; proof.data = bytes.concat(keccak256("RETURN_LIVENESS_BOND")); + uint256 balanceBeforeReimbursement = tko.balanceOf(Bob); + vm.prank(David, David); gp.approve(meta, tran, proof); vm.prank(Emma, Emma); @@ -804,7 +805,7 @@ contract TaikoL1LibProvingWithTiers is TaikoL1TestBase { // // Credited back the bond (not transferred to the user // wallet, // // but in-contract account credited only.) - assertEq(L1.getTaikoTokenBalance(Bob), 1 ether); + assertEq(tko.balanceOf(Bob) - balanceBeforeReimbursement, 1 ether); } else { // Prove as guardian proveBlock( diff --git a/packages/protocol/test/L1/TaikoL1TestBase.sol b/packages/protocol/test/L1/TaikoL1TestBase.sol index 0cd05baae6b..a7d1ef19958 100644 --- a/packages/protocol/test/L1/TaikoL1TestBase.sol +++ b/packages/protocol/test/L1/TaikoL1TestBase.sol @@ -21,6 +21,7 @@ import "../../contracts/L1/provers/GuardianProver.sol"; import "../../contracts/signal/SignalService.sol"; import "../../contracts/common/AddressResolver.sol"; import "../../contracts/L1/tiers/ITierProvider.sol"; +import "../../contracts/L1/hooks/AssignmentHook.sol"; contract MockVerifier { fallback(bytes calldata) external returns (bytes memory) { @@ -32,6 +33,7 @@ contract MockVerifier { // shared logics and data. abstract contract TaikoL1TestBase is TaikoTest { AddressManager public addressManager; + AssignmentHook public assignmentHook; TaikoToken public tko; SignalService public ss; TaikoL1 public L1; @@ -86,6 +88,9 @@ abstract contract TaikoL1TestBase is TaikoTest { bridge = new Bridge(); bridge.init(address(addressManager)); + assignmentHook = new AssignmentHook(); + assignmentHook.init(address(addressManager)); + registerAddress("taiko", address(L1)); registerAddress("tier_pse_zkevm", address(pv)); registerAddress("tier_sgx", address(sv)); @@ -140,8 +145,7 @@ abstract contract TaikoL1TestBase is TaikoTest { // anyways uint256 msgValue = 2 ether; - TaikoData.ProverAssignment memory assignment = TaikoData.ProverAssignment({ - prover: prover, + AssignmentHook.ProverAssignment memory assignment = AssignmentHook.ProverAssignment({ feeToken: address(0), tierFees: tierFees, expiry: uint64(block.timestamp + 60 minutes), @@ -151,9 +155,8 @@ abstract contract TaikoL1TestBase is TaikoTest { signature: new bytes(0) }); - bytes memory txList = new bytes(txListSize); - - assignment.signature = _signAssignment(prover, assignment, address(L1), keccak256(txList)); + assignment.signature = + _signAssignment(prover, assignment, address(L1), keccak256(new bytes(txListSize))); (, TaikoData.SlotB memory b) = L1.getStateVariables(); @@ -168,9 +171,14 @@ abstract contract TaikoL1TestBase is TaikoTest { meta.difficulty = bytes32(_difficulty); meta.gasLimit = gasLimit; + TaikoData.HookCall[] memory hookcalls = new TaikoData.HookCall[](1); + + hookcalls[0] = TaikoData.HookCall(address(assignmentHook), abi.encode(assignment)); + vm.prank(proposer, proposer); (meta, depositsProcessed) = L1.proposeBlock{ value: msgValue }( - abi.encode(TaikoData.BlockParams(assignment, 0, 0, 0, 0, false, 0)), txList + abi.encode(TaikoData.BlockParams(prover, 0, 0, 0, 0, false, 0, hookcalls)), + new bytes(txListSize) ); } @@ -295,7 +303,7 @@ abstract contract TaikoL1TestBase is TaikoTest { function _signAssignment( address signer, - TaikoData.ProverAssignment memory assignment, + AssignmentHook.ProverAssignment memory assignment, address taikoAddr, bytes32 blobHash ) @@ -303,7 +311,6 @@ abstract contract TaikoL1TestBase is TaikoTest { view returns (bytes memory signature) { - bytes32 digest = LibProposing.hashAssignment(assignment, taikoAddr, blobHash); uint256 signerPrivateKey; // In the test suite these are the 3 which acts as provers @@ -315,7 +322,9 @@ abstract contract TaikoL1TestBase is TaikoTest { signerPrivateKey = 0x3; } - (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPrivateKey, digest); + (uint8 v, bytes32 r, bytes32 s) = vm.sign( + signerPrivateKey, assignmentHook.hashAssignment(assignment, taikoAddr, blobHash) + ); signature = abi.encodePacked(r, s, v); } @@ -346,12 +355,12 @@ abstract contract TaikoL1TestBase is TaikoTest { function giveEthAndTko(address to, uint256 amountTko, uint256 amountEth) internal { vm.deal(to, amountEth); - console2.log("TKO balance this:", tko.balanceOf(address(this))); - console2.log(amountTko); tko.transfer(to, amountTko); vm.prank(to, to); tko.approve(address(L1), amountTko); + vm.prank(to, to); + tko.approve(address(assignmentHook), amountTko); console2.log("TKO balance:", to, tko.balanceOf(to)); console2.log("ETH balance:", to, to.balance); diff --git a/packages/protocol/test/L2/Lib1559Math.t.sol b/packages/protocol/test/L2/Lib1559Math.t.sol index 6bd46238f77..1f9bef46627 100644 --- a/packages/protocol/test/L2/Lib1559Math.t.sol +++ b/packages/protocol/test/L2/Lib1559Math.t.sol @@ -2,11 +2,9 @@ pragma solidity ^0.8.20; import "forge-std/console2.sol"; - import "../../contracts/L2/Lib1559Math.sol"; import "../../contracts/thirdparty/LibFixedPointMath.sol"; import "../../contracts/libs/LibMath.sol"; - import "../TestBase.sol"; contract TestLib1559Math is TaikoTest { diff --git a/packages/protocol/test/L2/TaikoL2.t.sol b/packages/protocol/test/L2/TaikoL2.t.sol index 145683f5c29..3a27c7d580c 100644 --- a/packages/protocol/test/L2/TaikoL2.t.sol +++ b/packages/protocol/test/L2/TaikoL2.t.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.20; import "lib/openzeppelin-contracts/contracts/utils/Strings.sol"; -import "lib/openzeppelin-contracts-upgradeable/contracts/utils/math/SafeCastUpgradeable.sol"; +import "lib/openzeppelin-contracts/contracts/utils/math/SafeCast.sol"; import "forge-std/console2.sol"; import "../../contracts/common/AddressManager.sol"; import "../../contracts/signal/SignalService.sol"; @@ -17,7 +17,7 @@ contract SkipBasefeeCheckL2 is TaikoL2EIP1559Configurable { } contract TestTaikoL2 is TaikoTest { - using SafeCastUpgradeable for uint256; + using SafeCast for uint256; // Initial salt for semi-random generation uint256 salt = 2_195_684_615_435_261_315_311;