@paimaexample/evm-contracts 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/deno.json +28 -0
- package/docs/templates/contract.hbs +144 -0
- package/docs/templates/helpers.js +61 -0
- package/docs/templates/page.hbs +7 -0
- package/docs/templates/properties.js +76 -0
- package/hardhat.config.ts +11 -0
- package/index.js +1 -0
- package/mod.ts +0 -0
- package/package.json +13 -0
- package/remappings.txt +1 -0
- package/src/companions/ERC165Contract.json +21 -0
- package/src/companions/ERC165Contract.ts +21 -0
- package/src/companions/ERC20Contract.json +222 -0
- package/src/companions/ERC20Contract.ts +222 -0
- package/src/companions/ERC6551RegistryContract.json +128 -0
- package/src/companions/ERC6551RegistryContract.ts +128 -0
- package/src/companions/ERC721Contract.json +248 -0
- package/src/companions/ERC721Contract.ts +222 -0
- package/src/companions/IERC1155Contract.json +295 -0
- package/src/companions/IERC1155Contract.ts +295 -0
- package/src/companions/OldERC6551RegistryContract.json +133 -0
- package/src/companions/OldERC6551RegistryContract.ts +133 -0
- package/src/companions/PaimaERC721Contract.json +787 -0
- package/src/companions/PaimaERC721Contract.ts +787 -0
- package/src/companions/PaimaL2Contract.json +134 -0
- package/src/companions/PaimaL2Contract.ts +134 -0
- package/src/companions/README.md +5 -0
- package/src/contracts/AnnotatedMintNft.sol +171 -0
- package/src/contracts/BaseState.sol +16 -0
- package/src/contracts/ERC1967.sol +43 -0
- package/src/contracts/Erc20NftSale.sol +186 -0
- package/src/contracts/GenericPayment.sol +60 -0
- package/src/contracts/NativeNftSale.sol +97 -0
- package/src/contracts/PaimaL2Contract.sol +54 -0
- package/src/contracts/Proxy/Erc20NftSaleProxy.sol +79 -0
- package/src/contracts/Proxy/GenericPaymentProxy.sol +64 -0
- package/src/contracts/Proxy/NativeNftSaleProxy.sol +72 -0
- package/src/contracts/Proxy/OrderbookDexProxy.sol +27 -0
- package/src/contracts/README.md +72 -0
- package/src/contracts/State.sol +25 -0
- package/src/contracts/dev/ERC721Dev.sol +13 -0
- package/src/contracts/dev/Erc20Dev.sol +13 -0
- package/src/contracts/dev/NativeNftSaleUpgradeDev.sol +9 -0
- package/src/contracts/dev/NftSaleUpgradeDev.sol +12 -0
- package/src/contracts/dev/NftTypeMapper.sol +38 -0
- package/src/contracts/dev/Token.sol +15 -0
- package/src/contracts/dev/UpgradeDev.sol +10 -0
- package/src/contracts/orderbook/IOrderbookDex.sol +215 -0
- package/src/contracts/orderbook/OrderbookDex.sol +435 -0
- package/src/contracts/token/IERC4906Agnostic.sol +17 -0
- package/src/contracts/token/IInverseAppProjected1155.sol +40 -0
- package/src/contracts/token/IInverseAppProjectedNft.sol +38 -0
- package/src/contracts/token/IInverseBaseProjected1155.sol +25 -0
- package/src/contracts/token/IInverseBaseProjectedNft.sol +29 -0
- package/src/contracts/token/IInverseProjected1155.sol +38 -0
- package/src/contracts/token/IInverseProjectedNft.sol +41 -0
- package/src/contracts/token/ITokenUri.sol +10 -0
- package/src/contracts/token/IUri.sol +13 -0
- package/src/contracts/token/InverseAppProjected1155.sol +218 -0
- package/src/contracts/token/InverseAppProjectedNft.sol +192 -0
- package/src/contracts/token/InverseBaseProjected1155.sol +170 -0
- package/src/contracts/token/InverseBaseProjectedNft.sol +158 -0
- package/src/plugin/common.ts +35 -0
- package/src/plugin/deployment.ts +161 -0
- package/src/plugin/mod.ts +6 -0
- package/src/plugin/paimaL2.ts +202 -0
- package/src/recommendedHardhat.ts +86 -0
- package/test/lib/StdInvariant.sol +96 -0
- package/test/lib/cheatcodes.sol +89 -0
- package/test/lib/console.sol +1884 -0
- package/test/lib/ctest.sol +678 -0
- package/test/src/InverseAppProjected1155.t.sol +207 -0
- package/test/src/InverseAppProjectedNft.t.sol +164 -0
- package/test/src/InverseBaseProjected1155.t.sol +171 -0
- package/test/src/InverseBaseProjectedNft.t.sol +141 -0
- package/test/src/OrderbookDex.t.sol +710 -0
- package/test/src/OrderbookDexInvariant.t.sol +426 -0
- package/test/src/PaimaL2ContractTest.sol +115 -0
|
@@ -0,0 +1,710 @@
|
|
|
1
|
+
// SPDX-License-Identifier: UNLICENSED
|
|
2
|
+
pragma solidity ^0.8.18;
|
|
3
|
+
|
|
4
|
+
import {CheatCodes} from "../lib/cheatcodes.sol";
|
|
5
|
+
import {CTest} from "../lib/ctest.sol";
|
|
6
|
+
import "../lib/console.sol";
|
|
7
|
+
|
|
8
|
+
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
|
|
9
|
+
import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
|
|
10
|
+
import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
|
|
11
|
+
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
|
|
12
|
+
|
|
13
|
+
import {IInverseAppProjected1155} from "../../src/contracts/token/IInverseAppProjected1155.sol";
|
|
14
|
+
import {InverseAppProjected1155} from "../../src/contracts/token/InverseAppProjected1155.sol";
|
|
15
|
+
import {IOrderbookDex} from "../../src/contracts/orderbook/IOrderbookDex.sol";
|
|
16
|
+
import {OrderbookDex} from "../../src/contracts/orderbook/OrderbookDex.sol";
|
|
17
|
+
import {OrderbookDexProxy} from "../../src/contracts/Proxy/OrderbookDexProxy.sol";
|
|
18
|
+
|
|
19
|
+
contract OrderbookDexTest is CTest, ERC1155Holder {
|
|
20
|
+
using Address for address payable;
|
|
21
|
+
|
|
22
|
+
CheatCodes vm = CheatCodes(HEVM_ADDRESS);
|
|
23
|
+
OrderbookDex public dex;
|
|
24
|
+
IInverseAppProjected1155 asset;
|
|
25
|
+
uint256 makerFee = 40;
|
|
26
|
+
uint256 takerFee = 60;
|
|
27
|
+
uint256 orderCreationFee = 10000 gwei;
|
|
28
|
+
address alice = vm.addr(uint256(keccak256(abi.encodePacked("alice"))));
|
|
29
|
+
address boris = vm.addr(uint256(keccak256(abi.encodePacked("boris"))));
|
|
30
|
+
|
|
31
|
+
function setUp() public {
|
|
32
|
+
asset = new InverseAppProjected1155("Gold", "GOLD", address(this));
|
|
33
|
+
dex = OrderbookDex(
|
|
34
|
+
address(
|
|
35
|
+
new OrderbookDexProxy(
|
|
36
|
+
address(new OrderbookDex()),
|
|
37
|
+
address(this),
|
|
38
|
+
makerFee,
|
|
39
|
+
takerFee,
|
|
40
|
+
orderCreationFee
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
);
|
|
44
|
+
asset.setApprovalForAll(address(dex), true);
|
|
45
|
+
vm.deal(alice, 1_000 ether);
|
|
46
|
+
vm.deal(boris, 1_000 ether);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function testFuzz_createSellOrder_satisfiesRequirements(
|
|
50
|
+
uint256 assetAmount,
|
|
51
|
+
uint256 pricePerAsset
|
|
52
|
+
) public {
|
|
53
|
+
vm.assume(assetAmount > 0 && pricePerAsset > 0);
|
|
54
|
+
|
|
55
|
+
uint256 orderId = dex.currentOrderId(address(asset));
|
|
56
|
+
uint256 assetId = asset.mint(assetAmount, "");
|
|
57
|
+
|
|
58
|
+
vm.expectEmit(true, true, true, true);
|
|
59
|
+
emit IOrderbookDex.OrderCreated(
|
|
60
|
+
address(asset),
|
|
61
|
+
assetId,
|
|
62
|
+
orderId,
|
|
63
|
+
address(this),
|
|
64
|
+
assetAmount,
|
|
65
|
+
pricePerAsset,
|
|
66
|
+
makerFee,
|
|
67
|
+
takerFee
|
|
68
|
+
);
|
|
69
|
+
dex.createSellOrder{value: orderCreationFee}(
|
|
70
|
+
address(asset),
|
|
71
|
+
assetId,
|
|
72
|
+
assetAmount,
|
|
73
|
+
pricePerAsset
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
IOrderbookDex.Order memory order = dex.getOrder(address(asset), orderId);
|
|
77
|
+
assertEq(order.assetId, assetId);
|
|
78
|
+
assertEq(order.assetAmount, assetAmount);
|
|
79
|
+
assertEq(order.pricePerAsset, pricePerAsset);
|
|
80
|
+
assertEq(asset.balanceOf(address(dex), assetId), assetAmount);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function testFuzz_createBatchSellOrder_satisfiesRequirements(uint256 orderCount) public {
|
|
84
|
+
orderCount = bound(orderCount, 0, 100);
|
|
85
|
+
uint256[] memory assetIds = new uint256[](orderCount);
|
|
86
|
+
uint256[] memory assetAmounts = new uint256[](orderCount);
|
|
87
|
+
uint256[] memory pricesPerAssets = new uint256[](orderCount);
|
|
88
|
+
|
|
89
|
+
for (uint256 i = 0; i < orderCount; ++i) {
|
|
90
|
+
assetAmounts[i] = i == 0 ? 1 : i;
|
|
91
|
+
pricesPerAssets[i] = i == 0 ? 1 : i;
|
|
92
|
+
assetIds[i] = asset.mint(assetAmounts[i], "");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
uint256 orderId = dex.currentOrderId(address(asset));
|
|
96
|
+
for (uint256 i = 0; i < orderCount; ++i) {
|
|
97
|
+
vm.expectEmit(true, true, true, true);
|
|
98
|
+
emit IOrderbookDex.OrderCreated(
|
|
99
|
+
address(asset),
|
|
100
|
+
assetIds[i],
|
|
101
|
+
orderId + i,
|
|
102
|
+
address(this),
|
|
103
|
+
assetAmounts[i],
|
|
104
|
+
pricesPerAssets[i],
|
|
105
|
+
makerFee,
|
|
106
|
+
takerFee
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
uint256[] memory orderIds = dex.createBatchSellOrder{
|
|
111
|
+
value: orderCreationFee * assetIds.length
|
|
112
|
+
}(address(asset), assetIds, assetAmounts, pricesPerAssets);
|
|
113
|
+
|
|
114
|
+
for (uint256 i = 0; i < orderCount; ++i) {
|
|
115
|
+
IOrderbookDex.Order memory order = dex.getOrder(address(asset), orderIds[i]);
|
|
116
|
+
assertEq(order.assetId, assetIds[i]);
|
|
117
|
+
assertEq(order.assetAmount, assetAmounts[i]);
|
|
118
|
+
assertEq(order.pricePerAsset, pricesPerAssets[i]);
|
|
119
|
+
assertEq(asset.balanceOf(address(dex), assetIds[i]), assetAmounts[i]);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function testFuzz_createBatchSellOrder_reverts_ifInvalidArrayLength(
|
|
124
|
+
uint256 length1,
|
|
125
|
+
uint256 length2,
|
|
126
|
+
uint256 length3
|
|
127
|
+
) public {
|
|
128
|
+
length1 = bound(length1, 0, 1000);
|
|
129
|
+
length2 = bound(length2, 0, 1000);
|
|
130
|
+
length3 = bound(length3, 0, 1000);
|
|
131
|
+
vm.assume(length1 != length2 || length2 != length3);
|
|
132
|
+
uint256[] memory assetIds = new uint256[](length1);
|
|
133
|
+
uint256[] memory assetAmounts = new uint256[](length2);
|
|
134
|
+
uint256[] memory pricesPerAssets = new uint256[](length3);
|
|
135
|
+
|
|
136
|
+
vm.expectRevert(OrderbookDex.InvalidArrayLength.selector);
|
|
137
|
+
dex.createBatchSellOrder{value: orderCreationFee * assetIds.length}(
|
|
138
|
+
address(asset),
|
|
139
|
+
assetIds,
|
|
140
|
+
assetAmounts,
|
|
141
|
+
pricesPerAssets
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function testFuzz_createSellOrder_reverts_ifAssetAmountIsZero(uint256 pricePerAsset) public {
|
|
146
|
+
uint256 assetId = asset.mint(0, "");
|
|
147
|
+
|
|
148
|
+
vm.expectRevert(abi.encodeWithSelector(OrderbookDex.InvalidInput.selector, 0));
|
|
149
|
+
dex.createSellOrder{value: orderCreationFee}(address(asset), assetId, 0, pricePerAsset);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function testFuzz_fillOrdersExactEth_transfersCorrectly(uint256 price) public {
|
|
153
|
+
uint256 ordersCount = 5;
|
|
154
|
+
vm.assume(price / ordersCount > 0);
|
|
155
|
+
vm.assume(price < type(uint256).max / ordersCount / orderCreationFee);
|
|
156
|
+
uint256[] memory orderIds = new uint256[](ordersCount);
|
|
157
|
+
address payable[] memory sellers = new address payable[](ordersCount);
|
|
158
|
+
uint256[] memory assetIds = new uint256[](ordersCount);
|
|
159
|
+
uint256 totalAssetAmount;
|
|
160
|
+
address buyer = address(this);
|
|
161
|
+
for (uint256 i = 0; i < ordersCount; i++) {
|
|
162
|
+
address payable seller = payable(vm.addr(uint256(keccak256(abi.encodePacked(i)))));
|
|
163
|
+
vm.deal(seller, orderCreationFee);
|
|
164
|
+
uint256 assetAmount = price / (ordersCount - i);
|
|
165
|
+
uint256 pricePerAsset = price / assetAmount;
|
|
166
|
+
orderIds[i] = dex.currentOrderId(address(asset));
|
|
167
|
+
sellers[i] = seller;
|
|
168
|
+
vm.startPrank(seller);
|
|
169
|
+
assetIds[i] = asset.mint(assetAmount, "");
|
|
170
|
+
asset.setApprovalForAll(address(dex), true);
|
|
171
|
+
dex.createSellOrder{value: orderCreationFee}(
|
|
172
|
+
address(asset),
|
|
173
|
+
assetIds[i],
|
|
174
|
+
assetAmount,
|
|
175
|
+
pricePerAsset
|
|
176
|
+
);
|
|
177
|
+
totalAssetAmount += assetAmount;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
{
|
|
181
|
+
vm.deal(buyer, type(uint256).max);
|
|
182
|
+
uint256 buyerBalanceBefore = buyer.balance;
|
|
183
|
+
uint256[] memory sellersBalancesBefore = new uint256[](ordersCount);
|
|
184
|
+
uint256[] memory expectedPayouts = new uint256[](ordersCount);
|
|
185
|
+
uint256[] memory ordersAssetAmounts = new uint256[](ordersCount);
|
|
186
|
+
uint256 totalExpectedPayout;
|
|
187
|
+
for (uint256 i = 0; i < ordersCount; i++) {
|
|
188
|
+
IOrderbookDex.Order memory order = dex.getOrder(address(asset), orderIds[i]);
|
|
189
|
+
expectedPayouts[i] = order.pricePerAsset * order.assetAmount;
|
|
190
|
+
ordersAssetAmounts[i] = order.assetAmount;
|
|
191
|
+
totalExpectedPayout += expectedPayouts[i];
|
|
192
|
+
sellersBalancesBefore[i] = dex.balances(sellers[i]);
|
|
193
|
+
vm.expectEmit(true, true, true, true);
|
|
194
|
+
emit IOrderbookDex.OrderFilled(
|
|
195
|
+
address(asset),
|
|
196
|
+
assetIds[i],
|
|
197
|
+
orderIds[i],
|
|
198
|
+
sellers[i],
|
|
199
|
+
buyer,
|
|
200
|
+
order.assetAmount,
|
|
201
|
+
order.pricePerAsset,
|
|
202
|
+
(expectedPayouts[i] * makerFee) / 10000,
|
|
203
|
+
(expectedPayouts[i] * takerFee) / 10000
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
vm.startPrank(buyer);
|
|
207
|
+
dex.fillOrdersExactEth{
|
|
208
|
+
value: totalExpectedPayout + (totalExpectedPayout * takerFee) / 10000 + 1000
|
|
209
|
+
}(address(asset), totalAssetAmount, orderIds);
|
|
210
|
+
|
|
211
|
+
{
|
|
212
|
+
uint256 takerFeeTotal;
|
|
213
|
+
for (uint256 i = 0; i < ordersCount; i++) {
|
|
214
|
+
takerFeeTotal += (expectedPayouts[i] * takerFee) / 10000;
|
|
215
|
+
}
|
|
216
|
+
assertEq(buyer.balance, buyerBalanceBefore - (totalExpectedPayout + takerFeeTotal));
|
|
217
|
+
}
|
|
218
|
+
for (uint256 i = 0; i < ordersCount; i++) {
|
|
219
|
+
IOrderbookDex.Order memory order = dex.getOrder(address(asset), orderIds[i]);
|
|
220
|
+
assertEq(
|
|
221
|
+
dex.balances(sellers[i]),
|
|
222
|
+
sellersBalancesBefore[i] +
|
|
223
|
+
expectedPayouts[i] -
|
|
224
|
+
((expectedPayouts[i] * makerFee) / 10000)
|
|
225
|
+
);
|
|
226
|
+
assertEq(order.assetAmount, 0);
|
|
227
|
+
assertEq(asset.balanceOf(address(dex), order.assetId), 0);
|
|
228
|
+
assertEq(asset.balanceOf(buyer, order.assetId), ordersAssetAmounts[i]);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
for (uint256 i = 0; i < ordersCount; i++) {
|
|
232
|
+
uint256 sellerBalancesBefore = sellers[i].balance;
|
|
233
|
+
uint256 sellerBalanceInDex = dex.balances(sellers[i]);
|
|
234
|
+
vm.startPrank(sellers[i]);
|
|
235
|
+
dex.claim();
|
|
236
|
+
assertEq(sellers[i].balance, sellerBalancesBefore + sellerBalanceInDex);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function testFuzz_fillOrdersExactAsset_transfersCorrectly(uint256 price) public {
|
|
242
|
+
uint256 ordersCount = 5;
|
|
243
|
+
vm.assume(price / ordersCount > 0);
|
|
244
|
+
vm.assume(price < type(uint256).max / ordersCount / orderCreationFee);
|
|
245
|
+
uint256[] memory orderIds = new uint256[](ordersCount);
|
|
246
|
+
address payable[] memory sellers = new address payable[](ordersCount);
|
|
247
|
+
uint256[] memory assetIds = new uint256[](ordersCount);
|
|
248
|
+
uint256 totalAssetAmount;
|
|
249
|
+
address buyer = address(this);
|
|
250
|
+
for (uint256 i = 0; i < ordersCount; i++) {
|
|
251
|
+
address payable seller = payable(vm.addr(uint256(keccak256(abi.encodePacked(i)))));
|
|
252
|
+
vm.deal(seller, orderCreationFee);
|
|
253
|
+
uint256 assetAmount = price / (ordersCount - i);
|
|
254
|
+
uint256 pricePerAsset = price / assetAmount;
|
|
255
|
+
orderIds[i] = dex.currentOrderId(address(asset));
|
|
256
|
+
sellers[i] = seller;
|
|
257
|
+
vm.startPrank(seller);
|
|
258
|
+
assetIds[i] = asset.mint(assetAmount, "");
|
|
259
|
+
asset.setApprovalForAll(address(dex), true);
|
|
260
|
+
dex.createSellOrder{value: orderCreationFee}(
|
|
261
|
+
address(asset),
|
|
262
|
+
assetIds[i],
|
|
263
|
+
assetAmount,
|
|
264
|
+
pricePerAsset
|
|
265
|
+
);
|
|
266
|
+
totalAssetAmount += assetAmount;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
{
|
|
270
|
+
vm.deal(buyer, type(uint256).max);
|
|
271
|
+
uint256 buyerBalanceBefore = buyer.balance;
|
|
272
|
+
uint256[] memory sellersBalancesBefore = new uint256[](ordersCount);
|
|
273
|
+
uint256[] memory expectedPayouts = new uint256[](ordersCount);
|
|
274
|
+
uint256[] memory ordersAssetAmounts = new uint256[](ordersCount);
|
|
275
|
+
uint256 totalExpectedPayout;
|
|
276
|
+
for (uint256 i = 0; i < ordersCount; i++) {
|
|
277
|
+
IOrderbookDex.Order memory order = dex.getOrder(address(asset), orderIds[i]);
|
|
278
|
+
expectedPayouts[i] = order.pricePerAsset * order.assetAmount;
|
|
279
|
+
ordersAssetAmounts[i] = order.assetAmount;
|
|
280
|
+
totalExpectedPayout += expectedPayouts[i];
|
|
281
|
+
sellersBalancesBefore[i] = dex.balances(sellers[i]);
|
|
282
|
+
vm.expectEmit(true, true, true, true);
|
|
283
|
+
emit IOrderbookDex.OrderFilled(
|
|
284
|
+
address(asset),
|
|
285
|
+
assetIds[i],
|
|
286
|
+
orderIds[i],
|
|
287
|
+
sellers[i],
|
|
288
|
+
buyer,
|
|
289
|
+
order.assetAmount,
|
|
290
|
+
order.pricePerAsset,
|
|
291
|
+
(expectedPayouts[i] * makerFee) / 10000,
|
|
292
|
+
(expectedPayouts[i] * takerFee) / 10000
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
vm.startPrank(buyer);
|
|
296
|
+
dex.fillOrdersExactAsset{
|
|
297
|
+
value: totalExpectedPayout + (totalExpectedPayout * takerFee) / 10000 + 1000
|
|
298
|
+
}(address(asset), totalAssetAmount, orderIds);
|
|
299
|
+
|
|
300
|
+
{
|
|
301
|
+
uint256 takerFeeTotal;
|
|
302
|
+
for (uint256 i = 0; i < ordersCount; i++) {
|
|
303
|
+
takerFeeTotal += (expectedPayouts[i] * takerFee) / 10000;
|
|
304
|
+
}
|
|
305
|
+
assertEq(buyer.balance, buyerBalanceBefore - (totalExpectedPayout + takerFeeTotal));
|
|
306
|
+
}
|
|
307
|
+
for (uint256 i = 0; i < ordersCount; i++) {
|
|
308
|
+
IOrderbookDex.Order memory order = dex.getOrder(address(asset), orderIds[i]);
|
|
309
|
+
assertEq(
|
|
310
|
+
dex.balances(sellers[i]),
|
|
311
|
+
sellersBalancesBefore[i] +
|
|
312
|
+
expectedPayouts[i] -
|
|
313
|
+
((expectedPayouts[i] * makerFee) / 10000)
|
|
314
|
+
);
|
|
315
|
+
assertEq(order.assetAmount, 0);
|
|
316
|
+
assertEq(asset.balanceOf(address(dex), order.assetId), 0);
|
|
317
|
+
assertEq(asset.balanceOf(buyer, order.assetId), ordersAssetAmounts[i]);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
for (uint256 i = 0; i < ordersCount; i++) {
|
|
321
|
+
uint256 sellerBalancesBefore = sellers[i].balance;
|
|
322
|
+
uint256 sellerBalanceInDex = dex.balances(sellers[i]);
|
|
323
|
+
vm.startPrank(sellers[i]);
|
|
324
|
+
dex.claim();
|
|
325
|
+
assertEq(sellers[i].balance, sellerBalancesBefore + sellerBalanceInDex);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function testFuzz_fillOrdersExactEth_transfersCorrectlyWithPartialFill(
|
|
331
|
+
uint256 assetAmount,
|
|
332
|
+
uint256 pricePerAsset,
|
|
333
|
+
uint256 assetAmountToBuy
|
|
334
|
+
) public {
|
|
335
|
+
vm.assume(assetAmount > 0 && pricePerAsset > 1);
|
|
336
|
+
vm.assume(assetAmount < type(uint256).max / pricePerAsset / orderCreationFee);
|
|
337
|
+
vm.assume(pricePerAsset < type(uint256).max / assetAmount);
|
|
338
|
+
vm.assume(assetAmountToBuy < assetAmount);
|
|
339
|
+
|
|
340
|
+
address buyer = alice;
|
|
341
|
+
address payable seller = payable(address(this));
|
|
342
|
+
vm.deal(buyer, (assetAmount * pricePerAsset * (10000 + takerFee)) / 10000);
|
|
343
|
+
uint256 orderId = dex.currentOrderId(address(asset));
|
|
344
|
+
vm.startPrank(seller);
|
|
345
|
+
uint256 assetId = asset.mint(assetAmount, "");
|
|
346
|
+
asset.setApprovalForAll(address(dex), true);
|
|
347
|
+
dex.createSellOrder{value: orderCreationFee}(
|
|
348
|
+
address(asset),
|
|
349
|
+
assetId,
|
|
350
|
+
assetAmount,
|
|
351
|
+
pricePerAsset
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
uint256 buyerBalanceBefore = buyer.balance;
|
|
355
|
+
uint256 sellerBalanceBefore = dex.balances(seller);
|
|
356
|
+
uint256[] memory orderIds = new uint256[](1);
|
|
357
|
+
orderIds[0] = orderId;
|
|
358
|
+
uint256 purchaseCost = assetAmountToBuy * pricePerAsset;
|
|
359
|
+
vm.startPrank(buyer);
|
|
360
|
+
dex.fillOrdersExactEth{value: (purchaseCost + (purchaseCost * takerFee) / 10000)}(
|
|
361
|
+
address(asset),
|
|
362
|
+
assetAmountToBuy,
|
|
363
|
+
orderIds
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
IOrderbookDex.Order memory order = dex.getOrder(address(asset), orderId);
|
|
367
|
+
assertEq(
|
|
368
|
+
buyer.balance,
|
|
369
|
+
buyerBalanceBefore - (purchaseCost + (purchaseCost * takerFee) / 10000)
|
|
370
|
+
);
|
|
371
|
+
assertEq(
|
|
372
|
+
dex.balances(seller),
|
|
373
|
+
sellerBalanceBefore + (purchaseCost - (purchaseCost * makerFee) / 10000)
|
|
374
|
+
);
|
|
375
|
+
assertEq(order.assetAmount, assetAmount - assetAmountToBuy);
|
|
376
|
+
assertEq(asset.balanceOf(address(dex), assetId), assetAmount - assetAmountToBuy);
|
|
377
|
+
assertEq(asset.balanceOf(buyer, assetId), assetAmountToBuy);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function testFuzz_fillOrdersExactAsset_transfersCorrectlyWithPartialFill(
|
|
381
|
+
uint256 assetAmount,
|
|
382
|
+
uint256 pricePerAsset,
|
|
383
|
+
uint256 assetAmountToBuy
|
|
384
|
+
) public {
|
|
385
|
+
vm.assume(assetAmount > 0 && pricePerAsset > 0);
|
|
386
|
+
vm.assume(assetAmount < type(uint256).max / pricePerAsset / orderCreationFee);
|
|
387
|
+
vm.assume(pricePerAsset < type(uint256).max / assetAmount);
|
|
388
|
+
vm.assume(assetAmountToBuy < assetAmount);
|
|
389
|
+
|
|
390
|
+
address buyer = alice;
|
|
391
|
+
address payable seller = payable(address(this));
|
|
392
|
+
vm.deal(buyer, (assetAmount * pricePerAsset * (10000 + takerFee)) / 10000);
|
|
393
|
+
uint256 orderId = dex.currentOrderId(address(asset));
|
|
394
|
+
vm.startPrank(seller);
|
|
395
|
+
uint256 assetId = asset.mint(assetAmount, "");
|
|
396
|
+
asset.setApprovalForAll(address(dex), true);
|
|
397
|
+
dex.createSellOrder{value: orderCreationFee}(
|
|
398
|
+
address(asset),
|
|
399
|
+
assetId,
|
|
400
|
+
assetAmount,
|
|
401
|
+
pricePerAsset
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
uint256 buyerBalanceBefore = buyer.balance;
|
|
405
|
+
uint256 sellerBalanceBefore = dex.balances(seller);
|
|
406
|
+
uint256[] memory orderIds = new uint256[](1);
|
|
407
|
+
orderIds[0] = orderId;
|
|
408
|
+
uint256 purchaseCost = assetAmountToBuy * pricePerAsset;
|
|
409
|
+
vm.startPrank(buyer);
|
|
410
|
+
dex.fillOrdersExactAsset{value: buyerBalanceBefore}(
|
|
411
|
+
address(asset),
|
|
412
|
+
assetAmountToBuy,
|
|
413
|
+
orderIds
|
|
414
|
+
);
|
|
415
|
+
|
|
416
|
+
IOrderbookDex.Order memory order = dex.getOrder(address(asset), orderId);
|
|
417
|
+
assertEq(
|
|
418
|
+
buyer.balance,
|
|
419
|
+
buyerBalanceBefore - (purchaseCost + (purchaseCost * takerFee) / 10000)
|
|
420
|
+
);
|
|
421
|
+
assertEq(
|
|
422
|
+
dex.balances(seller),
|
|
423
|
+
sellerBalanceBefore + (purchaseCost - (purchaseCost * makerFee) / 10000)
|
|
424
|
+
);
|
|
425
|
+
assertEq(order.assetAmount, assetAmount - assetAmountToBuy);
|
|
426
|
+
assertEq(asset.balanceOf(address(dex), assetId), assetAmount - assetAmountToBuy);
|
|
427
|
+
assertEq(asset.balanceOf(buyer, assetId), assetAmountToBuy);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function testFuzz_fillOrdersExactEth_refundsExcessValue(
|
|
431
|
+
uint256 assetAmount,
|
|
432
|
+
uint256 pricePerAsset
|
|
433
|
+
) public {
|
|
434
|
+
uint256 multiplier = 3;
|
|
435
|
+
vm.assume(assetAmount > 0 && pricePerAsset > 0);
|
|
436
|
+
vm.assume(assetAmount < type(uint256).max / pricePerAsset / multiplier / orderCreationFee);
|
|
437
|
+
vm.assume(pricePerAsset < type(uint256).max / assetAmount / multiplier);
|
|
438
|
+
|
|
439
|
+
vm.deal(alice, assetAmount * pricePerAsset * multiplier);
|
|
440
|
+
uint256 orderId = dex.currentOrderId(address(asset));
|
|
441
|
+
uint256 assetId = asset.mint(assetAmount, "");
|
|
442
|
+
dex.createSellOrder{value: orderCreationFee}(
|
|
443
|
+
address(asset),
|
|
444
|
+
assetId,
|
|
445
|
+
assetAmount,
|
|
446
|
+
pricePerAsset
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
uint256 purchaseCost = assetAmount * pricePerAsset;
|
|
450
|
+
uint256 paidTakerFee = (purchaseCost * takerFee) / 10000;
|
|
451
|
+
uint256 aliceBalanceBefore = alice.balance;
|
|
452
|
+
uint256[] memory orderIds = new uint256[](1);
|
|
453
|
+
orderIds[0] = orderId;
|
|
454
|
+
vm.startPrank(alice);
|
|
455
|
+
dex.fillOrdersExactEth{value: alice.balance}(address(asset), assetAmount, orderIds);
|
|
456
|
+
assertEq(alice.balance, aliceBalanceBefore - (purchaseCost + paidTakerFee));
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function testFuzz_fillOrdersExactAsset_refundsExcessValue(
|
|
460
|
+
uint256 assetAmount,
|
|
461
|
+
uint256 pricePerAsset
|
|
462
|
+
) public {
|
|
463
|
+
uint256 multiplier = 3;
|
|
464
|
+
vm.assume(assetAmount > 0 && pricePerAsset > 0);
|
|
465
|
+
vm.assume(assetAmount < type(uint256).max / pricePerAsset / multiplier / orderCreationFee);
|
|
466
|
+
vm.assume(pricePerAsset < type(uint256).max / assetAmount / multiplier);
|
|
467
|
+
|
|
468
|
+
vm.deal(alice, assetAmount * pricePerAsset * multiplier);
|
|
469
|
+
uint256 orderId = dex.currentOrderId(address(asset));
|
|
470
|
+
uint256 assetId = asset.mint(assetAmount, "");
|
|
471
|
+
dex.createSellOrder{value: orderCreationFee}(
|
|
472
|
+
address(asset),
|
|
473
|
+
assetId,
|
|
474
|
+
assetAmount,
|
|
475
|
+
pricePerAsset
|
|
476
|
+
);
|
|
477
|
+
|
|
478
|
+
uint256 purchaseCost = assetAmount * pricePerAsset;
|
|
479
|
+
uint256 paidTakerFee = (purchaseCost * takerFee) / 10000;
|
|
480
|
+
uint256 aliceBalanceBefore = alice.balance;
|
|
481
|
+
uint256[] memory orderIds = new uint256[](1);
|
|
482
|
+
orderIds[0] = orderId;
|
|
483
|
+
vm.startPrank(alice);
|
|
484
|
+
dex.fillOrdersExactAsset{value: alice.balance}(address(asset), assetAmount, orderIds);
|
|
485
|
+
assertEq(alice.balance, aliceBalanceBefore - (purchaseCost + paidTakerFee));
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function test_fillOrdersExactEth_wontFillOrderIfOrderWasCancelled() public {
|
|
489
|
+
uint256 assetAmount = 100;
|
|
490
|
+
uint256 pricePerAsset = 200;
|
|
491
|
+
uint256 orderId = dex.currentOrderId(address(asset));
|
|
492
|
+
uint256 assetId = asset.mint(assetAmount, "");
|
|
493
|
+
dex.createSellOrder{value: orderCreationFee}(
|
|
494
|
+
address(asset),
|
|
495
|
+
assetId,
|
|
496
|
+
assetAmount,
|
|
497
|
+
pricePerAsset
|
|
498
|
+
);
|
|
499
|
+
dex.cancelSellOrder(address(asset), orderId);
|
|
500
|
+
|
|
501
|
+
uint256 aliceBalanceBefore = alice.balance;
|
|
502
|
+
uint256[] memory orderIds = new uint256[](1);
|
|
503
|
+
orderIds[0] = orderId;
|
|
504
|
+
vm.startPrank(alice);
|
|
505
|
+
|
|
506
|
+
dex.fillOrdersExactEth{value: assetAmount * pricePerAsset}(address(asset), 0, orderIds);
|
|
507
|
+
assertEq(alice.balance, aliceBalanceBefore);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function test_fillOrdersExactAsset_wontFillOrderIfOrderWasCancelled() public {
|
|
511
|
+
uint256 assetAmount = 100;
|
|
512
|
+
uint256 pricePerAsset = 200;
|
|
513
|
+
uint256 orderId = dex.currentOrderId(address(asset));
|
|
514
|
+
uint256 assetId = asset.mint(assetAmount, "");
|
|
515
|
+
dex.createSellOrder{value: orderCreationFee}(
|
|
516
|
+
address(asset),
|
|
517
|
+
assetId,
|
|
518
|
+
assetAmount,
|
|
519
|
+
pricePerAsset
|
|
520
|
+
);
|
|
521
|
+
dex.cancelSellOrder(address(asset), orderId);
|
|
522
|
+
|
|
523
|
+
uint256 aliceBalanceBefore = alice.balance;
|
|
524
|
+
uint256[] memory orderIds = new uint256[](1);
|
|
525
|
+
orderIds[0] = orderId;
|
|
526
|
+
vm.startPrank(alice);
|
|
527
|
+
|
|
528
|
+
dex.fillOrdersExactAsset{value: assetAmount * pricePerAsset}(address(asset), 0, orderIds);
|
|
529
|
+
assertEq(alice.balance, aliceBalanceBefore);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
function test_fillOrdersExactEth_reverts_ifInsufficientEndAmount() public {
|
|
533
|
+
uint256 assetAmount = 10;
|
|
534
|
+
uint256 pricePerAsset = 100;
|
|
535
|
+
uint256 orderId = dex.currentOrderId(address(asset));
|
|
536
|
+
uint256 assetId = asset.mint(assetAmount, "");
|
|
537
|
+
dex.createSellOrder{value: orderCreationFee}(
|
|
538
|
+
address(asset),
|
|
539
|
+
assetId,
|
|
540
|
+
assetAmount,
|
|
541
|
+
pricePerAsset
|
|
542
|
+
);
|
|
543
|
+
|
|
544
|
+
address payable[] memory sellers = new address payable[](1);
|
|
545
|
+
sellers[0] = payable(address(this));
|
|
546
|
+
uint256[] memory orderIds = new uint256[](1);
|
|
547
|
+
orderIds[0] = orderId;
|
|
548
|
+
uint256 purchaseCost = assetAmount * pricePerAsset;
|
|
549
|
+
uint256 paidTakerFee = (purchaseCost * takerFee) / 10000;
|
|
550
|
+
vm.startPrank(alice);
|
|
551
|
+
vm.expectRevert(
|
|
552
|
+
abi.encodeWithSelector(
|
|
553
|
+
OrderbookDex.InsufficientEndAmount.selector,
|
|
554
|
+
assetAmount + 1,
|
|
555
|
+
assetAmount
|
|
556
|
+
)
|
|
557
|
+
);
|
|
558
|
+
dex.fillOrdersExactEth{value: purchaseCost + paidTakerFee}(
|
|
559
|
+
address(asset),
|
|
560
|
+
assetAmount + 1,
|
|
561
|
+
orderIds
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
function test_fillOrdersExactAsset_reverts_ifInsufficientEndAmount() public {
|
|
566
|
+
uint256 assetAmount = 10;
|
|
567
|
+
uint256 pricePerAsset = 100;
|
|
568
|
+
uint256 orderId = dex.currentOrderId(address(asset));
|
|
569
|
+
uint256 assetId = asset.mint(assetAmount, "");
|
|
570
|
+
dex.createSellOrder{value: orderCreationFee}(
|
|
571
|
+
address(asset),
|
|
572
|
+
assetId,
|
|
573
|
+
assetAmount,
|
|
574
|
+
pricePerAsset
|
|
575
|
+
);
|
|
576
|
+
|
|
577
|
+
address payable[] memory sellers = new address payable[](1);
|
|
578
|
+
sellers[0] = payable(address(this));
|
|
579
|
+
uint256[] memory orderIds = new uint256[](1);
|
|
580
|
+
orderIds[0] = orderId;
|
|
581
|
+
uint256 purchaseCost = assetAmount * pricePerAsset;
|
|
582
|
+
uint256 paidTakerFee = (purchaseCost * takerFee) / 10000;
|
|
583
|
+
vm.startPrank(alice);
|
|
584
|
+
vm.expectRevert(
|
|
585
|
+
abi.encodeWithSelector(
|
|
586
|
+
OrderbookDex.InsufficientEndAmount.selector,
|
|
587
|
+
assetAmount + 1,
|
|
588
|
+
assetAmount
|
|
589
|
+
)
|
|
590
|
+
);
|
|
591
|
+
dex.fillOrdersExactAsset{value: purchaseCost + paidTakerFee}(
|
|
592
|
+
address(asset),
|
|
593
|
+
assetAmount + 1,
|
|
594
|
+
orderIds
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
function test_supportsInterface_returnsTrueForImplementedInterfaces() public {
|
|
599
|
+
assertTrue(dex.supportsInterface(type(IERC165).interfaceId));
|
|
600
|
+
assertTrue(dex.supportsInterface(type(IERC165).interfaceId));
|
|
601
|
+
assertTrue(dex.supportsInterface(type(IOrderbookDex).interfaceId));
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
function testFuzz_cancelSellOrder_satisfiesRequirements(uint256 assetAmount) public {
|
|
605
|
+
vm.assume(assetAmount > 0);
|
|
606
|
+
uint256 assetId = asset.mint(assetAmount, "");
|
|
607
|
+
uint256 orderId = dex.currentOrderId(address(asset));
|
|
608
|
+
dex.createSellOrder{value: orderCreationFee}(address(asset), assetId, assetAmount, 200);
|
|
609
|
+
|
|
610
|
+
vm.expectEmit(true, true, true, true);
|
|
611
|
+
emit IOrderbookDex.OrderCancelled(address(asset), assetId, orderId);
|
|
612
|
+
dex.cancelSellOrder(address(asset), orderId);
|
|
613
|
+
IOrderbookDex.Order memory order = dex.getOrder(address(asset), orderId);
|
|
614
|
+
assertEq(order.assetAmount, 0);
|
|
615
|
+
assertEq(asset.balanceOf(address(dex), assetId), 0);
|
|
616
|
+
assertEq(asset.balanceOf(address(this), assetId), assetAmount);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
function test_cancelSellOrder_reverts_ifUnauthorized() public {
|
|
620
|
+
uint256 orderId = dex.currentOrderId(address(asset));
|
|
621
|
+
uint256 assetId = asset.mint(100, "");
|
|
622
|
+
dex.createSellOrder{value: orderCreationFee}(address(asset), assetId, 100, 200);
|
|
623
|
+
|
|
624
|
+
vm.startPrank(alice);
|
|
625
|
+
vm.expectRevert(abi.encodeWithSelector(OrderbookDex.Unauthorized.selector, alice));
|
|
626
|
+
dex.cancelSellOrder(address(asset), orderId);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
function testFuzz_cancelBatchSellOrder_satisfiesRequirements(uint256 orderCount) public {
|
|
630
|
+
orderCount = bound(orderCount, 0, 100);
|
|
631
|
+
uint256[] memory assetIds = new uint256[](orderCount);
|
|
632
|
+
uint256[] memory assetAmounts = new uint256[](orderCount);
|
|
633
|
+
uint256[] memory pricesPerAssets = new uint256[](orderCount);
|
|
634
|
+
|
|
635
|
+
for (uint256 i = 0; i < orderCount; ++i) {
|
|
636
|
+
assetAmounts[i] = i == 0 ? 1 : i;
|
|
637
|
+
pricesPerAssets[i] = i == 0 ? 1 : i;
|
|
638
|
+
assetIds[i] = asset.mint(assetAmounts[i], "");
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
uint256 orderId = dex.currentOrderId(address(asset));
|
|
642
|
+
|
|
643
|
+
uint256[] memory orderIds = dex.createBatchSellOrder{
|
|
644
|
+
value: orderCreationFee * assetIds.length
|
|
645
|
+
}(address(asset), assetIds, assetAmounts, pricesPerAssets);
|
|
646
|
+
|
|
647
|
+
for (uint256 i = 0; i < orderCount; ++i) {
|
|
648
|
+
vm.expectEmit(true, true, true, true);
|
|
649
|
+
emit IOrderbookDex.OrderCancelled(address(asset), assetIds[i], orderId + i);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
dex.cancelBatchSellOrder(address(asset), orderIds);
|
|
653
|
+
|
|
654
|
+
for (uint256 i = 0; i < orderCount; ++i) {
|
|
655
|
+
IOrderbookDex.Order memory order = dex.getOrder(address(asset), orderIds[i]);
|
|
656
|
+
assertEq(order.assetAmount, 0);
|
|
657
|
+
assertEq(asset.balanceOf(address(dex), assetIds[i]), 0);
|
|
658
|
+
assertEq(asset.balanceOf(address(this), assetIds[i]), assetAmounts[i]);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
function test_cancelBatchSellOrder_reverts_ifUnauthorized() public {
|
|
663
|
+
uint256 orderId = dex.currentOrderId(address(asset));
|
|
664
|
+
uint256 assetId = asset.mint(100, "");
|
|
665
|
+
dex.createSellOrder{value: orderCreationFee}(address(asset), assetId, 100, 200);
|
|
666
|
+
|
|
667
|
+
uint256[] memory orderIds = new uint256[](1);
|
|
668
|
+
orderIds[0] = orderId;
|
|
669
|
+
|
|
670
|
+
vm.startPrank(alice);
|
|
671
|
+
vm.expectRevert(abi.encodeWithSelector(OrderbookDex.Unauthorized.selector, alice));
|
|
672
|
+
dex.cancelBatchSellOrder(address(asset), orderIds);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// Not really a test, was used to measure gas usage
|
|
676
|
+
function test_fillOrders_getGasUsage() public {
|
|
677
|
+
uint256 orderCount = 500;
|
|
678
|
+
uint256[] memory assetIds = new uint256[](orderCount);
|
|
679
|
+
uint256[] memory assetAmounts = new uint256[](orderCount);
|
|
680
|
+
uint256[] memory pricesPerAssets = new uint256[](orderCount);
|
|
681
|
+
|
|
682
|
+
uint256 totalPurchaseCost;
|
|
683
|
+
for (uint256 i = 0; i < orderCount; ++i) {
|
|
684
|
+
assetAmounts[i] = i + 1;
|
|
685
|
+
pricesPerAssets[i] = i + 1;
|
|
686
|
+
assetIds[i] = asset.mint(assetAmounts[i], "");
|
|
687
|
+
totalPurchaseCost += assetAmounts[i] * pricesPerAssets[i];
|
|
688
|
+
}
|
|
689
|
+
uint256[] memory orderIds = dex.createBatchSellOrder{
|
|
690
|
+
value: orderCreationFee * assetIds.length
|
|
691
|
+
}(address(asset), assetIds, assetAmounts, pricesPerAssets);
|
|
692
|
+
uint256 paidTakerFee = (totalPurchaseCost * takerFee) / 10000;
|
|
693
|
+
|
|
694
|
+
assertEq(orderIds.length, orderCount);
|
|
695
|
+
|
|
696
|
+
uint256 minimumAsset = 1;
|
|
697
|
+
uint256 value = type(uint256).max;
|
|
698
|
+
vm.deal(address(this), value);
|
|
699
|
+
|
|
700
|
+
uint256 startGas = gasleft();
|
|
701
|
+
dex.fillOrdersExactEth{value: totalPurchaseCost + paidTakerFee}(
|
|
702
|
+
address(asset),
|
|
703
|
+
minimumAsset,
|
|
704
|
+
orderIds
|
|
705
|
+
);
|
|
706
|
+
console.log("fill orders gas used", startGas - gasleft());
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
receive() external payable {}
|
|
710
|
+
}
|