| 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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147 |  
 
 
 
 
 
 
 
 
 
 
 
 
13×
13×
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1×
 
 
 
 
1×
 
 
 
 
14×
 
 
 
 
1×
 
 
 
 
 
17×
 
 
 
 
1×
 
 
 
 
 
 
1×
 
 
 
 
 
 
 
2×
 
 
 
 
 
 
 
7×
6×
2×
1×
1×
 
5×
5×
5×
5×
 
 
 
 
 
 
2×
2×
2×
 
 
 
 
 
 
 
4×
4×
 
 
 
 
 
2×
 
 
 
 
2×
1×
 
 
 
 
 
6×
6×
 
5×
 
5×
5×
5×
 
 
 
 
 
 
3×
 
2×
2×
 
2×
2×
 
 
  | // SPDX-License-Identifier: GPL-2.0-or-later
 
pragma solidity ^0.8.0;
 
import "./Interfaces.sol";
import "./Utils.sol";
 
/// @notice Protected Tokens are simple wrappers for tokens, allowing you to use tokens as collateral without permitting borrowing
contract PToken {
    address immutable euler;
    address immutable underlyingToken;
 
    constructor(address euler_, address underlying_) {
        euler = euler_;
        underlyingToken = underlying_;
    }
 
 
    mapping(address => uint) balances;
    mapping(address => mapping(address => uint)) allowances;
    uint totalBalances;
 
 
    event Approval(address indexed owner, address indexed spender, uint value);
    event Transfer(address indexed from, address indexed to, uint value);
 
 
    /// @notice PToken name, ie "Euler Protected DAI"
    function name() external view returns (string memory) {
        return string(abi.encodePacked("Euler Protected ", IERC20(underlyingToken).name()));
    }
 
    /// @notice PToken symbol, ie "pDAI"
    function symbol() external view returns (string memory) {
        return string(abi.encodePacked("p", IERC20(underlyingToken).symbol()));
    }
 
    /// @notice Number of decimals, which is same as the underlying's
    function decimals() external view returns (uint8) {
        return IERC20(underlyingToken).decimals();
    }
 
    /// @notice Address of the underlying asset
    function underlying() external view returns (address) {
        return underlyingToken;
    }
 
 
    /// @notice Balance of an account's wrapped tokens
    function balanceOf(address who) external view returns (uint) {
        return balances[who];
    }
 
    /// @notice Sum of all wrapped token balances
    function totalSupply() external view returns (uint) {
        return totalBalances;
    }
 
    /// @notice Retrieve the current allowance
    /// @param holder Address giving permission to access tokens
    /// @param spender Trusted address
    function allowance(address holder, address spender) external view returns (uint) {
        return allowances[holder][spender];
    }
 
 
    /// @notice Transfer your own pTokens to another address
    /// @param recipient Recipient address
    /// @param amount Amount of wrapped token to transfer
    function transfer(address recipient, uint amount) external returns (bool) {
        return transferFrom(msg.sender, recipient, amount);
    }
 
    /// @notice Transfer pTokens from one address to another. The euler address is automatically granted approval.
    /// @param from This address must've approved the to address
    /// @param recipient Recipient address
    /// @param amount Amount to transfer
    function transferFrom(address from, address recipient, uint amount) public returns (bool) {
        require(balances[from] >= amount, "insufficient balance");
        if (from != msg.sender && msg.sender != euler && allowances[from][msg.sender] != type(uint).max) {
            require(allowances[from][msg.sender] >= amount, "insufficient allowance");
            allowances[from][msg.sender] -= amount;
            emit Approval(from, msg.sender, allowances[from][msg.sender]);
        }
        balances[from] -= amount;
        balances[recipient] += amount;
        emit Transfer(from, recipient, amount);
        return true;
    }
 
    /// @notice Allow spender to access an amount of your pTokens. It is not necessary to approve the euler address.
    /// @param spender Trusted address
    /// @param amount Use max uint256 for "infinite" allowance
    function approve(address spender, uint amount) external returns (bool) {
        allowances[msg.sender][spender] = amount;
        emit Approval(msg.sender, spender, amount);
        return true;
    }
 
 
 
    /// @notice Convert underlying tokens to pTokens
    /// @param amount In underlying units (which are equivalent to pToken units)
    function wrap(uint amount) external {
        Utils.safeTransferFrom(underlyingToken, msg.sender, address(this), amount);
        claimSurplus(msg.sender);
    }
 
    /// @notice Convert pTokens to underlying tokens
    /// @param amount In pToken units (which are equivalent to underlying units)
    function unwrap(uint amount) external {
        doUnwrap(msg.sender, amount);
    }
 
    // Only callable by the euler contract:
    function forceUnwrap(address who, uint amount) external {
        require(msg.sender == euler, "permission denied");
        doUnwrap(who, amount);
    }
 
    /// @notice Claim any surplus tokens held by the PToken contract. This should only be used by contracts.
    /// @param who Beneficiary to be credited for the surplus token amount
    function claimSurplus(address who) public {
        uint currBalance = IERC20(underlyingToken).balanceOf(address(this));
        require(currBalance > totalBalances, "no surplus balance to claim");
 
        uint amount = currBalance - totalBalances;
 
        totalBalances += amount;
        balances[who] += amount;
        emit Transfer(address(0), who, amount);
    }
 
 
    // Internal shared:
 
    function doUnwrap(address who, uint amount) private {
        require(balances[who] >= amount, "insufficient balance");
 
        totalBalances -= amount;
        balances[who] -= amount;
 
        Utils.safeTransfer(underlyingToken, who, amount);
        emit Transfer(who, address(0), amount);
    }
}
  |