Compare commits

..

No commits in common. "c04744d0c8646dc09c23ae6b660ddcb15060a2c6" and "aaf1c0fd4649f939a433aff8ef334d6d142baea5" have entirely different histories.

7 changed files with 63 additions and 62 deletions

6
.gitmodules vendored
View File

@ -4,3 +4,9 @@
[submodule "lib/openzeppelin-contracts"] [submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts path = lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts url = https://github.com/OpenZeppelin/openzeppelin-contracts
[submodule "lib/v3-periphery"]
path = lib/v3-periphery
url = https://github.com/uniswap/v3-periphery
[submodule "lib/v3-core"]
path = lib/v3-core
url = https://github.com/uniswap/v3-core

1
lib/v3-core Submodule

@ -0,0 +1 @@
Subproject commit e3589b192d0be27e100cd0daaf6c97204fdb1899

1
lib/v3-periphery Submodule

@ -0,0 +1 @@
Subproject commit 80f26c86c57b8a5e4b913f42844d4c8bd274d058

View File

@ -1 +1,3 @@
@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ @openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/
@uniswap/v3-periphery/contracts/=lib/v3-periphery/contracts
@uniswap/v3-core/contracts/=lib/v3-core/contracts

View File

@ -6,5 +6,5 @@ contract Constants {
address public constant DAO_TOKEN = 0x11dC980faf34A1D082Ae8A6a883db3A950a3c6E8; address public constant DAO_TOKEN = 0x11dC980faf34A1D082Ae8A6a883db3A950a3c6E8;
address public constant POOL = 0x27004f6d0c1bB7979367D32Ba9d6DF6d61A18926; address public constant POOL = 0x27004f6d0c1bB7979367D32Ba9d6DF6d61A18926;
address public constant WETH = 0x4200000000000000000000000000000000000006; address public constant WETH = 0x4200000000000000000000000000000000000006;
address public constant SWAP_ROUTER = 0x2626664c2603336E57B271c5C0b26F421741e481; address public constant SWAP_ROUTER = 0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD;
} }

View File

