From 93d6937bc1c453922cb59c12674000d16dc1d242 Mon Sep 17 00:00:00 2001 From: c-note Date: Tue, 19 Dec 2023 14:36:28 +0000 Subject: [PATCH 01/36] update USTB contract with further testcases. --- src/USTB.sol | 38 ++- test/Invariant/Handler.sol | 315 ++++++++++++++++++++++++ test/Invariant/LibAddressSet.sol | 48 ++++ test/Invariant/USDM.sol | 359 ++++++++++++++++++++++++++++ test/Invariant/USTBInvariants.t.sol | 125 ++++++++++ test/USTB.t.sol | 160 +++++++++++-- 6 files changed, 1016 insertions(+), 29 deletions(-) create mode 100644 test/Invariant/Handler.sol create mode 100644 test/Invariant/LibAddressSet.sol create mode 100644 test/Invariant/USDM.sol create mode 100644 test/Invariant/USTBInvariants.t.sol diff --git a/src/USTB.sol b/src/USTB.sol index f10545e..eedfe84 100644 --- a/src/USTB.sol +++ b/src/USTB.sol @@ -34,7 +34,8 @@ contract USTB is IUSTB, LayerZeroRebaseTokenUpgradeable, UUPSUpgradeable { } // keccak256(abi.encode(uint256(keccak256("tangible.storage.USTB")) - 1)) & ~bytes32(uint256(0xff)) - bytes32 private constant USTBStorageLocation = 0x56cb630b12f1f031f72de1d734e98085323517cc6515c1c85452dc02f218dd00; + bytes32 private constant USTBStorageLocation = + 0x56cb630b12f1f031f72de1d734e98085323517cc6515c1c85452dc02f218dd00; function _getUSTBStorage() private pure returns (USTBStorage storage $) { // slither-disable-next-line assembly @@ -70,15 +71,18 @@ contract USTB is IUSTB, LayerZeroRebaseTokenUpgradeable, UUPSUpgradeable { * @param endpoint The Layer Zero endpoint for cross-chain operations. * @custom:oz-upgrades-unsafe-allow constructor */ - constructor(address underlying, uint256 mainChainId, address endpoint) - CrossChainToken(mainChainId) - LayerZeroRebaseTokenUpgradeable(endpoint) - { + constructor( + address underlying, + uint256 mainChainId, + address endpoint + ) CrossChainToken(mainChainId) LayerZeroRebaseTokenUpgradeable(endpoint) { UNDERLYING = underlying; _disableInitializers(); } - function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} + function _authorizeUpgrade( + address newImplementation + ) internal override onlyOwner {} /** * @notice Initializes the USTB contract with essential parameters. @@ -146,11 +150,20 @@ contract USTB is IUSTB, LayerZeroRebaseTokenUpgradeable, UUPSUpgradeable { _disableRebase(account, disable); } - function rebaseIndexManager() external view override returns (address _rebaseIndexManager) { + function rebaseIndexManager() + external + view + override + returns (address _rebaseIndexManager) + { USTBStorage storage $ = _getUSTBStorage(); _rebaseIndexManager = $.rebaseIndexManager; } + function isNotRebase(address account) external view returns (bool) { + return _isRebaseDisabled(account); + } + /** * @notice Sets the rebase index and its corresponding nonce on non-main chains. * @dev This function allows the rebase index manager to manually update the rebase index and nonce when not on the @@ -162,7 +175,10 @@ contract USTB is IUSTB, LayerZeroRebaseTokenUpgradeable, UUPSUpgradeable { * @param index The new rebase index to set. * @param nonce The new nonce corresponding to the rebase index. */ - function setRebaseIndex(uint256 index, uint256 nonce) public onlyIndexManager mainChain(false) { + function setRebaseIndex( + uint256 index, + uint256 nonce + ) public onlyIndexManager mainChain(false) { _setRebaseIndex(index, nonce); } @@ -210,7 +226,11 @@ contract USTB is IUSTB, LayerZeroRebaseTokenUpgradeable, UUPSUpgradeable { * @param to The address to which tokens are being transferred or minted. * @param amount The amount of tokens being transferred, minted, or burned. */ - function _update(address from, address to, uint256 amount) internal virtual override { + function _update( + address from, + address to, + uint256 amount + ) internal virtual override { refreshRebaseIndex(); super._update(from, to, amount); } diff --git a/test/Invariant/Handler.sol b/test/Invariant/Handler.sol new file mode 100644 index 0000000..9829df1 --- /dev/null +++ b/test/Invariant/Handler.sol @@ -0,0 +1,315 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.20; + +import {USTB, IERC20} from "../../src/USTB.sol"; +import {CommonBase} from "forge-std/Base.sol"; +import {StdUtils} from "forge-std/StdUtils.sol"; +import {StdCheats} from "forge-std/StdCheats.sol"; +import {console2 as console} from "forge-std/Test.sol"; +import {AddressSet, LibAddressSet} from "./LibAddressSet.sol"; +import {RebaseTokenMath} from "tangible-foundation-contracts/libraries/RebaseTokenMath.sol"; + +contract Handler is CommonBase, StdCheats, StdUtils { + using RebaseTokenMath for uint256; + using LibAddressSet for AddressSet; + + AddressSet internal _actors; + + USTB public ustb; + USTB public ustb2; + IERC20 usdm; + + mapping(bytes32 => uint256) public calls; + + address currentActor; + uint256 public ghost_burntSum; + uint256 public ghost_zeroBurn; + + uint256 public ghost_zeroMint; + uint256 public ghost_mintedSum; + + uint256 public ghost_actualBurn; + uint256 public ghost_enableRebase; + + uint256 public ghost_zeroTransfer; + uint256 public ghost_disableRebase; + + uint256 public ghost_actualSendFrom; + uint256 public ghost_actualTransfer; + + uint256 public ghost_bridgedTokensTo; + uint256 public ghost_zeroAddressBurn; + + uint256 public ghost_zeroTransferFrom; + uint256 public ghost_bridgedTokensFrom; + + uint256 public ghost_actualTransferFrom; + uint256 public ghost_zeroAddressTransfer; + + uint256 public ghost_zeroAddressSendFrom; + uint256 public ghost_zeroAddressTransferFrom; + + constructor(USTB _ustb, USTB _ustb2, address _usdm) { + ustb = _ustb; + ustb2 = _ustb2; + usdm = IERC20(_usdm); + } + + modifier createActor() { + currentActor = msg.sender; + _actors.add(currentActor); + _; + } + + modifier useActor(uint256 actorIndexSeed) { + currentActor = _actors.rand(actorIndexSeed); + _; + } + + modifier countCall(bytes32 key) { + calls[key]++; + _; + } + + function mint(uint256 amount) public createActor countCall("mint") { + amount = bound(amount, 0, type(uint96).max); + ghost_mintedSum += amount; + + if (amount == 0) ghost_zeroMint++; + address to = currentActor; + + __mint(currentActor, amount); + vm.startPrank(currentActor); + + usdm.approve(address(ustb), amount); + ustb.mint(to, amount); + } + + function burn( + uint seed, + uint256 amount + ) public useActor(seed) countCall("burn") { + if (currentActor != address(0)) { + amount = bound(amount, 0, ustb.balanceOf(currentActor)); + ghost_burntSum += amount; + + ghost_actualBurn++; + if (amount == 0) ghost_zeroBurn++; + + address from = currentActor; + + vm.startPrank(currentActor); + ustb.burn(from, amount); + } else ghost_zeroAddressBurn++; + } + + function approve( + uint256 actorSeed, + uint256 spenderSeed, + uint256 amount + ) public useActor(actorSeed) countCall("approve") { + address spender = _actors.rand(spenderSeed); + if (currentActor != address(0)) { + vm.startPrank(currentActor); + ustb.approve(spender, amount); + } + } + + function disable( + uint256 seed, + bool flag + ) public useActor(seed) countCall("disableRebase") { + // if ( + // currentActor != address(0) && flag != ustb.isNotRebase(currentActor) + // ) { + // vm.startPrank(currentActor); + // ustb.disableRebase(currentActor, flag); + // } + } + + function transfer( + uint256 actorSeed, + uint256 toSeed, + uint256 amount + ) public useActor(actorSeed) countCall("transfer") { + address to = _actors.rand(toSeed); + + if (currentActor != address(0)) { + vm.deal(currentActor, 1 ether); + amount = bound(amount, 0, ustb.balanceOf(currentActor)); + + ghost_actualTransfer++; + if (amount == 0) ghost_zeroTransfer++; + + vm.startPrank(currentActor); + ustb.transfer(to, amount); + } else ghost_zeroAddressTransfer++; + } + + function sendFrom( + uint256 actorSeed, + uint256 toSeed, + uint256 amount + ) public useActor(actorSeed) countCall("sendFrom") { + address to = _actors.rand(toSeed); + if (currentActor != address(0)) { + ghost_actualSendFrom++; + vm.deal(to, 10 ether); + + vm.deal(currentActor, 10 ether); + amount = bound(amount, 0, ustb.balanceOf(currentActor)); + + if (amount == 0) ghost_zeroTransfer++; + vm.startPrank(currentActor); + + usdm.approve(address(ustb), amount); + uint256 nativeFee; + + (nativeFee, ) = ustb.estimateSendFee( + uint16(block.chainid), + abi.encodePacked(to), + amount, + false, + "" + ); + + uint256 contractBalBeforeBridge = ustb.balanceOf(address(this)); + + ustb.sendFrom{value: (nativeFee * 105) / 100}( + currentActor, + uint16(block.chainid), + abi.encodePacked(to), + amount, + payable(currentActor), + address(0), + "" + ); + + uint256 contractBalAfterBridge = ustb.balanceOf(address(this)); + + ghost_bridgedTokensTo += + contractBalAfterBridge - + contractBalBeforeBridge; + + vm.startPrank(to); + + (nativeFee, ) = ustb.estimateSendFee( + uint16(block.chainid), + abi.encodePacked(currentActor), + ustb2.balanceOf(to), + false, + "" + ); + + uint256 contractBalBeforeBridge0 = ustb.balanceOf(address(this)); + + ustb2.sendFrom{value: (nativeFee * 105) / 100}( + to, + uint16(block.chainid), + abi.encodePacked(currentActor), + ustb2.balanceOf(to), + payable(to), + address(0), + "" + ); + + uint256 contractBalAfterBridge0 = ustb2.balanceOf(address(this)); + + ghost_bridgedTokensFrom += + contractBalBeforeBridge0 - + contractBalAfterBridge0; + } else ghost_zeroAddressSendFrom++; + } + + function transferFrom( + uint256 actorSeed, + uint256 fromSeed, + uint256 toSeed, + bool _approve, + uint256 amount + ) public useActor(actorSeed) countCall("transferFrom") { + address from = _actors.rand(fromSeed); + address to = _actors.rand(toSeed); + amount = bound(amount, 0, ustb.balanceOf(from)); + + if (currentActor != address(0)) { + if (_approve) { + vm.startPrank(from); + ustb.approve(currentActor, amount); + vm.stopPrank(); + } else { + amount = bound(amount, 0, ustb.allowance(from, currentActor)); + } + + ghost_actualTransferFrom++; + if (amount == 0) ghost_zeroTransferFrom++; + + vm.startPrank(currentActor); + ustb.transferFrom(from, to, amount); + + vm.stopPrank(); + } else ghost_zeroAddressTransferFrom++; + } + + function reduceActors( + uint256 acc, + function(uint256, address) external returns (uint256) func + ) public returns (uint256) { + return _actors.reduce(acc, func); + } + + function forEachActor(function(address) external func) public { + return _actors.forEach(func); + } + + function callSummary() external view { + console.log("-------------------"); + console.log(" "); + console.log("Call summary:"); + console.log(" "); + + console.log("-------------------"); + console.log("Call Count:"); + console.log("-------------------"); + console.log("Mint(s)", calls["mint"]); + console.log("Burn(s)", calls["burn"]); + console.log("Approve(s)", calls["approve"]); + console.log("Transfer(s):", calls["transfer"]); + console.log("SendFrom(s):", calls["sendFrom"]); + console.log("TransferFrom(s):", calls["transferFrom"]); + console.log("DisableRebase(s):", calls["disableRebase"]); + + console.log("-------------------"); + console.log("Zero Calls:"); + console.log("-------------------"); + console.log("Mint(s):", ghost_zeroMint); + console.log("Burn(s):", ghost_zeroBurn); + console.log("Transfer(s):", ghost_zeroTransfer); + console.log("TransferFrom(s):", ghost_zeroTransferFrom); + + console.log("-------------------"); + console.log("Zero Address Call:"); + console.log("-------------------"); + console.log("Burn(s):", ghost_zeroAddressBurn); + console.log("Transfer(s):", ghost_zeroAddressTransfer); + console.log("sendFrom(s):", ghost_zeroAddressSendFrom); + console.log("TransferFrom(s):", ghost_zeroAddressTransferFrom); + + console.log("-------------------"); + console.log("Actual Calls:"); + console.log("-------------------"); + console.log("Burn(s):", ghost_actualBurn); + console.log("SendFrom(s):", ghost_actualSendFrom); + console.log("Transfer(s):", ghost_actualTransfer); + console.log("Enable Rebase:", ghost_enableRebase); + console.log("Disable Rebase:", ghost_disableRebase); + console.log("TransferFrom(s):", ghost_actualTransferFrom); + } + + function __mint(address addr, uint256 amount) internal { + (bool success, ) = address(usdm).call( + abi.encodeWithSignature("mintTokens(address,uint256)", addr, amount) + ); + assert(success); + } +} diff --git a/test/Invariant/LibAddressSet.sol b/test/Invariant/LibAddressSet.sol new file mode 100644 index 0000000..eea98bd --- /dev/null +++ b/test/Invariant/LibAddressSet.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.13; +import {console2 as console} from "forge-std/Test.sol"; + +struct AddressSet { + address[] addrs; + mapping(address => bool) saved; +} + +library LibAddressSet { + function add(AddressSet storage s, address addr) internal { + if (!s.saved[addr]) { + s.addrs.push(addr); + s.saved[addr] = true; + } + } + + function rand( + AddressSet storage s, + uint256 seed + ) internal view returns (address) { + if (s.addrs.length > 0) { + return s.addrs[seed % s.addrs.length]; + } else { + return address(0); + } + } + + function reduce( + AddressSet storage s, + uint256 acc, + function(uint256, address) external returns (uint256) func + ) internal returns (uint256) { + for (uint256 i; i < s.addrs.length; ++i) { + acc = func(acc, s.addrs[i]); + } + return acc; + } + + function forEach( + AddressSet storage s, + function(address) external func + ) internal { + for (uint256 i; i < s.addrs.length; ++i) { + func(s.addrs[i]); + } + } +} diff --git a/test/Invariant/USDM.sol b/test/Invariant/USDM.sol new file mode 100644 index 0000000..2d9dad0 --- /dev/null +++ b/test/Invariant/USDM.sol @@ -0,0 +1,359 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.20; + +contract USDM { + // Token name + string private _name; + // Token Symbol + string private _symbol; + // Total token shares + uint256 private _totalShares; + // Base value for rewardMultiplier + uint256 private constant _BASE = 1e18; + /** + * @dev rewardMultiplier represents a coefficient used in reward calculation logic. + * The value is represented with 18 decimal places for precision. + */ + uint256 public rewardMultiplier; + + // Mapping of shares per address + mapping(address => uint256) private _shares; + // Mapping of block status per address + mapping(address => bool) private _blocklist; + // Mapping of allowances per owner and spender + mapping(address => mapping(address => uint256)) private _allowances; + + // Events + event AccountBlocked(address indexed addr); + event AccountUnblocked(address indexed addr); + event RewardMultiplier(uint256 indexed value); + + /** + * Standard ERC20 Errors + * @dev See https://eips.ethereum.org/EIPS/eip-6093 + */ + error ERC20InsufficientBalance( + address sender, + uint256 shares, + uint256 sharesNeeded + ); + error ERC20InvalidSender(address sender); + error ERC20InvalidReceiver(address receiver); + error ERC20InsufficientAllowance( + address spender, + uint256 allowance, + uint256 needed + ); + error ERC20InvalidApprover(address approver); + error ERC20InvalidSpender(address spender); + // ERC2612 Errors + error ERC2612ExpiredDeadline(uint256 deadline, uint256 blockTimestamp); + error ERC2612InvalidSignature(address owner, address spender); + // USDM Errors + error USDMInvalidMintReceiver(address receiver); + error USDMInvalidBurnSender(address sender); + error USDMInsufficientBurnBalance( + address sender, + uint256 shares, + uint256 sharesNeeded + ); + error USDMInvalidRewardMultiplier(uint256 rewardMultiplier); + error USDMBlockedSender(address sender); + error USDMInvalidBlockedAccount(address account); + + constructor() { + _name = "USBM"; + _symbol = "US"; + _setRewardMultiplier(_BASE); + } + + function name() external view returns (string memory) { + return _name; + } + + function symbol() external view returns (string memory) { + return _symbol; + } + + function decimals() external pure returns (uint8) { + return 18; + } + + function convertToShares(uint256 amount) public view returns (uint256) { + return (amount * _BASE) / rewardMultiplier; + } + + function convertToTokens(uint256 shares) public view returns (uint256) { + return (shares * rewardMultiplier) / _BASE; + } + + function totalShares() external view returns (uint256) { + return _totalShares; + } + + function totalSupply() external view returns (uint256) { + return convertToTokens(_totalShares); + } + + function sharesOf(address account) public view returns (uint256) { + return _shares[account]; + } + + function balanceOf(address account) external view returns (uint256) { + return convertToTokens(sharesOf(account)); + } + + function _mint(address to, uint256 amount) private { + if (to == address(0)) { + revert USDMInvalidMintReceiver(to); + } + + _beforeTokenTransfer(address(0), to, amount); + + uint256 shares = convertToShares(amount); + _totalShares += shares; + + unchecked { + // Overflow not possible: shares + shares amount is at most totalShares + shares amount + // which is checked above. + _shares[to] += shares; + } + + _afterTokenTransfer(address(0), to, amount); + } + + function mintTokens(address to, uint256 amount) external { + _mint(to, amount); + } + + function _burn(address account, uint256 amount) private { + if (account == address(0)) { + revert USDMInvalidBurnSender(account); + } + + _beforeTokenTransfer(account, address(0), amount); + + uint256 shares = convertToShares(amount); + uint256 accountShares = sharesOf(account); + + if (accountShares < shares) { + revert USDMInsufficientBurnBalance(account, accountShares, shares); + } + + unchecked { + _shares[account] = accountShares - shares; + // Overflow not possible: amount <= accountShares <= totalShares. + _totalShares -= shares; + } + + _afterTokenTransfer(account, address(0), amount); + } + + function burn(address from, uint256 amount) external { + _burn(from, amount); + } + + function _beforeTokenTransfer( + address from, + address /* to */, + uint256 /* amount */ + ) private view { + // Each blocklist check is an SLOAD, which is gas intensive. + // We only block sender not receiver, so we don't tax every user + if (isBlocked(from)) { + revert USDMBlockedSender(from); + } + } + + function _afterTokenTransfer( + address from, + address to, + uint256 amount + ) private {} + + function _transfer(address from, address to, uint256 amount) private { + if (from == address(0)) { + revert ERC20InvalidSender(from); + } + if (to == address(0)) { + revert ERC20InvalidReceiver(to); + } + + _beforeTokenTransfer(from, to, amount); + + uint256 shares = convertToShares(amount); + uint256 fromShares = _shares[from]; + + if (fromShares < shares) { + revert ERC20InsufficientBalance(from, fromShares, shares); + } + + unchecked { + _shares[from] = fromShares - shares; + // Overflow not possible: the sum of all shares is capped by totalShares, and the sum is preserved by + // decrementing then incrementing. + _shares[to] += shares; + } + + _afterTokenTransfer(from, to, amount); + } + + function transfer(address to, uint256 amount) external returns (bool) { + address owner = msg.sender; + + _transfer(owner, to, amount); + + return true; + } + + function _blockAccount(address account) private { + if (isBlocked(account)) { + revert USDMInvalidBlockedAccount(account); + } + + _blocklist[account] = true; + emit AccountBlocked(account); + } + + function _unblockAccount(address account) private { + if (!isBlocked(account)) { + revert USDMInvalidBlockedAccount(account); + } + + _blocklist[account] = false; + emit AccountUnblocked(account); + } + + function blockAccounts(address[] calldata addresses) external { + for (uint256 i = 0; i < addresses.length; i++) { + _blockAccount(addresses[i]); + } + } + + function unblockAccounts(address[] calldata addresses) external { + for (uint256 i = 0; i < addresses.length; i++) { + _unblockAccount(addresses[i]); + } + } + + function isBlocked(address account) public view returns (bool) { + return _blocklist[account]; + } + + function _setRewardMultiplier(uint256 _rewardMultiplier) private { + if (_rewardMultiplier < _BASE) { + revert USDMInvalidRewardMultiplier(_rewardMultiplier); + } + + rewardMultiplier = _rewardMultiplier; + + emit RewardMultiplier(rewardMultiplier); + } + + function setRewardMultiplier(uint256 _rewardMultiplier) external { + _setRewardMultiplier(_rewardMultiplier); + } + + function addRewardMultiplier(uint256 _rewardMultiplierIncrement) external { + if (_rewardMultiplierIncrement == 0) { + revert USDMInvalidRewardMultiplier(_rewardMultiplierIncrement); + } + + _setRewardMultiplier(rewardMultiplier + _rewardMultiplierIncrement); + } + + function _approve(address owner, address spender, uint256 amount) private { + if (owner == address(0)) { + revert ERC20InvalidApprover(owner); + } + if (spender == address(0)) { + revert ERC20InvalidSpender(spender); + } + + _allowances[owner][spender] = amount; + } + + function approve(address spender, uint256 amount) external returns (bool) { + address owner = msg.sender; + + _approve(owner, spender, amount); + + return true; + } + + function allowance( + address owner, + address spender + ) public view returns (uint256) { + return _allowances[owner][spender]; + } + + function _spendAllowance( + address owner, + address spender, + uint256 amount + ) private { + uint256 currentAllowance = allowance(owner, spender); + + if (currentAllowance != type(uint256).max) { + if (currentAllowance < amount) { + revert ERC20InsufficientAllowance( + spender, + currentAllowance, + amount + ); + } + + unchecked { + _approve(owner, spender, currentAllowance - amount); + } + } + } + + function transferFrom( + address from, + address to, + uint256 amount + ) external returns (bool) { + address spender = msg.sender; + + _spendAllowance(from, spender, amount); + _transfer(from, to, amount); + + return true; + } + + function increaseAllowance( + address spender, + uint256 addedValue + ) external returns (bool) { + address owner = msg.sender; + + _approve(owner, spender, allowance(owner, spender) + addedValue); + + return true; + } + + function decreaseAllowance( + address spender, + uint256 subtractedValue + ) external returns (bool) { + address owner = msg.sender; + uint256 currentAllowance = allowance(owner, spender); + + if (currentAllowance < subtractedValue) { + revert ERC20InsufficientAllowance( + spender, + currentAllowance, + subtractedValue + ); + } + + unchecked { + _approve(owner, spender, currentAllowance - subtractedValue); + } + + return true; + } +} diff --git a/test/Invariant/USTBInvariants.t.sol b/test/Invariant/USTBInvariants.t.sol new file mode 100644 index 0000000..b6215cb --- /dev/null +++ b/test/Invariant/USTBInvariants.t.sol @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {USDM} from "./USDM.sol"; +import {USTB} from "../../src/USTB.sol"; +import {Handler, RebaseTokenMath} from "./Handler.sol"; +import {Test, console2 as console} from "forge-std/Test.sol"; +import {LZEndpointMock} from "@layerzerolabs/contracts/lzApp/mocks/LZEndpointMock.sol"; +import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; + +contract USTBInvariants is Test { + using RebaseTokenMath for uint256; + + USTB public ustb; + USTB ustbChild; + USDM public usdm; + Handler public handler; + + function setUp() public { + uint16 mainChainId = uint16(block.chainid); + uint16 sideChainId = mainChainId + 1; + + usdm = new USDM(); + + LZEndpointMock lzEndpoint = new LZEndpointMock(mainChainId); + ustb = new USTB(address(usdm), mainChainId, address(lzEndpoint)); + + vm.chainId(sideChainId); + + USTB child = new USTB(address(usdm), mainChainId, address(lzEndpoint)); + + vm.chainId(mainChainId); + + ERC1967Proxy mainProxy = new ERC1967Proxy( + address(ustb), + abi.encodeWithSelector(USTB.initialize.selector, address(2)) + ); + + ustb = USTB(address(mainProxy)); + + vm.chainId(sideChainId); + + ERC1967Proxy childProxy = new ERC1967Proxy( + address(child), + abi.encodeWithSelector(USTB.initialize.selector, address(2)) + ); + + ustbChild = USTB(address(childProxy)); + + vm.chainId(mainChainId); + + lzEndpoint.setDestLzEndpoint(address(ustb), address(lzEndpoint)); + lzEndpoint.setDestLzEndpoint(address(ustbChild), address(lzEndpoint)); + + bytes memory ustbAddress = abi.encodePacked(uint160(address(ustb))); + bytes memory ustbChildAddress = abi.encodePacked( + uint160(address(ustbChild)) + ); + + ustb.setTrustedRemoteAddress(mainChainId, ustbChildAddress); + ustbChild.setTrustedRemoteAddress(mainChainId, ustbAddress); + + handler = new Handler(ustb, ustbChild, address(usdm)); + + bytes4[] memory selectors = new bytes4[](7); + + selectors[0] = Handler.mint.selector; + selectors[1] = Handler.burn.selector; + selectors[2] = Handler.disable.selector; + selectors[3] = Handler.transfer.selector; + selectors[4] = Handler.sendFrom.selector; + selectors[5] = Handler.transferFrom.selector; + selectors[6] = Handler.approve.selector; + + targetSelector( + FuzzSelector({addr: address(handler), selectors: selectors}) + ); + + targetContract(address(handler)); + } + + // The USTB contract's token balance should always be + // at least as much as the sum of individual mints. + function invariant_mint() public { + assertEq( + handler.ghost_mintedSum() - handler.ghost_burntSum(), + ustb.totalSupply() + ); + } + + // All to and fro bridging should be balanced out. + function invariant_bridgedToken() public { + assertEq( + handler.ghost_bridgedTokensTo() - handler.ghost_bridgedTokensFrom(), + 0 + ); + } + + // The USTB contract's token balance should always be + // at least as much as the sum of individual balances + function invariant_totalBalance() public { + uint256 sumOfBalances = handler.reduceActors(0, this.accumulateBalance); + assertEq(sumOfBalances, ustb.totalSupply()); + } + + // No individual account balance can exceed the USTB totalSupply(). + function invariant_userBalances() public { + handler.forEachActor(this.assertAccountBalanceLteTotalSupply); + } + + function assertAccountBalanceLteTotalSupply(address account) external { + assertLe(ustb.balanceOf(account), ustb.totalSupply()); + } + + function accumulateBalance( + uint256 balance, + address caller + ) external view returns (uint256) { + return balance + ustb.balanceOf(caller); + } + + function invariant_callSummary() public view { + handler.callSummary(); + } +} diff --git a/test/USTB.t.sol b/test/USTB.t.sol index 007eb31..c780a47 100644 --- a/test/USTB.t.sol +++ b/test/USTB.t.sol @@ -11,6 +11,15 @@ import "@layerzerolabs/contracts/lzApp/mocks/LZEndpointMock.sol"; import "src/USTB.sol"; contract USTBTest is Test { + error NotAuthorized(address caller); + error ValueUnchanged(); + error ERC20InsufficientAllowance( + address spender, + uint256 allowance, + uint256 needed + ); + error InvalidZeroAddress(); + USTB ustb; USTB ustbChild; @@ -51,14 +60,18 @@ contract USTBTest is Test { usdm = IERC20(main.UNDERLYING()); - ERC1967Proxy mainProxy = - new ERC1967Proxy(address(main), abi.encodeWithSelector(USTB.initialize.selector, indexManager)); + ERC1967Proxy mainProxy = new ERC1967Proxy( + address(main), + abi.encodeWithSelector(USTB.initialize.selector, indexManager) + ); ustb = USTB(address(mainProxy)); vm.chainId(sideChainId); - ERC1967Proxy childProxy = - new ERC1967Proxy(address(child), abi.encodeWithSelector(USTB.initialize.selector, indexManager)); + ERC1967Proxy childProxy = new ERC1967Proxy( + address(child), + abi.encodeWithSelector(USTB.initialize.selector, indexManager) + ); ustbChild = USTB(address(childProxy)); vm.chainId(mainChainId); @@ -69,7 +82,9 @@ contract USTBTest is Test { lzEndpoint.setDestLzEndpoint(address(ustbChild), address(lzEndpoint)); bytes memory ustbAddress = abi.encodePacked(uint160(address(ustb))); - bytes memory ustbChildAddress = abi.encodePacked(uint160(address(ustbChild))); + bytes memory ustbChildAddress = abi.encodePacked( + uint160(address(ustbChild)) + ); ustb.setTrustedRemoteAddress(mainChainId, ustbChildAddress); ustbChild.setTrustedRemoteAddress(mainChainId, ustbAddress); @@ -85,8 +100,11 @@ contract USTBTest is Test { USTB instance2 = new USTB(usdmAddress, mainChainId, address(1)); - bytes32 slot = keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) - & ~bytes32(uint256(0xff)); + bytes32 slot = keccak256( + abi.encode( + uint256(keccak256("openzeppelin.storage.Initializable")) - 1 + ) + ) & ~bytes32(uint256(0xff)); vm.store(address(instance1), slot, 0); vm.store(address(instance2), slot, 0); @@ -147,7 +165,9 @@ contract USTBTest is Test { vm.roll(18349000); vm.startPrank(usdmController); - (bool success,) = address(usdm).call(abi.encodeWithSignature("addRewardMultiplier(uint256)", 134e12)); + (bool success, ) = address(usdm).call( + abi.encodeWithSignature("addRewardMultiplier(uint256)", 134e12) + ); assert(success); vm.startPrank(indexManager); @@ -171,7 +191,9 @@ contract USTBTest is Test { vm.roll(18349000); vm.startPrank(usdmController); - (bool success,) = address(usdm).call(abi.encodeWithSignature("addRewardMultiplier(uint256)", 134e12)); + (bool success, ) = address(usdm).call( + abi.encodeWithSignature("addRewardMultiplier(uint256)", 134e12) + ); assert(success); vm.startPrank(indexManager); @@ -190,7 +212,9 @@ contract USTBTest is Test { vm.roll(18350000); vm.startPrank(usdmController); - (success,) = address(usdm).call(abi.encodeWithSignature("addRewardMultiplier(uint256)", 134e12)); + (success, ) = address(usdm).call( + abi.encodeWithSignature("addRewardMultiplier(uint256)", 134e12) + ); assert(success); vm.startPrank(indexManager); @@ -209,18 +233,34 @@ contract USTBTest is Test { ustb.mint(usdmHolder, 1e18); uint256 nativeFee; - (nativeFee,) = ustb.estimateSendFee(uint16(block.chainid), abi.encodePacked(alice), 0.5e18, false, ""); - ustb.sendFrom{value: nativeFee * 105 / 100}( - usdmHolder, uint16(block.chainid), abi.encodePacked(alice), 0.5e18, payable(usdmHolder), address(0), "" + (nativeFee, ) = ustb.estimateSendFee( + uint16(block.chainid), + abi.encodePacked(alice), + 0.5e18, + false, + "" + ); + ustb.sendFrom{value: (nativeFee * 105) / 100}( + usdmHolder, + uint16(block.chainid), + abi.encodePacked(alice), + 0.5e18, + payable(usdmHolder), + address(0), + "" ); assertApproxEqAbs(ustb.balanceOf(usdmHolder), 0.5e18, 2); assertApproxEqAbs(ustbChild.balanceOf(alice), 0.5e18, 2); vm.startPrank(alice); - (nativeFee,) = ustb.estimateSendFee( - uint16(block.chainid), abi.encodePacked(usdmHolder), ustbChild.balanceOf(alice), false, "" + (nativeFee, ) = ustb.estimateSendFee( + uint16(block.chainid), + abi.encodePacked(usdmHolder), + ustbChild.balanceOf(alice), + false, + "" ); - ustbChild.sendFrom{value: nativeFee * 105 / 100}( + ustbChild.sendFrom{value: (nativeFee * 105) / 100}( alice, uint16(block.chainid), abi.encodePacked(usdmHolder), @@ -248,7 +288,9 @@ contract USTBTest is Test { vm.roll(18349000); vm.startPrank(usdmController); - (bool success,) = address(usdm).call(abi.encodeWithSignature("addRewardMultiplier(uint256)", 134e12)); + (bool success, ) = address(usdm).call( + abi.encodeWithSignature("addRewardMultiplier(uint256)", 134e12) + ); assert(success); vm.startPrank(indexManager); @@ -278,7 +320,9 @@ contract USTBTest is Test { vm.roll(18349000); vm.startPrank(usdmController); - (bool success,) = address(usdm).call(abi.encodeWithSignature("addRewardMultiplier(uint256)", 134e12)); + (bool success, ) = address(usdm).call( + abi.encodeWithSignature("addRewardMultiplier(uint256)", 134e12) + ); assert(success); vm.startPrank(indexManager); @@ -309,7 +353,9 @@ contract USTBTest is Test { vm.roll(18349000); vm.startPrank(usdmController); - (bool success,) = address(usdm).call(abi.encodeWithSignature("addRewardMultiplier(uint256)", 134e12)); + (bool success, ) = address(usdm).call( + abi.encodeWithSignature("addRewardMultiplier(uint256)", 134e12) + ); assert(success); vm.startPrank(indexManager); @@ -341,7 +387,9 @@ contract USTBTest is Test { vm.roll(18349000); vm.startPrank(usdmController); - (bool success,) = address(usdm).call(abi.encodeWithSignature("addRewardMultiplier(uint256)", 134e12)); + (bool success, ) = address(usdm).call( + abi.encodeWithSignature("addRewardMultiplier(uint256)", 134e12) + ); assert(success); vm.startPrank(indexManager); @@ -354,4 +402,76 @@ contract USTBTest is Test { assertEq(ustb.balanceOf(alice), 0); assertApproxEqAbs(ustb.balanceOf(bob), balance + balance, 1); } + + /////////////////////////////// NEW TEST /////////////////////////////////// + + function test_shouldFailTodisableRebaseIfCallerIsNotAuthorized() public { + vm.startPrank(usdmHolder); + usdm.approve(address(ustb), 1e18); + ustb.isNotRebase(usdmHolder); + + ustb.mint(usdmHolder, 1e18); + vm.stopPrank(); + + vm.expectRevert( + abi.encodeWithSelector(NotAuthorized.selector, address(this)) + ); + + ustb.disableRebase(usdmHolder, true); + } + + function test_shouldFailToDisableRebaseIfValueIsUnchanged() public { + vm.startPrank(usdmHolder); + usdm.approve(address(ustb), 1e18); + + ustb.mint(usdmHolder, 1e18); + vm.expectRevert(abi.encodeWithSelector(ValueUnchanged.selector)); + + ustb.disableRebase(usdmHolder, false); + } + + function test_burnViaApprovedAddress() public { + vm.startPrank(usdmHolder); + usdm.approve(address(ustb), 1e18); + + ustb.mint(usdmHolder, 1e18); + ustb.approve(address(this), 1e18); + + vm.stopPrank(); + ustb.burn(usdmHolder, ustb.balanceOf(usdmHolder)); + + assertEq(ustb.balanceOf(usdmHolder), 0); + assertEq(ustb.totalSupply(), 0); + } + + function test_failToBurnTokenFromNotApprovedOrOwner() public { + vm.startPrank(usdmHolder); + + usdm.approve(address(ustb), 1e18); + ustb.mint(usdmHolder, 1e18); + + ustb.approve(address(this), 1e18); + vm.stopPrank(); + + ustb.burn(usdmHolder, ustb.balanceOf(usdmHolder)); + + assertEq(ustb.balanceOf(usdmHolder), 0); + assertEq(ustb.totalSupply(), 0); + } + + function test_shouldFailToSetRebaseIndex() public { + vm.expectRevert( + abi.encodeWithSelector( + NotAuthorized.selector, + 0xaE0bDc4eEAC5E950B67C6819B118761CaAF61946 + ) + ); + ustbChild.setRebaseIndex(1e18, 1); + assertEq(ustbChild.rebaseIndex(), 1e18); + } + + function test_shouldFailTosetRebaseIndexManager() public { + vm.expectRevert(abi.encodeWithSelector(InvalidZeroAddress.selector)); + ustb.setRebaseIndexManager(address(0)); + } } From 7ae6fa260edf7bc9ba5b2579f58042a5fde5f07d Mon Sep 17 00:00:00 2001 From: c-note Date: Tue, 19 Dec 2023 15:41:14 +0000 Subject: [PATCH 02/36] Update param. --- test/USTB.t.sol | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/USTB.t.sol b/test/USTB.t.sol index c780a47..851ce02 100644 --- a/test/USTB.t.sol +++ b/test/USTB.t.sol @@ -461,10 +461,7 @@ contract USTBTest is Test { function test_shouldFailToSetRebaseIndex() public { vm.expectRevert( - abi.encodeWithSelector( - NotAuthorized.selector, - 0xaE0bDc4eEAC5E950B67C6819B118761CaAF61946 - ) + abi.encodeWithSelector(NotAuthorized.selector, deployer) ); ustbChild.setRebaseIndex(1e18, 1); assertEq(ustbChild.rebaseIndex(), 1e18); From d4a91a878d6dadc4800a8e21750519572c47b4cb Mon Sep 17 00:00:00 2001 From: c-note Date: Tue, 19 Dec 2023 16:31:33 +0000 Subject: [PATCH 03/36] Update Version. --- test/USTB.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/USTB.t.sol b/test/USTB.t.sol index 851ce02..4370952 100644 --- a/test/USTB.t.sol +++ b/test/USTB.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Unlicense -pragma solidity ^0.8.13; +pragma solidity 0.8.20; import "forge-std/Test.sol"; From 8688614c279504e6b7ddbc3dfb2733c8c12a24f3 Mon Sep 17 00:00:00 2001 From: c-note Date: Tue, 19 Dec 2023 16:34:44 +0000 Subject: [PATCH 04/36] Update Version. --- test/Invariant/LibAddressSet.sol | 2 +- test/Invariant/USTBInvariants.t.sol | 2 +- test/USTB.t.sol | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/Invariant/LibAddressSet.sol b/test/Invariant/LibAddressSet.sol index eea98bd..147032e 100644 --- a/test/Invariant/LibAddressSet.sol +++ b/test/Invariant/LibAddressSet.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.13; +pragma solidity 0.8.20; import {console2 as console} from "forge-std/Test.sol"; struct AddressSet { diff --git a/test/Invariant/USTBInvariants.t.sol b/test/Invariant/USTBInvariants.t.sol index b6215cb..d0fd32e 100644 --- a/test/Invariant/USTBInvariants.t.sol +++ b/test/Invariant/USTBInvariants.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; +pragma solidity 0.8.20; import {USDM} from "./USDM.sol"; import {USTB} from "../../src/USTB.sol"; diff --git a/test/USTB.t.sol b/test/USTB.t.sol index 4370952..851ce02 100644 --- a/test/USTB.t.sol +++ b/test/USTB.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Unlicense -pragma solidity 0.8.20; +pragma solidity ^0.8.13; import "forge-std/Test.sol"; From 4cc4955fdff06606e6abbfb21a049c85427dc120 Mon Sep 17 00:00:00 2001 From: c-note Date: Tue, 2 Jan 2024 16:00:28 +0000 Subject: [PATCH 05/36] Add audit. --- foundry.toml | 3 + test/Invariant/Handler.sol | 187 +++++++++++++++++++++---------------- test/Invariant/report.md | 128 +++++++++++++++++++++++++ test/USTB.t.sol | 3 + 4 files changed, 241 insertions(+), 80 deletions(-) create mode 100644 test/Invariant/report.md diff --git a/foundry.toml b/foundry.toml index fc4d67e..cc8a3f8 100644 --- a/foundry.toml +++ b/foundry.toml @@ -37,4 +37,7 @@ unreal = { key = "", url = "https://unreal.blockscout.com/api" } [fmt] wrap_comments = true +[invariant] +fail_on_revert = true + # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/test/Invariant/Handler.sol b/test/Invariant/Handler.sol index 9829df1..0f292cb 100644 --- a/test/Invariant/Handler.sol +++ b/test/Invariant/Handler.sol @@ -29,25 +29,28 @@ contract Handler is CommonBase, StdCheats, StdUtils { uint256 public ghost_mintedSum; uint256 public ghost_actualBurn; - uint256 public ghost_enableRebase; + uint256 public ghost_actualMint; + uint256 public ghost_enableRebase; uint256 public ghost_zeroTransfer; - uint256 public ghost_disableRebase; + uint256 public ghost_disableRebase; uint256 public ghost_actualSendFrom; - uint256 public ghost_actualTransfer; + uint256 public ghost_actualTransfer; uint256 public ghost_bridgedTokensTo; - uint256 public ghost_zeroAddressBurn; + uint256 public ghost_zeroAddressBurn; uint256 public ghost_zeroTransferFrom; - uint256 public ghost_bridgedTokensFrom; + uint256 public ghost_bridgedTokensFrom; uint256 public ghost_actualTransferFrom; - uint256 public ghost_zeroAddressTransfer; + uint256 public ghost_zeroAddressTransfer; uint256 public ghost_zeroAddressSendFrom; + uint256 public ghost_zeroAddressTransferFrom; + uint256 public ghost_zeroAddressDisableRebase; constructor(USTB _ustb, USTB _ustb2, address _usdm) { ustb = _ustb; @@ -76,6 +79,8 @@ contract Handler is CommonBase, StdCheats, StdUtils { ghost_mintedSum += amount; if (amount == 0) ghost_zeroMint++; + if (amount > 0) ghost_actualMint++; + address to = currentActor; __mint(currentActor, amount); @@ -115,16 +120,30 @@ contract Handler is CommonBase, StdCheats, StdUtils { } } + uint256 public ghost_rebaseAmount; + uint256 public ghost_nonRebaseAmount; + function disable( uint256 seed, bool flag ) public useActor(seed) countCall("disableRebase") { - // if ( - // currentActor != address(0) && flag != ustb.isNotRebase(currentActor) - // ) { - // vm.startPrank(currentActor); - // ustb.disableRebase(currentActor, flag); - // } + if ( + currentActor != address(0) && flag != ustb.isNotRebase(currentActor) + ) { + vm.startPrank(currentActor); + + flag == true + ? ( + ghost_disableRebase++, + ghost_rebaseAmount += ustb.balanceOf(currentActor) + ) + : ( + ghost_enableRebase++, + ghost_nonRebaseAmount += ustb.balanceOf(currentActor) + ); + + ustb.disableRebase(currentActor, flag); + } else ghost_zeroAddressDisableRebase++; } function transfer( @@ -151,74 +170,80 @@ contract Handler is CommonBase, StdCheats, StdUtils { uint256 toSeed, uint256 amount ) public useActor(actorSeed) countCall("sendFrom") { - address to = _actors.rand(toSeed); - if (currentActor != address(0)) { - ghost_actualSendFrom++; - vm.deal(to, 10 ether); - - vm.deal(currentActor, 10 ether); - amount = bound(amount, 0, ustb.balanceOf(currentActor)); - - if (amount == 0) ghost_zeroTransfer++; - vm.startPrank(currentActor); - - usdm.approve(address(ustb), amount); - uint256 nativeFee; - - (nativeFee, ) = ustb.estimateSendFee( - uint16(block.chainid), - abi.encodePacked(to), - amount, - false, - "" - ); - - uint256 contractBalBeforeBridge = ustb.balanceOf(address(this)); - - ustb.sendFrom{value: (nativeFee * 105) / 100}( - currentActor, - uint16(block.chainid), - abi.encodePacked(to), - amount, - payable(currentActor), - address(0), - "" - ); - - uint256 contractBalAfterBridge = ustb.balanceOf(address(this)); - - ghost_bridgedTokensTo += - contractBalAfterBridge - - contractBalBeforeBridge; - - vm.startPrank(to); - - (nativeFee, ) = ustb.estimateSendFee( - uint16(block.chainid), - abi.encodePacked(currentActor), - ustb2.balanceOf(to), - false, - "" - ); - - uint256 contractBalBeforeBridge0 = ustb.balanceOf(address(this)); - - ustb2.sendFrom{value: (nativeFee * 105) / 100}( - to, - uint16(block.chainid), - abi.encodePacked(currentActor), - ustb2.balanceOf(to), - payable(to), - address(0), - "" - ); - - uint256 contractBalAfterBridge0 = ustb2.balanceOf(address(this)); - - ghost_bridgedTokensFrom += - contractBalBeforeBridge0 - - contractBalAfterBridge0; - } else ghost_zeroAddressSendFrom++; + if (!ustb.isNotRebase(currentActor)) { + address to = _actors.rand(toSeed); + if (currentActor != address(0)) { + ghost_actualSendFrom++; + vm.deal(to, 10 ether); + + vm.deal(currentActor, 10 ether); + amount = bound(amount, 0, ustb.balanceOf(currentActor)); + + if (amount == 0) ghost_zeroTransfer++; + vm.startPrank(currentActor); + + usdm.approve(address(ustb), amount); + uint256 nativeFee; + + (nativeFee, ) = ustb.estimateSendFee( + uint16(block.chainid), + abi.encodePacked(to), + amount, + false, + "" + ); + + uint256 contractBalBeforeBridge = ustb.balanceOf(address(this)); + + ustb.sendFrom{value: (nativeFee * 105) / 100}( + currentActor, + uint16(block.chainid), + abi.encodePacked(to), + amount, + payable(currentActor), + address(0), + "" + ); + + uint256 contractBalAfterBridge = ustb.balanceOf(address(this)); + + ghost_bridgedTokensTo += + contractBalAfterBridge - + contractBalBeforeBridge; + + vm.startPrank(to); + + (nativeFee, ) = ustb.estimateSendFee( + uint16(block.chainid), + abi.encodePacked(currentActor), + ustb2.balanceOf(to), + false, + "" + ); + + uint256 contractBalBeforeBridge0 = ustb.balanceOf( + address(this) + ); + + ustb2.sendFrom{value: (nativeFee * 105) / 100}( + to, + uint16(block.chainid), + abi.encodePacked(currentActor), + ustb2.balanceOf(to), + payable(to), + address(0), + "" + ); + + uint256 contractBalAfterBridge0 = ustb2.balanceOf( + address(this) + ); + + ghost_bridgedTokensFrom += + contractBalBeforeBridge0 - + contractBalAfterBridge0; + } else ghost_zeroAddressSendFrom++; + } } function transferFrom( @@ -294,10 +319,12 @@ contract Handler is CommonBase, StdCheats, StdUtils { console.log("Transfer(s):", ghost_zeroAddressTransfer); console.log("sendFrom(s):", ghost_zeroAddressSendFrom); console.log("TransferFrom(s):", ghost_zeroAddressTransferFrom); + console.log("DisableRebase(s):", ghost_zeroAddressDisableRebase); console.log("-------------------"); console.log("Actual Calls:"); console.log("-------------------"); + console.log("Mint(s):", ghost_actualMint); console.log("Burn(s):", ghost_actualBurn); console.log("SendFrom(s):", ghost_actualSendFrom); console.log("Transfer(s):", ghost_actualTransfer); diff --git a/test/Invariant/report.md b/test/Invariant/report.md new file mode 100644 index 0000000..87aff28 --- /dev/null +++ b/test/Invariant/report.md @@ -0,0 +1,128 @@ +Findings +High + +### [H-1] `totalShares` not updated when tokens are transferred from a rebase user to a non-rebase user and vice-versa + +**Description:** In the `RebaseTokenUpgradeable.sol`, when token transfer is done from a rebase user to a non-rebase user the shares to be transferred out isn't substracted from `totalShares` and when token transfer is done from a non-rebase user to a rebase user the shares to be transferred in isn't added to `totalShares` in the `_update()`. + +**Impact:** USTB `totalSupply()` is inaccurate. + +**Proof of Concept:** +The code below contains two tests: + +`test_ReturnWrongTotalSupplyAfterTokenTransferFromRebaseToNonRebase()` shows how alice a rebase user transfers tokens to bob a non-rebase user and the `totalShares` of rebase tokens isn't reduced by the transferred amount. + +`test_ReturnWrongTotalSupplyAfterTokenTransferFromNonRebaseToRebase()` show how bob a non-rebase user transfers tokens to alice a rebase user and the transferred amount isn't added to the `totalShares` of non-rebase tokens. + +```javascript + function test_ReturnWrongTotalSupplyAfterTokenTransferFromRebaseToNonRebase() + public + { + vm.startPrank(usdmHolder); + usdm.transfer(alice, 100e18); + usdm.transfer(bob, 100e18); + + vm.startPrank(alice); + usdm.approve(address(ustb), 100e18); + ustb.mint(alice, 100e18); + + vm.startPrank(bob); + usdm.approve(address(ustb), 100e18); + + ustb.disableRebase(bob, true); + ustb.mint(bob, 100e18); + + vm.roll(18349000); + vm.startPrank(usdmController); + + (bool success, ) = address(usdm).call( + abi.encodeWithSignature("addRewardMultiplier(uint256)", 134e12) + ); + assert(success); + + vm.startPrank(indexManager); + ustb.refreshRebaseIndex(); // force update + + vm.startPrank(alice); + uint256 balance1 = ustb.balanceOf(alice); + uint256 balance2 = ustb.balanceOf(bob); + + //////////////////////////// Shows Bug //////////////////////////// + + console.log( + ustb.totalSupply(), + "Total supply before transferring tokens to bob" + ); + + uint256 totalSupplyBeforeTransfer = ustb.totalSupply(); + ustb.transfer(bob, balance1); + uint256 totalSupplyAfterTransfer = ustb.totalSupply(); + + console.log( + ustb.totalSupply(), + "Total supply before transferring tokens to bob" + ); + + assertLt(totalSupplyBeforeTransfer, totalSupplyAfterTransfer); + } + + function test_ReturnWrongTotalSupplyAfterTokenTransferFromNonRebaseToRebase() + public + { + vm.startPrank(usdmHolder); + usdm.transfer(bob, 100e18); + usdm.transfer(alice, 100e18); + + vm.startPrank(bob); + usdm.approve(address(ustb), 100e18); + + ustb.disableRebase(bob, true); + ustb.mint(bob, 100e18); + + vm.startPrank(alice); + usdm.approve(address(ustb), 100e18); + ustb.mint(alice, 100e18); + + vm.roll(18349000); + vm.startPrank(usdmController); + + (bool success, ) = address(usdm).call( + abi.encodeWithSignature("addRewardMultiplier(uint256)", 134e12) + ); + assert(success); + + vm.startPrank(indexManager); + ustb.refreshRebaseIndex(); // force update + + vm.startPrank(bob); + uint256 balance1 = ustb.balanceOf(bob); + + //////////////////////////// Shows Bug //////////////////////////// + + console.log( + ustb.totalSupply(), + "Total supply before transferring tokens to alice" + ); + + uint256 totalSupplyBeforeTransfer = ustb.totalSupply(); + ustb.transfer(alice, balance1); + uint256 totalSupplyAfterTransfer = ustb.totalSupply(); + + console.log( + ustb.totalSupply(), + "Total supply before transferring tokens to alice" + ); + + assertLt(totalSupplyAfterTransfer, totalSupplyBeforeTransfer); + } +``` + +**Recommended Mitigation:** + +```diff +After line 231 in RebaseTokenUpgradeable.sol ++ if (optOutTo && to != address(0)) $.totalShares -= shares; + +After line 252 in RebaseTokenUpgradeable.sol ++ if (optOutFrom) $.totalShares += shares; +``` diff --git a/test/USTB.t.sol b/test/USTB.t.sol index 851ce02..04611e2 100644 --- a/test/USTB.t.sol +++ b/test/USTB.t.sol @@ -230,6 +230,7 @@ contract USTBTest is Test { function test_sendFrom() public { vm.startPrank(usdmHolder); usdm.approve(address(ustb), 1e18); + // ustb.disableRebase(usdmHolder, true); ustb.mint(usdmHolder, 1e18); uint256 nativeFee; @@ -420,6 +421,8 @@ contract USTBTest is Test { ustb.disableRebase(usdmHolder, true); } + // function + function test_shouldFailToDisableRebaseIfValueIsUnchanged() public { vm.startPrank(usdmHolder); usdm.approve(address(ustb), 1e18); From 630bb8c2ff8ab52204e9a4bf88ce133c2d6ec444 Mon Sep 17 00:00:00 2001 From: c-note Date: Tue, 2 Jan 2024 16:05:35 +0000 Subject: [PATCH 06/36] Update audit report. --- test/Invariant/report.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index 87aff28..0996ed9 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -45,7 +45,6 @@ The code below contains two tests: vm.startPrank(alice); uint256 balance1 = ustb.balanceOf(alice); - uint256 balance2 = ustb.balanceOf(bob); //////////////////////////// Shows Bug //////////////////////////// @@ -60,7 +59,7 @@ The code below contains two tests: console.log( ustb.totalSupply(), - "Total supply before transferring tokens to bob" + "Total supply after transferring tokens to bob" ); assertLt(totalSupplyBeforeTransfer, totalSupplyAfterTransfer); @@ -110,7 +109,7 @@ The code below contains two tests: console.log( ustb.totalSupply(), - "Total supply before transferring tokens to alice" + "Total supply after transferring tokens to alice" ); assertLt(totalSupplyAfterTransfer, totalSupplyBeforeTransfer); From 9e0f8357b6153983ea2a551005dbc34220fc043a Mon Sep 17 00:00:00 2001 From: c-note Date: Tue, 2 Jan 2024 16:07:04 +0000 Subject: [PATCH 07/36] Update audit report. --- test/Invariant/report.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index 0996ed9..79efd4d 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -1,5 +1,6 @@ -Findings -High +# Findings + +## High ### [H-1] `totalShares` not updated when tokens are transferred from a rebase user to a non-rebase user and vice-versa From 3fd1e80f9e96408e3e34a3bdb629f193c93147f5 Mon Sep 17 00:00:00 2001 From: c-note Date: Tue, 2 Jan 2024 16:56:38 +0000 Subject: [PATCH 08/36] Update Audit Report. --- test/Invariant/report.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index 79efd4d..27b958a 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -120,9 +120,9 @@ The code below contains two tests: **Recommended Mitigation:** ```diff -After line 231 in RebaseTokenUpgradeable.sol +After line 231 in [RebaseTokenUpgradeable.sol](https://github.com/TangibleTNFT/tangible-foundation-contracts/blob/c98ea3cb772c8c3939527be5fd1ebe21ce7e9cc3/src/tokens/RebaseTokenUpgradeable.sol#L231) + if (optOutTo && to != address(0)) $.totalShares -= shares; -After line 252 in RebaseTokenUpgradeable.sol +After line 252 in [RebaseTokenUpgradeable.sol](https://github.com/TangibleTNFT/tangible-foundation-contracts/blob/c98ea3cb772c8c3939527be5fd1ebe21ce7e9cc3/src/tokens/RebaseTokenUpgradeable.sol#L252) + if (optOutFrom) $.totalShares += shares; ``` From c6a7b7bbc3979653e3c6fd6efec6d99993c089a6 Mon Sep 17 00:00:00 2001 From: c-note Date: Tue, 2 Jan 2024 16:58:46 +0000 Subject: [PATCH 09/36] Update Audit Report. --- test/Invariant/report.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index 27b958a..bf885a3 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -118,9 +118,10 @@ The code below contains two tests: ``` **Recommended Mitigation:** +After line 231 in [RebaseTokenUpgradeable.sol](https://github.com/TangibleTNFT/tangible-foundation-contracts/blob/c98ea3cb772c8c3939527be5fd1ebe21ce7e9cc3/src/tokens/RebaseTokenUpgradeable.sol#L231) ```diff -After line 231 in [RebaseTokenUpgradeable.sol](https://github.com/TangibleTNFT/tangible-foundation-contracts/blob/c98ea3cb772c8c3939527be5fd1ebe21ce7e9cc3/src/tokens/RebaseTokenUpgradeable.sol#L231) + + if (optOutTo && to != address(0)) $.totalShares -= shares; After line 252 in [RebaseTokenUpgradeable.sol](https://github.com/TangibleTNFT/tangible-foundation-contracts/blob/c98ea3cb772c8c3939527be5fd1ebe21ce7e9cc3/src/tokens/RebaseTokenUpgradeable.sol#L252) From be34d9213ce31d1c3641fe6d0f03dd4f2f74d02c Mon Sep 17 00:00:00 2001 From: c-note Date: Tue, 2 Jan 2024 17:02:35 +0000 Subject: [PATCH 10/36] Update Audit Report. --- test/Invariant/report.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index bf885a3..56e9569 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -117,13 +117,18 @@ The code below contains two tests: } ``` +[foundry's cast](https://github.com/foundry-rs/foundry) + **Recommended Mitigation:** + After line 231 in [RebaseTokenUpgradeable.sol](https://github.com/TangibleTNFT/tangible-foundation-contracts/blob/c98ea3cb772c8c3939527be5fd1ebe21ce7e9cc3/src/tokens/RebaseTokenUpgradeable.sol#L231) ```diff - + if (optOutTo && to != address(0)) $.totalShares -= shares; +``` After line 252 in [RebaseTokenUpgradeable.sol](https://github.com/TangibleTNFT/tangible-foundation-contracts/blob/c98ea3cb772c8c3939527be5fd1ebe21ce7e9cc3/src/tokens/RebaseTokenUpgradeable.sol#L252) + +```diff + if (optOutFrom) $.totalShares += shares; ``` From 007a65ab2c534c054942dcb33747c58d05f53a50 Mon Sep 17 00:00:00 2001 From: c-note Date: Tue, 2 Jan 2024 17:47:38 +0000 Subject: [PATCH 11/36] Update Audit Report. --- test/Invariant/report.md | 76 ++++++++++++++++++++++++++++++++++++++-- test/USTB.t.sol | 9 ++++- 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index 56e9569..f4a9b05 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -117,8 +117,6 @@ The code below contains two tests: } ``` -[foundry's cast](https://github.com/foundry-rs/foundry) - **Recommended Mitigation:** After line 231 in [RebaseTokenUpgradeable.sol](https://github.com/TangibleTNFT/tangible-foundation-contracts/blob/c98ea3cb772c8c3939527be5fd1ebe21ce7e9cc3/src/tokens/RebaseTokenUpgradeable.sol#L231) @@ -132,3 +130,77 @@ After line 252 in [RebaseTokenUpgradeable.sol](https://github.com/TangibleTNFT/t ```diff + if (optOutFrom) $.totalShares += shares; ``` + +## Medium + +### [M-1] Fails to bridge tokens for non-rebase users + +**Description:** In the `LayerZeroRebaseTokenUpgradeable.sol`, when a non-rebase user tries to bridge tokens it fails because when `_debitFrom()` is called `_transferableShares()` gets called as well which is soley used to check rebase user balance before tranferring tokens, given the user trying to bridge token is a non-rebase it fails stating `AmountExceedsBalance()` + +**Impact:** Fails everytime a non-rebase users tries to bridge tokens. + +**Proof of Concept:** +The code below contains two tests: + +`test_shouldFailWhenSenderIsNonRebaseUser()` shows how a non-rebase user fails to bridge tokens. + +```Javascript + error AmountExceedsBalance( + address account, + uint256 balance, + uint256 amount + ); + + function test_shouldFailWhenSenderIsNonRebaseUser() public { + vm.startPrank(usdmHolder); + usdm.approve(address(ustb), 1e18); + + // user becomes non-rebase + ustb.disableRebase(usdmHolder, true); + ustb.mint(usdmHolder, 1e18); + + uint256 nativeFee; + (nativeFee, ) = ustb.estimateSendFee( + uint16(block.chainid), + abi.encodePacked(alice), + 0.5e18, + false, + "" + ); + + // Catch AmountExceedsBalance error. + vm.expectRevert( + abi.encodeWithSelector( + AmountExceedsBalance.selector, + usdmHolder, + 0, + 0.5e18 + ) + ); + + ustb.sendFrom{value: (nativeFee * 105) / 100}( + usdmHolder, + uint16(block.chainid), + abi.encodePacked(alice), + 0.5e18, + payable(usdmHolder), + address(0), + "" + ); + } +``` + +**Recommended Mitigation:** + +If only rebase user are allowed to bridge tokens, then after line 154 in [LayerZeroRebaseTokenUpgradeable.sol](https://github.com/TangibleTNFT/tangible-foundation-contracts/blob/c98ea3cb772c8c3939527be5fd1ebe21ce7e9cc3/src/tokens/LayerZeroRebaseTokenUpgradeable.sol#L154) + +```diff ++ error OnlyRebaseTokensCanBridgeToken(); + ................................ + ++ if (_isRebaseDisabled(from)) { ++ revert OnlyRebaseTokensCanBridgeToken(); ++ } +``` + +If both rebase and non-rebase user are allowed to bridge tokens then the logic in `_debitFrom()` needs to be rewritten. diff --git a/test/USTB.t.sol b/test/USTB.t.sol index 04611e2..d49ac6c 100644 --- a/test/USTB.t.sol +++ b/test/USTB.t.sol @@ -227,10 +227,16 @@ contract USTBTest is Test { assertGt(balanceAfter, balanceBefore); } + error AmountExceedsBalance( + address account, + uint256 balance, + uint256 amount + ); + function test_sendFrom() public { vm.startPrank(usdmHolder); usdm.approve(address(ustb), 1e18); - // ustb.disableRebase(usdmHolder, true); + ustb.disableRebase(usdmHolder, true); ustb.mint(usdmHolder, 1e18); uint256 nativeFee; @@ -254,6 +260,7 @@ contract USTBTest is Test { assertApproxEqAbs(ustbChild.balanceOf(alice), 0.5e18, 2); vm.startPrank(alice); + (nativeFee, ) = ustb.estimateSendFee( uint16(block.chainid), abi.encodePacked(usdmHolder), From 25313fc697e689928229023645d00dc194493e49 Mon Sep 17 00:00:00 2001 From: c-note Date: Wed, 3 Jan 2024 09:49:00 +0000 Subject: [PATCH 12/36] Update Audit Report. --- test/Invariant/report.md | 73 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 4 deletions(-) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index f4a9b05..add4d72 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -4,7 +4,7 @@ ### [H-1] `totalShares` not updated when tokens are transferred from a rebase user to a non-rebase user and vice-versa -**Description:** In the `RebaseTokenUpgradeable.sol`, when token transfer is done from a rebase user to a non-rebase user the shares to be transferred out isn't substracted from `totalShares` and when token transfer is done from a non-rebase user to a rebase user the shares to be transferred in isn't added to `totalShares` in the `_update()`. +**Description:** In `RebaseTokenUpgradeable.sol`, when token transfer is done from a rebase user to a non-rebase user the shares to be transferred out isn't substracted from `totalShares` and when token transfer is done from a non-rebase user to a rebase user the shares to be transferred in isn't added to `totalShares` in the `_update()`. **Impact:** USTB `totalSupply()` is inaccurate. @@ -13,7 +13,7 @@ The code below contains two tests: `test_ReturnWrongTotalSupplyAfterTokenTransferFromRebaseToNonRebase()` shows how alice a rebase user transfers tokens to bob a non-rebase user and the `totalShares` of rebase tokens isn't reduced by the transferred amount. -`test_ReturnWrongTotalSupplyAfterTokenTransferFromNonRebaseToRebase()` show how bob a non-rebase user transfers tokens to alice a rebase user and the transferred amount isn't added to the `totalShares` of non-rebase tokens. +`test_ReturnWrongTotalSupplyAfterTokenTransferFromNonRebaseToRebase()` shows how bob a non-rebase user transfers tokens to alice a rebase user and the transferred amount isn't added to the `totalShares` of non-rebase tokens. ```javascript function test_ReturnWrongTotalSupplyAfterTokenTransferFromRebaseToNonRebase() @@ -135,12 +135,12 @@ After line 252 in [RebaseTokenUpgradeable.sol](https://github.com/TangibleTNFT/t ### [M-1] Fails to bridge tokens for non-rebase users -**Description:** In the `LayerZeroRebaseTokenUpgradeable.sol`, when a non-rebase user tries to bridge tokens it fails because when `_debitFrom()` is called `_transferableShares()` gets called as well which is soley used to check rebase user balance before tranferring tokens, given the user trying to bridge token is a non-rebase it fails stating `AmountExceedsBalance()` +**Description:** In `LayerZeroRebaseTokenUpgradeable.sol`, when a non-rebase user tries to bridge tokens it fails because when `_debitFrom()` is called `_transferableShares()` gets called as well which is solely used to check rebase user balance before tranferring tokens, given the user trying to bridge token is a non-rebase it fails stating `AmountExceedsBalance()` **Impact:** Fails everytime a non-rebase users tries to bridge tokens. **Proof of Concept:** -The code below contains two tests: +The code below contains one test: `test_shouldFailWhenSenderIsNonRebaseUser()` shows how a non-rebase user fails to bridge tokens. @@ -204,3 +204,68 @@ If only rebase user are allowed to bridge tokens, then after line 154 in [LayerZ ``` If both rebase and non-rebase user are allowed to bridge tokens then the logic in `_debitFrom()` needs to be rewritten. + +## Medium + +### [L-2] Amount newly minted to non-rebase users are not checked for `totalSupply` overflow + +**Description:** In `RebaseTokenUpgradeable.sol`, when a non-rebase user mints tokens the `_update()` does not check if the addition of minted amount plus `totalShares` plus `ERC20Upgradeable.totalSupply()` overflows. + +**Impact:** When this happens calls to `RebaseTokenUpgradeable.totalSupply()` overflows thereby making `totalSupply() unreachable`. + +**Proof of Concept:** +The code below contains one test: + +`test_TotalSupplyUnreachableWhenNonRebaseMintsTokenAboveSupply()` shows how `totalSupply()` overflows. + +```Javascript + // To detail this error I exposed the `_mint()` + + function exposedMintForTesting(address to, uint256 amount) external mainChain(true) { + _mint(to, amount); + } + + // Test + function test_TotalSupplyUnreachableWhenNonRebaseMintsTokenAboveSupply() + public + { + address usdmMinter = 0x48AEB395FB0E4ff8433e9f2fa6E0579838d33B62; + vm.startPrank(usdmMinter); + + (bool success, ) = address(usdm).call( + abi.encodeWithSignature("mint(address,uint256)", address(3), 1e18) + ); + + assert(success); + + vm.startPrank(address(3)); + usdm.approve(address(ustb), type(uint256).max); + ustb.mint(address(3), 1e18); + + vm.startPrank(address(7)); + usdm.approve(address(ustb), type(uint256).max); + ustb.disableRebase(address(7), true); + ustb.exposedMintForTesting(address(7), type(uint256).max); + + //////////////////////////// Fails with arithmetic underflow or overflow //////////////////////////// + + vm.expectRevert(); + ustb.totalSupply(); + } +``` + +**Recommended Mitigation:** + +After line 245 in [RebaseTokenUpgradeable.sol](https://github.com/TangibleTNFT/tangible-foundation-contracts/blob/c98ea3cb772c8c3939527be5fd1ebe21ce7e9cc3/src/tokens/RebaseTokenUpgradeable.sol#L245) + +```diff ++ _checkTotalSupplyOverFlow(amount); + ....................... ++ function _checkTotalSupplyOverFlow(uint256 amount) private view { ++ unchecked { ++ if (amount + totalSupply() < totalSupply()) { ++ revert SupplyOverflow(); ++ } ++ } ++ } +``` From e78d6cd44707f6732c230bbe7185d9df37bfeae9 Mon Sep 17 00:00:00 2001 From: c-note Date: Wed, 3 Jan 2024 09:50:35 +0000 Subject: [PATCH 13/36] Update Audit Report. --- test/Invariant/report.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index add4d72..68ea3f5 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -260,7 +260,13 @@ After line 245 in [RebaseTokenUpgradeable.sol](https://github.com/TangibleTNFT/t ```diff + _checkTotalSupplyOverFlow(amount); + ....................... + ++ error SupplyOverflow(); + + ....................... + + function _checkTotalSupplyOverFlow(uint256 amount) private view { + unchecked { + if (amount + totalSupply() < totalSupply()) { From f1a285b54afd3cab6af3e6152d6ea7f576f1e7e2 Mon Sep 17 00:00:00 2001 From: c-note Date: Wed, 3 Jan 2024 10:05:29 +0000 Subject: [PATCH 14/36] Update Audit Report. --- foundry.toml | 1 - test/Invariant/report.md | 6 +++--- test/USTB.t.sol | 11 +---------- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/foundry.toml b/foundry.toml index cc8a3f8..58815c2 100644 --- a/foundry.toml +++ b/foundry.toml @@ -38,6 +38,5 @@ unreal = { key = "", url = "https://unreal.blockscout.com/api" } wrap_comments = true [invariant] -fail_on_revert = true # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/test/Invariant/report.md b/test/Invariant/report.md index 68ea3f5..687bf85 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -207,9 +207,9 @@ If both rebase and non-rebase user are allowed to bridge tokens then the logic i ## Medium -### [L-2] Amount newly minted to non-rebase users are not checked for `totalSupply` overflow +### [M-2] Amount newly minted to non-rebase users are not checked for `totalSupply` overflow -**Description:** In `RebaseTokenUpgradeable.sol`, when a non-rebase user mints tokens the `_update()` does not check if the addition of minted amount plus `totalShares` plus `ERC20Upgradeable.totalSupply()` overflows. +**Description:** In `RebaseTokenUpgradeable.sol`, when a non-rebase user mints tokens the `_update()` does not check if the addition of minted `amount` plus `totalShares` plus `ERC20Upgradeable.totalSupply()` overflows. **Impact:** When this happens calls to `RebaseTokenUpgradeable.totalSupply()` overflows thereby making `totalSupply() unreachable`. @@ -247,7 +247,7 @@ The code below contains one test: ustb.disableRebase(address(7), true); ustb.exposedMintForTesting(address(7), type(uint256).max); - //////////////////////////// Fails with arithmetic underflow or overflow //////////////////////////// + //////////////////////////// Fails with overflow //////////////////////////// vm.expectRevert(); ustb.totalSupply(); diff --git a/test/USTB.t.sol b/test/USTB.t.sol index d49ac6c..0bd039b 100644 --- a/test/USTB.t.sol +++ b/test/USTB.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Unlicense pragma solidity ^0.8.13; -import "forge-std/Test.sol"; +import {Test} from "forge-std/Test.sol"; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; @@ -227,16 +227,9 @@ contract USTBTest is Test { assertGt(balanceAfter, balanceBefore); } - error AmountExceedsBalance( - address account, - uint256 balance, - uint256 amount - ); - function test_sendFrom() public { vm.startPrank(usdmHolder); usdm.approve(address(ustb), 1e18); - ustb.disableRebase(usdmHolder, true); ustb.mint(usdmHolder, 1e18); uint256 nativeFee; @@ -428,8 +421,6 @@ contract USTBTest is Test { ustb.disableRebase(usdmHolder, true); } - // function - function test_shouldFailToDisableRebaseIfValueIsUnchanged() public { vm.startPrank(usdmHolder); usdm.approve(address(ustb), 1e18); From 2323ea46a6621894a202d590d0862779e3169258 Mon Sep 17 00:00:00 2001 From: c-note Date: Wed, 3 Jan 2024 10:13:34 +0000 Subject: [PATCH 15/36] Update Audit Report. --- test/Invariant/Handler.sol | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/test/Invariant/Handler.sol b/test/Invariant/Handler.sol index 0f292cb..bc19f49 100644 --- a/test/Invariant/Handler.sol +++ b/test/Invariant/Handler.sol @@ -120,9 +120,6 @@ contract Handler is CommonBase, StdCheats, StdUtils { } } - uint256 public ghost_rebaseAmount; - uint256 public ghost_nonRebaseAmount; - function disable( uint256 seed, bool flag @@ -132,16 +129,6 @@ contract Handler is CommonBase, StdCheats, StdUtils { ) { vm.startPrank(currentActor); - flag == true - ? ( - ghost_disableRebase++, - ghost_rebaseAmount += ustb.balanceOf(currentActor) - ) - : ( - ghost_enableRebase++, - ghost_nonRebaseAmount += ustb.balanceOf(currentActor) - ); - ustb.disableRebase(currentActor, flag); } else ghost_zeroAddressDisableRebase++; } From 70fdb45629226d42ed1196bb601d961a76ec2fdb Mon Sep 17 00:00:00 2001 From: c-note Date: Wed, 3 Jan 2024 14:08:28 +0000 Subject: [PATCH 16/36] Update Audit Report. --- test/Invariant/report.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index 687bf85..1a0916d 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -63,6 +63,8 @@ The code below contains two tests: "Total supply after transferring tokens to bob" ); + // totalSupplyBeforeTransfer is meant to be equal to totalSupplyAfterTransfer + // because tokens are only transferred between users not burnt/minted. assertLt(totalSupplyBeforeTransfer, totalSupplyAfterTransfer); } @@ -113,6 +115,8 @@ The code below contains two tests: "Total supply after transferring tokens to alice" ); + // totalSupplyBeforeTransfer is meant to be equal to totalSupplyAfterTransfer + // because tokens are only transferred between users not burnt/minted. assertLt(totalSupplyAfterTransfer, totalSupplyBeforeTransfer); } ``` From 4948bef1154deeea3f4ceadcf5da79d1d01d2b87 Mon Sep 17 00:00:00 2001 From: c-note Date: Wed, 3 Jan 2024 14:19:33 +0000 Subject: [PATCH 17/36] Update Audit Report. --- test/Invariant/report.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index 1a0916d..60fbc00 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -16,6 +16,7 @@ The code below contains two tests: `test_ReturnWrongTotalSupplyAfterTokenTransferFromNonRebaseToRebase()` shows how bob a non-rebase user transfers tokens to alice a rebase user and the transferred amount isn't added to the `totalShares` of non-rebase tokens. ```javascript + function test_ReturnWrongTotalSupplyAfterTokenTransferFromRebaseToNonRebase() public { @@ -121,6 +122,8 @@ The code below contains two tests: } ``` +#### To `run` add to `USTB.t.sol` + **Recommended Mitigation:** After line 231 in [RebaseTokenUpgradeable.sol](https://github.com/TangibleTNFT/tangible-foundation-contracts/blob/c98ea3cb772c8c3939527be5fd1ebe21ce7e9cc3/src/tokens/RebaseTokenUpgradeable.sol#L231) @@ -194,6 +197,8 @@ The code below contains one test: } ``` +#### To `run` add to `USTB.t.sol` + **Recommended Mitigation:** If only rebase user are allowed to bridge tokens, then after line 154 in [LayerZeroRebaseTokenUpgradeable.sol](https://github.com/TangibleTNFT/tangible-foundation-contracts/blob/c98ea3cb772c8c3939527be5fd1ebe21ce7e9cc3/src/tokens/LayerZeroRebaseTokenUpgradeable.sol#L154) @@ -258,6 +263,8 @@ The code below contains one test: } ``` +#### To `run` add to `USTB.t.sol` + **Recommended Mitigation:** After line 245 in [RebaseTokenUpgradeable.sol](https://github.com/TangibleTNFT/tangible-foundation-contracts/blob/c98ea3cb772c8c3939527be5fd1ebe21ce7e9cc3/src/tokens/RebaseTokenUpgradeable.sol#L245) From 6e0cd0ad0b4e8c27b46ebb162e1e0de393738897 Mon Sep 17 00:00:00 2001 From: c-note Date: Wed, 3 Jan 2024 14:21:06 +0000 Subject: [PATCH 18/36] Update Audit Report. --- test/Invariant/report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index 60fbc00..8447f79 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -263,7 +263,7 @@ The code below contains one test: } ``` -#### To `run` add to `USTB.t.sol` +**_ To `run` add to `USTB.t.sol` _** **Recommended Mitigation:** From f67b16e695637199d31a8c01a4620f49fb2655c7 Mon Sep 17 00:00:00 2001 From: c-note Date: Wed, 3 Jan 2024 14:22:06 +0000 Subject: [PATCH 19/36] Update Audit Report. --- test/Invariant/report.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index 8447f79..8178719 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -122,7 +122,7 @@ The code below contains two tests: } ``` -#### To `run` add to `USTB.t.sol` +To `run` add to `USTB.t.sol`. **Recommended Mitigation:** @@ -197,7 +197,7 @@ The code below contains one test: } ``` -#### To `run` add to `USTB.t.sol` +To `run` add to `USTB.t.sol`. **Recommended Mitigation:** @@ -263,7 +263,7 @@ The code below contains one test: } ``` -**_ To `run` add to `USTB.t.sol` _** +To `run` add to `USTB.t.sol`. **Recommended Mitigation:** From 6e452eb1f19961caf342f0b983ccd0bccc500288 Mon Sep 17 00:00:00 2001 From: c-note Date: Wed, 3 Jan 2024 14:36:18 +0000 Subject: [PATCH 20/36] Update Audit Report. --- test/Invariant/report.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index 8178719..d40b9b2 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -51,8 +51,8 @@ The code below contains two tests: //////////////////////////// Shows Bug //////////////////////////// console.log( - ustb.totalSupply(), - "Total supply before transferring tokens to bob" + "Total supply before transferring tokens to bob", + ustb.totalSupply() ); uint256 totalSupplyBeforeTransfer = ustb.totalSupply(); @@ -60,8 +60,8 @@ The code below contains two tests: uint256 totalSupplyAfterTransfer = ustb.totalSupply(); console.log( - ustb.totalSupply(), - "Total supply after transferring tokens to bob" + "Total supply after transferring tokens to bob", + ustb.totalSupply() ); // totalSupplyBeforeTransfer is meant to be equal to totalSupplyAfterTransfer @@ -103,8 +103,8 @@ The code below contains two tests: //////////////////////////// Shows Bug //////////////////////////// console.log( - ustb.totalSupply(), - "Total supply before transferring tokens to alice" + "Total supply before transferring tokens to bob", + ustb.totalSupply() ); uint256 totalSupplyBeforeTransfer = ustb.totalSupply(); @@ -112,8 +112,8 @@ The code below contains two tests: uint256 totalSupplyAfterTransfer = ustb.totalSupply(); console.log( - ustb.totalSupply(), - "Total supply after transferring tokens to alice" + "Total supply after transferring tokens to bob", + ustb.totalSupply() ); // totalSupplyBeforeTransfer is meant to be equal to totalSupplyAfterTransfer @@ -230,6 +230,7 @@ The code below contains one test: ```Javascript // To detail this error I exposed the `_mint()` + // Add to USTB.sol function exposedMintForTesting(address to, uint256 amount) external mainChain(true) { _mint(to, amount); } @@ -247,10 +248,12 @@ The code below contains one test: assert(success); + // Mints 1e18 rebase tokens to address(3) vm.startPrank(address(3)); usdm.approve(address(ustb), type(uint256).max); ustb.mint(address(3), 1e18); + // Mints max of uint256 rebase tokens to address(7) vm.startPrank(address(7)); usdm.approve(address(ustb), type(uint256).max); ustb.disableRebase(address(7), true); @@ -258,6 +261,7 @@ The code below contains one test: //////////////////////////// Fails with overflow //////////////////////////// + // 1e18 + type(uint256).max which overflows vm.expectRevert(); ustb.totalSupply(); } From cd6e03f331b0941381e950c54af0a6e3f664038d Mon Sep 17 00:00:00 2001 From: c-note Date: Wed, 3 Jan 2024 14:46:56 +0000 Subject: [PATCH 21/36] Update Audit Report. --- test/Invariant/report.md | 154 +++++++++++++++++++-------------------- 1 file changed, 77 insertions(+), 77 deletions(-) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index d40b9b2..23d80bb 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -140,83 +140,7 @@ After line 252 in [RebaseTokenUpgradeable.sol](https://github.com/TangibleTNFT/t ## Medium -### [M-1] Fails to bridge tokens for non-rebase users - -**Description:** In `LayerZeroRebaseTokenUpgradeable.sol`, when a non-rebase user tries to bridge tokens it fails because when `_debitFrom()` is called `_transferableShares()` gets called as well which is solely used to check rebase user balance before tranferring tokens, given the user trying to bridge token is a non-rebase it fails stating `AmountExceedsBalance()` - -**Impact:** Fails everytime a non-rebase users tries to bridge tokens. - -**Proof of Concept:** -The code below contains one test: - -`test_shouldFailWhenSenderIsNonRebaseUser()` shows how a non-rebase user fails to bridge tokens. - -```Javascript - error AmountExceedsBalance( - address account, - uint256 balance, - uint256 amount - ); - - function test_shouldFailWhenSenderIsNonRebaseUser() public { - vm.startPrank(usdmHolder); - usdm.approve(address(ustb), 1e18); - - // user becomes non-rebase - ustb.disableRebase(usdmHolder, true); - ustb.mint(usdmHolder, 1e18); - - uint256 nativeFee; - (nativeFee, ) = ustb.estimateSendFee( - uint16(block.chainid), - abi.encodePacked(alice), - 0.5e18, - false, - "" - ); - - // Catch AmountExceedsBalance error. - vm.expectRevert( - abi.encodeWithSelector( - AmountExceedsBalance.selector, - usdmHolder, - 0, - 0.5e18 - ) - ); - - ustb.sendFrom{value: (nativeFee * 105) / 100}( - usdmHolder, - uint16(block.chainid), - abi.encodePacked(alice), - 0.5e18, - payable(usdmHolder), - address(0), - "" - ); - } -``` - -To `run` add to `USTB.t.sol`. - -**Recommended Mitigation:** - -If only rebase user are allowed to bridge tokens, then after line 154 in [LayerZeroRebaseTokenUpgradeable.sol](https://github.com/TangibleTNFT/tangible-foundation-contracts/blob/c98ea3cb772c8c3939527be5fd1ebe21ce7e9cc3/src/tokens/LayerZeroRebaseTokenUpgradeable.sol#L154) - -```diff -+ error OnlyRebaseTokensCanBridgeToken(); - ................................ - -+ if (_isRebaseDisabled(from)) { -+ revert OnlyRebaseTokensCanBridgeToken(); -+ } -``` - -If both rebase and non-rebase user are allowed to bridge tokens then the logic in `_debitFrom()` needs to be rewritten. - -## Medium - -### [M-2] Amount newly minted to non-rebase users are not checked for `totalSupply` overflow +### [M-1] Amount newly minted to non-rebase users are not checked for `totalSupply` overflow **Description:** In `RebaseTokenUpgradeable.sol`, when a non-rebase user mints tokens the `_update()` does not check if the addition of minted `amount` plus `totalShares` plus `ERC20Upgradeable.totalSupply()` overflows. @@ -290,3 +214,79 @@ After line 245 in [RebaseTokenUpgradeable.sol](https://github.com/TangibleTNFT/t + } + } ``` + +## Low + +### [L-1] Fails to bridge tokens for non-rebase users + +**Description:** In `LayerZeroRebaseTokenUpgradeable.sol`, when a non-rebase user tries to bridge tokens it fails because when `_debitFrom()` is called `_transferableShares()` gets called as well which is solely used to check rebase user balance before tranferring tokens, given the user trying to bridge token is a non-rebase it fails stating `AmountExceedsBalance()` + +**Impact:** Fails everytime a non-rebase users tries to bridge tokens. + +**Proof of Concept:** +The code below contains one test: + +`test_shouldFailWhenSenderIsNonRebaseUser()` shows how a non-rebase user fails to bridge tokens. + +```Javascript + error AmountExceedsBalance( + address account, + uint256 balance, + uint256 amount + ); + + function test_shouldFailWhenSenderIsNonRebaseUser() public { + vm.startPrank(usdmHolder); + usdm.approve(address(ustb), 1e18); + + // user becomes non-rebase + ustb.disableRebase(usdmHolder, true); + ustb.mint(usdmHolder, 1e18); + + uint256 nativeFee; + (nativeFee, ) = ustb.estimateSendFee( + uint16(block.chainid), + abi.encodePacked(alice), + 0.5e18, + false, + "" + ); + + // Catch AmountExceedsBalance error. + vm.expectRevert( + abi.encodeWithSelector( + AmountExceedsBalance.selector, + usdmHolder, + 0, + 0.5e18 + ) + ); + + ustb.sendFrom{value: (nativeFee * 105) / 100}( + usdmHolder, + uint16(block.chainid), + abi.encodePacked(alice), + 0.5e18, + payable(usdmHolder), + address(0), + "" + ); + } +``` + +To `run` add to `USTB.t.sol`. + +**Recommended Mitigation:** + +If only rebase user are allowed to bridge tokens, then after line 154 in [LayerZeroRebaseTokenUpgradeable.sol](https://github.com/TangibleTNFT/tangible-foundation-contracts/blob/c98ea3cb772c8c3939527be5fd1ebe21ce7e9cc3/src/tokens/LayerZeroRebaseTokenUpgradeable.sol#L154) + +```diff ++ error OnlyRebaseTokensCanBridgeToken(); + ................................ + ++ if (_isRebaseDisabled(from)) { ++ revert OnlyRebaseTokensCanBridgeToken(); ++ } +``` + +If both rebase and non-rebase user are allowed to bridge tokens then the logic in `_debitFrom()` needs to be rewritten. From d1ed482dff2acf522912968bb7af67ab26e15754 Mon Sep 17 00:00:00 2001 From: c-note Date: Wed, 3 Jan 2024 14:57:30 +0000 Subject: [PATCH 22/36] Update Audit Report. --- test/Invariant/report.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index 23d80bb..27a50ac 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -281,11 +281,11 @@ To `run` add to `USTB.t.sol`. If only rebase user are allowed to bridge tokens, then after line 154 in [LayerZeroRebaseTokenUpgradeable.sol](https://github.com/TangibleTNFT/tangible-foundation-contracts/blob/c98ea3cb772c8c3939527be5fd1ebe21ce7e9cc3/src/tokens/LayerZeroRebaseTokenUpgradeable.sol#L154) ```diff -+ error OnlyRebaseTokensCanBridgeToken(); ++ error OnlyRebaseTokensCanBridged(); ................................ + if (_isRebaseDisabled(from)) { -+ revert OnlyRebaseTokensCanBridgeToken(); ++ revert OnlyRebaseTokensCanBridged(); + } ``` From 7d509a03b5e01e5a4f73ecb394c1073efa8b63e1 Mon Sep 17 00:00:00 2001 From: c-note Date: Wed, 3 Jan 2024 14:58:13 +0000 Subject: [PATCH 23/36] Update Audit Report. --- test/Invariant/report.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index 27a50ac..5c2203a 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -281,11 +281,11 @@ To `run` add to `USTB.t.sol`. If only rebase user are allowed to bridge tokens, then after line 154 in [LayerZeroRebaseTokenUpgradeable.sol](https://github.com/TangibleTNFT/tangible-foundation-contracts/blob/c98ea3cb772c8c3939527be5fd1ebe21ce7e9cc3/src/tokens/LayerZeroRebaseTokenUpgradeable.sol#L154) ```diff -+ error OnlyRebaseTokensCanBridged(); ++ error OnlyRebaseTokensCanBeBridged(); ................................ + if (_isRebaseDisabled(from)) { -+ revert OnlyRebaseTokensCanBridged(); ++ revert OnlyRebaseTokensCanBeBridged(); + } ``` From 7ab0af785a327968bea7d6c6245290f23e36448e Mon Sep 17 00:00:00 2001 From: c-note Date: Wed, 3 Jan 2024 16:17:37 +0000 Subject: [PATCH 24/36] Update Audit Report. --- test/Invariant/report.md | 65 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index 5c2203a..4adeaf1 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -1,3 +1,68 @@ +--- +title: USTB Audit Report +author: c-n-o-t-e +date: January 3, 2024 +--- + +# USTB Audit Report + +Prepared by: C-N-O-T-E + +# Table of contents + +
+ +See table + +- [USTB Audit Report](#ustb-audit-report) +- [Table of contents](#table-of-contents) +- [Disclaimer](#disclaimer) +- [Risk Classification](#risk-classification) +- [Audit Details](#audit-details) + - [Scope](#scope) +- [Protocol Summary](#protocol-summary) + - [Roles](#roles) +- [Executive Summary](#executive-summary) + - [Issues found](#issues-found) + +# Disclaimer + +I makes all effort to find as many vulnerabilities in the code in the given time period, but holds no responsibilities for the the findings provided in this document. A security audit by me is not an endorsement of the underlying business or product. The audit was time-boxed and the review of the code was solely on the security aspects of the solidity implementation of the contracts. + +# Risk Classification + +| | | Impact | | | +| ---------- | ------ | ------ | ------ | --- | +| | | High | Medium | Low | +| | High | H | H/M | M | +| Likelihood | Medium | H/M | M | M/L | +| | Low | M | M/L | L | + +# Audit Details + +## Scope + +``` +src/ +--- USTB.sol +--- RebaseTokenUpgradeable.sol +--- LayerZeroRebaseTokenUpgradeable.sol +``` + +# Protocol Summary + +USTB extends the functionality of `LayerZeroRebaseTokenUpgradeable` to provide additional features specific to USTB. It adds capabilities for minting and burning tokens backed by an underlying asset, and dynamically updates the rebase index. + +# Executive Summary + +## Issues found + +| Severity | Number of issues found | +| -------- | ---------------------- | +| High | 1 | +| Medium | 1 | +| Low | 1 | + # Findings ## High From e9790f6de78354b31875cc0712fbcad9ea72f80d Mon Sep 17 00:00:00 2001 From: c-note Date: Wed, 3 Jan 2024 16:19:37 +0000 Subject: [PATCH 25/36] Update Audit Report. --- test/Invariant/report.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index 4adeaf1..9e15ac3 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -25,6 +25,9 @@ Prepared by: C-N-O-T-E - [Executive Summary](#executive-summary) - [Issues found](#issues-found) +
+
+ # Disclaimer I makes all effort to find as many vulnerabilities in the code in the given time period, but holds no responsibilities for the the findings provided in this document. A security audit by me is not an endorsement of the underlying business or product. The audit was time-boxed and the review of the code was solely on the security aspects of the solidity implementation of the contracts. From 570a07655b6eeb9317f7a814f62567594b5062f7 Mon Sep 17 00:00:00 2001 From: c-note Date: Wed, 3 Jan 2024 16:23:39 +0000 Subject: [PATCH 26/36] Update Audit Report. --- test/Invariant/report.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index 9e15ac3..313f55a 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -30,7 +30,7 @@ Prepared by: C-N-O-T-E # Disclaimer -I makes all effort to find as many vulnerabilities in the code in the given time period, but holds no responsibilities for the the findings provided in this document. A security audit by me is not an endorsement of the underlying business or product. The audit was time-boxed and the review of the code was solely on the security aspects of the solidity implementation of the contracts. +I make all effort to find as many vulnerabilities in the code in the given time period, but holds no responsibilities for the the findings provided in this document. A security audit by me is not an endorsement of the underlying business or product. The audit was time-boxed and the review of the code was solely on the security aspects of the solidity implementation of the contracts. # Risk Classification @@ -48,6 +48,8 @@ I makes all effort to find as many vulnerabilities in the code in the given time ``` src/ --- USTB.sol + +lib/tangible-foundation-contracts/src/tokens --- RebaseTokenUpgradeable.sol --- LayerZeroRebaseTokenUpgradeable.sol ``` From 03e595019f4df65ddd1d564000a2594667199f09 Mon Sep 17 00:00:00 2001 From: c-note Date: Wed, 3 Jan 2024 16:25:51 +0000 Subject: [PATCH 27/36] Update Audit Report. --- test/Invariant/report.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index 313f55a..b5bf91a 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -1,9 +1,13 @@ ---- +##
+ title: USTB Audit Report author: c-n-o-t-e date: January 3, 2024 + --- +
+ # USTB Audit Report Prepared by: C-N-O-T-E From b75dc2ec4053757ef2cfdd8e858c023228b10415 Mon Sep 17 00:00:00 2001 From: c-note Date: Wed, 3 Jan 2024 16:26:57 +0000 Subject: [PATCH 28/36] Update Audit Report. --- test/Invariant/report.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index b5bf91a..2189bb8 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -1,5 +1,7 @@ ##
+--- + title: USTB Audit Report author: c-n-o-t-e date: January 3, 2024 From 6d3c48bd2ece35e907bddbb897e0cd9dd4ea08b2 Mon Sep 17 00:00:00 2001 From: c-note Date: Wed, 3 Jan 2024 16:27:48 +0000 Subject: [PATCH 29/36] Update Audit Report. --- test/Invariant/report.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index 2189bb8..313f55a 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -1,15 +1,9 @@ -##
- --- - title: USTB Audit Report author: c-n-o-t-e date: January 3, 2024 - --- -
- # USTB Audit Report Prepared by: C-N-O-T-E From 1d3487626386c27c8d0f5511cf6aa15e96d519e1 Mon Sep 17 00:00:00 2001 From: c-note Date: Wed, 3 Jan 2024 16:28:24 +0000 Subject: [PATCH 30/36] Update Audit Report. --- test/Invariant/report.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index 313f55a..d80df5b 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -1,8 +1,8 @@ ---- -title: USTB Audit Report -author: c-n-o-t-e -date: January 3, 2024 ---- + --- + title: USTB Audit Report + author: c-n-o-t-e + date: January 3, 2024 + --- # USTB Audit Report From 864caddf544b9c037749855163dba889eeb95cd0 Mon Sep 17 00:00:00 2001 From: c-note Date: Wed, 3 Jan 2024 16:28:49 +0000 Subject: [PATCH 31/36] Update Audit Report. --- test/Invariant/report.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index d80df5b..313f55a 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -1,8 +1,8 @@ - --- - title: USTB Audit Report - author: c-n-o-t-e - date: January 3, 2024 - --- +--- +title: USTB Audit Report +author: c-n-o-t-e +date: January 3, 2024 +--- # USTB Audit Report From c5230b5aeea2cc392be6db279e6212a7bd3a67a8 Mon Sep 17 00:00:00 2001 From: c-note Date: Wed, 3 Jan 2024 16:29:26 +0000 Subject: [PATCH 32/36] Update Audit Report. --- test/Invariant/report.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index 313f55a..cf46edd 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -1,7 +1,7 @@ --- -title: USTB Audit Report -author: c-n-o-t-e -date: January 3, 2024 +Title: USTB Audit Report +Author: c-n-o-t-e +Date: January 3, 2024 --- # USTB Audit Report From f5b6873fa4c17beaf64bc9022a0b3f49ed159cb4 Mon Sep 17 00:00:00 2001 From: c-note Date: Wed, 3 Jan 2024 16:33:18 +0000 Subject: [PATCH 33/36] Update Audit Report. --- test/Invariant/report.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index cf46edd..87ae7fc 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -32,6 +32,8 @@ Prepared by: C-N-O-T-E I make all effort to find as many vulnerabilities in the code in the given time period, but holds no responsibilities for the the findings provided in this document. A security audit by me is not an endorsement of the underlying business or product. The audit was time-boxed and the review of the code was solely on the security aspects of the solidity implementation of the contracts. +**NOTE:** Gas optimization wasn't prioritize during testing. + # Risk Classification | | | Impact | | | From 2ca6961c18db3b60d7c7e11817d90e26b3d2a97a Mon Sep 17 00:00:00 2001 From: c-note Date: Wed, 3 Jan 2024 16:33:58 +0000 Subject: [PATCH 34/36] Update Audit Report. --- test/Invariant/report.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index 87ae7fc..9b8da06 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -6,8 +6,6 @@ Date: January 3, 2024 # USTB Audit Report -Prepared by: C-N-O-T-E - # Table of contents
From 7f76608e82dac33d8eee8f0086441cd5e2c856b6 Mon Sep 17 00:00:00 2001 From: c-note Date: Wed, 3 Jan 2024 16:34:40 +0000 Subject: [PATCH 35/36] Update Audit Report. --- test/Invariant/report.md | 1 - 1 file changed, 1 deletion(-) diff --git a/test/Invariant/report.md b/test/Invariant/report.md index 9b8da06..ad5e285 100644 --- a/test/Invariant/report.md +++ b/test/Invariant/report.md @@ -1,5 +1,4 @@ --- -Title: USTB Audit Report Author: c-n-o-t-e Date: January 3, 2024 --- From 89e22ac5e970659bebf6912965450b713ea3a3f4 Mon Sep 17 00:00:00 2001 From: c-note <58487146+c-n-o-t-e@users.noreply.github.com> Date: Tue, 23 Jan 2024 08:46:50 +0000 Subject: [PATCH 36/36] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index da3f79b..bba5214 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## Overview -The USTB Token project is a Solidity-based smart contract system designed to provide a rebase token with cross-chain and Layer Zero functionalities. It's built with upgradeability in mind, ensuring that the token logic can evolve over time. +The USTB Token project is a Solidity-based smart contract system designed to provide a rebase token with cross-chain and Layer Zero functionalities. It's built with upgradeability in mind, ensuring that the token logic can evolve. ## Key Components