Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(wip): introduce wrapped ip and predeploy #282

Merged
merged 4 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion contracts/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ remappings = [
"test/=test",
"@openzeppelin/=node_modules/@openzeppelin/",
"@openzeppelin-upgrades/contracts/=node_modules/@openzeppelin/contracts-upgradeable",
"erc6551/=node_modules/erc6551/"
"erc6551/=node_modules/erc6551/",
"solady/=node_modules/solady/",
]

fs_permissions = [
Expand Down
1 change: 1 addition & 0 deletions contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"@openzeppelin/contracts": "5.0.2",
"@openzeppelin/contracts-upgradeable": "5.0.2",
"erc6551": "^0.3.1",
"solady": "^0.0.259",
"solmate": "^6.2.0"
}
}
8 changes: 8 additions & 0 deletions contracts/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions contracts/script/GenerateAlloc.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { InitializableHelper } from "./utils/InitializableHelper.sol";
import { Predeploys } from "../src/libraries/Predeploys.sol";
import { Create3 } from "../src/deploy/Create3.sol";
import { ERC6551Registry } from "erc6551/ERC6551Registry.sol";
import { WIP } from "../src/token/WIP.sol";
/**
* @title GenerateAlloc
* @dev A script to generate the alloc section of EL genesis
Expand Down Expand Up @@ -185,6 +186,7 @@ contract GenerateAlloc is Script {
setCreate3();
deployTimelock();
setERC6551();
setWIP();

// Set proxies for all predeploys
setProxies();
Expand Down Expand Up @@ -368,6 +370,19 @@ contract GenerateAlloc is Script {
console2.log("ERC6551 deployed at:", Predeploys.ERC6551Registry);
}

function setWIP() internal {
address tmp = address(new WIP());
vm.etch(Predeploys.WIP, tmp.code);

// reset tmp
vm.etch(tmp, "");
vm.store(tmp, 0, "0x");
vm.resetNonce(tmp);

vm.deal(Predeploys.WIP, 1);
console2.log("WIP deployed at:", Predeploys.WIP);
}

function setAllocations() internal {
// EL Predeploys
// Geth precompile 1 wei allocation (Accounts with 0 balance and no EVM code may be removed from
Expand Down
2 changes: 1 addition & 1 deletion contracts/src/libraries/Predeploys.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ library Predeploys {
uint256 internal constant NamespaceSize = 1024;

/// @notice Predeploys
address internal constant WIP = 0x1513000000000000000000000000000000000000;
address internal constant WIP = 0x1516000000000000000000000000000000000000;
address internal constant Staking = 0xCCcCcC0000000000000000000000000000000001;
address internal constant UBIPool = 0xCccCCC0000000000000000000000000000000002;
address internal constant Upgrades = 0xccCCcc0000000000000000000000000000000003;
Expand Down
53 changes: 53 additions & 0 deletions contracts/src/token/WIP.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.23;

import { ERC20 } from "solady/src/tokens/ERC20.sol";
/// @notice Wrapped IP implementation.
/// @author Inspired by WETH9 (https://github.com/dapphub/ds-weth/blob/master/src/weth9.sol)
contract WIP is ERC20 {
/// @notice emitted when IP is deposited in exchange for WIP
event Deposit(address indexed from, uint amount);
/// @notice emitted when WIP is withdrawn in exchange for IP
event Withdrawal(address indexed to, uint amount);
/// @notice emitted when a transfer of IP fails
error IPTransferFailed();

/// @notice triggered when IP is deposited in exchange for WIP
receive() external payable {
deposit();
}

/// @notice deposits IP in exchange for WIP
/// @dev the amount of IP deposited is equal to the amount of WIP minted
function deposit() public payable {
_mint(msg.sender, msg.value);
emit Deposit(msg.sender, msg.value);
}

/// @notice withdraws WIP in exchange for IP
/// @dev the amount of IP minted is equal to the amount of WIP burned
/// @param value the amount of WIP to burn and withdraw
function withdraw(uint value) external {
_burn(msg.sender, value);
(bool success, ) = msg.sender.call{ value: value }("");
if (!success) {
revert IPTransferFailed();
}
emit Withdrawal(msg.sender, value);
}

/// @notice returns the name of the token
function name() public view override returns (string memory) {
return "Wrapped IP";
}

/// @notice returns the symbol of the token
function symbol() public view override returns (string memory) {
return "WIP";
}

/// @dev Sets Permit2 contract's allowance to infinity.
function _givePermit2InfiniteAllowance() internal pure override returns (bool) {
return true;
}
}
123 changes: 123 additions & 0 deletions contracts/test/token/WIP.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import { Test } from "../utils/Test.sol";
import { WIP } from "../../src/token/WIP.sol";

contract ContractWithoutReceive {}

contract WIPTest is Test {
function testMetadata() public view {
assertEq(wip.name(), "Wrapped IP");
assertEq(wip.symbol(), "WIP");
assertEq(wip.decimals(), 18);
}

function testFallbackDeposit() public {
assertEq(wip.balanceOf(address(this)), 0);
assertEq(wip.totalSupply(), 0);

(bool success, ) = address(wip).call{ value: 1 ether }("");
assertTrue(success);

assertEq(wip.balanceOf(address(this)), 1 ether);
assertEq(wip.totalSupply(), 1 ether);
}

function testDeposit() public {
assertEq(wip.balanceOf(address(this)), 0);
assertEq(wip.totalSupply(), 0);

wip.deposit{ value: 1 ether }();

assertEq(wip.balanceOf(address(this)), 1 ether);
assertEq(wip.totalSupply(), 1 ether);
}

function testWithdraw() public {
uint256 startingBalance = address(this).balance;

wip.deposit{ value: 1 ether }();

wip.withdraw(1 ether);

uint256 balanceAfterWithdraw = address(this).balance;

assertEq(balanceAfterWithdraw, startingBalance);
assertEq(wip.balanceOf(address(this)), 0);
assertEq(wip.totalSupply(), 0);
}

function testPartialWithdraw() public {
wip.deposit{ value: 1 ether }();

uint256 balanceBeforeWithdraw = address(this).balance;

wip.withdraw(0.5 ether);

uint256 balanceAfterWithdraw = address(this).balance;

assertEq(balanceAfterWithdraw, balanceBeforeWithdraw + 0.5 ether);
assertEq(wip.balanceOf(address(this)), 0.5 ether);
assertEq(wip.totalSupply(), 0.5 ether);
}

function testWithdrawToContractWithoutReceiveReverts() public {
address owner = address(new ContractWithoutReceive());

vm.deal(owner, 1 ether);

vm.prank(owner);
wip.deposit{ value: 1 ether }();

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

vm.expectRevert(WIP.IPTransferFailed.selector);
vm.prank(owner);
wip.withdraw(1 ether);
}

function testFallbackDeposit(uint256 amount) public {
amount = _bound(amount, 0, address(this).balance);

assertEq(wip.balanceOf(address(this)), 0);
assertEq(wip.totalSupply(), 0);

(bool success, ) = address(wip).call{ value: amount }("");
assertTrue(success);

assertEq(wip.balanceOf(address(this)), amount);
assertEq(wip.totalSupply(), amount);
}

function testDeposit(uint256 amount) public {
amount = _bound(amount, 0, address(this).balance);

assertEq(wip.balanceOf(address(this)), 0);
assertEq(wip.totalSupply(), 0);

wip.deposit{ value: amount }();

assertEq(wip.balanceOf(address(this)), amount);
assertEq(wip.totalSupply(), amount);
}

function testWithdraw(uint256 depositAmount, uint256 withdrawAmount) public {
depositAmount = _bound(depositAmount, 0, address(this).balance);
withdrawAmount = _bound(withdrawAmount, 0, depositAmount);

wip.deposit{ value: depositAmount }();

uint256 balanceBeforeWithdraw = address(this).balance;

wip.withdraw(withdrawAmount);

uint256 balanceAfterWithdraw = address(this).balance;

assertEq(balanceAfterWithdraw, balanceBeforeWithdraw + withdrawAmount);
assertEq(wip.balanceOf(address(this)), depositAmount - withdrawAmount);
assertEq(wip.totalSupply(), depositAmount - withdrawAmount);
}

receive() external payable {}
}
3 changes: 3 additions & 0 deletions contracts/test/utils/Test.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Create3 } from "../../src/deploy/Create3.sol";
import { GenerateAlloc } from "../../script/GenerateAlloc.s.sol";
import { TimelockController } from "@openzeppelin/contracts/governance/TimelockController.sol";
import { ERC6551Registry } from "erc6551/ERC6551Registry.sol";
import { WIP } from "../../src/token/WIP.sol";

contract Test is ForgeTest {
address internal admin = address(0x123);
Expand All @@ -27,6 +28,7 @@ contract Test is ForgeTest {
Create3 internal create3;
ERC6551Registry internal erc6551Registry;
TimelockController internal timelock;
WIP internal wip;

function setUp() public virtual {
GenerateAlloc initializer = new GenerateAlloc();
Expand All @@ -37,6 +39,7 @@ contract Test is ForgeTest {
upgradeEntrypoint = UpgradeEntrypoint(Predeploys.Upgrades);
ubiPool = UBIPool(Predeploys.UBIPool);
create3 = Create3(Predeploys.Create3);
wip = WIP(payable(Predeploys.WIP));
erc6551Registry = ERC6551Registry(Predeploys.ERC6551Registry);
address timelockAddress = create3.getDeployed(deployer, keccak256("STORY_TIMELOCK_CONTROLLER"));
timelock = TimelockController(payable(timelockAddress));
Expand Down
Loading