Skip to content

Instantly share code, notes, and snippets.

@RareSkills
Created March 10, 2025 06:52
Show Gist options
  • Select an option

  • Save RareSkills/5419beb9f7b06d4b4bb79473d3257d2c to your computer and use it in GitHub Desktop.

Select an option

Save RareSkills/5419beb9f7b06d4b4bb79473d3257d2c to your computer and use it in GitHub Desktop.
// this contract is buggy, don't use in production!
pragma solidity 0.8.28;
import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
contract RebasingERC20 is IERC20Errors, IERC20 {
uint256 private _totalShares;
mapping(address => uint256) private _shares;
mapping(address owner => mapping(address spender => uint256 allowance)) public allowance;
// transfering tokens to this contract causes a rebase upward
receive() external payable {}
function mint(address to) external payable {
_totalShares += msg.value;
_shares[to] += msg.value;
emit Transfer(address(0), to, msg.value);
}
function burn(address from, uint256 amount) external {
_spendAllowanceOrBlock(from, msg.sender, amount);
_shares[from] -= balanceToShares(amount);
_totalShares -= balanceToShares(amount);
emit Transfer(from, address(0), amount);
}
function balanceToShares(uint256 amount) internal view returns (uint256) {
if (address(this).balance == 0) {
return 0;
}
return amount * _totalShares / address(this).balance;
}
function totalSupply() public view returns (uint256) {
return address(this).balance;
}
function balanceOf(address account) public view returns (uint256) {
if (address(this).balance == 0 || _totalShares == 0) {
return 0;
}
return _shares[account] * address(this).balance / _totalShares;
}
function transfer(address to, uint256 amount) external returns (bool) {
transferFrom(msg.sender, to, amount);
return true;
}
function transferFrom(address from, address to, uint256 amount) public returns (bool) {
require(to != address(0), ERC20InvalidReceiver(to));
_spendAllowanceOrBlock(from, msg.sender, amount);
uint256 shareTransfer = balanceToShares(amount);
_shares[from] -= shareTransfer;
_shares[to] += shareTransfer;
emit Transfer(from, to, amount);
return true;
}
function approve(address spender, uint256 amount) external returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function _spendAllowanceOrBlock(address owner, address spender, uint256 amount) internal {
if (owner != msg.sender && allowance[owner][spender] != type(uint256).max) {
uint256 currentAllowance = allowance[owner][spender];
require(currentAllowance >= amount, ERC20InsufficientAllowance(spender, currentAllowance, amount));
allowance[owner][spender] = currentAllowance - amount;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment