Skip to content

Instantly share code, notes, and snippets.

@darkerego
Created December 2, 2024 17:30
Show Gist options
  • Save darkerego/af2ce27ab740d249364ba493a58481bb to your computer and use it in GitHub Desktop.
Save darkerego/af2ce27ab740d249364ba493a58481bb to your computer and use it in GitHub Desktop.
Minimalist WETH
// 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