1/5
## Setting the Stage: Cross-Chain Token Deployment Goals Our primary objective is to prepare and deploy smart contracts onto live testnets. This will allow us to thoroughly test the cross-chain functionality, specifically focusing on sending tokens between different blockchain networks. The deployment strategy involves two key testnets: * **On Sepolia Testnet:** * Deploy a `Vault` contract. * Deploy a `RebaseToken` contract. * Deploy an associated `TokenPool` contract for the `RebaseToken`. * **On zkSync Sepolia Testnet:** * Deploy another distinct token contract. * Deploy an associated `TokenPool` contract for this second token. The ultimate aim of these deployments is to enable and verify the process of bridging tokens from the Sepolia testnet over to the zkSync Sepolia testnet. ## Compiling Your Contracts: Initial Build and Debugging in Foundry Before we can deploy, it's essential to ensure all our smart contracts and accompanying deployment scripts compile successfully. We'll use Foundry for this critical step. To initiate the build process, execute the following command in your terminal: ```bash forge build ``` During an initial build attempt, you might encounter compilation errors. For instance, a common issue is related to data location for struct variables in Solidity scripts: ``` Compiler run failed: Error (6651): Data location must be "storage", "memory" or "calldata" for variable, but none was given. --> script/BridgeTokens.s.sol:28:9: | 28 | Client.EVM2AnyMessage message = Client.EVM2AnyMessage({ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ``` This error message clearly indicates that the `message` variable, which is of type `Client.EVM2AnyMessage` (a struct), was declared without specifying its data location (e.g., `memory`, `storage`, or `calldata`). To resolve this, you need to modify the script file, in this case, `script/BridgeTokens.s.sol`. The fix involves explicitly adding the `memory` keyword to the struct variable declaration: ```solidity // Original problematic line: // Client.EVM2AnyMessage message = Client.EVM2AnyMessage({ ... }); // Corrected line: Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({ ... }); ``` After applying such fixes, running `forge build` again should result in the successful compilation of your contracts and scripts. ## Securing Your Setup: RPC URL Configuration and Best Practices When working with live testnets or mainnets, you'll need to configure RPC (Remote Procedure Call) endpoints. These are typically managed in your `foundry.toml` file. It is critically important to handle your RPC URLs with care. **Never share your personal RPC URLs publicly.** The RPC URLs you might see in demonstration videos or tutorials are often temporary and will be deactivated. If your personal RPC URL, especially one tied to an account with real funds or services you pay for, is exposed, it could be misused by malicious actors. This could lead to rate-limiting issues, denial of service, or even attempts to attribute malicious activity to your endpoint. Always protect your RPC URLs like you would any other sensitive credential. ## Navigating Foundry's `via_ir` Compiler and the `vm.warp()` Quirk For projects that utilize Continuous Integration (CI) pipelines, such as GitHub Actions, ensuring consistent behavior across different environments is key. Foundry's `via_ir` compiler option can be relevant here. Enabling `via_ir` instructs the Solidity compiler to generate an intermediate representation (Yul IR) before bytecode generation, which can sometimes affect optimizations or compatibility. You can enable this option by adding the following line to your `foundry.toml` file: ```toml # foundry.toml # ... via_ir = true # ... ``` However, there's a known issue: Foundry's `vm.warp()` cheatcode, which is invaluable for testing time-dependent logic by manipulating `block.timestamp`, may not function as expected when `via_ir` is enabled. This discrepancy is documented in the Foundry GitHub repository, specifically under an issue titled "vm.warp() does not work as intended when via_ir = true" (e.g., `github.com/foundry-rs/foundry/issues/8102`). This bug can cause tests that rely heavily on `vm.warp` to pass locally (where `via_ir` might be off by default) but fail in a CI environment if `via_ir` is active there. ## Crafting Robust Tests: Workarounds for the `via_ir` and `vm.warp()` Interaction Given the potential conflict between `via_ir` and `vm.warp()`, adjustments to your test files, particularly those involving time manipulation, may be necessary. Let's consider an example within `test/CrossChain.t.sol`. Tests that use `vm.warp` and then immediately assert state changes that depend on the warped timestamp can be problematic. For instance, checking a `userInterestRate` before a `vm.warp` and expecting it to remain unchanged relative to a pre-warp stored value on a *remote* contract after the warp can lead to failures when `via_ir` is active. The workaround strategy involves making your tests less sensitive to the exact timing behavior around `vm.warp` when `via_ir` is enabled: 1. **Refactor Assertions Around `vm.warp`:** In helper functions or test setups where `vm.warp` is used, avoid storing local variables that capture state (like an interest rate) *before* the `vm.warp` and then asserting their equality with a *remote* state *after* the `vm.warp`. For example, lines like these within a `bridgeTokens` helper function were identified as sources of potential issues and were removed or refactored: ```solidity // Problematic pattern when via_ir is enabled: // uint256 localUserInterestRate = localToken.getUserInterestRate(user); // Value before vm.warp // ... // vm.warp(block.timestamp + 20 minutes); // Time is warped // ... // // This assertion might fail due to via_ir affecting how state is perceived post-warp // assertEq(remoteToken.getUserInterestRate(user), localUserInterestRate); ``` 2. **Adjust Fee Handling (Optional Optimization):** While not directly a fix for the `vm.warp` issue, inlining fee calculations or variables directly into function calls rather than storing them in intermediate local variables can sometimes simplify logic and reduce potential points of failure, though this is more of a general refactoring tip. 3. **Restructure Assertion Logic:** Instead of making fine-grained assertions around `vm.warp` calls within helper functions, shift these critical checks. For instance, verify interest rates or other key states on the source chain and the destination chain independently *after* the entire cross-chain operation has completed within your main test function (e.g., `testBridgeAllTokens`). This approach focuses on the end-to-end correctness rather than intermediate state consistency that might be affected by the `via_ir` and `vm.warp` interaction. After implementing these test modifications to enhance robustness against the `via_ir` and `vm.warp()` behavior, running `forge build` should once again succeed. It's important to note that with these test adjustments, enabling `via_ir` might not be strictly "needed" for a local build to pass if it wasn't failing before. However, these changes ensure better compatibility and reliability if `via_ir` *is* used, for example, in a GitHub CI environment. ## Final Considerations for `via_ir` and Test Stability If you intend to use the `via_ir` compiler option, perhaps for its optimization benefits or to match a CI environment, implementing the aforementioned workarounds in your test suite becomes crucial. These changes, focused on how local variables are used around `vm.warp` and how assertions are structured, are key to maintaining test reliability. It's worth noting that some workarounds suggested in community discussions or GitHub issues (such as using `vm.getBlockTimestamp()` as an alternative way to fetch the current timestamp in tests) might not resolve the issue in all specific scenarios; the effectiveness can vary. For a more in-depth understanding of this particular Foundry bug and its related discussions, consulting the primary GitHub issue (`#8102`) and any linked issues (e.g., `#3806`, `#4934`, `#6180`) on the `foundry-rs/foundry` repository is highly recommended. These resources provide context and track the progress of potential fixes. By addressing these build and testing nuances, your project will be better prepared for successful deployment and robust cross-chain functionality verification.
An in-depth walkthrough to Mastering Cross-Chain Foundry Deployments and `via_ir` Test Stability - Prepare your smart contracts for cross-chain deployment across testnets like Sepolia and zkSync, focusing on successful compilation and secure RPC setup. Dive into resolving the `via_ir` and `vm.warp()` testing conflict in Foundry to ensure your CI pipelines run smoothly with robust tests.
Previous lesson
Previous
Next lesson
Next
Give us feedback
Course Overview
About the course
Advanced smart contract development
How to develop a stablecoin
How to develop a DeFi protocol
How to develop a DAO
Advanced smart contracts testing
Fuzz testing
Manual verification
Web3 Developer Relations
$85,000 - $125,000 (avg. salary)
Web3 developer
$60,000 - $150,000 (avg. salary)
Smart Contract Engineer
$100,000 - $150,000 (avg. salary)
Smart Contract Auditor
$100,000 - $200,000 (avg. salary)
Security researcher
$49,999 - $120,000 (avg. salary)
Web3 engineer, educator, and Cyfrin co-founder. Patrick's smart contract development and security courses have helped hundreds of thousands of engineers kickstarting their careers into web3.
Guest lecturers:
Last updated on May 20, 2025
Solidity Developer
Advanced FoundryDuration: 36min
Duration: 3h 06min
Duration: 5h 02min
Duration: 6h 02min
Duration: 2h 47min
Duration: 1h 23min
Duration: 4h 28min
Duration: 1h 19min
Duration: 1h 10min
Course Overview
About the course
Advanced smart contract development
How to develop a stablecoin
How to develop a DeFi protocol
How to develop a DAO
Advanced smart contracts testing
Fuzz testing
Manual verification
Web3 Developer Relations
$85,000 - $125,000 (avg. salary)
Web3 developer
$60,000 - $150,000 (avg. salary)
Smart Contract Engineer
$100,000 - $150,000 (avg. salary)
Smart Contract Auditor
$100,000 - $200,000 (avg. salary)
Security researcher
$49,999 - $120,000 (avg. salary)
Web3 engineer, educator, and Cyfrin co-founder. Patrick's smart contract development and security courses have helped hundreds of thousands of engineers kickstarting their careers into web3.
Guest lecturers:
Last updated on May 20, 2025