Created
December 2, 2024 17:30
-
-
Save darkerego/af2ce27ab740d249364ba493a58481bb to your computer and use it in GitHub Desktop.
Minimalist WETH
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity ^ 0.8.18; | |
contract MinimalWrappedEther { | |
address public immutable admin; | |
uint256 public totalSupply; | |
uint256 public reservedEther; | |
string public name; | |
string public symbol; | |
uint8 public constant decimals = 18; // 1 byte | |
error AddressZero(address); | |
error AssetTransferFailed(address asset); | |
error InsufficientBalance(address senderOrToken, uint256 amount); | |
error InsufficientAllowance(address spender, address _owner, uint256 amount); | |
error EtherRequiredToMint(); | |
error NoSuchFunction(bytes4 signature); | |
error UseDeposit(); | |
// Mappings (dynamic storage, occupy their own slots) | |
mapping (address account => uint256 balance) public balanceOf; | |
mapping (address owner => mapping (address spender => uint256 value)) public allowance; | |
// Events (not stored in state) | |
event Transfer(address indexed from, address indexed to, uint256 amount); | |
event Approval(address indexed _account, address indexed spender, uint amount); | |
constructor(string memory _name, string memory _symbol) { | |
name = _name; | |
symbol = _symbol; | |
admin = msg.sender; | |
} | |
receive() external payable { depositTo(msg.sender); } | |
fallback() external payable { | |
revert NoSuchFunction(msg.sig); | |
} | |
function transferFrom(address from, address to, uint256 amount) external returns(bool) { | |
_approve(from, msg.sender, 0, amount, false); | |
return _transfer(from, to, amount); | |
} | |
function transfer(address to, uint256 amount) external returns(bool) { | |
return _transfer(msg.sender, to, amount); | |
} | |
function _transfer(address _from, address _to, uint256 _amount) private returns(bool) { | |
if (_from == address(0) && _to != address(0)) { | |
// Mint tokens | |
totalSupply += _amount; | |
balanceOf[_to] += _amount; | |
emit Transfer(_from, _to, _amount); | |
return true; | |
} | |
else if (_to == address(0) && _from != address(0)) { | |
// Burn tokens | |
if (balanceOf[_from] < _amount) { | |
revert InsufficientBalance(_from, _amount); | |
} | |
totalSupply -= _amount; | |
balanceOf[_from] -= _amount; | |
emit Transfer(_from, _to, _amount); | |
return true; | |
} | |
else { | |
// Regular transfer | |
if (balanceOf[_from] < _amount) { | |
revert InsufficientBalance(_from, _amount); | |
} | |
balanceOf[_from] -= _amount; | |
balanceOf[_to] += _amount; | |
emit Transfer(_from, _to, _amount); | |
return true; | |
}} | |
/* | |
@notice allow `_spender` to transfer `_amount` tokens from `msg.sender` | |
@param `_spender` authorized caller | |
@param `_amount` authorized qty | |
@dev the boolean in _approve is for whether or not to emit an `Approval` event | |
*/ | |
function approve(address _spender, uint256 _amount) public returns(bool) { | |
return _approve(msg.sender, _spender, _amount, 0, true); | |
} | |
/** | |
* @dev Approves or spends allowance for `spender` on behalf of `owner_`. | |
* If `value` is provided, sets the allowance; otherwise, deducts the allowance based on `spendValue`. | |
* | |
* Emits an {Approval} event if `emitEvent` is true. | |
* Reverts if insufficient allowance is available or invalid inputs are provided. | |
*/ | |
function _approve( | |
address owner_, | |
address spender, | |
uint256 value, | |
uint256 spendValue, | |
bool emitEvent | |
) internal virtual returns (bool success) { | |
// Validate addresses | |
(owner_, spender) = (validateAddress(owner_), validateAddress(spender)); | |
if (spendValue > 0) { | |
// Handle spending allowance | |
uint256 currentAllowance = allowance[owner_][spender]; | |
if (currentAllowance != type(uint256).max) { | |
if (currentAllowance < spendValue) { | |
revert InsufficientAllowance(spender, owner_, spendValue); | |
} | |
unchecked { | |
allowance[owner_][spender] = currentAllowance - spendValue; | |
} | |
} | |
} else { | |
// Handle approval | |
allowance[owner_][spender] = value; | |
success = true; | |
} | |
// Emit event if required | |
if (emitEvent && spendValue == 0) { | |
emit Approval(owner_, spender, value); | |
} | |
return success; | |
} | |
function validateAddress(address addr) internal pure returns(address _addr) { | |
bytes4 errorSelector = AddressZero.selector; | |
assembly { | |
if eq(addr, 0) { | |
let ptr := mload(0x40) // Free memory pointer | |
mstore(ptr, add(errorSelector, addr)) | |
mstore(add(ptr, 4), addr) | |
revert(ptr, 0x24) // 4 bytes for selector + 32 bytes for address | |
} | |
_addr := addr | |
} | |
} | |
function deposit() external payable { | |
depositTo(msg.sender); | |
} | |
function withdraw(uint256 amount) external { | |
withdrawTo(msg.sender, amount); | |
} | |
function depositTo(address account) public payable { | |
_mint(account, msg.value); | |
} | |
function withdrawTo(address account, uint256 amount) public { | |
_burn(msg.sender, account, amount); | |
} | |
/* | |
@dev mint tokens | |
*/ | |
function _mint(address _to, uint256 amount) internal returns(bool) { | |
if (msg.value > 0) { | |
if (_transfer(address(0), _to, amount)) { | |
reservedEther += msg.value; | |
assert(reservedEther >= totalSupply); | |
return true; | |
} else { | |
revert AssetTransferFailed(address(this)); | |
} | |
} else { | |
revert EtherRequiredToMint(); | |
} | |
} | |
/* | |
@notice Destroy tokens | |
*/ | |
function _burn(address from, address to, uint amount) internal returns(bool success) { | |
if (balanceOf[from] >= amount) { | |
if (_transfer(from, address(0), amount)) { | |
reservedEther -= amount; | |
(success, ) = payable(to).call{value: amount}(""); | |
assert(reservedEther >= totalSupply); | |
if (!success) { | |
revert AssetTransferFailed(address(0)); | |
} | |
} else { | |
revert AssetTransferFailed(address(this)); | |
} | |
} else { | |
revert InsufficientBalance(msg.sender, amount); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment