5/5
## Building Cross-Chain Rebase Tokens with Foundry and Chainlink CCIP Welcome to this advanced lesson where we'll dive deep into constructing, rigorously testing, and deploying a sophisticated financial instrument: a rebase token capable of operating and being transferred across multiple blockchains. We'll be leveraging the power of the Foundry development framework and Chainlink's Cross-Chain Interoperability Protocol (CCIP) to achieve this. This lesson, inspired by Ciara Nightingale's "Advanced Foundry Course" from Cyfrin, will equip you with the knowledge to build truly interoperable DeFi applications. The concepts and code structure are based on the `foundry-cross-chain-rebase-token-cu` repository. ## Core Concepts: Understanding the Building Blocks Before we delve into the code, let's establish a firm understanding of the fundamental concepts that underpin this project. * **Rebase Token:** At its heart, a rebase token is a type of cryptocurrency where the total supply adjusts algorithmically. This adjustment is distributed proportionally among all token holders. Consequently, a user's token balance changes not due to direct transfers in or out of their wallet, but because the effective quantity or "value" represented by each token unit shifts with the supply. In our specific implementation, this rebase mechanism will be tied to an interest rate, causing user balances to appear to grow over time as interest accrues. * **Cross-Chain Functionality:** This refers to the capability of our rebase token and its associated logic to operate across different, independent blockchains. The core challenge here is enabling the token, or at least its value representation, to move from a source chain to a destination chain seamlessly. * **Chainlink CCIP (Cross-Chain Interoperability Protocol):** CCIP is the pivotal technology enabling our token's cross-chain capabilities. It provides a secure and reliable way for smart contracts on one blockchain to send messages and transfer tokens to smart contracts on another blockchain. * **Burn-and-Mint Mechanism (for CCIP token transfers):** To maintain a consistent total circulating supply across all integrated chains (barring changes from the rebase mechanism itself), we employ a burn-and-mint strategy. When tokens are transferred from a source chain to a destination chain: 1. Tokens are "burned" (irrevocably destroyed) on the source chain. 2. An equivalent amount of new tokens is "minted" (created) on the destination chain. * **Foundry:** Our development environment of choice is Foundry, a powerful and fast smart contract development toolkit written in Rust. We'll use Foundry for writing, testing (including complex scenarios like fuzzing and fork testing), and deploying our Solidity smart contracts. * **Linear Interest:** The rebase token in this project will accrue interest based on a straightforward linear model. The interest earned will be a product of the user's specific interest rate and the time elapsed since their last balance update or interaction. * **Fork Testing:** A crucial testing methodology we'll utilize is fork testing. This involves creating a local, isolated copy (a "fork") of an actual blockchain (e.g., Sepolia testnet, Arbitrum Sepolia testnet) at a specific block height. This allows us to test our smart contracts' interactions with existing, deployed contracts and protocols in a highly realistic environment without incurring real gas costs or requiring deployment to a live testnet for every iteration. * **Local CCIP Simulation:** To streamline development and testing of cross-chain interactions, we will use tools like Chainlink Local's `CCIPLocalSimulatorFork`. This enables us to simulate CCIP message passing and token transfers entirely locally, which is invaluable for debugging and verifying logic before engaging with public testnets. ## Learning Objectives for This Section By the end of this lesson, you will have a comprehensive understanding of: * The fundamentals and practical application of Chainlink CCIP. * How to enable an existing token for CCIP compatibility. * Techniques for creating *custom* tokens specifically designed for CCIP, going beyond standard ERC20s. * The design and implementation of a rebase token. * Advanced Solidity and Foundry concepts, including: * Effective use of the `super` keyword for inheriting and extending contract functionality. * Sophisticated testing strategies. * Understanding and mitigating issues related to "token dust" (minute, economically insignificant token balances). * Handling precision and truncation challenges inherent in financial calculations, especially critical for rebase mechanisms. * Practical application of fork testing. * The use of nested structs for organizing complex data. * The mechanics of bridging tokens between different blockchains. * The intricacies of cross-chain transfers. ## Smart Contract Architecture and Implementation Details Let's explore the key smart contracts that constitute our cross-chain rebase token system. ### `RebaseToken.sol`: The Heart of Our Interest-Bearing Token This contract defines the core logic for our rebase token. * **Purpose:** To implement an ERC20-like token whose balances effectively increase over time due to an accrued interest mechanism. * **Key Functions/Logic:** * `constructor()`: Initializes the token with standard parameters like name (e.g., "RebaseToken") and symbol (e.g., "RBT"). * `balanceOf(address user) public view override returns (uint256)`: This function is a cornerstone of the rebase mechanism and behaves distinctively. Instead of merely returning a stored balance from a mapping, it *dynamically calculates* the user's current effective balance. It achieves this by taking the user's `currentPrincipalBalance` (which is the underlying ERC20-like balance, accessed via `super.balanceOf(user)`) and adding the `_calculateUserAccumulatedInterestSinceLastUpdate(user)`. The result is then adjusted by a `PRECISION_FACTOR` to manage decimal precision. ```solidity // Conceptual Solidity representation // function balanceOf(address user) public view override returns (uint256) { // uint256 currentPrincipalBalance = super.balanceOf(user); // Underlying stored balance // if (currentPrincipalBalance == 0) return 0; // // 'shares' represents the newly accrued interest for the user since their last update // uint256 accruedInterest = _calculateUserAccumulatedInterestSinceLastUpdate(user); // return (currentPrincipalBalance + accruedInterest) / PRECISION_FACTOR; // Or multiplied, depending on precision handling strategy // } ``` * **Modified Mint and Burn Functions:** The standard `_mint` and `_burn` functions (or their equivalents) will be augmented. Before any transfer, minting, or burning operation that affects a user's balance, a function like `_mintAccruedInterest(address user)` will be called. This function crystallizes any pending accrued interest into the user's principal balance, ensuring that all subsequent operations are based on their most up-to-date holdings. * **Interest Calculation Logic:** * `_calculateUserAccumulatedInterestSinceLastUpdate(address user)`: This internal function determines the amount of interest a user has earned since their last balance-modifying transaction or explicit interest update. * `_calculateLinearInterest(uint256 interestRate, uint256 timeDifference)`: This function implements the linear interest model: `linearInterest = (interestRate * timeDifference) / PRECISION_FACTOR`. It uses `s_userInterestRate[user]` (a mapping storing each user's specific interest rate) and the `timeDifference` since the last interest calculation point. ### `RebaseTokenPool.sol`: Enabling Cross-Chain Transfers with CCIP This contract is responsible for managing the cross-chain movement of our rebase token using Chainlink CCIP. It will likely inherit from or extensively utilize Chainlink's `Pool.sol` contract or similar CCIP-specific base contracts. * **Purpose:** To facilitate the burn-and-mint mechanism for transferring the rebase token between different blockchains via CCIP. * **Key Functions/Logic:** * It implements the burn-and-mint pattern: * `lockOrBurn(...)` (or a similarly named function): When tokens are sent from the source chain, this function is called. It burns the specified amount of rebase tokens on the source chain and then constructs and dispatches a CCIP message to the destination chain, instructing it to mint an equivalent amount. * `releaseOrMint(...)` (or a similarly named function): On the destination chain, this function is triggered upon receiving a valid CCIP message from the source chain's pool. It then mints the appropriate amount of rebase tokens to the recipient. * It will also handle the encoding of necessary data, such as the user's current interest rate, to be sent along with the CCIP message. This ensures that user-specific parameters are correctly propagated across chains. ### `Vault.sol`: Interacting with the Rebase Token The `Vault.sol` contract serves as an interface for users to acquire or redeem rebase tokens using a base asset (e.g., ETH). * **Purpose:** To allow users to deposit a base asset (like ETH) and receive rebase tokens in return, and conversely, to redeem their rebase tokens for the underlying base asset. * **Key Functions/Logic:** * `deposit() public payable`: Users send ETH to this function. The contract then mints an appropriate amount of rebase tokens to the user, calculated based on the current exchange rate (which might implicitly consider the token's accrued interest characteristics). * `redeem(uint256 amount)`: Users call this function, specifying the amount of rebase tokens they wish to redeem. The contract burns these rebase tokens from the user's balance and sends the corresponding amount of the base asset (ETH) back to the user. * `receive() external payable {}`: This special function allows the contract to receive ETH directly (e.g., through simple transfers), which is typically routed to the deposit logic. ## Development Workflow: Scripts and Testing Strategies Foundry's scripting and testing capabilities are integral to our development process. ### Foundry Scripts (`script/` directory) This directory houses Solidity scripts (`.s.sol` files) for automating deployment, configuration, and on-chain interactions. * `Deployer.s.sol`: This script handles the deployment of the `RebaseToken`, `RebaseTokenPool`, and `Vault` contracts to the target blockchain. * `ConfigurePool.s.sol`: After deployment, this script is used to configure the CCIP settings on the `RebaseTokenPool` contracts on each chain. This includes setting parameters like supported remote chains (using their chain selectors), addresses of token contracts on other chains, and rate limits for CCIP transfers. * `BridgeTokens.s.sol`: This script provides a convenient way to initiate a cross-chain token transfer, automating the calls to the `RebaseTokenPool` for locking/burning and CCIP message dispatch. * `Interactions.s.sol`: (Implied) This script would likely contain functions for other general interactions with the deployed contracts, such as depositing into the vault or checking balances. ### Foundry Tests (`test/` directory) Rigorous testing is paramount, especially for financial applications involving cross-chain interactions. * **`RebaseToken.t.sol`**: * **Purpose:** Contains unit and fuzz tests specifically for the `RebaseToken.sol` contract. * **Key Test Feature:** Employs `assertApproxEqAbs(value1, value2, delta)` for assertions. Due to the nature of interest calculations over time and potential floating-point arithmetic nuances (even when emulated with fixed-point in Solidity), rebase calculations can lead to very minor precision differences. Using `assertApproxEqAbs` allows us to verify that calculated values are within an acceptable tolerance (delta) of expected values, rather than insisting on exact equality (`assertEq`) which might lead to spurious test failures. * **`CrossChain.t.sol`**: * **Purpose:** Contains fork tests designed to validate the end-to-end cross-chain functionality. * **Key Test Features:** * Utilizes `vm.createFork("rpc_url")` to create local forks of testnets like Sepolia and Arbitrum Sepolia. This allows tests to run against a snapshot of the real chain state. * Integrates `CCIPLocalSimulatorFork` from Chainlink Local. This powerful tool enables the simulation of CCIP message routing and execution between these local forks, effectively creating a local, two-chain (or multi-chain) test environment. * The test setup involves initializing two (or more) forked environments to represent the source and destination chains for the cross-chain operations. ## Automating Deployment and Cross-Chain Operations: The `bridgeToZkSync.sh` Script To streamline the entire process from deployment to a live cross-chain transfer, a bash script like `bridgeToZkSync.sh` is invaluable. * **Purpose:** This script automates a complex sequence of operations involving contract deployments, configurations, and interactions across multiple chains (e.g., Sepolia and zkSync Sepolia). * **Steps it typically performs:** 1. Sets necessary permissions for the `RebaseTokenPool` contract, often involving CCIP-specific roles. 2. Assigns CCIP roles and configures permissions for inter-chain communication. 3. Deploys the core contracts (`RebaseToken`, `RebaseTokenPool`, `Vault`) to a source chain (e.g., Sepolia) using `script/Deployer.s.sol`. 4. Parses the deployment output to extract the addresses of the newly deployed contracts. 5. Deploys the `Vault` (and potentially `RebaseToken` and `RebaseTokenPool` if not already deployed as part of a unified script) on the destination chain (e.g., zkSync Sepolia). 6. Configures the `RebaseTokenPool` on the source chain (Sepolia) using `script/ConfigurePool.s.sol`, linking it to the destination chain by setting remote chain selectors, token addresses on the destination chain, and CCIP rate limits. 7. Simulates user interaction by depositing funds (e.g., ETH) into the `Vault` on Sepolia, thereby minting rebase tokens. 8. Includes a pause or wait period to allow some interest to accrue on the rebase tokens. 9. Configures the `RebaseTokenPool` on the destination chain (zkSync Sepolia), establishing the reciprocal CCIP linkage. 10. Initiates a cross-chain transfer of the rebase tokens from Sepolia to zkSync Sepolia using `script/BridgeTokens.s.sol`. 11. Performs balance checks on both chains before and after the bridge operation to verify the successful transfer and correct accounting. ## Key Takeaways and Best Practices Several important principles and practices emerge from this advanced development exercise: * **Precision is Paramount:** When working with rebase tokens and time-based interest calculations, meticulous attention to precision is crucial. Use fixed-point arithmetic carefully in Solidity and employ approximate equality assertions like `assertApproxEqAbs` in your tests to account for minor, unavoidable discrepancies. * **Local Simulation is Vital:** The ability to simulate complex cross-chain interactions locally, using tools like `CCIPLocalSimulatorFork`, dramatically accelerates the development and debugging cycle. It allows for rapid iteration before deploying to live testnets. * **Automation through Scripting:** For multi-step, multi-chain deployment and interaction workflows, bash scripting (or other scripting languages) provides powerful automation, reducing manual errors and improving repeatability. * **Leverage `super` for Extensibility:** The `super` keyword in Solidity is essential when inheriting from base contracts (like standard ERC20 implementations or CCIP base contracts). It allows you to extend or modify parent contract functionality while still being able to invoke the original parent implementation where needed. * **Handle Token Dust:** Be mindful of "token dust"—very small, often economically insignificant, token balances that can arise from precision issues or certain transaction patterns. While not explicitly detailed in the summary for this token, it's a general consideration in token design. * **Address Truncation:** Understand how integer division in Solidity can lead to truncation and ensure your calculations, especially those involving rates and time, are designed to minimize adverse effects. ## Example Use Case: Putting It All Together By the conclusion of this section's practical application, a developer would be able to execute the following end-to-end flow: 1. **Deployment:** Deploy the `RebaseToken`, `RebaseTokenPool`, and `Vault` smart contracts onto the Sepolia testnet. 2. **Cross-Chain Deployment:** Deploy the corresponding smart contracts (or at least the `RebaseTokenPool` and potentially a `RebaseToken` representation) onto a second testnet, such as zkSync Sepolia. 3. **CCIP Configuration:** Configure Chainlink CCIP lanes between the deployed `RebaseTokenPool` contracts on Sepolia and zkSync Sepolia, enabling them to communicate and transfer tokens. 4. **Acquire Rebase Tokens:** Interact with the `Vault` contract on Sepolia by depositing ETH, thereby receiving an initial balance of rebase tokens. 5. **Interest Accrual:** Observe as the rebase token balance in the Sepolia wallet increases over time, reflecting the accrued interest as per the token's rebase mechanism. 6. **Cross-Chain Transfer:** Execute the `BridgeTokens.s.sol` Foundry script (or the overarching `bridgeToZkSync.sh` bash script). This script will: * Instruct the `RebaseTokenPool` on Sepolia to burn a specified amount of the user's rebase tokens. * Initiate a CCIP message to the `RebaseTokenPool` on zkSync Sepolia. * Upon successful CCIP message relay, the `RebaseTokenPool` on zkSync Sepolia will mint an equivalent amount of rebase tokens to the user's address on that chain. 7. **Verification:** The user can then verify their new rebase token balance on zkSync Sepolia and the correspondingly reduced (or zeroed, if all tokens were bridged) balance on Sepolia. This comprehensive example demonstrates the power of combining custom token logic with robust cross-chain interoperability solutions to create sophisticated and flexible DeFi applications.
A comprehensive masterclass to Building Cross-Chain Rebase Tokens with Foundry and Chainlink CCIP - Learn to design, implement, and deploy interest-accruing rebase tokens across chains using Foundry and Chainlink CCIP. Covers burn-and-mint logic, advanced fork testing with local CCIP simulation, and automated deployment scripts.
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