Skip to content

Commit 8123239

Browse files
Merge pull request #2 from piplabs/import-libs
Refactor WIP to reuse Solay's ERC20 implementation
2 parents 686c0d2 + aa13eaf commit 8123239

File tree

5 files changed

+24
-212
lines changed

5 files changed

+24
-212
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
[submodule "lib/forge-std"]
22
path = lib/forge-std
33
url = https://github.com/foundry-rs/forge-std
4+
[submodule "lib/solady"]
5+
path = lib/solady
6+
url = https://github.com/vectorized/solady

lib/solady

Submodule solady added at 7deab02

remappings.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
forge-std/=lib/forge-std/src/
2+
solady/=lib/solady/src/

src/WIP.sol

Lines changed: 16 additions & 210 deletions
Original file line numberDiff line numberDiff line change
@@ -16,236 +16,42 @@
1616

1717
pragma 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
}

test/WIP.t.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
pragma solidity ^0.8.26;
33

44
import { Test } from "forge-std/Test.sol";
5-
import { IWIP, WIP } from "../src/WIP.sol";
5+
import { WIP } from "../src/WIP.sol";
66

77
contract ContractWithoutReceive {}
88

@@ -78,7 +78,7 @@ contract WIPTest is Test {
7878

7979
assertEq(wip.balanceOf(owner), 1 ether);
8080

81-
vm.expectRevert(IWIP.WIP_IPTransferFailed.selector);
81+
vm.expectRevert(WIP.IPTransferFailed.selector);
8282
vm.prank(owner);
8383
wip.withdraw(1 ether);
8484
}

0 commit comments

Comments
 (0)