-
Notifications
You must be signed in to change notification settings - Fork 8
/
ERC20RewardHook.t.sol
186 lines (154 loc) · 6.09 KB
/
ERC20RewardHook.t.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;
import {Order, OfferItem, ItemType} from "@seaport-types/lib/ConsiderationStructs.sol";
import {
ERC20RewardHook,
RevenueShare
} from "@src/examples/revenue-share/ERC20RewardHook.sol";
import {
Hook,
OrderType,
OrderMetadata,
RentalOrder
} from "@src/libraries/RentalStructs.sol";
import {BaseTest} from "@test/BaseTest.sol";
import {MockERC20} from "@test/mocks/tokens/standard/MockERC20.sol";
import {MockERC1155} from "@test/mocks/tokens/standard/MockERC1155.sol";
contract Hook_ERC20RewardHook_Test is BaseTest {
// hook contract
ERC20RewardHook public hook;
// token that the hook contract will distribute
MockERC20 public rewardToken;
// token which will be rented
MockERC1155 public gameToken;
function setUp() public override {
super.setUp();
// deploy contracts needed for hook
rewardToken = new MockERC20();
gameToken = new MockERC1155();
hook = new ERC20RewardHook(
address(create),
address(stop),
address(gameToken),
address(rewardToken)
);
// admin enables the hook. Use binary 00000110 so that the hook
// is enabled for `onStart` and `onStop` calls
vm.prank(deployer.addr);
guard.updateHookStatus(address(hook), uint8(6));
// admin whitelists the game token for rental
vm.prank(deployer.addr);
admin.toggleWhitelistAsset(address(gameToken), uint8(3));
// fund the hook contract with some reward tokens
rewardToken.mint(address(hook), 100e18);
// fund the lender with some game tokens
gameToken.mint(alice.addr, 1e18);
// approve the game token to be spent by the Seaport conduit
vm.prank(alice.addr);
gameToken.setApprovalForAll(address(conduit), true);
// set the token label with the index
vm.label(address(rewardToken), "MERC20_RewardToken");
// assert that hook set up was successful
assertEq(STORE.hookOnStart(address(hook)), true);
assertEq(STORE.hookOnStop(address(hook)), true);
// assert that the hook was funded with the reward token
assertEq(rewardToken.balanceOf(address(hook)), 100e18);
}
// Helper function to start a rental using the game token
function _startRentalWithGameToken()
internal
returns (RentalOrder memory rentalOrder)
{
// create a BASE order where a lender offers the `gameToken` in
// exchange for some erc20 tokens
createOrder({
offerer: alice,
orderType: OrderType.BASE,
erc721Offers: 0,
erc1155Offers: 1,
erc20Offers: 0,
erc721Considerations: 0,
erc1155Considerations: 0,
erc20Considerations: 1
});
// create a custom offer item that uses the game token
OfferItem[] memory offers = new OfferItem[](1);
offers[0] = OfferItem({
itemType: ItemType.ERC1155,
token: address(gameToken),
identifierOrCriteria: 0,
startAmount: 1e18,
endAmount: 1e18
});
// Define the extra data to be used by the hook
RevenueShare memory revenueShareData = RevenueShare({
// lender address which will receive the rewards
lender: alice.addr,
// lender wants to take 70% of the revenue
lenderShare: 70
});
// Define the hook for the rental
Hook[] memory hooks = new Hook[](1);
hooks[0] = Hook({
// the hook contract to target
target: address(hook),
// index of the item in the order to apply the hook to
itemIndex: 0,
// any extra data that the hook will need.
extraData: abi.encode(revenueShareData)
});
// use an amendment to switch the offer item to the game token
withReplacedOfferItems(offers);
// use an amendment to add hooks to the metadata
withOrderMetadata(
OrderMetadata({
// the type of order being created
orderType: OrderType.BASE,
// the duration of the rental in seconds
rentDuration: 500,
// the hooks that will act as middleware for the items in the order
hooks: hooks,
// any extra data to be emitted upon order fulfillment
emittedExtraData: bytes("")
})
);
// finalize the order creation
(
Order memory order,
bytes32 orderHash,
OrderMetadata memory metadata
) = finalizeOrder();
// create an order fulfillment
createOrderFulfillment({
_fulfiller: bob,
order: order,
orderHash: orderHash,
metadata: metadata
});
// finalize the base order fulfillment. This executes the token swap
// and starts the rental.
rentalOrder = finalizeBaseOrderFulfillment();
}
function test_Success_RewardShare() public {
// start the rental. This should activate the hook and begin
// accruing rewards while the rental is active.
RentalOrder memory rentalOrder = _startRentalWithGameToken();
// roll ahead by 100 blocks so that rewards can accrue
vm.roll(block.number + 100);
// speed up in time past the rental expiration
vm.warp(block.timestamp + 750);
// stop the rental
vm.prank(alice.addr);
stop.stopRent(rentalOrder);
// owner of the safe can claim tokens
vm.prank(bob.addr);
hook.claimRewards(address(bob.safe));
// lender of the rental can claim tokens
vm.prank(alice.addr);
hook.claimRewards(alice.addr);
// earned rewards should be 100 blocks * 1 gwei reward per block * 1e18 token,
// which is 100 gwei
assertEq(rewardToken.balanceOf(bob.addr), 30000000000); // 30 gwei
assertEq(rewardToken.balanceOf(alice.addr), 70000000000); // 70 gwei
}
}