1+ // SPDX-License-Identifier: AGPL-3.0-or-later
2+ // Copyright (C) 2015, 2016, 2017 Dapphub
3+
4+ // This program is free software: you can redistribute it and/or modify
5+ // it under the terms of the GNU General Public License as published by
6+ // the Free Software Foundation, either version 3 of the License, or
7+ // (at your option) any later version.
8+
9+ // This program is distributed in the hope that it will be useful,
10+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
11+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+ // GNU General Public License for more details.
13+
14+ // You should have received a copy of the GNU General Public License
15+ // along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+ pragma solidity 0.8.26 ;
18+
19+ interface IWIP {
20+ function deposit () external payable ;
21+ function withdraw (uint wad ) external ;
22+
23+ event Deposit (address indexed dst , uint wad );
24+ event Withdrawal (address indexed src , uint wad );
25+
26+ error WIP_IPTransferFailed ();
27+ error WIP_InvalidSignature ();
28+ error WIP_ExpiredSignature ();
29+ error WIP_InvalidTransferRecipient ();
30+
31+ // ERC20
32+ function name () external view returns (string memory );
33+ function symbol () external view returns (string memory );
34+ function decimals () external view returns (uint8 );
35+
36+ function totalSupply () external view returns (uint );
37+ function balanceOf (address guy ) external view returns (uint );
38+ function allowance (address src , address dst ) external view returns (uint );
39+
40+ function approve (address spender , uint wad ) external returns (bool );
41+ function transfer (address dst , uint wad ) external returns (bool );
42+ function transferFrom (address src , address dst , uint wad ) external returns (bool );
43+
44+ event Approval (address indexed src , address indexed dst , uint wad );
45+ event Transfer (address indexed src , address indexed dst , uint wad );
46+
47+ // ERC-165
48+ function supportsInterface (bytes4 interfaceID ) external view returns (bool );
49+
50+ // ERC-2612
51+ function permit (address owner , address spender , uint value , uint deadline , uint8 v , bytes32 r , bytes32 s ) external ;
52+ function nonces (address owner ) external view returns (uint );
53+ function DOMAIN_SEPARATOR () external view returns (bytes32 );
54+
55+ // Permit2
56+ function permit2 (address owner , address spender , uint amount , uint deadline , bytes calldata signature ) external ;
57+ }
58+
59+ contract WIP is IWIP {
60+ string public constant override name = "Wrapped IP " ;
61+ string public constant override symbol = "WIP " ;
62+ uint8 public override decimals = 18 ;
63+
64+ mapping (address => uint ) public override balanceOf;
65+ mapping (address => mapping (address => uint )) public override allowance;
66+
67+ // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
68+ bytes32 private constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9 ;
69+ // bytes4(keccak256("isValidSignature(bytes32,bytes)")
70+ bytes4 private constant MAGICVALUE = 0x1626ba7e ;
71+ mapping (address => uint ) public override nonces;
72+
73+ uint private immutable INITIAL_CHAIN_ID;
74+ bytes32 private immutable INITIAL_DOMAIN_SEPARATOR;
75+
76+ constructor () {
77+ INITIAL_CHAIN_ID = block .chainid ;
78+ INITIAL_DOMAIN_SEPARATOR = _computeDomainSeparator ();
79+ }
80+
81+ receive () external payable {
82+ deposit ();
83+ }
84+
85+ function supportsInterface (bytes4 interfaceID ) external pure override returns (bool ) {
86+ return
87+ // ERC-165
88+ interfaceID == this .supportsInterface.selector ||
89+ // ERC-2612
90+ interfaceID == this .permit.selector ||
91+ // Permit2
92+ interfaceID == this .permit2.selector ;
93+ }
94+
95+ function DOMAIN_SEPARATOR () public view override returns (bytes32 ) {
96+ return block .chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : _computeDomainSeparator ();
97+ }
98+
99+ function _computeDomainSeparator () private view returns (bytes32 ) {
100+ return keccak256 (
101+ abi.encode (
102+ // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
103+ 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f ,
104+ // keccak256(bytes('Wrapped IP')),
105+ 0x4a24dd8304360c3edc71acded1e27d8467d787ccaeefb153eaaadce60e21753b ,
106+ // keccak256(bytes("1"))
107+ 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6 ,
108+ block .chainid ,
109+ address (this )
110+ )
111+ );
112+ }
113+
114+ function deposit () public payable {
115+ balanceOf[msg .sender ] += msg .value ;
116+ emit Deposit (msg .sender , msg .value );
117+ }
118+
119+ function withdraw (uint value ) external override {
120+ balanceOf[msg .sender ] -= value;
121+ (bool success , ) = msg .sender .call {value: value}("" );
122+ if (! success) {
123+ revert WIP_IPTransferFailed ();
124+ }
125+ emit Withdrawal (msg .sender , value);
126+ }
127+
128+ function totalSupply () external view override returns (uint ) {
129+ return address (this ).balance;
130+ }
131+
132+ function approve (address spender , uint value ) external override returns (bool ) {
133+ allowance[msg .sender ][spender] = value;
134+ emit Approval (msg .sender , spender, value);
135+ return true ;
136+ }
137+
138+ modifier ensuresRecipient (address to ) {
139+ // Prevents from burning or sending WIP tokens to the contract.
140+ if (to == address (0 )) {
141+ revert WIP_InvalidTransferRecipient ();
142+ }
143+ if (to == address (this )) {
144+ revert WIP_InvalidTransferRecipient ();
145+ }
146+ _;
147+ }
148+
149+ function transfer (address to , uint value ) external ensuresRecipient (to) override returns (bool ) {
150+ balanceOf[msg .sender ] -= value;
151+ balanceOf[to] += value;
152+
153+ emit Transfer (msg .sender , to, value);
154+ return true ;
155+ }
156+
157+ function transferFrom (address from , address to , uint value ) external ensuresRecipient (to) override returns (bool ) {
158+ if (from != msg .sender ) {
159+ uint _allowance = allowance[from][msg .sender ];
160+ if (_allowance != type (uint ).max) {
161+ allowance[from][msg .sender ] -= value;
162+ }
163+ }
164+
165+ balanceOf[from] -= value;
166+ balanceOf[to] += value;
167+
168+ emit Transfer (from, to, value);
169+ return true ;
170+ }
171+
172+ function permit (address owner , address spender , uint value , uint deadline , uint8 v , bytes32 r , bytes32 s ) external override {
173+ if (block .timestamp > deadline) {
174+ revert WIP_ExpiredSignature ();
175+ }
176+ bytes32 digest = keccak256 (
177+ abi.encodePacked (
178+ '\x19\x01 ' ,
179+ DOMAIN_SEPARATOR (),
180+ keccak256 (abi.encode (PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++ , deadline))
181+ )
182+ );
183+ address recoveredAddress = ecrecover (digest, v, r, s);
184+ if (recoveredAddress != owner) {
185+ revert WIP_InvalidSignature ();
186+ }
187+ if (recoveredAddress == address (0 )) {
188+ revert WIP_InvalidSignature ();
189+ }
190+ allowance[owner][spender] = value;
191+ emit Approval (owner, spender, value);
192+ }
193+
194+ function permit2 (address owner , address spender , uint value , uint deadline , bytes calldata signature ) external override {
195+ if (block .timestamp > deadline) {
196+ revert WIP_ExpiredSignature ();
197+ }
198+ bytes32 digest = keccak256 (
199+ abi.encodePacked (
200+ '\x19\x01 ' ,
201+ DOMAIN_SEPARATOR (),
202+ keccak256 (abi.encode (PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++ , deadline))
203+ )
204+ );
205+ if (! _checkSignature (owner, digest, signature)) {
206+ revert WIP_InvalidSignature ();
207+ }
208+ allowance[owner][spender] = value;
209+ emit Approval (owner, spender, value);
210+ }
211+
212+ function _checkSignature (address signer , bytes32 hash , bytes memory signature ) private view returns (bool ) {
213+ (address recoveredAddress ) = _recover (hash, signature);
214+ if (recoveredAddress == signer) {
215+ if (recoveredAddress != address (0 )) {
216+ return true ;
217+ }
218+ }
219+
220+ (bool success , bytes memory result ) = signer.staticcall (
221+ abi.encodeWithSelector (MAGICVALUE, hash, signature)
222+ );
223+ return (
224+ success &&
225+ result.length == 32 &&
226+ abi.decode (result, (bytes32 )) == bytes32 (MAGICVALUE)
227+ );
228+ }
229+
230+ function _recover (bytes32 hash , bytes memory signature ) private pure returns (address ) {
231+ if (signature.length != 65 ) {
232+ return address (0 );
233+ }
234+
235+ bytes32 r;
236+ bytes32 s;
237+ uint8 v;
238+
239+ assembly {
240+ r := mload (add (signature, 0x20 ))
241+ s := mload (add (signature, 0x40 ))
242+ v := byte (0 , mload (add (signature, 0x60 )))
243+ }
244+
245+ if (uint (s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 ) {
246+ return address (0 );
247+ }
248+
249+ return ecrecover (hash, v, r, s);
250+ }
251+ }
0 commit comments