@ -3,12 +3,14 @@ pragma solidity ^0.8.13;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/access/Ownable.sol";
import '@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol';
import '@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol';
import {RaidGeldUtils} from "../src/RaidGeldUtils.sol"; import {RaidGeldUtils} from "../src/RaidGeldUtils.sol";
import {Army, Player, Raider} from "../src/RaidGeldStructs.sol"; import {Army, Player, Raider} from "../src/RaidGeldStructs.sol";
import "../src/Constants.sol"; import "../src/Constants.sol";
contract RaidGeld is ERC20, Ownable, Constants { contract RaidGeld is ERC20, Ownable, Constants {
uint256 public constant MANTISSA = 1e4; uint256 public constant MANTISSA = 1e4;
uint256 public constant BUY_IN_AMOUNT = 0.0005 ether; uint256 public constant BUY_IN_AMOUNT = 0.0005 ether;
uint256 public immutable BUY_IN_DAO_TOKEN_AMOUNT; uint256 public immutable BUY_IN_DAO_TOKEN_AMOUNT;
@ -17,13 +19,13 @@ contract RaidGeld is ERC20, Ownable, Constants {
mapping(address => Army) private armies; mapping(address => Army) private armies;
// WETH // WETH
IWETH public immutable weth = IWETH(WETH); IERC20 public immutable weth = IERC20(WETH);
// RGCVII token // RGCVII token
ERC20 public daoToken; ERC20 public daoToken;
// RGCVII pool // RGCVII pool
address public pool; address public pool;
// Uniswap // Uniswap
ISwapRouter02 private constant router = ISwapRouter02(SWAP_ROUTER); ISwapRouter private constant router = ISwapRouter(SWAP_ROUTER);
// Modifier for functions that should only be available to registered players // Modifier for functions that should only be available to registered players
modifier onlyPlayer() { modifier onlyPlayer() {
@ -31,7 +33,6 @@ contract RaidGeld is ERC20, Ownable, Constants {
_; _;
} }
// Modifier for functions that should only be available to non initialized players // Modifier for functions that should only be available to non initialized players
modifier newPlayer() { modifier newPlayer() {
require(players[msg.sender].created_at == 0, "Whoops, player already exists :)"); require(players[msg.sender].created_at == 0, "Whoops, player already exists :)");
_; _;
@ -60,14 +61,14 @@ contract RaidGeld is ERC20, Ownable, Constants {
// New player want to register with ETH // New player want to register with ETH
function register_eth() external payable newPlayer { function register_eth() external payable newPlayer {
require(msg.value == BUY_IN_AMOUNT, "Incorrect buy in amount"); require(weth.transferFrom(msg.sender, address(this), BUY_IN_AMOUNT), "Make sure to send exactly 0.0005 eth");
weth.deposit{value: BUY_IN_AMOUNT}(); TransferHelper.safeApprove(WETH, address(router), BUY_IN_AMOUNT);
weth.approve(address(router), BUY_IN_AMOUNT); ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
ISwapRouter02.ExactInputSingleParams memory params = ISwapRouter02.ExactInputSingleParams({
tokenIn: WETH, tokenIn: WETH,
tokenOut: DAO_TOKEN, tokenOut: DAO_TOKEN,
fee: 10000, fee: 10000,
recipient: address(this), recipient: address(this),
deadline: block.timestamp,
amountIn: BUY_IN_AMOUNT, amountIn: BUY_IN_AMOUNT,
amountOutMinimum: 0, amountOutMinimum: 0,
sqrtPriceLimitX96: 0 sqrtPriceLimitX96: 0
@ -81,7 +82,8 @@ contract RaidGeld is ERC20, Ownable, Constants {
//@notice this is not safe for arbitrary tokens, which may not follow the interface eg. USDT //@notice this is not safe for arbitrary tokens, which may not follow the interface eg. USDT
//@notice but should be fine for the DAO token //@notice but should be fine for the DAO token
require( require(
daoToken.transferFrom(msg.sender, address(this), BUY_IN_DAO_TOKEN_AMOUNT), "Failed to transfer DAO tokens" daoToken.transferFrom(msg.sender, address(this), BUY_IN_DAO_TOKEN_AMOUNT),
"Failed to transfer DAO tokens"
); );
// Init player // Init player
init_player(msg.sender); init_player(msg.sender);
@ -92,8 +94,13 @@ contract RaidGeld is ERC20, Ownable, Constants {
return 4; return 4;
} }
// Allows the owner to withdraw DAO tokens // Allows the owner to withdraw
function withdraw() external onlyOwner { function withdraw() external onlyOwner {
payable(owner()).transfer(address(this).balance);
}
// Allows the owner to withdraw DAO tokens
function withdraw_dao() external onlyOwner {
uint256 amount = daoToken.balanceOf(address(this)); uint256 amount = daoToken.balanceOf(address(this));
daoToken.approve(address(this), amount); daoToken.approve(address(this), amount);
daoToken.transferFrom(address(this), owner(), amount); daoToken.transferFrom(address(this), owner(), amount);
@ -193,40 +200,3 @@ contract RaidGeld is ERC20, Ownable, Constants {
revert("No fallback calls accepted"); revert("No fallback calls accepted");
} }
} }
interface ISwapRouter02 {
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
function exactInputSingle(ExactInputSingleParams calldata params)
external
payable
returns (uint256 amountOut);
struct ExactOutputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 amountOut;
uint256 amountInMaximum;
uint160 sqrtPriceLimitX96;
}
function exactOutputSingle(ExactOutputSingleParams calldata params)
external
payable
returns (uint256 amountIn);
}
interface IWETH is IERC20 {
function deposit() external payable;
function withdraw(uint256 amount) external;
}

View File

@ -6,8 +6,10 @@ import {stdStorage, StdStorage} from "forge-std/Test.sol";
import {RaidGeld, Army, Player} from "../src/RaidGeld.sol"; import {RaidGeld, Army, Player} from "../src/RaidGeld.sol";
import "../src/RaidGeldUtils.sol"; import "../src/RaidGeldUtils.sol";
import {Constants} from "../src/Constants.sol"; import {Constants} from "../src/Constants.sol";
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
contract raid_geldTest is Test, Constants { contract raid_geldTest is Test, Constants {
using stdStorage for StdStorage; using stdStorage for StdStorage;
RaidGeld public raid_geld; RaidGeld public raid_geld;
@ -22,16 +24,23 @@ contract raid_geldTest is Test, Constants {
fundAccount(player1); fundAccount(player1);
vm.prank(owner); vm.prank(owner);
raid_geld = new RaidGeld(DAO_TOKEN, POOL); raid_geld = new RaidGeld(DAO_TOKEN, POOL);
raid_geld.weth().deposit{value: 5 ether}();
} }
function fundAccount(address _acc) private { function fundAccount(address _acc) private {
vm.deal(_acc, 10 ether); vm.deal(_acc, 10 ether);
stdstore.target(DAO_TOKEN).sig("balanceOf(address)").with_key(_acc).checked_write(100 ether); stdstore.target(DAO_TOKEN).sig("balanceOf(address)").with_key(_acc).checked_write(100 ether);
stdstore.target(WETH).sig("balanceOf(address)").with_key(_acc).checked_write(100 ether);
}
function getPoolLiquidity() public view {
IUniswapV3Pool pool = IUniswapV3Pool(POOL);
console.log(pool.liquidity());
} }
function registerPlayer() private { function registerPlayer() private {
raid_geld.register_eth{value: raid_geld.BUY_IN_AMOUNT()}(); getPoolLiquidity();
raid_geld.weth().approve(address(raid_geld), raid_geld.BUY_IN_AMOUNT());
raid_geld.register_eth();
} }
function registerPlayerWithDaoToken() private { function registerPlayerWithDaoToken() private {
@ -54,8 +63,7 @@ contract raid_geldTest is Test, Constants {
function test_02_1_registrationWithEth() public { function test_02_1_registrationWithEth() public {
vm.startPrank(player1); vm.startPrank(player1);
uint256 contractBalance = raid_geld.daoToken().balanceOf(address(raid_geld)); uint256 initialBalance = address(raid_geld).balance;
uint256 userBalance = address(player1).balance;
// Send registration fee ETH to the contract // Send registration fee ETH to the contract
registerPlayer(); registerPlayer();
@ -64,13 +72,7 @@ contract raid_geldTest is Test, Constants {
assertEq(raid_geld.balanceOf(player1), raid_geld.INITIAL_GELD()); assertEq(raid_geld.balanceOf(player1), raid_geld.INITIAL_GELD());
// Verify the contract balance is updated // Verify the contract balance is updated
uint256 contractBalance2 = raid_geld.daoToken().balanceOf(address(raid_geld)); assertEq(address(raid_geld).balance, initialBalance + raid_geld.BUY_IN_AMOUNT());
uint256 userBalance2 = address(player1).balance;
// Contract should get DAO tokens
assertLt(contractBalance, contractBalance2);
// Player should lose ETH
assertEq(userBalance2, userBalance - raid_geld.BUY_IN_AMOUNT());
// Verify player is set initially // Verify player is set initially
Player memory player = raid_geld.getPlayer(player1); Player memory player = raid_geld.getPlayer(player1);
@ -114,9 +116,28 @@ contract raid_geldTest is Test, Constants {
assertEq(army.apprentice.level, 0); assertEq(army.apprentice.level, 0);
assertEq(army.anointed.level, 0); assertEq(army.anointed.level, 0);
assertEq(army.champion.level, 0); assertEq(army.champion.level, 0);
} }
function test_dao_token_can_be_withdrawn() public { function test_03_01_ETH_funds_can_be_withdrawn() public {
uint256 initialBalance = owner.balance;
// Switch to Player 1 and register it
vm.startPrank(player1);
registerPlayer();
// Switch back to owner and withdraw funds
vm.startPrank(owner);
raid_geld.withdraw();
uint256 newBalance = owner.balance;
uint256 newContractBalance = address(raid_geld).balance;
// contract balance should be empty
assertEq(newContractBalance, 0);
// owner should have the extra funds
assertEq(newBalance, initialBalance + raid_geld.BUY_IN_AMOUNT());
}
function test_03_02_RGCVII_funds_can_be_withdrawn() public {
uint256 initialBalance = raid_geld.daoToken().balanceOf(address(raid_geld)); uint256 initialBalance = raid_geld.daoToken().balanceOf(address(raid_geld));
// Switch to Player 1 and register it // Switch to Player 1 and register it
@ -125,7 +146,7 @@ contract raid_geldTest is Test, Constants {
// Switch back to owner and withdraw funds // Switch back to owner and withdraw funds
vm.startPrank(owner); vm.startPrank(owner);
raid_geld.withdraw(); raid_geld.withdraw_dao();
uint256 newBalance = raid_geld.daoToken().balanceOf(address(owner)); uint256 newBalance = raid_geld.daoToken().balanceOf(address(owner));
uint256 newContractBalance = raid_geld.daoToken().balanceOf(address(raid_geld)); uint256 newContractBalance = raid_geld.daoToken().balanceOf(address(raid_geld));