Skip to content

Commit

Permalink
feat(wip): introduce wrapped ip and predeploy (#282)
Browse files Browse the repository at this point in the history
Introduce Wrapped IP and predeploy WIP in genesis

issue: none
  • Loading branch information
kingster-will authored Oct 23, 2024
1 parent 1dddd27 commit ae61c40
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 2 deletions.
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

0 comments on commit ae61c40

Please sign in to comment.