-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
8270aa1
commit 22f0acd
Showing
10 changed files
with
205 additions
and
57 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"discord.enabled": false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ | ||
ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/ | ||
erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/ | ||
forge-std/=lib/forge-std/src/ | ||
halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/ | ||
openzeppelin-contracts/=lib/openzeppelin-contracts/ |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// // SPDX-License-Identifier: UNLICENSED | ||
// pragma solidity ^0.8.13; | ||
|
||
// import {Script, console} from "forge-std/Script.sol"; | ||
// import {WanderStaking} from "../src/WanderStaking.sol"; | ||
|
||
// contract CounterScript is Script { | ||
// WanderStaking public counter; | ||
|
||
// function setUp() public {} | ||
|
||
// function run() public { | ||
// vm.startBroadcast(); | ||
|
||
// counter = new WanderStaking(); | ||
|
||
// vm.stopBroadcast(); | ||
// } | ||
// } |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// SPDX-License-Identifier: MIT | ||
// Compatible with OpenZeppelin Contracts ^5.0.0 | ||
pragma solidity ^0.8.22; | ||
|
||
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; | ||
|
||
contract TestToken is ERC20 { | ||
constructor() | ||
ERC20("Test Token", "TEST") | ||
{} | ||
|
||
function mint(address to, uint256 amount) public { | ||
_mint(to, amount); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.13; | ||
|
||
import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | ||
|
||
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; | ||
import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol"; | ||
|
||
contract WanderStaking is Ownable, Pausable { | ||
using SafeERC20 for IERC20; | ||
IERC20 public immutable token; | ||
|
||
event Stake(address indexed user, uint256 amount); | ||
event Unstake(address indexed user, uint256 amount); | ||
|
||
error ZeroAmount(); | ||
error InsufficientBalance(); | ||
|
||
mapping(address => uint256) userStake; | ||
uint256 internal totalStaked; | ||
|
||
constructor(address initialOwner, IERC20 _token) | ||
Ownable(initialOwner) | ||
{ | ||
token = _token; | ||
} | ||
|
||
function pause() public onlyOwner { | ||
_pause(); | ||
} | ||
|
||
function unpause() public onlyOwner() { | ||
_unpause(); | ||
} | ||
|
||
function stake(uint256 amount) external whenNotPaused { | ||
if (amount == 0) { | ||
revert ZeroAmount(); | ||
} | ||
|
||
userStake[msg.sender] += amount; | ||
totalStaked += amount; | ||
|
||
emit Stake(msg.sender, amount); | ||
|
||
token.safeTransferFrom(msg.sender, address(this), amount); | ||
} | ||
|
||
function unstake(uint256 amount) external whenNotPaused { | ||
if (amount == 0) { | ||
revert ZeroAmount(); | ||
} | ||
|
||
if (userStake[msg.sender] < amount) { | ||
revert InsufficientBalance(); | ||
} | ||
|
||
userStake[msg.sender] -= amount; | ||
totalStaked -= amount; | ||
|
||
emit Unstake(msg.sender, amount); | ||
|
||
token.safeTransfer(msg.sender, amount); | ||
} | ||
|
||
function getTotalStaked() external view returns (uint256) { | ||
return totalStaked; | ||
} | ||
|
||
function getAmountStaked(address user) external view returns (uint256) { | ||
return userStake[user]; | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.13; | ||
|
||
import {Test, console} from "forge-std/Test.sol"; | ||
import {WanderStaking} from "../src/WanderStaking.sol"; | ||
import {TestToken} from "../src/TestToken.sol"; | ||
|
||
import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | ||
|
||
contract WanderStakingTest is Test { | ||
WanderStaking public staking; | ||
TestToken public token; | ||
|
||
function setUp() public { | ||
token = new TestToken(); | ||
|
||
staking = new WanderStaking(address(this), IERC20(token)); | ||
token.approve(address(staking), ~uint256(0)); | ||
} | ||
|
||
function test_stake(uint64 _amount) public { | ||
vm.assume(_amount > 0); | ||
|
||
uint256 amount = uint256(_amount) * (10 ** 18); | ||
token.mint(address(this), amount); | ||
|
||
staking.stake(amount); | ||
|
||
assert(staking.getTotalStaked() == amount); | ||
} | ||
|
||
function test_stake_insufficient_balance(uint64 _amount) public { | ||
vm.assume(_amount > 0); | ||
uint256 amount = uint256(_amount) * (10 ** 18); | ||
|
||
token.mint(address(this), amount); | ||
|
||
vm.expectRevert(); | ||
staking.stake(amount * 2); | ||
} | ||
|
||
function test_unstake(uint64 _amount, uint64 _unstakeAmount) public { | ||
vm.assume(_amount > 0); | ||
vm.assume(_unstakeAmount > 0); | ||
vm.assume(_amount >= _unstakeAmount); | ||
uint256 amount = uint256(_amount) * (10 ** 18); | ||
uint256 unstakeAmount = uint256(_unstakeAmount) * (10 ** 18); | ||
|
||
token.mint(address(this), amount); | ||
|
||
staking.stake(amount); | ||
staking.unstake(unstakeAmount); | ||
|
||
assert(staking.getTotalStaked() == (amount - unstakeAmount)); | ||
} | ||
|
||
function test_unstake_over(uint64 _amount, uint64 _unstakeAmount) public { | ||
vm.assume(_amount > 0); | ||
vm.assume(_unstakeAmount > 0); | ||
vm.assume(_amount < _unstakeAmount); | ||
uint256 amount = uint256(_amount) * (10 ** 18); | ||
uint256 unstakeAmount = uint256(_unstakeAmount) * (10 ** 18); | ||
|
||
// Pad staking contract with some tokens first | ||
vm.startPrank(msg.sender); | ||
token.mint(msg.sender, amount * 2); | ||
token.approve(address(staking), ~uint256(0)); | ||
staking.stake(amount * 2); | ||
vm.stopPrank(); | ||
|
||
token.mint(address(this), amount); | ||
staking.stake(amount); | ||
|
||
vm.expectRevert(WanderStaking.InsufficientBalance.selector); | ||
staking.unstake(unstakeAmount); | ||
} | ||
|
||
function test_unstake_new(uint64 _unstakeAmount) public { | ||
vm.assume(_unstakeAmount > 0); | ||
uint256 unstakeAmount = uint256(_unstakeAmount) * (10 ** 18); | ||
|
||
vm.expectRevert(); | ||
staking.unstake(unstakeAmount); | ||
} | ||
} |