├── .gitignore ├── .gitmodules ├── README.md ├── foundry.toml ├── src ├── RollupEncoder.sol ├── interfaces │ ├── IRollupProcessor.sol │ └── IRollupProcessorV2.sol └── libraries │ ├── AztecTypes.sol │ ├── EventsErrorsV2.sol │ └── RollupProcessorLibrary.sol └── test └── RollupEncoder.t.sol /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiler files 2 | cache/ 3 | out/ 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/forge-std"] 2 | path = lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | branch = v1.1.1 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rollup Encoder 2 | 3 | `RollupEncoder` is a contract which allows for creation of Aztec Connect rollup blocks without running the L2 infrastructure (sequencer, prover...). 4 | The goal of this project is to give bridge developers an easy way to test their contracts (no typescript, just solidity + foundry). 5 | 6 | An example of how to use this encoder can be found in our [aztec-connect-bridges](https://github.com/AztecProtocol/aztec-connect-bridges#aztec-connect-bridges). -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = 'src' 3 | out = 'out' 4 | libs = ['lib'] 5 | solc_version = '0.8.10' 6 | eth-rpc-url = 'https://mainnet.infura.io/v3/9928b52099854248b3a096be07a6b23c' 7 | 8 | # See more config options https://github.com/foundry-rs/foundry/tree/master/config -------------------------------------------------------------------------------- /src/RollupEncoder.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright 2022 Aztec. 3 | pragma solidity >=0.8.4; 4 | 5 | import {Vm} from "forge-std/Vm.sol"; 6 | import {EventsErrorsV2} from "./libraries/EventsErrorsV2.sol"; 7 | import {Script} from "forge-std/Script.sol"; 8 | import {IRollupProcessorV2} from "./interfaces/IRollupProcessorV2.sol"; 9 | import {AztecTypes} from "./libraries/AztecTypes.sol"; 10 | import {RollupProcessorLibrary} from "./libraries/RollupProcessorLibrary.sol"; 11 | 12 | /** 13 | * @notice A contract which allows testing bridges against live RollupProcessor deployment. 14 | * @author Lasse Herskind, Jan Benes 15 | * @dev Inheriting from Script in order to have access to `vm` 16 | * @dev This contract allows for testing of bridges and RollupProcessor itself. The external functions can be used 17 | * to register L2 transactions which are then passed to RollupProcessor in a rollup block when `processRollup()` 18 | * function or some of its variations is called. After rollup block is processed the L2 transactions are wiped 19 | * from this contract and the next block starts from a clean slate. 20 | */ 21 | contract RollupEncoder is Script, EventsErrorsV2 { 22 | /* solhint-enable error-name-mixedcase */ 23 | error UnsupportedAsset(address); 24 | 25 | // @dev An enum describing proof/L2 tx type 26 | enum ProofId { 27 | PADDING, 28 | DEPOSIT, 29 | WITHDRAW, 30 | SEND, 31 | ACCOUNT, 32 | DEFI_DEPOSIT, 33 | DEFI_CLAIM 34 | } 35 | 36 | /** 37 | * @dev A container for deposit related information 38 | * @param assetId An id of the deposited asset 39 | * @param amount An amount of the deposited asset 40 | * @param fee An amount of the deposited asset to be paid as a fee 41 | * @param privKey A private key of the deposit owner (address is automatically derived and set as deposit owner) 42 | * @param proofHash A keccak256 hash of the inner proof public inputs (allows the owner to prove deposit ownership 43 | * on L2) 44 | * @dev For testing we assume fee is paid in the same asset as the deposit 45 | */ 46 | struct DepositStruct { 47 | uint256 assetId; 48 | uint256 amount; 49 | uint256 fee; // Just assume it is the same asset 50 | uint256 privKey; 51 | bytes32 proofHash; 52 | } 53 | 54 | /** 55 | * @dev A container for withdraw related information 56 | * @param assetId An id of the asset to withdraw 57 | * @param amount An amount of the asset to withdraw 58 | * @param publicOwner A recipient of the withdrawal on L1 59 | */ 60 | struct WithdrawStruct { 61 | uint256 assetId; 62 | uint256 amount; 63 | address publicOwner; 64 | } 65 | 66 | /** 67 | * @dev A container for withdraw related information 68 | * @param encodedBridgeCallData A bit-array that contains data describing a specific bridge call 69 | * @param totalInputValue An amount of used input assets to be passed to a bridge before bridge call 70 | */ 71 | struct DefiInteractionStruct { 72 | uint256 encodedBridgeCallData; 73 | uint256 totalInputValue; 74 | } 75 | 76 | /** 77 | * @dev A container for information which is expected to be emitted in a `DefiBridgeProcessed` event once 78 | * `processRollup()` is called. 79 | * @param encodedBridgeCallData A bit-array that contains data describing a specific bridge call 80 | * @param nonce A nonce of the bridge interaction 81 | * @param totalInputValue An amount of used input assets to be passed to a bridge before bridge call 82 | * @param outputValueA An amount of output asset A returned from the interaction 83 | * @param outputValueB An amount of output asset B returned from the interaction (0 if asset B unused) 84 | * @param result A flag indicating whether the interaction succeeded 85 | * @param errorReason Error reason (empty if result = true) 86 | */ 87 | struct DefiBridgeProcessedStruct { 88 | uint256 encodedBridgeCallData; 89 | uint256 nonce; 90 | uint256 totalInputValue; 91 | uint256 outputValueA; 92 | uint256 outputValueB; 93 | bool result; 94 | bytes errorReason; 95 | } 96 | 97 | // Constants copied from RollupProcessor and Decoder 98 | uint256 public constant NUMBER_OF_ASSETS = 16; 99 | 100 | uint256 private constant INPUT_ASSET_ID_A_SHIFT = 32; 101 | uint256 private constant INPUT_ASSET_ID_B_SHIFT = 62; 102 | uint256 private constant OUTPUT_ASSET_ID_A_SHIFT = 92; 103 | uint256 private constant OUTPUT_ASSET_ID_B_SHIFT = 122; 104 | uint256 private constant BITCONFIG_SHIFT = 152; 105 | uint256 private constant AUX_DATA_SHIFT = 184; 106 | uint256 public constant VIRTUAL_ASSET_ID_FLAG_SHIFT = 29; 107 | uint256 public constant VIRTUAL_ASSET_ID_FLAG = 0x20000000; // 2 ** 29 108 | uint256 private constant MASK_THIRTY_TWO_BITS = 0xffffffff; 109 | uint256 private constant MASK_THIRTY_BITS = 0x3fffffff; 110 | uint256 private constant MASK_SIXTY_FOUR_BITS = 0xffffffffffffffff; 111 | 112 | bytes32 private constant DATA_ROOT = 0x18ceb5cd201e1cee669a5c3ad96d3c4e933a365b37046fc3178264bede32c68d; 113 | bytes32 private constant NULL_ROOT = 0x298329c7d0936453f354e4a5eef4897296cc0bf5a66f2a528318508d2088dafa; 114 | bytes32 private constant DATA_ROOTS_ROOT = 0x2fd2364bfe47ccb410eba3a958be9f39a8c6aca07db1abd15f5a211f51505071; 115 | bytes32 private constant DEFI_ROOT = 0x2e4ab7889ab3139204945f9e722c7a8fdb84e66439d787bd066c3d896dba04ea; 116 | 117 | bytes32 private constant DEFI_RESULT_ZERO_HASH = 0x2d25a1e3a51eb293004c4b56abe12ed0da6bca2b4a21936752a85d102593c1b4; 118 | 119 | uint256 private constant CIRCUIT_MODULUS = 120 | 21888242871839275222246405745257275088548364400416034343698204186575808495617; 121 | 122 | uint256 private constant TX_PUBLIC_INPUT_LENGTH = 256; 123 | 124 | uint256 private constant NUM_ASYNC_DEFI_INTERACTION_HASHES_MASK = 125 | type(uint256).max - (uint256(type(uint16).max) << 192); 126 | 127 | address public constant ROLLUP_PROVIDER = payable(0xA173BDdF4953C1E8be2cA0695CFc07502Ff3B1e7); 128 | 129 | bytes32 public constant BRIDGE_PROCESSED_EVENT_SIG = 130 | keccak256("DefiBridgeProcessed(uint256,uint256,uint256,uint256,uint256,bool,bytes)"); 131 | bytes32 public constant ASYNC_BRIDGE_PROCESSED_EVENT_SIG = 132 | keccak256("AsyncDefiBridgeProcessed(uint256,uint256,uint256)"); 133 | 134 | mapping(uint256 => DepositStruct) private depositsL2; 135 | mapping(uint256 => WithdrawStruct) private withdrawalsL2; 136 | mapping(uint256 => DefiInteractionStruct) private defiInteractionsL2; 137 | mapping(uint256 => DefiBridgeProcessedStruct) private defiBridgeProcessedStructs; 138 | // assetId => fee amount 139 | mapping(uint256 => uint256) private fees; 140 | 141 | uint256 public depositsL2Length; 142 | uint256 public withdrawalsL2Length; 143 | uint256 public defiInteractionLength; 144 | uint256 public defiBridgeProcessedStructsLength; 145 | 146 | // @dev Placing `mockVerifierCall` next to `rollupBeneficiary` would cause revert in a Decoder because these 2 147 | // variables would get packed to 1 slot and then the boolean value would get accidentally saved to the proof 148 | // along with rollupBeneficiary (we pass there the whole storage slot during proof data construction) 149 | bool private mockVerifierCall = true; 150 | uint256 public nextRollupId = 0; 151 | address public rollupBeneficiary; 152 | IRollupProcessorV2 public ROLLUP_PROCESSOR; 153 | 154 | /** 155 | * @notice Sets address of rollup processor 156 | * @param _rollupProcessor Address of rollup processor 157 | */ 158 | constructor(address _rollupProcessor) { 159 | ROLLUP_PROCESSOR = IRollupProcessorV2(_rollupProcessor); 160 | } 161 | 162 | /** 163 | * @notice Saves contents of DefiBridgeProcessed event to be later checked in `processRollup(...)` 164 | * @dev The array gets cleaned up after rollup is processed 165 | * @param encodedBridgeCallData A bit-array that contains data describing a specific bridge call 166 | * @param nonce A nonce of the bridge interaction 167 | * @param totalInputValue An amount of used input assets to be passed to a bridge before bridge call 168 | * @param outputValueA An amount of output asset A returned from the interaction 169 | * @param outputValueB An amount of output asset B returned from the interaction (0 if asset B unused) 170 | * @param result A flag indicating whether the interaction succeeded 171 | * @param errorReason Error reason (empty if result = true) 172 | */ 173 | function registerEventToBeChecked( 174 | uint256 encodedBridgeCallData, 175 | uint256 nonce, 176 | uint256 totalInputValue, 177 | uint256 outputValueA, 178 | uint256 outputValueB, 179 | bool result, 180 | bytes memory errorReason 181 | ) external { 182 | defiBridgeProcessedStructs[defiBridgeProcessedStructsLength++] = DefiBridgeProcessedStruct( 183 | encodedBridgeCallData, nonce, totalInputValue, outputValueA, outputValueB, result, errorReason 184 | ); 185 | } 186 | 187 | /** 188 | * @notice Constructs and submits rollup batch to `rollupProcessor` 189 | * @dev Before calling this method deposits, withdrawals and defiInteractions should be registered through 190 | * `depositL2(...)`, `withdrawL2(...)`, `defiInteractionL2(...)` functions. 191 | * @dev Checks DefiBridgeProcessed events if registered via `registerEventToBeChecked(...)` 192 | */ 193 | function processRollup() external { 194 | (bytes memory encodedProofData, bytes memory signatures) = prepProcessorAndGetRollupBlock(); 195 | 196 | for (uint256 i = 0; i < defiBridgeProcessedStructsLength; i++) { 197 | DefiBridgeProcessedStruct memory temp = defiBridgeProcessedStructs[i]; 198 | vm.expectEmit(true, true, false, true); 199 | emit DefiBridgeProcessed( 200 | temp.encodedBridgeCallData, 201 | temp.nonce, 202 | temp.totalInputValue, 203 | temp.outputValueA, 204 | temp.outputValueB, 205 | temp.result, 206 | temp.errorReason 207 | ); 208 | } 209 | 210 | vm.prank(ROLLUP_PROVIDER); 211 | ROLLUP_PROCESSOR.processRollup(encodedProofData, signatures); 212 | nextRollupId++; 213 | } 214 | 215 | /** 216 | * @notice Constructs and submits a failing rollup batch to `rollupProcessor` 217 | * @dev Before calling this method deposits, withdrawals and defiInteractions should be registered through 218 | * `depositL2(...)`, `withdrawL2(...)`, `defiInteractionL2(...)` functions. 219 | * @param _err An error with which RollupProcessor's `processRollup(...)` function is expected to revert 220 | */ 221 | function processRollupFail(bytes memory _err) external { 222 | (bytes memory encodedProofData, bytes memory signatures) = prepProcessorAndGetRollupBlock(); 223 | vm.prank(ROLLUP_PROVIDER); 224 | vm.expectRevert(_err); 225 | ROLLUP_PROCESSOR.processRollup(encodedProofData, signatures); 226 | nextRollupId++; 227 | } 228 | 229 | /** 230 | * @notice Construct and submits a failing rollup batch to `rollupProcessor` 231 | * @dev Before calling this method deposits, withdrawals and defiInteractions should be registered through 232 | * `depositL2(...)`, `withdrawL2(...)`, `defiInteractionL2(...)` functions. 233 | * @param _err A custom error selector with which RollupProcessor's `processRollup(...)` function is expected to 234 | * revert 235 | */ 236 | function processRollupFail(bytes4 _err) external { 237 | (bytes memory encodedProofData, bytes memory signatures) = prepProcessorAndGetRollupBlock(); 238 | vm.prank(ROLLUP_PROVIDER); 239 | vm.expectRevert(_err); 240 | ROLLUP_PROCESSOR.processRollup(encodedProofData, signatures); 241 | nextRollupId++; 242 | } 243 | 244 | /** 245 | * @notice Construct and submits rollup batch to `rollupProcessor` 246 | * @return outputValueA The amount of outputAssetA returned from the DeFi bridge interaction in this rollup 247 | * @return outputValueB The amount of outputAssetB returned from the DeFi bridge interaction in this rollup 248 | * @return isAsync A flag indicating whether the DeFi bridge interaction in this rollup was async 249 | * @dev Before calling this method deposits, withdrawals and defiInteractions should be registered through 250 | * `depositL2(...)`, `withdrawL2(...)`, `defiInteractionL2(...)` functions. 251 | */ 252 | function processRollupAndGetBridgeResult() 253 | external 254 | returns (uint256 outputValueA, uint256 outputValueB, bool isAsync) 255 | { 256 | (bytes memory encodedProofData, bytes memory signatures) = prepProcessorAndGetRollupBlock(); 257 | 258 | vm.recordLogs(); 259 | vm.prank(ROLLUP_PROVIDER); 260 | ROLLUP_PROCESSOR.processRollup(encodedProofData, signatures); 261 | nextRollupId++; 262 | 263 | return _getDefiBridgeProcessedData(); 264 | } 265 | 266 | /** 267 | * @notice Registers an L2 deposit to be processed in the next rollup block 268 | * @param _assetId Id of the deposited asset 269 | * @param _amount Amount of the deposited asset 270 | * @param _fee Fee to be taken by the sequencer for processing the deposit 271 | * @param _privKey Private key corresponding to the deposit owner 272 | * @dev This is a claim on L2 on funds deposited on L1 273 | * @dev Note: For deposits to pass the corresponding deposit has to be in `rollupProcessor.userPendingDeposits`. 274 | */ 275 | function depositL2(uint256 _assetId, uint256 _amount, uint256 _fee, uint256 _privKey) external { 276 | depositsL2[depositsL2Length++] = 277 | DepositStruct({assetId: _assetId, amount: _amount, fee: _fee, privKey: _privKey, proofHash: bytes32("")}); 278 | 279 | if (_fee > 0) { 280 | require( 281 | _assetId < NUMBER_OF_ASSETS, "Functionality handling assetId >= MAX_ASSETS_IN_BLOCK not implemented" 282 | ); 283 | fees[_assetId] += _fee; 284 | } 285 | } 286 | 287 | /** 288 | * @notice Registers an L2 deposit to be processed in the next rollup block 289 | * @param _assetId Id of the deposited asset 290 | * @param _amount Amount of the deposited asset 291 | * @param _fee Fee to be taken by the sequencer for processing the deposit 292 | * @param _privKey Private key corresponding to the deposit owner 293 | * @param _proofHash A keccak256 hash of the inner proof public inputs (allows the owner to prove deposit ownership 294 | * on L2) 295 | * @dev This is a claim on L2 on funds deposited on L1 296 | * @dev Note: For deposits to pass the corresponding deposit has to be in `rollupProcessor.userPendingDeposits`. 297 | */ 298 | function depositL2(uint256 _assetId, uint256 _amount, uint256 _fee, uint256 _privKey, bytes32 _proofHash) 299 | external 300 | { 301 | depositsL2[depositsL2Length++] = 302 | DepositStruct({assetId: _assetId, amount: _amount, fee: _fee, privKey: _privKey, proofHash: _proofHash}); 303 | if (_fee > 0) { 304 | require(_assetId < NUMBER_OF_ASSETS, "Functionality handling assetId >= NUMBER_OF_ASSETS not implemented"); 305 | fees[_assetId] += _fee; 306 | } 307 | } 308 | 309 | /** 310 | * @notice Registers a withdrawal from L2 to be processed in the next rollup block 311 | * @param _assetId Id of the deposited asset 312 | * @param _amount Amount of the deposited asset 313 | * @param _owner Address on L1 which will receive the withdrawn funds once the rollup is processed 314 | */ 315 | function withdrawL2(uint256 _assetId, uint256 _amount, address _owner) external { 316 | withdrawalsL2[withdrawalsL2Length++] = WithdrawStruct({assetId: _assetId, amount: _amount, publicOwner: _owner}); 317 | } 318 | 319 | /** 320 | * @notice Registers a bridge interaction to be processed in the next rollup 321 | * @param _bridgeAddressId Id of the bridge address 322 | * @param _inputAssetA A struct detailing the first input asset 323 | * @param _inputAssetB A struct detailing the second input asset 324 | * @param _outputAssetA A struct detailing the first output asset 325 | * @param _outputAssetB A struct detailing the second output asset 326 | * @param _auxData Bridge specific data to be passed into the bridge contract (e.g. slippage, nftID etc.) 327 | * @param _totalInputValue An amount of input assets transferred to the bridge 328 | * @return A bit-string encoded bridge call data 329 | */ 330 | function defiInteractionL2( 331 | uint256 _bridgeAddressId, 332 | AztecTypes.AztecAsset memory _inputAssetA, 333 | AztecTypes.AztecAsset memory _inputAssetB, 334 | AztecTypes.AztecAsset memory _outputAssetA, 335 | AztecTypes.AztecAsset memory _outputAssetB, 336 | uint64 _auxData, 337 | uint256 _totalInputValue 338 | ) external returns (uint256) { 339 | uint256 encodedBridgeCallData = 340 | encodeBridgeCallData(_bridgeAddressId, _inputAssetA, _inputAssetB, _outputAssetA, _outputAssetB, _auxData); 341 | defiInteractionsL2[defiInteractionLength++] = 342 | DefiInteractionStruct({encodedBridgeCallData: encodedBridgeCallData, totalInputValue: _totalInputValue}); 343 | return encodedBridgeCallData; 344 | } 345 | 346 | /** 347 | * @notice Registers a bridge interaction to be processed in the next rollup 348 | * @param _encodedBridgeCallData The encodedBridgeCallData for the given interaction 349 | * @param _totalInputValue An amount of input assets transferred to the bridge 350 | */ 351 | function defiInteractionL2(uint256 _encodedBridgeCallData, uint256 _totalInputValue) external { 352 | defiInteractionsL2[defiInteractionLength++] = 353 | DefiInteractionStruct({encodedBridgeCallData: _encodedBridgeCallData, totalInputValue: _totalInputValue}); 354 | } 355 | 356 | /** 357 | * @notice Sets `rollupBeneficiary` storage variable 358 | * @param _rollupBeneficiary An address which receives rollup block's fee 359 | */ 360 | function setRollupBeneficiary(address _rollupBeneficiary) external { 361 | rollupBeneficiary = _rollupBeneficiary; 362 | } 363 | 364 | /** 365 | * @notice Sets `mockVerifierCall` storage variable 366 | * @param _mockVerifierCall A flag specifying whether a call to verifier should be mocked 367 | * @dev Used in NoCode.t.sol in the internal repository 368 | */ 369 | function setMockVerifierCall(bool _mockVerifierCall) external { 370 | mockVerifierCall = _mockVerifierCall; 371 | } 372 | 373 | /** 374 | * @notice Sets `nextRollupId` storage variable 375 | * @param _nextRollupId Next rollupId 376 | * @dev Used only in RollupProcessor tests (DefiBridge.t.sol). Does not need to be touched when developing bridges. 377 | */ 378 | function setNextRollupId(uint256 _nextRollupId) external { 379 | nextRollupId = _nextRollupId; 380 | } 381 | 382 | /** 383 | * @notice Gets an `AztecAsset` object for the supported real `_asset` 384 | * @dev if `_asset` is not supported will revert with `UnsupportedAsset(_asset)`. 385 | * @dev Virtual assets are not supported by the function 386 | * @param _asset The address of the asset to fetch 387 | * @return A populated supported `AztecAsset` 388 | */ 389 | function getRealAztecAsset(address _asset) external view returns (AztecTypes.AztecAsset memory) { 390 | if (_asset == address(0)) { 391 | return AztecTypes.AztecAsset({id: 0, erc20Address: address(0), assetType: AztecTypes.AztecAssetType.ETH}); 392 | } else { 393 | return AztecTypes.AztecAsset({ 394 | id: getAssetId(_asset), 395 | erc20Address: _asset, 396 | assetType: AztecTypes.AztecAssetType.ERC20 397 | }); 398 | } 399 | } 400 | 401 | /** 402 | * @notice Gets nonce for the next bridge interaction 403 | * @return The nonce of the next bridge interaction 404 | */ 405 | function getNextNonce() external view returns (uint256) { 406 | return nextRollupId * 32; 407 | } 408 | 409 | /** 410 | * @notice Gets the id a given `_asset` 411 | * @dev if `_asset` is not supported will revert with `UnsupportedAsset(_asset)` 412 | * @param _asset The address of the asset to fetch id for 413 | * @return The id matching `_asset` 414 | */ 415 | function tokenToId(address _asset) external view returns (uint256) { 416 | if (_asset == address(0)) { 417 | return 0; 418 | } 419 | uint256 length = ROLLUP_PROCESSOR.getSupportedAssetsLength(); 420 | for (uint256 i = 1; i <= length; i++) { 421 | address fetched = ROLLUP_PROCESSOR.getSupportedAsset(i); 422 | if (fetched == _asset) { 423 | return i; 424 | } 425 | } 426 | revert UnsupportedAsset(_asset); 427 | } 428 | 429 | /** 430 | * @notice Checks whether `_asset` is supported or not 431 | * @param _asset The address of the asset 432 | * @return True if the asset is supported, false otherwise 433 | */ 434 | function isSupportedAsset(address _asset) external view returns (bool) { 435 | if (_asset == address(0)) { 436 | return true; 437 | } 438 | uint256 length = ROLLUP_PROCESSOR.getSupportedAssetsLength(); 439 | for (uint256 i = 1; i <= length; i++) { 440 | address fetched = ROLLUP_PROCESSOR.getSupportedAsset(i); 441 | if (fetched == _asset) { 442 | return true; 443 | } 444 | } 445 | return false; 446 | } 447 | 448 | /** 449 | * @notice Computes defiInteractionHash 450 | * @param _encodedBridgeCallData The encodedBridgeCallData of the given interaction 451 | * @param _interactionNonce Nonce of the interaction 452 | * @param _totalInputValue An amount of input assets transferred to the bridge 453 | * @param _outputValueA An amount of `_outputAssetA` returned from this interaction 454 | * @param _outputValueB An amount of `_outputAssetB` returned from this interaction 455 | * @param _success A flag indicating whether the interaction succeeded 456 | * @return defiInteractionHash of a given interaction 457 | */ 458 | function computeDefiInteractionHash( 459 | uint256 _encodedBridgeCallData, 460 | uint256 _interactionNonce, 461 | uint256 _totalInputValue, 462 | uint256 _outputValueA, 463 | uint256 _outputValueB, 464 | bool _success 465 | ) external pure returns (bytes32) { 466 | return bytes32( 467 | uint256( 468 | sha256( 469 | abi.encode( 470 | _encodedBridgeCallData, 471 | _interactionNonce, 472 | _totalInputValue, 473 | _outputValueA, 474 | _outputValueB, 475 | _success 476 | ) 477 | ) 478 | ) % CIRCUIT_MODULUS 479 | ); 480 | } 481 | 482 | /** 483 | * @notice Prepares rollup processor state for rollup block, computes rollup block and cleans this contract state 484 | * so that next rollup block starts with a clean slate 485 | * @dev Rollup block consists of proof data and signatures 486 | * @return proofData Proof data 487 | * @return signatures Signatures corresponding to the proof data 488 | */ 489 | function prepProcessorAndGetRollupBlock() public returns (bytes memory proofData, bytes memory signatures) { 490 | _prepRollupProcessorState(); 491 | 492 | // TODO make the size computation here precise and dynamic 493 | proofData = new bytes(256 * 64); 494 | signatures = new bytes(0x20 + 0x60 * depositsL2Length); 495 | 496 | uint256 numRealTxs = depositsL2Length + withdrawalsL2Length + defiInteractionLength; 497 | 498 | { 499 | uint256 _nextRollupId = nextRollupId; 500 | uint256 numDataLeaves = numRealTxs * 2; 501 | uint256 dataSize = ROLLUP_PROCESSOR.getDataSize(); 502 | 503 | uint256 newDataStartIndex = 504 | (dataSize % numDataLeaves == 0) ? dataSize : (dataSize + numDataLeaves - (dataSize % numDataLeaves)); 505 | 506 | assembly { 507 | mstore(add(proofData, add(0x20, mul(0x20, 0))), _nextRollupId) 508 | // mstore(add(proofData, add(0x20, mul(0x20, 1))), size) 509 | mstore(add(proofData, add(0x20, mul(0x20, 2))), newDataStartIndex) 510 | mstore(add(proofData, add(0x20, mul(0x20, 3))), DATA_ROOT) 511 | mstore(add(proofData, add(0x20, mul(0x20, 4))), DATA_ROOT) 512 | mstore(add(proofData, add(0x20, mul(0x20, 5))), NULL_ROOT) 513 | mstore(add(proofData, add(0x20, mul(0x20, 6))), NULL_ROOT) 514 | mstore(add(proofData, add(0x20, mul(0x20, 7))), DATA_ROOTS_ROOT) 515 | mstore(add(proofData, add(0x20, mul(0x20, 8))), DATA_ROOTS_ROOT) 516 | mstore(add(proofData, add(0x20, mul(0x20, 9))), DEFI_ROOT) 517 | mstore(add(proofData, add(0x20, mul(0x20, 10))), DEFI_ROOT) 518 | } 519 | } 520 | 521 | // Time to loop for the bridges. Ignores any proof data related to the interaction 522 | for (uint256 i = 0; i < 32; i++) { 523 | DefiInteractionStruct storage interaction = defiInteractionsL2[i]; 524 | if (interaction.encodedBridgeCallData != 0) { 525 | uint256 encodedBridgeCallData = interaction.encodedBridgeCallData; 526 | uint256 totalInputValue = interaction.totalInputValue; 527 | assembly { 528 | mstore(add(proofData, add(0x20, mul(0x20, add(11, i)))), encodedBridgeCallData) 529 | mstore(add(proofData, add(0x20, mul(0x20, add(43, i)))), totalInputValue) 530 | } 531 | } 532 | } 533 | 534 | // Fees 535 | for (uint256 assetId = 0; assetId < NUMBER_OF_ASSETS; assetId++) { 536 | uint256 feeAmount = fees[assetId]; 537 | if (feeAmount > 0) { 538 | assembly { 539 | mstore(add(proofData, add(0x20, mul(0x20, add(75, assetId)))), assetId) 540 | mstore(add(proofData, add(0x20, mul(0x20, add(91, assetId)))), feeAmount) 541 | } 542 | delete fees[assetId]; 543 | } else { 544 | assembly { 545 | mstore(add(proofData, add(0x20, mul(0x20, add(75, assetId)))), 0x40000000) 546 | } 547 | } 548 | } 549 | 550 | { 551 | // Interaction notes. For mock, ignore any proofs related to this 552 | uint256 defiInteractionLength_ = ROLLUP_PROCESSOR.getDefiInteractionHashesLength(); 553 | uint256 numPending = 32 > defiInteractionLength_ ? defiInteractionLength_ : 32; 554 | uint256 offset = defiInteractionLength_ - numPending; 555 | for (uint256 i = 0; i < 32; i++) { 556 | if (offset + i < defiInteractionLength_) { 557 | { 558 | bytes32 hash = ROLLUP_PROCESSOR.defiInteractionHashes(offset + i); 559 | assembly { 560 | mstore(add(proofData, add(0x20, mul(0x20, add(107, i)))), hash) 561 | } 562 | } 563 | } else { 564 | assembly { 565 | mstore(add(proofData, add(0x20, mul(0x20, add(107, i)))), DEFI_RESULT_ZERO_HASH) 566 | } 567 | } 568 | } 569 | bytes32 prevDefiInteractionsHash; 570 | assembly { 571 | let hashData := add(proofData, add(0x20, mul(0x20, 107))) 572 | pop(staticcall(gas(), 0x2, hashData, 1024, 0x00, 0x20)) 573 | prevDefiInteractionsHash := mod(mload(0x00), CIRCUIT_MODULUS) 574 | } 575 | vm.store(address(ROLLUP_PROCESSOR), bytes32(uint256(16)), prevDefiInteractionsHash); 576 | } 577 | 578 | { 579 | bytes32 prev = ROLLUP_PROCESSOR.prevDefiInteractionsHash(); 580 | address rollupBeneficiaryUnpacked = rollupBeneficiary; 581 | assembly { 582 | mstore(add(proofData, add(0x20, mul(0x20, 139))), prev) 583 | mstore(add(proofData, add(0x20, mul(0x20, 140))), rollupBeneficiaryUnpacked) 584 | } 585 | } 586 | 587 | // Handle deposits 588 | uint256 proofLoc = 0x20 + 0x11c8; 589 | uint256 sigIndex = 0x20; 590 | for (uint256 i = 0; i < defiInteractionLength; i++) { 591 | proofLoc = _addDefiTx(proofData, proofLoc, i); 592 | } 593 | for (uint256 i = 0; i < depositsL2Length; i++) { 594 | (proofLoc, sigIndex) = _addDepositTx(proofData, proofLoc, signatures, sigIndex, i); 595 | } 596 | for (uint256 i = 0; i < withdrawalsL2Length; i++) { 597 | proofLoc = _addWithdrawTx(proofData, proofLoc, i); 598 | } 599 | 600 | { 601 | uint256 length = proofLoc - (0x20 + 0x11c8); 602 | assembly { 603 | mstore(add(proofData, add(0x20, mul(0x20, 141))), numRealTxs) 604 | mstore(add(proofData, add(0x20, mul(0x20, 1))), numRealTxs) 605 | let pre := mload(add(proofData, add(0x20, 0x11c0))) 606 | let txCount := shl(224, numRealTxs) 607 | let encodedTxDataLength := shl(192, length) 608 | let after := or(pre, or(encodedTxDataLength, txCount)) 609 | mstore(add(proofData, add(0x20, 0x11c0)), after) 610 | } 611 | } 612 | 613 | { 614 | uint256 proofSize = proofLoc - 0x20; 615 | uint256 sigSize = sigIndex - 0x20; 616 | assembly { 617 | mstore(proofData, proofSize) 618 | mstore(signatures, sigSize) 619 | } 620 | } 621 | 622 | // Reset "array" lengths 623 | depositsL2Length = withdrawalsL2Length = defiInteractionLength = defiBridgeProcessedStructsLength = 0; 624 | } 625 | 626 | /** 627 | * @notice Gets the id a given `_asset` 628 | * @dev if `_asset` is not supported will revert with `UnsupportedAsset(_asset)` 629 | * @param _asset The address of the asset to fetch id for 630 | * @return The id matching `_asset` 631 | */ 632 | function getAssetId(address _asset) public view returns (uint256) { 633 | if (_asset == address(0)) { 634 | return 0; 635 | } 636 | uint256 length = ROLLUP_PROCESSOR.getSupportedAssetsLength(); 637 | for (uint256 i = 1; i <= length; i++) { 638 | address fetched = ROLLUP_PROCESSOR.getSupportedAsset(i); 639 | if (fetched == _asset) { 640 | return i; 641 | } 642 | } 643 | revert UnsupportedAsset(_asset); 644 | } 645 | 646 | /** 647 | * @notice Encodes bridge call data into a bit-string 648 | * @dev For more info see the rollup implementation at "rollup.aztec.eth" that decodes 649 | * @param _bridgeAddressId id of the specific bridge (index in supportedBridge + 1) 650 | * @param _inputAssetA The first input asset 651 | * @param _inputAssetB The second input asset 652 | * @param _outputAssetA The first output asset 653 | * @param _outputAssetB The second output asset 654 | * @param _auxData Auxiliary data that is passed to the bridge 655 | * @return encodedBridgeCallData - The encoded bitmap containing encoded information about the call 656 | */ 657 | function encodeBridgeCallData( 658 | uint256 _bridgeAddressId, 659 | AztecTypes.AztecAsset memory _inputAssetA, 660 | AztecTypes.AztecAsset memory _inputAssetB, 661 | AztecTypes.AztecAsset memory _outputAssetA, 662 | AztecTypes.AztecAsset memory _outputAssetB, 663 | uint256 _auxData 664 | ) public pure returns (uint256 encodedBridgeCallData) { 665 | encodedBridgeCallData = _bridgeAddressId & MASK_THIRTY_TWO_BITS; 666 | 667 | // Input assets 668 | encodedBridgeCallData = encodedBridgeCallData | (_encodeAsset(_inputAssetA) << INPUT_ASSET_ID_A_SHIFT); 669 | encodedBridgeCallData = encodedBridgeCallData | (_encodeAsset(_inputAssetB) << INPUT_ASSET_ID_B_SHIFT); 670 | encodedBridgeCallData = encodedBridgeCallData | (_encodeAsset(_outputAssetA) << OUTPUT_ASSET_ID_A_SHIFT); 671 | encodedBridgeCallData = encodedBridgeCallData | (_encodeAsset(_outputAssetB) << OUTPUT_ASSET_ID_B_SHIFT); 672 | 673 | // Aux data 674 | encodedBridgeCallData = encodedBridgeCallData | ((_auxData & MASK_SIXTY_FOUR_BITS) << AUX_DATA_SHIFT); 675 | 676 | // bit config 677 | uint256 bitConfig = (_inputAssetB.assetType != AztecTypes.AztecAssetType.NOT_USED ? 1 : 0) 678 | | (_outputAssetB.assetType != AztecTypes.AztecAssetType.NOT_USED ? 2 : 0); 679 | encodedBridgeCallData = encodedBridgeCallData | (bitConfig << BITCONFIG_SHIFT); 680 | } 681 | 682 | /** 683 | * @notice Iterates through logs, decodes relevant events and returns values which were originally returned from 684 | * bridge's `convert(...)` function. 685 | * @dev You have to call `vm.recordLogs()` before calling this function 686 | * @dev If there are multiple DefiBridgeProcessed events, values of the last one are returned --> this occurs when 687 | * the bridge finalises interactions within it's convert functions. Returning values of the last ones works 688 | * because the last emitted DefiBridgeProcessed event corresponds to the `convert(...)` call. 689 | * @return outputValueA the amount of outputAssetA returned from the DeFi bridge interaction in this rollup 690 | * @return outputValueB the amount of outputAssetB returned from the DeFi bridge interaction in this rollup 691 | * @return isAsync a flag indicating whether the DeFi bridge interaction in this rollup was async 692 | */ 693 | function _getDefiBridgeProcessedData() 694 | internal 695 | returns (uint256 outputValueA, uint256 outputValueB, bool isAsync) 696 | { 697 | Vm.Log[] memory logs = vm.getRecordedLogs(); 698 | 699 | for (uint256 i; i < logs.length; ++i) { 700 | if (logs[i].topics[0] == BRIDGE_PROCESSED_EVENT_SIG) { 701 | (, outputValueA, outputValueB) = abi.decode(logs[i].data, (uint256, uint256, uint256)); 702 | } else if (logs[i].topics[0] == ASYNC_BRIDGE_PROCESSED_EVENT_SIG) { 703 | // We don't return totalInputValue so there is no need to decode the event's data 704 | return (0, 0, true); 705 | } 706 | } 707 | } 708 | 709 | function _prepRollupProcessorState() private { 710 | // Overwrite the rollup state hash 711 | { 712 | bytes32 rollupStateHash = keccak256( 713 | abi.encode( 714 | uint256(nextRollupId), 715 | 0x18ceb5cd201e1cee669a5c3ad96d3c4e933a365b37046fc3178264bede32c68d, 716 | 0x298329c7d0936453f354e4a5eef4897296cc0bf5a66f2a528318508d2088dafa, 717 | 0x2fd2364bfe47ccb410eba3a958be9f39a8c6aca07db1abd15f5a211f51505071, 718 | 0x2e4ab7889ab3139204945f9e722c7a8fdb84e66439d787bd066c3d896dba04ea 719 | ) 720 | ); 721 | vm.store(address(ROLLUP_PROCESSOR), bytes32(uint256(9)), rollupStateHash); 722 | } 723 | 724 | if (nextRollupId == 0) { 725 | // Overwrite the rollup state to zero (only verifier address) 726 | vm.store( 727 | address(ROLLUP_PROCESSOR), bytes32(uint256(2)), bytes32(uint256(uint160(ROLLUP_PROCESSOR.verifier()))) 728 | ); 729 | } 730 | if (mockVerifierCall) { 731 | vm.mockCall(ROLLUP_PROCESSOR.verifier(), "", abi.encode(true)); 732 | } 733 | } 734 | 735 | function _addDepositTx( 736 | bytes memory _encodedProofData, 737 | uint256 _encodedProofLocation, 738 | bytes memory _signatures, 739 | uint256 _sigIndex, 740 | uint256 id 741 | ) private returns (uint256, uint256) { 742 | DepositStruct memory deposit_ = depositsL2[id]; 743 | 744 | address publicOwner = vm.addr(deposit_.privKey); 745 | 746 | bytes memory encoded = abi.encodePacked( 747 | uint8(ProofId.DEPOSIT), // 1 748 | bytes32("leaf1"), // 32 (33) 749 | bytes32("leaf2"), // 32 (65) 750 | bytes32("null1"), // 32 (97) 751 | bytes32("null2"), // 32 (129) 752 | bytes32(deposit_.amount + deposit_.fee), // 32 (161) 753 | publicOwner, // 20 (181) 754 | uint32(deposit_.assetId) // 4 (185) 755 | ); 756 | 757 | assembly { 758 | mstore(add(_encodedProofData, _encodedProofLocation), mload(add(encoded, 0x20))) 759 | mstore(add(_encodedProofData, add(_encodedProofLocation, 0x20)), mload(add(encoded, 0x40))) 760 | mstore(add(_encodedProofData, add(_encodedProofLocation, 0x40)), mload(add(encoded, 0x60))) 761 | mstore(add(_encodedProofData, add(_encodedProofLocation, 0x60)), mload(add(encoded, 0x80))) 762 | mstore(add(_encodedProofData, add(_encodedProofLocation, 0x80)), mload(add(encoded, 0xa0))) 763 | mstore(add(_encodedProofData, add(_encodedProofLocation, 0xa0)), mload(add(encoded, 0xc0))) 764 | } 765 | 766 | if (deposit_.proofHash != bytes32("")) { 767 | delete depositsL2[id]; 768 | return (_encodedProofLocation + 0xb9, _sigIndex); 769 | } 770 | 771 | bytes memory full = abi.encodePacked( 772 | bytes32(uint256(ProofId.DEPOSIT)), 773 | bytes32("leaf1"), 774 | bytes32("leaf2"), 775 | bytes32("null1"), 776 | bytes32("null2"), 777 | bytes32(deposit_.amount + deposit_.fee), 778 | bytes32(uint256(uint160(publicOwner))), 779 | bytes32(deposit_.assetId) 780 | ); 781 | 782 | bytes32 proofHash = keccak256(full); 783 | bytes32 hashedMessage = RollupProcessorLibrary.getSignedMessageForTxId(proofHash); 784 | (uint8 v, bytes32 r, bytes32 s) = vm.sign(deposit_.privKey, hashedMessage); 785 | 786 | assembly { 787 | mstore(add(_signatures, _sigIndex), r) 788 | mstore(add(_signatures, add(_sigIndex, 0x20)), s) 789 | mstore(add(_signatures, add(_sigIndex, 0x40)), v) 790 | } 791 | delete depositsL2[id]; 792 | return (_encodedProofLocation + 0xb9, _sigIndex + 0x60); 793 | } 794 | 795 | function _addWithdrawTx(bytes memory _encodedProofData, uint256 _encodedProofLocation, uint256 id) 796 | private 797 | returns (uint256) 798 | { 799 | WithdrawStruct memory withdraw_ = withdrawalsL2[id]; 800 | 801 | bytes memory encoded = abi.encodePacked( 802 | uint8(ProofId.WITHDRAW), // 1 803 | bytes32("leaf1"), // 32 (33) 804 | bytes32("leaf2"), // 32 (65) 805 | bytes32("null1"), // 32 (97) 806 | bytes32("null2"), // 32 (129) 807 | bytes32(withdraw_.amount), // 32 (161) 808 | withdraw_.publicOwner, // 20 (181) 809 | uint32(withdraw_.assetId) // 4 (185) 810 | ); 811 | 812 | assembly { 813 | mstore(add(_encodedProofData, _encodedProofLocation), mload(add(encoded, 0x20))) 814 | mstore(add(_encodedProofData, add(_encodedProofLocation, 0x20)), mload(add(encoded, 0x40))) 815 | mstore(add(_encodedProofData, add(_encodedProofLocation, 0x40)), mload(add(encoded, 0x60))) 816 | mstore(add(_encodedProofData, add(_encodedProofLocation, 0x60)), mload(add(encoded, 0x80))) 817 | mstore(add(_encodedProofData, add(_encodedProofLocation, 0x80)), mload(add(encoded, 0xa0))) 818 | mstore(add(_encodedProofData, add(_encodedProofLocation, 0xa0)), mload(add(encoded, 0xc0))) 819 | } 820 | 821 | delete withdrawalsL2[id]; 822 | 823 | return _encodedProofLocation + 0xb9; 824 | } 825 | 826 | function _addDefiTx(bytes memory _encodedProofData, uint256 _encodedProofLocation, uint256 id) 827 | private 828 | returns (uint256) 829 | { 830 | bytes memory encoded = abi.encodePacked( 831 | uint8(ProofId.DEFI_DEPOSIT), // 1 832 | bytes32("leaf1"), // 32 (33) 833 | bytes32("leaf2"), // 32 (65) 834 | bytes32("null1"), // 32 (97) 835 | bytes32("null2") // 32 (129) 836 | ); 837 | 838 | assembly { 839 | mstore(add(_encodedProofData, _encodedProofLocation), mload(add(encoded, 0x20))) 840 | mstore(add(_encodedProofData, add(_encodedProofLocation, 0x20)), mload(add(encoded, 0x40))) 841 | mstore(add(_encodedProofData, add(_encodedProofLocation, 0x40)), mload(add(encoded, 0x60))) 842 | mstore(add(_encodedProofData, add(_encodedProofLocation, 0x60)), mload(add(encoded, 0x80))) 843 | mstore(add(_encodedProofData, add(_encodedProofLocation, 0x80)), mload(add(encoded, 0xa0))) 844 | } 845 | 846 | delete defiInteractionsL2[id]; 847 | 848 | return _encodedProofLocation + 0x81; 849 | } 850 | 851 | function _encodeAsset(AztecTypes.AztecAsset memory _asset) private pure returns (uint256) { 852 | if (_asset.assetType == AztecTypes.AztecAssetType.VIRTUAL) { 853 | return (_asset.id & MASK_THIRTY_BITS) | VIRTUAL_ASSET_ID_FLAG; 854 | } 855 | return _asset.id & MASK_THIRTY_BITS; 856 | } 857 | } 858 | -------------------------------------------------------------------------------- /src/interfaces/IRollupProcessor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright 2022 Aztec 3 | pragma solidity >=0.8.4; 4 | 5 | // @dev For documentation of the functions within this interface see RollupProcessor contract 6 | interface IRollupProcessor { 7 | /*---------------------------------------- 8 | MUTATING FUNCTIONS 9 | ----------------------------------------*/ 10 | 11 | function pause() external; 12 | 13 | function unpause() external; 14 | 15 | function setRollupProvider(address _provider, bool _valid) external; 16 | 17 | function setVerifier(address _verifier) external; 18 | 19 | function setAllowThirdPartyContracts(bool _allowThirdPartyContracts) external; 20 | 21 | function setDefiBridgeProxy(address _defiBridgeProxy) external; 22 | 23 | function setSupportedAsset(address _token, uint256 _gasLimit) external; 24 | 25 | function setSupportedBridge(address _bridge, uint256 _gasLimit) external; 26 | 27 | function processRollup(bytes calldata _encodedProofData, bytes calldata _signatures) external; 28 | 29 | function receiveEthFromBridge(uint256 _interactionNonce) external payable; 30 | 31 | function approveProof(bytes32 _proofHash) external; 32 | 33 | function depositPendingFunds(uint256 _assetId, uint256 _amount, address _owner, bytes32 _proofHash) 34 | external 35 | payable; 36 | 37 | function offchainData(uint256 _rollupId, uint256 _chunk, uint256 _totalChunks, bytes calldata _offchainTxData) 38 | external; 39 | 40 | function processAsyncDefiInteraction(uint256 _interactionNonce) external returns (bool); 41 | 42 | /*---------------------------------------- 43 | NON-MUTATING FUNCTIONS 44 | ----------------------------------------*/ 45 | 46 | function rollupStateHash() external view returns (bytes32); 47 | 48 | function userPendingDeposits(uint256 _assetId, address _user) external view returns (uint256); 49 | 50 | function defiBridgeProxy() external view returns (address); 51 | 52 | function prevDefiInteractionsHash() external view returns (bytes32); 53 | 54 | function paused() external view returns (bool); 55 | 56 | function verifier() external view returns (address); 57 | 58 | function getDataSize() external view returns (uint256); 59 | 60 | function getPendingDefiInteractionHashesLength() external view returns (uint256); 61 | 62 | function getDefiInteractionHashesLength() external view returns (uint256); 63 | 64 | function getAsyncDefiInteractionHashesLength() external view returns (uint256); 65 | 66 | function getSupportedBridge(uint256 _bridgeAddressId) external view returns (address); 67 | 68 | function getSupportedBridgesLength() external view returns (uint256); 69 | 70 | function getSupportedAssetsLength() external view returns (uint256); 71 | 72 | function getSupportedAsset(uint256 _assetId) external view returns (address); 73 | 74 | function getEscapeHatchStatus() external view returns (bool, uint256); 75 | 76 | function assetGasLimits(uint256 _bridgeAddressId) external view returns (uint256); 77 | 78 | function bridgeGasLimits(uint256 _bridgeAddressId) external view returns (uint256); 79 | 80 | function allowThirdPartyContracts() external view returns (bool); 81 | } 82 | -------------------------------------------------------------------------------- /src/interfaces/IRollupProcessorV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright 2022 Aztec 3 | pragma solidity >=0.8.4; 4 | 5 | import {IRollupProcessor} from "./IRollupProcessor.sol"; 6 | 7 | // @dev For documentation of the functions within this interface see RollupProcessorV2 contract 8 | interface IRollupProcessorV2 is IRollupProcessor { 9 | function getCapped() external view returns (bool); 10 | 11 | function defiInteractionHashes(uint256) external view returns (bytes32); 12 | } 13 | -------------------------------------------------------------------------------- /src/libraries/AztecTypes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright 2022 Aztec 3 | pragma solidity >=0.8.4; 4 | 5 | library AztecTypes { 6 | enum AztecAssetType { 7 | NOT_USED, 8 | ETH, 9 | ERC20, 10 | VIRTUAL 11 | } 12 | 13 | struct AztecAsset { 14 | uint256 id; 15 | address erc20Address; 16 | AztecAssetType assetType; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/libraries/EventsErrorsV2.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | /** 4 | * @notice Errors and events copied from RollupProcessorV2. 5 | * @dev This contract is extended by RollupEncoder in order for the errors to get properly resolved in Foundry tests. 6 | */ 7 | contract EventsErrorsV2 { 8 | /*---------------------------------------- 9 | ERROR TAGS 10 | ----------------------------------------*/ 11 | 12 | error PAUSED(); 13 | error NOT_PAUSED(); 14 | error LOCKED_NO_REENTER(); 15 | error INVALID_PROVIDER(); 16 | error THIRD_PARTY_CONTRACTS_FLAG_NOT_SET(); 17 | error INSUFFICIENT_DEPOSIT(); 18 | error INVALID_ADDRESS_NO_CODE(); 19 | error INVALID_ASSET_GAS(); 20 | error INVALID_ASSET_ID(); 21 | error INVALID_ASSET_ADDRESS(); 22 | error INVALID_BRIDGE_GAS(); 23 | error INVALID_BRIDGE_CALL_DATA(); 24 | error INVALID_BRIDGE_ADDRESS(); 25 | error INVALID_ESCAPE_BOUNDS(); 26 | error INCONSISTENT_BRIDGE_CALL_DATA(); 27 | error BRIDGE_WITH_IDENTICAL_INPUT_ASSETS(uint256 inputAssetId); 28 | error BRIDGE_WITH_IDENTICAL_OUTPUT_ASSETS(uint256 outputAssetId); 29 | error ZERO_TOTAL_INPUT_VALUE(); 30 | error ARRAY_OVERFLOW(); 31 | error MSG_VALUE_WRONG_AMOUNT(); 32 | error INSUFFICIENT_ETH_PAYMENT(); 33 | error WITHDRAW_TO_ZERO_ADDRESS(); 34 | error DEPOSIT_TOKENS_WRONG_PAYMENT_TYPE(); 35 | error INSUFFICIENT_TOKEN_APPROVAL(); 36 | error NONZERO_OUTPUT_VALUE_ON_NOT_USED_ASSET(uint256 outputValue); 37 | error INCORRECT_STATE_HASH(bytes32 oldStateHash, bytes32 newStateHash); 38 | error INCORRECT_DATA_START_INDEX(uint256 providedIndex, uint256 expectedIndex); 39 | error INCORRECT_PREVIOUS_DEFI_INTERACTION_HASH( 40 | bytes32 providedDefiInteractionHash, bytes32 expectedDefiInteractionHash 41 | ); 42 | error PUBLIC_INPUTS_HASH_VERIFICATION_FAILED(uint256, uint256); 43 | error PROOF_VERIFICATION_FAILED(); 44 | error PENDING_CAP_SURPASSED(); 45 | error DAILY_CAP_SURPASSED(); 46 | 47 | /*---------------------------------------- 48 | EVENTS 49 | ----------------------------------------*/ 50 | event OffchainData(uint256 indexed rollupId, uint256 chunk, uint256 totalChunks, address sender); 51 | event RollupProcessed(uint256 indexed rollupId, bytes32[] nextExpectedDefiHashes, address sender); 52 | event DefiBridgeProcessed( 53 | uint256 indexed encodedBridgeCallData, 54 | uint256 indexed nonce, 55 | uint256 totalInputValue, 56 | uint256 totalOutputValueA, 57 | uint256 totalOutputValueB, 58 | bool result, 59 | bytes errorReason 60 | ); 61 | event AsyncDefiBridgeProcessed( 62 | uint256 indexed encodedBridgeCallData, uint256 indexed nonce, uint256 totalInputValue 63 | ); 64 | event Deposit(uint256 indexed assetId, address indexed depositorAddress, uint256 depositValue); 65 | event AssetAdded(uint256 indexed assetId, address indexed assetAddress, uint256 assetGasLimit); 66 | event BridgeAdded(uint256 indexed bridgeAddressId, address indexed bridgeAddress, uint256 bridgeGasLimit); 67 | event RollupProviderUpdated(address indexed providerAddress, bool valid); 68 | event VerifierUpdated(address indexed verifierAddress); 69 | event AllowThirdPartyContractsUpdated(bool allowed); 70 | event DefiBridgeProxyUpdated(address defiBridgeProxy); 71 | event Paused(address account); 72 | event Unpaused(address account); 73 | event DelayBeforeEscapeHatchUpdated(uint32 delay); 74 | event AssetCapUpdated(uint256 assetId, uint256 pendingCap, uint256 dailyCap); 75 | event CappedUpdated(bool isCapped); 76 | } 77 | -------------------------------------------------------------------------------- /src/libraries/RollupProcessorLibrary.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright 2022 Aztec 3 | pragma solidity >=0.8.4; 4 | 5 | library RollupProcessorLibrary { 6 | error SIGNATURE_ADDRESS_IS_ZERO(); 7 | error SIGNATURE_RECOVERY_FAILED(); 8 | error INVALID_SIGNATURE(); 9 | 10 | /** 11 | * Extracts the address of the signer with ECDSA. Performs checks on `s` and `v` to 12 | * to prevent signature malleability based attacks 13 | * @param digest - Hashed data being signed over. 14 | * @param signature - ECDSA signature over the secp256k1 elliptic curve. 15 | * @param signer - Address that signs the signature. 16 | */ 17 | function validateSignature(bytes32 digest, bytes memory signature, address signer) internal view { 18 | bool result; 19 | address recoveredSigner = address(0x0); 20 | if (signer == address(0x0)) { 21 | revert SIGNATURE_ADDRESS_IS_ZERO(); 22 | } 23 | 24 | // prepend "\x19Ethereum Signed Message:\n32" to the digest to create the signed message 25 | bytes32 message; 26 | assembly { 27 | mstore(0, "\x19Ethereum Signed Message:\n32") 28 | mstore(add(0, 28), digest) 29 | message := keccak256(0, 60) 30 | } 31 | assembly { 32 | let mPtr := mload(0x40) 33 | let byteLength := mload(signature) 34 | 35 | // store the signature digest 36 | mstore(mPtr, message) 37 | 38 | // load 'v' - we need it for a condition check 39 | // add 0x60 to jump over 3 words - length of bytes array, r and s 40 | let v := shr(248, mload(add(signature, 0x60))) // bitshifting, to resemble padLeft 41 | let s := mload(add(signature, 0x40)) 42 | 43 | /** 44 | * Original memory map for input to precompile 45 | * 46 | * signature : signature + 0x20 message 47 | * signature + 0x20 : signature + 0x40 r 48 | * signature + 0x40 : signature + 0x60 s 49 | * signature + 0x60 : signature + 0x80 v 50 | * Desired memory map for input to precompile 51 | * 52 | * signature : signature + 0x20 message 53 | * signature + 0x20 : signature + 0x40 v 54 | * signature + 0x40 : signature + 0x60 r 55 | * signature + 0x60 : signature + 0x80 s 56 | */ 57 | 58 | // store s 59 | mstore(add(mPtr, 0x60), s) 60 | // store r 61 | mstore(add(mPtr, 0x40), mload(add(signature, 0x20))) 62 | // store v 63 | mstore(add(mPtr, 0x20), v) 64 | result := 65 | and( 66 | and( 67 | // validate s is in lower half order 68 | lt(s, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A1), 69 | and( 70 | // validate signature length == 0x41 71 | eq(byteLength, 0x41), 72 | // validate v == 27 or v == 28 73 | or(eq(v, 27), eq(v, 28)) 74 | ) 75 | ), 76 | // validate call to ecrecover precompile succeeds 77 | staticcall(gas(), 0x01, mPtr, 0x80, mPtr, 0x20) 78 | ) 79 | 80 | // save the recoveredSigner only if the first word in signature is not `message` anymore 81 | switch eq(message, mload(mPtr)) 82 | case 0 { recoveredSigner := mload(mPtr) } 83 | mstore(mPtr, byteLength) // and put the byte length back where it belongs 84 | 85 | // validate that recoveredSigner is not address(0x00) 86 | result := and(result, not(iszero(recoveredSigner))) 87 | } 88 | if (!result) { 89 | revert SIGNATURE_RECOVERY_FAILED(); 90 | } 91 | if (recoveredSigner != signer) { 92 | revert INVALID_SIGNATURE(); 93 | } 94 | } 95 | 96 | /** 97 | * Extracts the address of the signer with ECDSA. Performs checks on `s` and `v` to 98 | * to prevent signature malleability based attacks 99 | * This 'Unpacked' version expects 'signature' to be a 96-byte array. 100 | * i.e. the `v` parameter occupies a full 32 bytes of memory, not 1 byte 101 | * @param hashedMessage - Hashed data being signed over. This function only works if the message has been pre formated to EIP https://eips.ethereum.org/EIPS/eip-191 102 | * @param signature - ECDSA signature over the secp256k1 elliptic curve. 103 | * @param signer - Address that signs the signature. 104 | */ 105 | function validateShieldSignatureUnpacked(bytes32 hashedMessage, bytes memory signature, address signer) 106 | internal 107 | view 108 | { 109 | bool result; 110 | address recoveredSigner = address(0x0); 111 | if (signer == address(0x0)) { 112 | revert SIGNATURE_ADDRESS_IS_ZERO(); 113 | } 114 | assembly { 115 | // There's a little trick we can pull. We expect `signature` to be a byte array, of length 0x60, with 116 | // 'v', 'r' and 's' located linearly in memory. Preceeding this is the length parameter of `signature`. 117 | // We *replace* the length param with the signature msg to get a memory block formatted for the precompile 118 | // load length as a temporary variable 119 | // N.B. we mutate the signature by re-ordering r, s, and v! 120 | let byteLength := mload(signature) 121 | 122 | // store the signature digest 123 | mstore(signature, hashedMessage) 124 | 125 | // load 'v' - we need it for a condition check 126 | // add 0x60 to jump over 3 words - length of bytes array, r and s 127 | let v := mload(add(signature, 0x60)) 128 | let s := mload(add(signature, 0x40)) 129 | 130 | /** 131 | * Original memory map for input to precompile 132 | * 133 | * signature : signature + 0x20 message 134 | * signature + 0x20 : signature + 0x40 r 135 | * signature + 0x40 : signature + 0x60 s 136 | * signature + 0x60 : signature + 0x80 v 137 | * Desired memory map for input to precompile 138 | * 139 | * signature : signature + 0x20 message 140 | * signature + 0x20 : signature + 0x40 v 141 | * signature + 0x40 : signature + 0x60 r 142 | * signature + 0x60 : signature + 0x80 s 143 | */ 144 | 145 | // move s to v position 146 | mstore(add(signature, 0x60), s) 147 | // move r to s position 148 | mstore(add(signature, 0x40), mload(add(signature, 0x20))) 149 | // move v to r position 150 | mstore(add(signature, 0x20), v) 151 | result := 152 | and( 153 | and( 154 | // validate s is in lower half order 155 | lt(s, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A1), 156 | and( 157 | // validate signature length == 0x60 (unpacked) 158 | eq(byteLength, 0x60), 159 | // validate v == 27 or v == 28 160 | or(eq(v, 27), eq(v, 28)) 161 | ) 162 | ), 163 | // validate call to ecrecover precompile succeeds 164 | staticcall(gas(), 0x01, signature, 0x80, signature, 0x20) 165 | ) 166 | 167 | // save the recoveredSigner only if the first word in signature is not `message` anymore 168 | switch eq(hashedMessage, mload(signature)) 169 | case 0 { recoveredSigner := mload(signature) } 170 | mstore(signature, byteLength) // and put the byte length back where it belongs 171 | 172 | // validate that recoveredSigner is not address(0x00) 173 | result := and(result, not(iszero(recoveredSigner))) 174 | } 175 | if (!result) { 176 | revert SIGNATURE_RECOVERY_FAILED(); 177 | } 178 | if (recoveredSigner != signer) { 179 | revert INVALID_SIGNATURE(); 180 | } 181 | } 182 | 183 | /** 184 | * Extracts the address of the signer with ECDSA. Performs checks on `s` and `v` to 185 | * to prevent signature malleability based attacks 186 | * This 'Unpacked' version expects 'signature' to be a 96-byte array. 187 | * i.e. the `v` parameter occupies a full 32 bytes of memory, not 1 byte 188 | * @param digest - Hashed data being signed over. 189 | * @param signature - ECDSA signature over the secp256k1 elliptic curve. 190 | * @param signer - Address that signs the signature. 191 | */ 192 | function validateUnpackedSignature(bytes32 digest, bytes memory signature, address signer) internal view { 193 | bool result; 194 | address recoveredSigner = address(0x0); 195 | if (signer == address(0x0)) { 196 | revert SIGNATURE_ADDRESS_IS_ZERO(); 197 | } 198 | 199 | // prepend "\x19Ethereum Signed Message:\n32" to the digest to create the signed message 200 | bytes32 message; 201 | assembly { 202 | mstore(0, "\x19Ethereum Signed Message:\n32") 203 | mstore(28, digest) 204 | message := keccak256(0, 60) 205 | } 206 | assembly { 207 | // There's a little trick we can pull. We expect `signature` to be a byte array, of length 0x60, with 208 | // 'v', 'r' and 's' located linearly in memory. Preceeding this is the length parameter of `signature`. 209 | // We *replace* the length param with the signature msg to get a memory block formatted for the precompile 210 | // load length as a temporary variable 211 | // N.B. we mutate the signature by re-ordering r, s, and v! 212 | let byteLength := mload(signature) 213 | 214 | // store the signature digest 215 | mstore(signature, message) 216 | 217 | // load 'v' - we need it for a condition check 218 | // add 0x60 to jump over 3 words - length of bytes array, r and s 219 | let v := mload(add(signature, 0x60)) 220 | let s := mload(add(signature, 0x40)) 221 | 222 | /** 223 | * Original memory map for input to precompile 224 | * 225 | * signature : signature + 0x20 message 226 | * signature + 0x20 : signature + 0x40 r 227 | * signature + 0x40 : signature + 0x60 s 228 | * signature + 0x60 : signature + 0x80 v 229 | * Desired memory map for input to precompile 230 | * 231 | * signature : signature + 0x20 message 232 | * signature + 0x20 : signature + 0x40 v 233 | * signature + 0x40 : signature + 0x60 r 234 | * signature + 0x60 : signature + 0x80 s 235 | */ 236 | 237 | // move s to v position 238 | mstore(add(signature, 0x60), s) 239 | // move r to s position 240 | mstore(add(signature, 0x40), mload(add(signature, 0x20))) 241 | // move v to r position 242 | mstore(add(signature, 0x20), v) 243 | result := 244 | and( 245 | and( 246 | // validate s is in lower half order 247 | lt(s, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A1), 248 | and( 249 | // validate signature length == 0x60 (unpacked) 250 | eq(byteLength, 0x60), 251 | // validate v == 27 or v == 28 252 | or(eq(v, 27), eq(v, 28)) 253 | ) 254 | ), 255 | // validate call to ecrecover precompile succeeds 256 | staticcall(gas(), 0x01, signature, 0x80, signature, 0x20) 257 | ) 258 | 259 | // save the recoveredSigner only if the first word in signature is not `message` anymore 260 | switch eq(message, mload(signature)) 261 | case 0 { recoveredSigner := mload(signature) } 262 | mstore(signature, byteLength) // and put the byte length back where it belongs 263 | 264 | // validate that recoveredSigner is not address(0x00) 265 | result := and(result, not(iszero(recoveredSigner))) 266 | } 267 | if (!result) { 268 | revert SIGNATURE_RECOVERY_FAILED(); 269 | } 270 | if (recoveredSigner != signer) { 271 | revert INVALID_SIGNATURE(); 272 | } 273 | } 274 | 275 | /** 276 | * Convert a bytes32 into an ASCII encoded hex string 277 | * @param input bytes32 variable 278 | * @return result hex-encoded string 279 | */ 280 | function toHexString(bytes32 input) public pure returns (string memory result) { 281 | if (uint256(input) == 0x00) { 282 | assembly { 283 | result := mload(0x40) 284 | mstore(result, 0x40) 285 | mstore(add(result, 0x20), 0x3030303030303030303030303030303030303030303030303030303030303030) 286 | mstore(add(result, 0x40), 0x3030303030303030303030303030303030303030303030303030303030303030) 287 | mstore(0x40, add(result, 0x60)) 288 | } 289 | return result; 290 | } 291 | assembly { 292 | result := mload(0x40) 293 | let table := add(result, 0x60) 294 | 295 | // Store lookup table that maps an integer from 0 to 99 into a 2-byte ASCII equivalent 296 | // Store lookup table that maps an integer from 0 to ff into a 2-byte ASCII equivalent 297 | mstore(add(table, 0x1e), 0x3030303130323033303430353036303730383039306130623063306430653066) 298 | mstore(add(table, 0x3e), 0x3130313131323133313431353136313731383139316131623163316431653166) 299 | mstore(add(table, 0x5e), 0x3230323132323233323432353236323732383239326132623263326432653266) 300 | mstore(add(table, 0x7e), 0x3330333133323333333433353336333733383339336133623363336433653366) 301 | mstore(add(table, 0x9e), 0x3430343134323433343434353436343734383439346134623463346434653466) 302 | mstore(add(table, 0xbe), 0x3530353135323533353435353536353735383539356135623563356435653566) 303 | mstore(add(table, 0xde), 0x3630363136323633363436353636363736383639366136623663366436653666) 304 | mstore(add(table, 0xfe), 0x3730373137323733373437353736373737383739376137623763376437653766) 305 | mstore(add(table, 0x11e), 0x3830383138323833383438353836383738383839386138623863386438653866) 306 | mstore(add(table, 0x13e), 0x3930393139323933393439353936393739383939396139623963396439653966) 307 | mstore(add(table, 0x15e), 0x6130613161326133613461356136613761386139616161626163616461656166) 308 | mstore(add(table, 0x17e), 0x6230623162326233623462356236623762386239626162626263626462656266) 309 | mstore(add(table, 0x19e), 0x6330633163326333633463356336633763386339636163626363636463656366) 310 | mstore(add(table, 0x1be), 0x6430643164326433643464356436643764386439646164626463646464656466) 311 | mstore(add(table, 0x1de), 0x6530653165326533653465356536653765386539656165626563656465656566) 312 | mstore(add(table, 0x1fe), 0x6630663166326633663466356636663766386639666166626663666466656666) 313 | /** 314 | * Convert `input` into ASCII. 315 | * 316 | * Slice 2 base-10 digits off of the input, use to index the ASCII lookup table. 317 | * 318 | * We start from the least significant digits, write results into mem backwards, 319 | * this prevents us from overwriting memory despite the fact that each mload 320 | * only contains 2 byteso f useful data. 321 | * 322 | */ 323 | 324 | let base := input 325 | function slice(v, tableptr) { 326 | mstore(0x1e, mload(add(tableptr, shl(1, and(v, 0xff))))) 327 | mstore(0x1c, mload(add(tableptr, shl(1, and(shr(8, v), 0xff))))) 328 | mstore(0x1a, mload(add(tableptr, shl(1, and(shr(16, v), 0xff))))) 329 | mstore(0x18, mload(add(tableptr, shl(1, and(shr(24, v), 0xff))))) 330 | mstore(0x16, mload(add(tableptr, shl(1, and(shr(32, v), 0xff))))) 331 | mstore(0x14, mload(add(tableptr, shl(1, and(shr(40, v), 0xff))))) 332 | mstore(0x12, mload(add(tableptr, shl(1, and(shr(48, v), 0xff))))) 333 | mstore(0x10, mload(add(tableptr, shl(1, and(shr(56, v), 0xff))))) 334 | mstore(0x0e, mload(add(tableptr, shl(1, and(shr(64, v), 0xff))))) 335 | mstore(0x0c, mload(add(tableptr, shl(1, and(shr(72, v), 0xff))))) 336 | mstore(0x0a, mload(add(tableptr, shl(1, and(shr(80, v), 0xff))))) 337 | mstore(0x08, mload(add(tableptr, shl(1, and(shr(88, v), 0xff))))) 338 | mstore(0x06, mload(add(tableptr, shl(1, and(shr(96, v), 0xff))))) 339 | mstore(0x04, mload(add(tableptr, shl(1, and(shr(104, v), 0xff))))) 340 | mstore(0x02, mload(add(tableptr, shl(1, and(shr(112, v), 0xff))))) 341 | mstore(0x00, mload(add(tableptr, shl(1, and(shr(120, v), 0xff))))) 342 | } 343 | 344 | mstore(result, 0x40) 345 | slice(base, table) 346 | mstore(add(result, 0x40), mload(0x1e)) 347 | base := shr(128, base) 348 | slice(base, table) 349 | mstore(add(result, 0x20), mload(0x1e)) 350 | mstore(0x40, add(result, 0x60)) 351 | } 352 | } 353 | 354 | function getSignedMessageForTxId(bytes32 txId) internal pure returns (bytes32 hashedMessage) { 355 | // we know this string length is 64 bytes 356 | string memory txIdHexString = toHexString(txId); 357 | 358 | assembly { 359 | let mPtr := mload(0x40) 360 | mstore(add(mPtr, 32), "\x19Ethereum Signed Message:\n210") 361 | mstore(add(mPtr, 61), "Signing this message will allow ") 362 | mstore(add(mPtr, 93), "your pending funds to be spent i") 363 | mstore(add(mPtr, 125), "n Aztec transaction:\n\n0x") 364 | mstore(add(mPtr, 149), mload(add(txIdHexString, 0x20))) 365 | mstore(add(mPtr, 181), mload(add(txIdHexString, 0x40))) 366 | mstore(add(mPtr, 213), "\n\nIMPORTANT: Only sign the messa") 367 | mstore(add(mPtr, 245), "ge if you trust the client") 368 | hashedMessage := keccak256(add(mPtr, 32), 239) 369 | } 370 | } 371 | } 372 | -------------------------------------------------------------------------------- /test/RollupEncoder.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | // Copyright 2022 Aztec. 3 | pragma solidity >=0.8.4; 4 | 5 | import {Test} from "forge-std/Test.sol"; 6 | 7 | import {AztecTypes} from "../src/libraries/AztecTypes.sol"; 8 | import {RollupEncoder} from "../src/RollupEncoder.sol"; 9 | 10 | contract RollupEncoderTest is RollupEncoder, Test { 11 | uint256 internal constant ADDRESS_MASK = 0x00ffffffffffffffffffffffffffffffffffffffff; 12 | uint256 internal constant ROLLUP_BENEFICIARY_OFFSET = 4512; // ROLLUP_HEADER_LENGTH - 0x20 13 | 14 | AztecTypes.AztecAsset internal emptyAsset; 15 | 16 | constructor() RollupEncoder(0xFF1F2B4ADb9dF6FC8eAFecDcbF96A2B351680455) {} 17 | 18 | function testVirtualAssetFlagApplied(uint32 _assetId) public { 19 | uint256 assetId = bound(_assetId, 0, VIRTUAL_ASSET_ID_FLAG - 1); 20 | uint256 virtualAsset = assetId + VIRTUAL_ASSET_ID_FLAG; 21 | 22 | AztecTypes.AztecAsset memory decoded = _decodeAsset(virtualAsset); 23 | assertEq(decoded.erc20Address, address(0), "Virtual asset has erc20 address"); 24 | assertEq(decoded.id, assetId, "Asset Id not matching"); 25 | assertTrue(decoded.assetType == AztecTypes.AztecAssetType.VIRTUAL, "Not virtual"); 26 | } 27 | 28 | function testNonVirtual(uint32 _assetId) public { 29 | uint256 assetId = bound(_assetId, 0, ROLLUP_PROCESSOR.getSupportedAssetsLength()); 30 | 31 | address assetAddress = ROLLUP_PROCESSOR.getSupportedAsset(assetId); 32 | 33 | AztecTypes.AztecAsset memory decoded = _decodeAsset(assetId); 34 | 35 | assertEq(decoded.erc20Address, assetAddress, "asset address not matching"); 36 | assertEq(decoded.id, assetId, "Asset Id not matching"); 37 | if (assetAddress == address(0)) { 38 | assertTrue(decoded.assetType == AztecTypes.AztecAssetType.ETH, "Not eth"); 39 | } else { 40 | assertTrue(decoded.assetType == AztecTypes.AztecAssetType.ERC20, "Not erc20"); 41 | } 42 | } 43 | 44 | function testGetDefiBridgeProcessedData() public { 45 | uint256 outputValueAReference = 896; 46 | uint256 outputValueBReference = 351; 47 | 48 | vm.recordLogs(); 49 | emit DefiBridgeProcessed(126, 12, 41, outputValueAReference, outputValueBReference, true, ""); 50 | (uint256 outputValueA, uint256 outputValueB, bool isAsync) = _getDefiBridgeProcessedData(); 51 | 52 | assertEq(outputValueA, outputValueAReference, "Incorrect outputValueA"); 53 | assertEq(outputValueB, outputValueBReference, "Incorrect outputValueB"); 54 | assertFalse(isAsync, "Interaction unexpectedly async"); 55 | } 56 | 57 | function testGetDefiBridgeProcessedDataAsync() public { 58 | vm.recordLogs(); 59 | emit AsyncDefiBridgeProcessed(126, 12, 1e18); 60 | (uint256 outputValueA, uint256 outputValueB, bool isAsync) = _getDefiBridgeProcessedData(); 61 | 62 | assertEq(outputValueA, 0, "Incorrect outputValueA"); 63 | assertEq(outputValueB, 0, "Incorrect outputValueB"); 64 | assertTrue(isAsync, "Interaction unexpectedly async"); 65 | } 66 | 67 | function testRollupBeneficiaryEncoding() public { 68 | address beneficiary = 0x508383c4cbD351dC2d4F632C65Ee9d2BC79612EC; 69 | this.setRollupBeneficiary(beneficiary); 70 | this.defiInteractionL2(5, emptyAsset, emptyAsset, emptyAsset, emptyAsset, 0, 0); 71 | (bytes memory encodedProofData,) = prepProcessorAndGetRollupBlock(); 72 | address decodedBeneficiary = _extractRollupBeneficiary(encodedProofData); 73 | assertEq(decodedBeneficiary, beneficiary, "Decoded address does not match"); 74 | } 75 | 76 | function testMiniDeposit() public { 77 | uint256 user1Priv = 123123123; 78 | address user1 = vm.addr(user1Priv); 79 | address user2 = address(0xdead); 80 | emit log_named_address("user ", user1); 81 | vm.deal(user1, 2 ether); 82 | vm.deal(user2, 0); 83 | 84 | emit log_named_decimal_uint("Balance R", address(ROLLUP_PROCESSOR).balance, 18); 85 | emit log_named_decimal_uint("Balance U", user2.balance, 18); 86 | 87 | vm.prank(user1); 88 | ROLLUP_PROCESSOR.depositPendingFunds{value: 2 ether}(0, 2 ether, user1, bytes32("")); 89 | 90 | emit log_named_uint("pending", ROLLUP_PROCESSOR.userPendingDeposits(0, user1)); 91 | this.depositL2(0, 1 ether, 0, user1Priv); 92 | 93 | this.withdrawL2(0, 5 ether, user2); 94 | 95 | this.processRollup(); 96 | 97 | emit log_named_uint("pending", ROLLUP_PROCESSOR.userPendingDeposits(0, user1)); 98 | emit log_named_decimal_uint("Balance R", address(ROLLUP_PROCESSOR).balance, 18); 99 | emit log_named_decimal_uint("Balance U", user2.balance, 18); 100 | 101 | this.withdrawL2(0, 5 ether, user2); 102 | this.processRollup(); 103 | 104 | emit log_named_uint("pending", ROLLUP_PROCESSOR.userPendingDeposits(0, user1)); 105 | emit log_named_decimal_uint("Balance R", address(ROLLUP_PROCESSOR).balance, 18); 106 | emit log_named_decimal_uint("Balance U", user2.balance, 18); 107 | } 108 | 109 | function _decodeAsset(uint256 _assetId) internal view returns (AztecTypes.AztecAsset memory) { 110 | if (_assetId >> VIRTUAL_ASSET_ID_FLAG_SHIFT == 1) { 111 | return AztecTypes.AztecAsset({ 112 | id: _assetId - VIRTUAL_ASSET_ID_FLAG, 113 | erc20Address: address(0), 114 | assetType: AztecTypes.AztecAssetType.VIRTUAL 115 | }); 116 | } else { 117 | address erc20Address = ROLLUP_PROCESSOR.getSupportedAsset(_assetId); 118 | return AztecTypes.AztecAsset({ 119 | id: _assetId, 120 | erc20Address: erc20Address, 121 | assetType: erc20Address == address(0) ? AztecTypes.AztecAssetType.ETH : AztecTypes.AztecAssetType.ERC20 122 | }); 123 | } 124 | } 125 | 126 | // copied from Decoder.sol 127 | function _extractRollupBeneficiary(bytes memory _proofData) internal pure returns (address rollupBeneficiary) { 128 | /* solhint-disable no-inline-assembly */ 129 | assembly { 130 | rollupBeneficiary := mload(add(_proofData, ROLLUP_BENEFICIARY_OFFSET)) 131 | // Validate `rollupBeneficiary` is an address 132 | if gt(rollupBeneficiary, ADDRESS_MASK) { revert(0, 0) } 133 | } 134 | /* solhint-enable no-inline-assembly */ 135 | } 136 | } 137 | --------------------------------------------------------------------------------