1616
1717pragma solidity 0.8.26 ;
1818
19- interface IWIP {
20- function deposit () external payable ;
21- function withdraw (uint wad ) external ;
19+ import { ERC20 } from "solady/tokens/ERC20.sol " ;
20+ /// @notice Wrapped IP implementation.
21+ /// @author Inspired by WETH9 (https://github.com/dapphub/ds-weth/blob/master/src/weth9.sol)
22+ contract WIP is ERC20 {
23+ event Deposit (address indexed from , uint amount );
24+ event Withdrawal (address indexed to , uint amount );
2225
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- }
26+ error IPTransferFailed ();
8027
8128 receive () external payable {
8229 deposit ();
8330 }
8431
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-
11432 function deposit () public payable {
115- balanceOf[ msg .sender ] += msg .value ;
33+ _mint ( msg .sender , msg .value ) ;
11634 emit Deposit (msg .sender , msg .value );
11735 }
11836
119- function withdraw (uint value ) external override {
120- balanceOf[ msg .sender ] -= value;
37+ function withdraw (uint value ) external {
38+ _burn ( msg .sender , value) ;
12139 (bool success , ) = msg .sender .call {value: value}("" );
12240 if (! success) {
123- revert WIP_IPTransferFailed ();
41+ revert IPTransferFailed ();
12442 }
12543 emit Withdrawal (msg .sender , value);
12644 }
12745
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- _;
46+ function name () public view virtual override returns (string memory ) {
47+ return "Wrapped IP " ;
14748 }
14849
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 ;
50+ function symbol () public view virtual override returns (string memory ) {
51+ return "WIP " ;
15552 }
15653
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);
54+ function _givePermit2InfiniteAllowance () internal view override returns (bool ) {
16955 return true ;
17056 }
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- }
25157}
0 commit comments