DEV Community

jack
jack

Posted on

Tutorial: Handling Rebasing Tokens (like USDM) in Your Smart Contracts

Rebasing tokens like Mountain Protocol's USDM are powerful for users but require special handling by developers. Because the balanceOf() an address changes daily, you can't use it as a stable measure of a user's "share." Let's explore the correct way to handle this.

The Problem with Simple Balance Tracking
If you track a user's deposit in your dApp using balanceOf(), the daily rebase will break your accounting. A user's balance will increase on its own, and your contract won't know if it's from a new deposit or a rebase event.

Wrong way:

solidity
// DO NOT DO THIS
mapping(address => uint256) public userDeposits;

function deposit(uint256 amount) external {
usdmToken.transferFrom(msg.sender, address(this), amount);
userDeposits[msg.sender] += amount; // This is now incorrect after a rebase
}
The Solution: Tracking Shares, Not Balances
The correct method is to create a "shares" system. Your contract tracks a user's proportion of the total funds it holds, not the absolute amount.

Correct way (Conceptual):

solidity
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IRebasingUSDM is IERC20 {
// A rebasing token often has a 'shares' mechanism
function sharesOf(address account) external view returns (uint256);
function getSharesByAmount(uint256 amount) external view returns (uint256);
}

contract MyDApp {
IRebasingUSDM public usdmToken;
mapping(address => uint256) public userShares;
uint256 public totalShares;

function deposit(uint256 amount) external {
    // Convert the amount of USDM to a fixed number of shares
    uint256 sharesToMint = usdmToken.getSharesByAmount(amount);

    userShares[msg.sender] += sharesToMint;
    totalShares += sharesToMint;

    usdmToken.transferFrom(msg.sender, address(this), amount);
}

function withdraw(uint256 sharesToBurn) external {
    // When a user withdraws, their shares are converted back to the
    // current (rebased) amount of USDM.
    uint256 amountToWithdraw = (usdmToken.balanceOf(address(this)) * sharesToBurn) / totalShares;

    userShares[msg.sender] -= sharesToBurn;
    totalShares -= sharesToBurn;

    usdmToken.transfer(msg.sender, amountToWithdraw);
}
Enter fullscreen mode Exit fullscreen mode

}
By using shares, your application becomes compatible with the daily Rewards distribution of the Mountain Protocol. For full integration details, the official community documentation is the definitive Guide.

Top comments (0)