@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,215 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.20;
|
|
4
|
+
|
|
5
|
+
import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
|
|
6
|
+
|
|
7
|
+
/// @notice Facilitates base-chain trading of assets that are living on a different app-chain.
|
|
8
|
+
/// @dev Orders are identified by an asset-specific unique incremental `orderId`.
|
|
9
|
+
interface IOrderbookDex is IERC1155Receiver {
|
|
10
|
+
struct FeeInfo {
|
|
11
|
+
/// @dev The maker fee collected from the seller when a sell order is filled. Expressed in basis points.
|
|
12
|
+
uint256 makerFee;
|
|
13
|
+
/// @dev The taker fee collected from the buyer when a sell order is filled. Expressed in basis points.
|
|
14
|
+
uint256 takerFee;
|
|
15
|
+
/// @dev Flag indicating whether the fees are set.
|
|
16
|
+
bool set;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
struct Order {
|
|
20
|
+
/// @dev The asset's unique token identifier.
|
|
21
|
+
uint256 assetId;
|
|
22
|
+
/// @dev The amount of the asset that is available to be sold.
|
|
23
|
+
uint256 assetAmount;
|
|
24
|
+
/// @dev The price per one unit of asset.
|
|
25
|
+
uint256 pricePerAsset;
|
|
26
|
+
/// @dev The seller's address.
|
|
27
|
+
address payable seller;
|
|
28
|
+
/// @dev The maker fee in basis points, set when order is created, defined by the asset's fee info.
|
|
29
|
+
uint256 makerFee;
|
|
30
|
+
/// @dev The taker fee in basis points, set when order is created, defined by the asset's fee info.
|
|
31
|
+
uint256 takerFee;
|
|
32
|
+
/// @dev The order creation fee paid by the seller when creating the order, refunded if sell order is cancelled.
|
|
33
|
+
uint256 creationFeePaid;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/// @param user The address that claimed the balance.
|
|
37
|
+
/// @param amount The amount that was claimed.
|
|
38
|
+
event BalanceClaimed(address indexed user, uint256 amount);
|
|
39
|
+
|
|
40
|
+
/// @param asset The asset's address (zero address if changing default fees).
|
|
41
|
+
/// @param makerFee The new maker fee in basis points.
|
|
42
|
+
/// @param takerFee The new taker fee in basis points.
|
|
43
|
+
event FeeInfoChanged(address indexed asset, uint256 makerFee, uint256 takerFee);
|
|
44
|
+
|
|
45
|
+
/// @param asset The asset's address.
|
|
46
|
+
/// @param assetId The asset's unique token identifier.
|
|
47
|
+
/// @param orderId The order's asset-specific unique identifier.
|
|
48
|
+
/// @param seller The seller's address.
|
|
49
|
+
/// @param assetAmount The amount of the asset that has been put for sale.
|
|
50
|
+
/// @param pricePerAsset The requested price per one unit of asset.
|
|
51
|
+
/// @param makerFee The maker fee in basis points.
|
|
52
|
+
/// @param takerFee The taker fee in basis points.
|
|
53
|
+
event OrderCreated(
|
|
54
|
+
address indexed asset,
|
|
55
|
+
uint256 indexed assetId,
|
|
56
|
+
uint256 indexed orderId,
|
|
57
|
+
address seller,
|
|
58
|
+
uint256 assetAmount,
|
|
59
|
+
uint256 pricePerAsset,
|
|
60
|
+
uint256 makerFee,
|
|
61
|
+
uint256 takerFee
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
/// @param asset The asset's address.
|
|
65
|
+
/// @param assetId The asset's unique token identifier.
|
|
66
|
+
/// @param orderId The order's asset-specific unique identifier.
|
|
67
|
+
/// @param seller The seller's address.
|
|
68
|
+
/// @param buyer The buyer's address.
|
|
69
|
+
/// @param assetAmount The amount of the asset that was traded.
|
|
70
|
+
/// @param pricePerAsset The price per one unit of asset that was paid.
|
|
71
|
+
/// @param makerFeeCollected The maker fee in native tokens that was collected.
|
|
72
|
+
/// @param takerFeeCollected The taker fee in native tokens that was collected.
|
|
73
|
+
event OrderFilled(
|
|
74
|
+
address indexed asset,
|
|
75
|
+
uint256 indexed assetId,
|
|
76
|
+
uint256 indexed orderId,
|
|
77
|
+
address seller,
|
|
78
|
+
address buyer,
|
|
79
|
+
uint256 assetAmount,
|
|
80
|
+
uint256 pricePerAsset,
|
|
81
|
+
uint256 makerFeeCollected,
|
|
82
|
+
uint256 takerFeeCollected
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
/// @param asset The asset's address.
|
|
86
|
+
/// @param assetId The asset's unique token identifier.
|
|
87
|
+
/// @param orderId The order's asset-specific unique identifier.
|
|
88
|
+
event OrderCancelled(address indexed asset, uint256 indexed assetId, uint256 indexed orderId);
|
|
89
|
+
|
|
90
|
+
/// @param oldFee The old fee value.
|
|
91
|
+
/// @param newFee The new fee value.
|
|
92
|
+
event OrderCreationFeeChanged(uint256 oldFee, uint256 newFee);
|
|
93
|
+
|
|
94
|
+
/// @param receiver The address that received the fees.
|
|
95
|
+
/// @param amount The amount of fees that were withdrawn.
|
|
96
|
+
event FeesWithdrawn(address indexed receiver, uint256 amount);
|
|
97
|
+
|
|
98
|
+
/// @notice The balance of `user` that's claimable by `claim` function.
|
|
99
|
+
function balances(address user) external view returns (uint256);
|
|
100
|
+
|
|
101
|
+
/// @notice Withdraw the claimable balance of the caller.
|
|
102
|
+
function claim() external;
|
|
103
|
+
|
|
104
|
+
/// @notice The `orderId` of the next sell order for specific `asset`.
|
|
105
|
+
function currentOrderId(address asset) external view returns (uint256);
|
|
106
|
+
|
|
107
|
+
/// @notice The total amount of fees collected by the contract.
|
|
108
|
+
function collectedFees() external view returns (uint256);
|
|
109
|
+
|
|
110
|
+
/// @notice The default maker fee, used if fee information for asset is not set.
|
|
111
|
+
function defaultMakerFee() external view returns (uint256);
|
|
112
|
+
|
|
113
|
+
/// @notice The default taker fee, used if fee information for asset is not set.
|
|
114
|
+
function defaultTakerFee() external view returns (uint256);
|
|
115
|
+
|
|
116
|
+
/// @notice The flat fee paid by the seller when creating a sell order, to prevent spam.
|
|
117
|
+
function orderCreationFee() external view returns (uint256);
|
|
118
|
+
|
|
119
|
+
/// @notice The maximum fee, maker/taker fees cannot be set to exceed this amount.
|
|
120
|
+
function maxFee() external view returns (uint256);
|
|
121
|
+
|
|
122
|
+
/// @notice The fee information of `asset`.
|
|
123
|
+
function getAssetFeeInfo(address asset) external view returns (FeeInfo memory);
|
|
124
|
+
|
|
125
|
+
/// @notice Returns the asset fees if set, otherwise returns the default fees.
|
|
126
|
+
function getAssetAppliedFees(
|
|
127
|
+
address asset
|
|
128
|
+
) external view returns (uint256 makerFee, uint256 takerFee);
|
|
129
|
+
|
|
130
|
+
/// @notice Set the fee information of `asset`. Executable only by the owner.
|
|
131
|
+
/// @dev MUST revert if `makerFee` or `takerFee` exceeds `maxFee`.
|
|
132
|
+
/// MUST revert if called by unauthorized account.
|
|
133
|
+
function setAssetFeeInfo(address asset, uint256 makerFee, uint256 takerFee) external;
|
|
134
|
+
|
|
135
|
+
/// @notice Set the default fee information that is used if fee information for asset is not set. Executable only by the owner.
|
|
136
|
+
/// @dev MUST revert if `makerFee` or `takerFee` exceeds `maxFee`.
|
|
137
|
+
/// MUST revert if called by unauthorized account.
|
|
138
|
+
function setDefaultFeeInfo(uint256 makerFee, uint256 takerFee) external;
|
|
139
|
+
|
|
140
|
+
/// @notice Set the flat fee paid by the seller when creating a sell order, to prevent spam. Executable only by the owner.
|
|
141
|
+
/// @dev MUST revert if called by unauthorized account.
|
|
142
|
+
function setOrderCreationFee(uint256 fee) external;
|
|
143
|
+
|
|
144
|
+
/// @notice Returns the Order struct information about an order identified by the `orderId` for specific `asset`.
|
|
145
|
+
function getOrder(address asset, uint256 orderId) external view returns (Order memory);
|
|
146
|
+
|
|
147
|
+
/// @notice Creates a sell order for the `assetAmount` of `asset` with ID `assetId` at `pricePerAsset`. Requires payment of `orderCreationFee`.
|
|
148
|
+
/// @dev The order information is saved in a mapping `asset -> orderId -> Order`, with `orderId` being an asset-specific unique incremental identifier.
|
|
149
|
+
/// MUST transfer the `assetAmount` of `asset` with ID `assetId` from the seller to the contract.
|
|
150
|
+
/// MUST emit `OrderCreated` event.
|
|
151
|
+
/// MUST revert if `msg.value` is less than `orderCreationFee`.
|
|
152
|
+
/// @return The asset-specific unique identifier of the created order.
|
|
153
|
+
function createSellOrder(
|
|
154
|
+
address asset,
|
|
155
|
+
uint256 assetId,
|
|
156
|
+
uint256 assetAmount,
|
|
157
|
+
uint256 pricePerAsset
|
|
158
|
+
) external payable returns (uint256);
|
|
159
|
+
|
|
160
|
+
/// @notice Creates a batch of sell orders for the `assetAmount` of `asset` with ID `assetId` at `pricePerAsset`. Requires payment of `orderCreationFee` times the amount of orders.
|
|
161
|
+
/// @dev This is a batched version of `createSellOrder` that simply iterates through the arrays to call said function.
|
|
162
|
+
/// MUST revert if `msg.value` is less than `orderCreationFee * assetIds.length`.
|
|
163
|
+
/// @return The asset-specific unique identifiers of the created orders.
|
|
164
|
+
function createBatchSellOrder(
|
|
165
|
+
address asset,
|
|
166
|
+
uint256[] memory assetIds,
|
|
167
|
+
uint256[] memory assetAmounts,
|
|
168
|
+
uint256[] memory pricesPerAssets
|
|
169
|
+
) external payable returns (uint256[] memory);
|
|
170
|
+
|
|
171
|
+
/// @notice Consecutively fills an array of orders of `asset` identified by the asset-specific `orderId` of each order,
|
|
172
|
+
/// by providing an exact amount of ETH and requesting a specific minimum amount of asset to receive.
|
|
173
|
+
/// @dev Transfers portions of msg.value to the orders' sellers according to the price.
|
|
174
|
+
/// The sum of asset amounts of filled orders MUST be at least `minimumAsset`.
|
|
175
|
+
/// If msg.value is more than the sum of orders' prices, it MUST refund the excess back to `msg.sender`.
|
|
176
|
+
/// MUST decrease the `assetAmount` parameter for the specified order according to how much of it was filled,
|
|
177
|
+
/// and transfer that amount of the order's `asset` with ID `assetId` to the buyer.
|
|
178
|
+
/// MUST emit `OrderFilled` event for each order accordingly.
|
|
179
|
+
function fillOrdersExactEth(
|
|
180
|
+
address asset,
|
|
181
|
+
uint256 minimumAsset,
|
|
182
|
+
uint256[] memory orderIds
|
|
183
|
+
) external payable;
|
|
184
|
+
|
|
185
|
+
/// @notice Consecutively fills an array of orders identified by the asset-specific `orderId` of each order,
|
|
186
|
+
/// by providing a possibly surplus amount of ETH and requesting an exact amount of asset to receive.
|
|
187
|
+
/// @dev Transfers portions of msg.value to the orders' sellers according to the price.
|
|
188
|
+
/// The sum of asset amounts of filled orders MUST be exactly `assetAmount`. Excess ETH MUST be returned back to `msg.sender`.
|
|
189
|
+
/// MUST decrease the `assetAmount` parameter for the specified order according to how much of it was filled,
|
|
190
|
+
/// and transfer that amount of the order's `asset` with ID `assetId` to the buyer.
|
|
191
|
+
/// MUST emit `OrderFilled` event for each order accordingly.
|
|
192
|
+
/// If msg.value is more than the sum of orders' prices, it MUST refund the difference back to `msg.sender`.
|
|
193
|
+
function fillOrdersExactAsset(
|
|
194
|
+
address asset,
|
|
195
|
+
uint256 assetAmount,
|
|
196
|
+
uint256[] memory orderIds
|
|
197
|
+
) external payable;
|
|
198
|
+
|
|
199
|
+
/// @notice Cancels the sell order of `asset` with asset-specific `orderId`, transferring the order's assets back to the seller.
|
|
200
|
+
/// @dev MUST revert if the order's seller is not `msg.sender`.
|
|
201
|
+
/// MUST change the `assetAmount` parameter for the specified order to `0`.
|
|
202
|
+
/// MUST emit `OrderCancelled` event.
|
|
203
|
+
/// MUST transfer the order's `assetAmount` of `asset` with `assetId` back to the seller.
|
|
204
|
+
function cancelSellOrder(address asset, uint256 orderId) external;
|
|
205
|
+
|
|
206
|
+
/// @notice Cancels a batch of sell orders of `asset` with asset-specific `orderIds`, transferring the orders' assets back to the seller.
|
|
207
|
+
/// @dev This is a batched version of `cancelSellOrder` that simply iterates through the array to call said function.
|
|
208
|
+
function cancelBatchSellOrder(address asset, uint256[] memory orderIds) external;
|
|
209
|
+
|
|
210
|
+
/// @notice Withdraws the contract balance (containing collected fees) to the owner. Executable only by the owner.
|
|
211
|
+
/// @dev MUST transfer the entire contract balance to the owner.
|
|
212
|
+
/// MUST revert if called by unauthorized account.
|
|
213
|
+
/// MUST emit `FeesWithdrawn` event.
|
|
214
|
+
function withdrawFees() external;
|
|
215
|
+
}
|
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.20;
|
|
4
|
+
|
|
5
|
+
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
|
|
6
|
+
import {Arrays} from "@openzeppelin/contracts/utils/Arrays.sol";
|
|
7
|
+
import {ERC1155HolderUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC1155/utils/ERC1155HolderUpgradeable.sol";
|
|
8
|
+
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
|
|
9
|
+
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
|
|
10
|
+
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
|
|
11
|
+
import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
|
|
12
|
+
|
|
13
|
+
import {IInverseProjected1155} from "../token/IInverseProjected1155.sol";
|
|
14
|
+
import {IOrderbookDex} from "./IOrderbookDex.sol";
|
|
15
|
+
|
|
16
|
+
/// @notice Facilitates base-chain trading of assets that are living on a different app-chain.
|
|
17
|
+
/// @dev Orders are identified by an asset-specific unique incremental `orderId`.
|
|
18
|
+
contract OrderbookDex is
|
|
19
|
+
IOrderbookDex,
|
|
20
|
+
ERC1155HolderUpgradeable,
|
|
21
|
+
OwnableUpgradeable,
|
|
22
|
+
UUPSUpgradeable,
|
|
23
|
+
ReentrancyGuardUpgradeable
|
|
24
|
+
{
|
|
25
|
+
using Address for address payable;
|
|
26
|
+
using Arrays for uint256[];
|
|
27
|
+
using Arrays for address[];
|
|
28
|
+
|
|
29
|
+
uint256 constant basisPoints = 10000;
|
|
30
|
+
/// @notice Maximum fee is 10% (1000 bps).
|
|
31
|
+
uint256 public constant maxFee = basisPoints / 10;
|
|
32
|
+
/// @inheritdoc IOrderbookDex
|
|
33
|
+
mapping(address user => uint256 value) public balances;
|
|
34
|
+
/// @inheritdoc IOrderbookDex
|
|
35
|
+
mapping(address asset => uint256 orderId) public currentOrderId;
|
|
36
|
+
mapping(address asset => mapping(uint256 orderId => Order)) internal orders;
|
|
37
|
+
mapping(address asset => FeeInfo) internal assetFeeInfo;
|
|
38
|
+
/// @inheritdoc IOrderbookDex
|
|
39
|
+
uint256 public collectedFees;
|
|
40
|
+
/// @inheritdoc IOrderbookDex
|
|
41
|
+
uint256 public defaultMakerFee;
|
|
42
|
+
/// @inheritdoc IOrderbookDex
|
|
43
|
+
uint256 public defaultTakerFee;
|
|
44
|
+
/// @inheritdoc IOrderbookDex
|
|
45
|
+
uint256 public orderCreationFee;
|
|
46
|
+
|
|
47
|
+
error FeeTooHigh();
|
|
48
|
+
error OrderDoesNotExist(uint256 orderId);
|
|
49
|
+
error InsufficientEndAmount(uint256 expectedAmount, uint256 actualAmount);
|
|
50
|
+
error InsufficientPayment();
|
|
51
|
+
error InvalidArrayLength();
|
|
52
|
+
error InvalidInput(uint256 input);
|
|
53
|
+
error Unauthorized(address sender);
|
|
54
|
+
|
|
55
|
+
/// @custom:oz-upgrades-unsafe-allow constructor
|
|
56
|
+
constructor() {
|
|
57
|
+
_disableInitializers();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function initialize(
|
|
61
|
+
address _owner,
|
|
62
|
+
uint256 _defaultMakerFee,
|
|
63
|
+
uint256 _defaultTakerFee,
|
|
64
|
+
uint256 _orderCreationFee
|
|
65
|
+
) public initializer {
|
|
66
|
+
__Ownable_init(_owner);
|
|
67
|
+
__UUPSUpgradeable_init();
|
|
68
|
+
__ERC1155Holder_init();
|
|
69
|
+
__ReentrancyGuard_init();
|
|
70
|
+
|
|
71
|
+
defaultMakerFee = _defaultMakerFee;
|
|
72
|
+
defaultTakerFee = _defaultTakerFee;
|
|
73
|
+
orderCreationFee = _orderCreationFee;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/// @inheritdoc IOrderbookDex
|
|
77
|
+
function claim() external {
|
|
78
|
+
uint256 amount = balances[msg.sender];
|
|
79
|
+
delete balances[msg.sender];
|
|
80
|
+
emit BalanceClaimed(msg.sender, amount);
|
|
81
|
+
payable(msg.sender).sendValue(amount);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/// @inheritdoc IOrderbookDex
|
|
85
|
+
function getAssetFeeInfo(address asset) public view virtual returns (FeeInfo memory) {
|
|
86
|
+
return assetFeeInfo[asset];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/// @inheritdoc IOrderbookDex
|
|
90
|
+
function getAssetAppliedFees(
|
|
91
|
+
address asset
|
|
92
|
+
) public view virtual returns (uint256 makerFee, uint256 takerFee) {
|
|
93
|
+
FeeInfo memory feeInfo = assetFeeInfo[asset];
|
|
94
|
+
if (feeInfo.set) {
|
|
95
|
+
return (feeInfo.makerFee, feeInfo.takerFee);
|
|
96
|
+
}
|
|
97
|
+
return (defaultMakerFee, defaultTakerFee);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/// @inheritdoc IOrderbookDex
|
|
101
|
+
function setAssetFeeInfo(address asset, uint256 makerFee, uint256 takerFee) public onlyOwner {
|
|
102
|
+
if (makerFee > maxFee || takerFee > maxFee) {
|
|
103
|
+
revert FeeTooHigh();
|
|
104
|
+
}
|
|
105
|
+
assetFeeInfo[asset] = FeeInfo({makerFee: makerFee, takerFee: takerFee, set: true});
|
|
106
|
+
emit FeeInfoChanged(asset, makerFee, takerFee);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/// @inheritdoc IOrderbookDex
|
|
110
|
+
function setDefaultFeeInfo(uint256 makerFee, uint256 takerFee) public onlyOwner {
|
|
111
|
+
if (makerFee > maxFee || takerFee > maxFee) {
|
|
112
|
+
revert FeeTooHigh();
|
|
113
|
+
}
|
|
114
|
+
defaultMakerFee = makerFee;
|
|
115
|
+
defaultTakerFee = takerFee;
|
|
116
|
+
emit FeeInfoChanged(address(0), makerFee, takerFee);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/// @inheritdoc IOrderbookDex
|
|
120
|
+
function setOrderCreationFee(uint256 fee) public onlyOwner {
|
|
121
|
+
emit OrderCreationFeeChanged(orderCreationFee, fee);
|
|
122
|
+
orderCreationFee = fee;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/// @inheritdoc IOrderbookDex
|
|
126
|
+
function getOrder(address asset, uint256 orderId) public view virtual returns (Order memory) {
|
|
127
|
+
return orders[asset][orderId];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/// @inheritdoc IOrderbookDex
|
|
131
|
+
function createSellOrder(
|
|
132
|
+
address asset,
|
|
133
|
+
uint256 assetId,
|
|
134
|
+
uint256 assetAmount,
|
|
135
|
+
uint256 pricePerAsset
|
|
136
|
+
) external payable virtual nonReentrant returns (uint256) {
|
|
137
|
+
if (msg.value < orderCreationFee) {
|
|
138
|
+
revert InsufficientPayment();
|
|
139
|
+
}
|
|
140
|
+
collectedFees += msg.value;
|
|
141
|
+
return _createSellOrder(asset, assetId, assetAmount, pricePerAsset);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/// @inheritdoc IOrderbookDex
|
|
145
|
+
function createBatchSellOrder(
|
|
146
|
+
address asset,
|
|
147
|
+
uint256[] memory assetIds,
|
|
148
|
+
uint256[] memory assetAmounts,
|
|
149
|
+
uint256[] memory pricesPerAssets
|
|
150
|
+
) external payable virtual nonReentrant returns (uint256[] memory) {
|
|
151
|
+
if (assetIds.length != assetAmounts.length || assetIds.length != pricesPerAssets.length) {
|
|
152
|
+
revert InvalidArrayLength();
|
|
153
|
+
}
|
|
154
|
+
if (msg.value < orderCreationFee * assetIds.length) {
|
|
155
|
+
revert InsufficientPayment();
|
|
156
|
+
}
|
|
157
|
+
collectedFees += msg.value;
|
|
158
|
+
uint256[] memory orderIds = new uint256[](assetIds.length);
|
|
159
|
+
for (uint256 i; i < assetIds.length; ) {
|
|
160
|
+
orderIds[i] = _createSellOrder(
|
|
161
|
+
asset,
|
|
162
|
+
assetIds.unsafeMemoryAccess(i),
|
|
163
|
+
assetAmounts.unsafeMemoryAccess(i),
|
|
164
|
+
pricesPerAssets.unsafeMemoryAccess(i)
|
|
165
|
+
);
|
|
166
|
+
unchecked {
|
|
167
|
+
++i;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return orderIds;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/// @inheritdoc IOrderbookDex
|
|
174
|
+
function fillOrdersExactEth(
|
|
175
|
+
address asset,
|
|
176
|
+
uint256 minimumAsset,
|
|
177
|
+
uint256[] memory orderIds
|
|
178
|
+
) external payable virtual nonReentrant {
|
|
179
|
+
uint256 length = orderIds.length;
|
|
180
|
+
uint256 remainingEth = msg.value;
|
|
181
|
+
uint256 totalAssetReceived;
|
|
182
|
+
for (uint256 i; i < length; ++i) {
|
|
183
|
+
uint256 orderId = orderIds.unsafeMemoryAccess(i);
|
|
184
|
+
Order storage order = orders[asset][orderId];
|
|
185
|
+
if (order.assetAmount == 0) {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
uint256 purchaseCost = (remainingEth * basisPoints) / (order.takerFee + basisPoints);
|
|
190
|
+
// After the integer division by fees, purchase cost needs to be rounded up (fees are rounded down)
|
|
191
|
+
if (purchaseCost * (order.takerFee + basisPoints) != (remainingEth * basisPoints)) {
|
|
192
|
+
++purchaseCost;
|
|
193
|
+
}
|
|
194
|
+
uint256 assetsToBuy = purchaseCost / order.pricePerAsset;
|
|
195
|
+
|
|
196
|
+
if (assetsToBuy == 0) {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (assetsToBuy > order.assetAmount) {
|
|
200
|
+
assetsToBuy = order.assetAmount;
|
|
201
|
+
}
|
|
202
|
+
purchaseCost = assetsToBuy * order.pricePerAsset;
|
|
203
|
+
|
|
204
|
+
// Can be unchecked because assetsToBuy is less than or equal to order.assetAmount.
|
|
205
|
+
unchecked {
|
|
206
|
+
order.assetAmount -= assetsToBuy;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
uint256 makerFee = (purchaseCost * order.makerFee) / basisPoints;
|
|
210
|
+
uint256 takerFee = (purchaseCost * order.takerFee) / basisPoints;
|
|
211
|
+
|
|
212
|
+
if (remainingEth < purchaseCost + takerFee) {
|
|
213
|
+
revert InsufficientPayment();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Can be unchecked because (purchaseCost + takerFee) is less than or equal to remainingEth.
|
|
217
|
+
unchecked {
|
|
218
|
+
remainingEth -= (purchaseCost + takerFee);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
collectedFees += makerFee + takerFee;
|
|
222
|
+
|
|
223
|
+
totalAssetReceived += assetsToBuy;
|
|
224
|
+
IInverseProjected1155(asset).safeTransferFrom(
|
|
225
|
+
address(this),
|
|
226
|
+
msg.sender,
|
|
227
|
+
order.assetId,
|
|
228
|
+
assetsToBuy,
|
|
229
|
+
bytes("")
|
|
230
|
+
);
|
|
231
|
+
balances[order.seller] += purchaseCost - makerFee;
|
|
232
|
+
emit OrderFilled(
|
|
233
|
+
asset,
|
|
234
|
+
order.assetId,
|
|
235
|
+
orderId,
|
|
236
|
+
order.seller,
|
|
237
|
+
msg.sender,
|
|
238
|
+
assetsToBuy,
|
|
239
|
+
order.pricePerAsset,
|
|
240
|
+
makerFee,
|
|
241
|
+
takerFee
|
|
242
|
+
);
|
|
243
|
+
if (remainingEth == 0) {
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (totalAssetReceived < minimumAsset) {
|
|
248
|
+
revert InsufficientEndAmount(minimumAsset, totalAssetReceived);
|
|
249
|
+
}
|
|
250
|
+
if (remainingEth > 0) {
|
|
251
|
+
payable(msg.sender).sendValue(remainingEth);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/// @inheritdoc IOrderbookDex
|
|
256
|
+
function fillOrdersExactAsset(
|
|
257
|
+
address asset,
|
|
258
|
+
uint256 assetAmount,
|
|
259
|
+
uint256[] memory orderIds
|
|
260
|
+
) external payable virtual nonReentrant {
|
|
261
|
+
uint256 length = orderIds.length;
|
|
262
|
+
uint256 remainingAsset = assetAmount;
|
|
263
|
+
uint256 remainingEth = msg.value;
|
|
264
|
+
for (uint256 i; i < length; ++i) {
|
|
265
|
+
uint256 orderId = orderIds.unsafeMemoryAccess(i);
|
|
266
|
+
Order storage order = orders[asset][orderId];
|
|
267
|
+
if (order.assetAmount == 0) {
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
uint256 assetsToBuy = order.assetAmount;
|
|
271
|
+
if (assetsToBuy > remainingAsset) {
|
|
272
|
+
assetsToBuy = remainingAsset;
|
|
273
|
+
}
|
|
274
|
+
if (assetsToBuy == 0) {
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Can be unchecked because assetsToBuy is less than or equal to order.assetAmount.
|
|
279
|
+
unchecked {
|
|
280
|
+
order.assetAmount -= assetsToBuy;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
uint256 purchaseCost = assetsToBuy * order.pricePerAsset;
|
|
284
|
+
uint256 makerFee = (purchaseCost * order.makerFee) / basisPoints;
|
|
285
|
+
uint256 takerFee = (purchaseCost * order.takerFee) / basisPoints;
|
|
286
|
+
|
|
287
|
+
if (remainingEth < purchaseCost + takerFee) {
|
|
288
|
+
revert InsufficientPayment();
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Can be unchecked because (purchaseCost + takerFee) is less than or equal to remainingEth.
|
|
292
|
+
unchecked {
|
|
293
|
+
remainingEth -= (purchaseCost + takerFee);
|
|
294
|
+
}
|
|
295
|
+
// Can be unchecked because assetsToBuy is less than or equal to remainingAsset.
|
|
296
|
+
unchecked {
|
|
297
|
+
remainingAsset -= assetsToBuy;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
collectedFees += makerFee + takerFee;
|
|
301
|
+
|
|
302
|
+
IInverseProjected1155(asset).safeTransferFrom(
|
|
303
|
+
address(this),
|
|
304
|
+
msg.sender,
|
|
305
|
+
order.assetId,
|
|
306
|
+
assetsToBuy,
|
|
307
|
+
bytes("")
|
|
308
|
+
);
|
|
309
|
+
balances[order.seller] += purchaseCost - makerFee;
|
|
310
|
+
emit OrderFilled(
|
|
311
|
+
asset,
|
|
312
|
+
order.assetId,
|
|
313
|
+
orderId,
|
|
314
|
+
order.seller,
|
|
315
|
+
msg.sender,
|
|
316
|
+
assetsToBuy,
|
|
317
|
+
order.pricePerAsset,
|
|
318
|
+
makerFee,
|
|
319
|
+
takerFee
|
|
320
|
+
);
|
|
321
|
+
if (remainingAsset == 0) {
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (remainingAsset > 0) {
|
|
326
|
+
revert InsufficientEndAmount(assetAmount, assetAmount - remainingAsset);
|
|
327
|
+
}
|
|
328
|
+
if (remainingEth > 0) {
|
|
329
|
+
payable(msg.sender).sendValue(remainingEth);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/// @inheritdoc IOrderbookDex
|
|
334
|
+
function cancelSellOrder(address asset, uint256 orderId) external virtual nonReentrant {
|
|
335
|
+
uint256 creationFeePaid = orders[asset][orderId].creationFeePaid;
|
|
336
|
+
_cancelSellOrder(asset, orderId);
|
|
337
|
+
collectedFees -= creationFeePaid;
|
|
338
|
+
payable(msg.sender).sendValue(creationFeePaid);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/// @inheritdoc IOrderbookDex
|
|
342
|
+
function cancelBatchSellOrder(
|
|
343
|
+
address asset,
|
|
344
|
+
uint256[] memory orderIds
|
|
345
|
+
) external virtual nonReentrant {
|
|
346
|
+
uint256 totalCreationFeePaid;
|
|
347
|
+
for (uint256 i; i < orderIds.length; ) {
|
|
348
|
+
totalCreationFeePaid += orders[asset][orderIds.unsafeMemoryAccess(i)].creationFeePaid;
|
|
349
|
+
_cancelSellOrder(asset, orderIds.unsafeMemoryAccess(i));
|
|
350
|
+
unchecked {
|
|
351
|
+
++i;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
collectedFees -= totalCreationFeePaid;
|
|
355
|
+
payable(msg.sender).sendValue(totalCreationFeePaid);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/// @inheritdoc IOrderbookDex
|
|
359
|
+
function withdrawFees() external onlyOwner {
|
|
360
|
+
uint256 amount = collectedFees;
|
|
361
|
+
delete collectedFees;
|
|
362
|
+
emit FeesWithdrawn(owner(), amount);
|
|
363
|
+
payable(owner()).sendValue(amount);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/// @dev Returns true if this contract implements the interface defined by `interfaceId`. See EIP165.
|
|
367
|
+
function supportsInterface(
|
|
368
|
+
bytes4 interfaceId
|
|
369
|
+
) public view virtual override(ERC1155HolderUpgradeable, IERC165) returns (bool) {
|
|
370
|
+
return
|
|
371
|
+
interfaceId == type(IOrderbookDex).interfaceId || super.supportsInterface(interfaceId);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function _authorizeUpgrade(address) internal override onlyOwner {}
|
|
375
|
+
|
|
376
|
+
function _createSellOrder(
|
|
377
|
+
address asset,
|
|
378
|
+
uint256 assetId,
|
|
379
|
+
uint256 assetAmount,
|
|
380
|
+
uint256 pricePerAsset
|
|
381
|
+
) internal virtual returns (uint256) {
|
|
382
|
+
if (assetAmount == 0 || pricePerAsset == 0) {
|
|
383
|
+
revert InvalidInput(0);
|
|
384
|
+
}
|
|
385
|
+
IInverseProjected1155(asset).safeTransferFrom(
|
|
386
|
+
msg.sender,
|
|
387
|
+
address(this),
|
|
388
|
+
assetId,
|
|
389
|
+
assetAmount,
|
|
390
|
+
bytes("")
|
|
391
|
+
);
|
|
392
|
+
(uint256 makerFee, uint256 takerFee) = getAssetAppliedFees(asset);
|
|
393
|
+
Order memory newOrder = Order({
|
|
394
|
+
assetId: assetId,
|
|
395
|
+
assetAmount: assetAmount,
|
|
396
|
+
pricePerAsset: pricePerAsset,
|
|
397
|
+
seller: payable(msg.sender),
|
|
398
|
+
makerFee: makerFee,
|
|
399
|
+
takerFee: takerFee,
|
|
400
|
+
creationFeePaid: orderCreationFee
|
|
401
|
+
});
|
|
402
|
+
uint256 orderId = currentOrderId[asset];
|
|
403
|
+
orders[asset][orderId] = newOrder;
|
|
404
|
+
emit OrderCreated(
|
|
405
|
+
asset,
|
|
406
|
+
assetId,
|
|
407
|
+
orderId,
|
|
408
|
+
msg.sender,
|
|
409
|
+
assetAmount,
|
|
410
|
+
pricePerAsset,
|
|
411
|
+
makerFee,
|
|
412
|
+
takerFee
|
|
413
|
+
);
|
|
414
|
+
++currentOrderId[asset];
|
|
415
|
+
return orderId;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function _cancelSellOrder(address asset, uint256 orderId) internal virtual {
|
|
419
|
+
Order storage order = orders[asset][orderId];
|
|
420
|
+
if (msg.sender != order.seller) {
|
|
421
|
+
revert Unauthorized(msg.sender);
|
|
422
|
+
}
|
|
423
|
+
uint256 assetAmount = order.assetAmount;
|
|
424
|
+
delete order.assetAmount;
|
|
425
|
+
delete order.creationFeePaid;
|
|
426
|
+
IInverseProjected1155(asset).safeTransferFrom(
|
|
427
|
+
address(this),
|
|
428
|
+
msg.sender,
|
|
429
|
+
order.assetId,
|
|
430
|
+
assetAmount,
|
|
431
|
+
bytes("")
|
|
432
|
+
);
|
|
433
|
+
emit OrderCancelled(asset, order.assetId, orderId);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Forked from OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC4906.sol)
|
|
3
|
+
|
|
4
|
+
pragma solidity ^0.8.13;
|
|
5
|
+
|
|
6
|
+
/// @title Agnostic Metadata Update Extension
|
|
7
|
+
interface IERC4906Agnostic {
|
|
8
|
+
/// @dev This event emits when the metadata of a token is changed.
|
|
9
|
+
/// So that the third-party platforms such as NFT market could
|
|
10
|
+
/// timely update the images and related attributes of the token.
|
|
11
|
+
event MetadataUpdate(uint256 _tokenId);
|
|
12
|
+
|
|
13
|
+
/// @dev This event emits when the metadata of a range of tokens is changed.
|
|
14
|
+
/// So that the third-party platforms such as NFT market could
|
|
15
|
+
/// timely update the images and related attributes of the tokens.
|
|
16
|
+
event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
|
|
17
|
+
}
|