julien/lovepublic Fork 0
3cc0f3b8ce3eedfacdb7d55793fef3cdf53e13f7
Commits
Clone
git clone https://git.rickub.com/julien/love.git
git clone ssh://git@rickub.com/julien/love.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

add natspec 3cc0f3b · on 3cc0f3b8ce3eedfacdb7d55793fef3cdf53e13f7 · julienbrg · 8h ago
Love.sol · 81 lines · 3.8 KBSolidity Blame HistoryRaw
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/// @title Love
/// @author Julien Béranger
/// @notice An ERC-20 pegged to wETH at a fixed rate of 100000 LOVE per wETH.
///         Every LOVE in circulation is backed by wETH held by this contract:
///         supply only moves through `deposit` and `withdraw`, and both are
///         permissionless.
/// @dev There is no mint entrypoint, no owner and no upgrade path, so the peg
///      cannot be diluted. `totalSupply() == WETH.balanceOf(address(this)) * RATE`
///      holds after every call; wETH transferred straight to this contract
///      raises the backing and is not redeemable.
contract Love is ERC20 {
    using SafeERC20 for IERC20;

    /// @notice LOVE minted per unit of wETH, and burned per unit released.
    uint256 public constant RATE = 100_000;

    /// @notice The wETH this token is pegged to and collateralised with.
    /// @dev Immutable, and part of the creation code — two chains only give this
    ///      contract the same CREATE2 address if they share a wETH address.
    IERC20 public immutable WETH;

    /// @notice Thrown when a withdrawal amount is not a multiple of `RATE`.
    /// @param loveAmount The rejected LOVE amount.
    /// @param rate The rate it has to be a multiple of.
    error AmountNotDivisibleByRate(uint256 loveAmount, uint256 rate);

    /// @notice Emitted when wETH is locked and LOVE minted.
    /// @param account The depositor, who pays the wETH and receives the LOVE.
    /// @param wethAmount The wETH pulled in.
    /// @param loveAmount The LOVE minted, `wethAmount * RATE`.
    event Deposit(address indexed account, uint256 wethAmount, uint256 loveAmount);

    /// @notice Emitted when LOVE is burned and wETH released.
    /// @param account The redeemer, who burns the LOVE and receives the wETH.
    /// @param loveAmount The LOVE burned.
    /// @param wethAmount The wETH released, `loveAmount / RATE`.
    event Withdraw(address indexed account, uint256 loveAmount, uint256 wethAmount);

    /// @param weth_ The wETH to peg to. Set once, never changed.
    constructor(IERC20 weth_) ERC20("Love", "LOVE") {
        WETH = weth_;
    }

    /// @notice Lock `wethAmount` wETH and mint `wethAmount * RATE` LOVE to the caller.
    /// @dev The caller must have approved this contract for `wethAmount` first.
    ///      Reverts on overflow of `wethAmount * RATE`, and on the wETH transfer
    ///      failing for want of balance or allowance.
    /// @param wethAmount The wETH to lock, in wei.
    function deposit(uint256 wethAmount) external {
        uint256 loveAmount = wethAmount * RATE;

        WETH.safeTransferFrom(msg.sender, address(this), wethAmount);
        _mint(msg.sender, loveAmount);

        emit Deposit(msg.sender, wethAmount, loveAmount);
    }

    /// @notice Burn `loveAmount` LOVE and release `loveAmount / RATE` wETH to the caller.
    /// @dev Reverts with `AmountNotDivisibleByRate` unless `loveAmount` is a
    ///      multiple of `RATE`, so the peg never rounds against the caller or
    ///      the remaining holders. LOVE is burned before the wETH leaves, and
    ///      the burn already caps the amount at the caller's balance.
    /// @param loveAmount The LOVE to burn, a multiple of `RATE`.
    function withdraw(uint256 loveAmount) external {
        if (loveAmount % RATE != 0) revert AmountNotDivisibleByRate(loveAmount, RATE);

        uint256 wethAmount = loveAmount / RATE;

        _burn(msg.sender, loveAmount);
        WETH.safeTransfer(msg.sender, wethAmount);

        emit Withdraw(msg.sender, loveAmount, wethAmount);
    }
}