Skip to content

Commit ef06747

Browse files
committed
more testing. add reentrancy guard
1 parent bef8383 commit ef06747

File tree

5 files changed

+637
-6
lines changed

5 files changed

+637
-6
lines changed

contracts/Geyser.sol

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import "@openzeppelin/contracts/math/SafeMath.sol";
1818
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
1919
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
2020
import "@openzeppelin/contracts/access/Ownable.sol";
21+
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
2122

2223
import "./IGeyser.sol";
2324
import "./GeyserPool.sol";
@@ -26,7 +27,7 @@ import "./MathUtils.sol";
2627
/**
2728
* @title Geyser
2829
*/
29-
contract Geyser is IGeyser {
30+
contract Geyser is IGeyser, ReentrancyGuard {
3031
using SafeMath for uint256;
3132
using SafeERC20 for IERC20;
3233
using MathUtils for int128;
@@ -279,7 +280,7 @@ contract Geyser is IGeyser {
279280
/**
280281
* @inheritdoc IGeyser
281282
*/
282-
function update() external override {
283+
function update() external override nonReentrant {
283284
_update(msg.sender);
284285
}
285286

@@ -325,7 +326,7 @@ contract Geyser is IGeyser {
325326
address staker,
326327
address beneficiary,
327328
uint256 amount
328-
) private {
329+
) private nonReentrant {
329330
// validate
330331
require(amount > 0, "Geyser: stake amount is zero");
331332
require(
@@ -370,7 +371,11 @@ contract Geyser is IGeyser {
370371
* @param gysr number of GYSR tokens applied to unstaking operation
371372
* @return number of reward tokens distributed
372373
*/
373-
function _unstake(uint256 amount, uint256 gysr) private returns (uint256) {
374+
function _unstake(uint256 amount, uint256 gysr)
375+
private
376+
nonReentrant
377+
returns (uint256)
378+
{
374379
// validate
375380
require(amount > 0, "Geyser: unstake amount is zero");
376381
require(

contracts/test/TestERC20.sol

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,13 @@ contract TestLiquidityToken is ERC20 {
2424
_mint(msg.sender, _totalSupply);
2525
}
2626
}
27+
28+
contract TestIndivisibleToken is ERC20 {
29+
uint256 DECIMALS = 0;
30+
uint256 _totalSupply = 1000;
31+
32+
constructor() public ERC20("TestIndivisibleToken", "IND") {
33+
_setupDecimals(uint8(DECIMALS));
34+
_mint(msg.sender, _totalSupply);
35+
}
36+
}

contracts/test/TestReentrant.sol

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.6.12;
4+
5+
import "@openzeppelin/contracts/token/ERC777/ERC777.sol";
6+
import "@openzeppelin/contracts/token/ERC777/IERC777Sender.sol";
7+
import "@openzeppelin/contracts/introspection/ERC1820Implementer.sol";
8+
import "@openzeppelin/contracts/introspection/IERC1820Registry.sol";
9+
import "../IGeyser.sol";
10+
11+
contract TestReentrantToken is ERC777 {
12+
uint256 _totalSupply = 10 * 10**6 * 10**18;
13+
14+
constructor() public ERC777("ReentrantToken", "RE", new address[](0)) {
15+
_mint(msg.sender, _totalSupply, "", "");
16+
}
17+
}
18+
19+
contract TestReentrantProxy is IERC777Sender, ERC1820Implementer {
20+
address private _geyser;
21+
uint256 private _last;
22+
uint256 private _amount;
23+
uint256 private _mode;
24+
25+
constructor() public {
26+
_geyser = address(0);
27+
_last = 0;
28+
_amount = 0;
29+
_mode = 0;
30+
}
31+
32+
function register(
33+
bytes32 interfaceHash,
34+
address addr,
35+
address registry
36+
) external {
37+
_registerInterfaceForAddress(interfaceHash, addr);
38+
IERC1820Registry reg = IERC1820Registry(registry);
39+
reg.setInterfaceImplementer(
40+
address(this),
41+
interfaceHash,
42+
address(this)
43+
);
44+
}
45+
46+
function target(
47+
address geyser,
48+
uint256 amount,
49+
uint256 mode
50+
) external {
51+
_geyser = geyser;
52+
_amount = amount;
53+
_mode = mode;
54+
}
55+
56+
function deposit(address token, uint256 amount) external {
57+
IERC20 tkn = IERC20(token);
58+
uint256 temp = _mode;
59+
_mode = 0;
60+
tkn.transferFrom(msg.sender, address(this), amount);
61+
_mode = temp;
62+
tkn.approve(_geyser, 100000 * 10**18);
63+
}
64+
65+
function withdraw(address token, uint256 amount) external {
66+
IERC20 tkn = IERC20(token);
67+
uint256 temp = _mode;
68+
_mode = 0;
69+
tkn.transfer(msg.sender, amount);
70+
_mode = temp;
71+
}
72+
73+
function stake(uint256 amount) external {
74+
IGeyser geyser = IGeyser(_geyser);
75+
geyser.stake(amount, "");
76+
}
77+
78+
function unstake(uint256 amount) external {
79+
IGeyser geyser = IGeyser(_geyser);
80+
geyser.unstake(amount, "");
81+
}
82+
83+
function tokensToSend(
84+
address,
85+
address,
86+
address,
87+
uint256,
88+
bytes calldata,
89+
bytes calldata
90+
) external override {
91+
if (block.timestamp == _last) {
92+
return;
93+
}
94+
_last = block.timestamp;
95+
_exploit();
96+
}
97+
98+
function _exploit() private {
99+
if (_geyser == address(0)) {
100+
return;
101+
}
102+
IGeyser geyser = IGeyser(_geyser);
103+
if (_mode == 1) {
104+
geyser.stake(_amount, "");
105+
} else if (_mode == 2) {
106+
geyser.unstake(_amount, "");
107+
} else if (_mode == 3) {
108+
geyser.update();
109+
}
110+
}
111+
}

test/geyser.js

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1162,6 +1162,82 @@ describe('geyser', function () {
11621162
expect(mult).to.be.equal(3.0);
11631163
});
11641164
});
1165+
1166+
describe('when configured with 0 day time bonus period', function () {
1167+
1168+
beforeEach(async function () {
1169+
// owner creates geyser
1170+
this.geyser = await Geyser.new(
1171+
this.staking.address,
1172+
this.reward.address,
1173+
bonus(0.5),
1174+
bonus(2),
1175+
days(0),
1176+
this.gysr.address,
1177+
{ from: owner }
1178+
);
1179+
// owner funds geyser
1180+
await this.reward.transfer(owner, tokens(10000), { from: org });
1181+
await this.reward.approve(this.geyser.address, tokens(100000), { from: owner });
1182+
await this.geyser.methods['fund(uint256,uint256)'](
1183+
tokens(1000), days(180),
1184+
{ from: owner }
1185+
);
1186+
});
1187+
1188+
it('should be max time bonus for t = 0', async function () {
1189+
const mult = fromFixedPointBigNumber(await this.geyser.timeBonus(0), 10, 18);
1190+
expect(mult).to.be.equal(3.0);
1191+
});
1192+
1193+
it('should be max time bonus for t > 0 days', async function () {
1194+
const mult = fromFixedPointBigNumber(await this.geyser.timeBonus(days(30)), 10, 18);
1195+
expect(mult).to.be.equal(3.0);
1196+
});
1197+
});
1198+
1199+
describe('when configured with 0.0 max time bonus', function () {
1200+
1201+
beforeEach(async function () {
1202+
// owner creates geyser
1203+
this.geyser = await Geyser.new(
1204+
this.staking.address,
1205+
this.reward.address,
1206+
bonus(0.0),
1207+
bonus(0.0),
1208+
days(90),
1209+
this.gysr.address,
1210+
{ from: owner }
1211+
);
1212+
// owner funds geyser
1213+
await this.reward.transfer(owner, tokens(10000), { from: org });
1214+
await this.reward.approve(this.geyser.address, tokens(100000), { from: owner });
1215+
await this.geyser.methods['fund(uint256,uint256)'](
1216+
tokens(1000), days(180),
1217+
{ from: owner }
1218+
);
1219+
});
1220+
1221+
it('should be 1.0 time bonus for t = 0', async function () {
1222+
const mult = fromFixedPointBigNumber(await this.geyser.timeBonus(0), 10, 18);
1223+
expect(mult).to.be.equal(1.0);
1224+
});
1225+
1226+
it('should be 1.0 time bonus for 0 < t < period', async function () {
1227+
const mult = fromFixedPointBigNumber(await this.geyser.timeBonus(days(30)), 10, 18);
1228+
expect(mult).to.be.equal(1.0);
1229+
});
1230+
1231+
it('should be 1.0 time bonus for t = period', async function () {
1232+
const mult = fromFixedPointBigNumber(await this.geyser.timeBonus(days(90)), 10, 18);
1233+
expect(mult).to.be.equal(1.0);
1234+
});
1235+
1236+
it('should be 1.0 time bonus for t > period', async function () {
1237+
const mult = fromFixedPointBigNumber(await this.geyser.timeBonus(days(120)), 10, 18);
1238+
expect(mult).to.be.equal(1.0);
1239+
});
1240+
});
11651241
});
11661242

11671243
describe('withdraw', function () {

0 commit comments

Comments
 (0)