Skip to content

Instantly share code, notes, and snippets.

@vivekascoder
Created March 3, 2026 12:37
Show Gist options
  • Select an option

  • Save vivekascoder/72515b564f15ebca0364cdbba865465c to your computer and use it in GitHub Desktop.

Select an option

Save vivekascoder/72515b564f15ebca0364cdbba865465c to your computer and use it in GitHub Desktop.
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.28;
import {Test} from "forge-std/Test.sol";
import {console2} from "forge-std/console2.sol";
import {AutopoolETH, IAutopool} from "../contracts/tranches/strategies/autousd/IAutopoolETH.sol";
/**
* @title Autopool APR Fork Test
* @notice Forks Ethereum mainnet and computes the base APR for autoUSD
* vault using its profit unlock mechanism.
*/
contract AutopoolAPRTest is Test {
uint256 constant SECONDS_PER_YEAR = 365 days;
uint256 constant MAX_BPS_PROFIT = 1_000_000_000;
// Tokemak / Auto Finance autoUSD vault on Ethereum mainnet
address constant AUTO_USD = 0xa7569A44f348d3D70d8ad5889e50F78E33d80D35;
function setUp() public {
vm.createSelectFork(vm.envString("MAINNET_RPC_URL"));
}
// ─── APR computation ────────────────────────────────────────────────
/// @notice Computes the base APR from the profit unlock mechanism
/// @param vault The autopool vault address
/// @return apr The annualized yield rate (scaled by 1e18, i.e. 1e18 = 100%)
function _getAPRbase(address vault) internal view returns (uint256 apr) {
AutopoolETH autopool = AutopoolETH(vault);
// Get profit unlock settings
IAutopool.ProfitUnlockSettings memory settings = autopool.getProfitUnlockSettings();
// If no unlock period or unlock already completed, APR is 0
if (settings.unlockPeriodInSeconds == 0 || settings.fullProfitUnlockTime <= block.timestamp) {
return 0;
}
// Time remaining until all locked profit is fully unlocked
uint256 timeRemaining = uint256(settings.fullProfitUnlockTime) - block.timestamp;
// Remaining locked shares = profitUnlockRate * timeRemaining / MAX_BPS_PROFIT
uint256 remainingLockedShares = settings.profitUnlockRate * timeRemaining / MAX_BPS_PROFIT;
// Get current vault state
uint256 totalAssets = autopool.totalAssets();
uint256 totalSupply = autopool.totalSupply();
if (totalSupply == 0 || totalAssets == 0) {
return 0;
}
// Unvested profit in asset terms
uint256 unvestedProfit = remainingLockedShares * totalAssets / totalSupply;
// APR = (unvestedProfit / timeRemaining) * SECONDS_PER_YEAR / totalAssets
// Rearranged: unvestedProfit * SECONDS_PER_YEAR * 1e18 / (timeRemaining * totalAssets)
apr = unvestedProfit * SECONDS_PER_YEAR * 1e18 / (timeRemaining * totalAssets);
}
// ─── Tests ──────────────────────────────────────────────────────────
function test_autoUSD_APR() public view {
uint256 apr = _getAPRbase(AUTO_USD);
console2.log("===== autoUSD =====");
console2.log("APR (1e18 = 100%):", apr);
console2.log("APR (%) :", apr * 100 / 1e18);
console2.log("APR (bps) :", apr * 10_000 / 1e18);
_logVaultState(AUTO_USD);
}
// ─── Helpers ────────────────────────────────────────────────────────
function _logVaultState(address vault) internal view {
AutopoolETH autopool = AutopoolETH(vault);
IAutopool.ProfitUnlockSettings memory settings = autopool.getProfitUnlockSettings();
console2.log("--- Vault State ---");
console2.log("totalAssets :", autopool.totalAssets());
console2.log("totalSupply :", autopool.totalSupply());
console2.log("--- Profit Unlock Settings ---");
console2.log("unlockPeriod (s) :", uint256(settings.unlockPeriodInSeconds));
console2.log("fullProfitUnlock :", uint256(settings.fullProfitUnlockTime));
console2.log("lastProfitUnlock :", uint256(settings.lastProfitUnlockTime));
console2.log("profitUnlockRate :", settings.profitUnlockRate);
console2.log("block.timestamp :", block.timestamp);
if (settings.fullProfitUnlockTime > block.timestamp) {
console2.log("timeRemaining (s):", uint256(settings.fullProfitUnlockTime) - block.timestamp);
} else {
console2.log("timeRemaining (s): 0 (unlock complete)");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment