function testBuyOrderRaceCondition() public nftDivided {
ERC20Mock erc20Mock = ERC20Mock(tokenDivider.getErc20InfoFromNft(address(erc721Mock)).erc20Address);
vm.startPrank(USER);
erc20Mock.approve(address(tokenDivider), AMOUNT);
tokenDivider.sellErc20(address(erc721Mock), 1e18, AMOUNT / 4);
tokenDivider.sellErc20(address(erc721Mock), 2e18, AMOUNT / 4);
tokenDivider.sellErc20(address(erc721Mock), 3e18, AMOUNT / 4);
tokenDivider.sellErc20(address(erc721Mock), 4e18, AMOUNT / 4);
vm.stopPrank();
uint256 user2InitialBalance = USER2.balance;
uint256 user3InitialBalance = STARTING_USER_BALANCE;
uint256 sellerInitialBalance = USER.balance;
vm.startPrank(USER2);
vm.deal(USER2, STARTING_USER_BALANCE);
tokenDivider.buyOrder{value: 2e18}(1, USER);
vm.stopPrank();
address USER3 = makeAddr("user3");
vm.startPrank(USER3);
vm.deal(USER3, STARTING_USER_BALANCE);
tokenDivider.buyOrder{value: 4e18}(1, USER);
vm.stopPrank();
uint256 user2FinalBalance = USER2.balance;
uint256 user3FinalBalance = USER3.balance;
uint256 sellerFinalBalance = USER.balance;
uint256 user3Tokens = tokenDivider.getBalanceOf(USER3, address(erc20Mock));
uint256 user2Tokens = tokenDivider.getBalanceOf(USER2, address(erc20Mock));
assertEq(user2Tokens, AMOUNT / 4, "USER2 did not receive tokens for Order 1");
assertEq(user3Tokens, AMOUNT / 4, "USER3 did not receive tokens for Order 3");
assertEq( user2InitialBalance - user2FinalBalance, 2e18);
assertEq(user3InitialBalance - user3FinalBalance, 4e18);
}
+ uint256 public nextOrderId;
mapping(address user => mapping(address erc20Address => uint256 amount)) balances;
mapping(address nft => ERC20Info) nftToErc20Info;
- mapping(address user => SellOrder[] orders) s_userToSellOrders;
+ mapping(uint256 orderId => SellOrder) public sellOrders;
+ mapping(uint256 orderId => address seller) public orderOwners;
mapping(address erc20 => address nft) erc20ToNft;
mapping(address erc20 => uint256 totalErc20Minted) erc20ToMintedAmount;
.
.
.
function sellErc20(address nftPegged, uint256 price,uint256 amount) external {
if(nftPegged == address(0)) {
revert TokenDivider__NftAddressIsZero();
}
if( amount == 0) {
revert TokenDivider__AmountCantBeZero();
}
ERC20Info memory tokenInfo = nftToErc20Info[nftPegged];
if(balances[msg.sender][tokenInfo.erc20Address] < amount) {
revert TokenDivider__InsuficientBalance();
}
balances[msg.sender][tokenInfo.erc20Address] -= amount;
- s_userToSellOrders[msg.sender].push(
- SellOrder({
- seller: msg.sender,
- erc20Address: tokenInfo.erc20Address,
- price: price,
- amount: amount
- })
- );
+ uint256 orderId = nextOrderId++;
+ sellOrders[orderId] = SellOrder({
+ seller: msg.sender,
+ erc20Address: tokenInfo.erc20Address,
+ price: price,
+ amount: amount
+ });
+ orderOwners[orderId] = msg.sender;
emit OrderPublished(amount,msg.sender, nftPegged);
IERC20(tokenInfo.erc20Address).transferFrom(msg.sender,address(this), amount);
}
- function buyOrder(uint256 orderIndex, address seller) external payable {
+ function buyOrder(uint256 orderId, address seller) external payable {
if(seller == address(0)) {
revert TokenDivider__InvalidSeller();
}
- SellOrder memory order = s_userToSellOrders[seller][orderIndex];
+ SellOrder memory order = sellOrders[orderId];
if(msg.value < order.price) {
revert TokenDivider__IncorrectEtherAmount();
}
uint256 fee = order.price / 100;
uint256 sellerFee = fee / 2;
if(msg.value < order.price + sellerFee) {
revert TokenDivider__InsuficientEtherForFees();
}
balances[msg.sender][order.erc20Address] += order.amount;
- s_userToSellOrders[seller][orderIndex] = s_userToSellOrders[seller][s_userToSellOrders[seller].length - 1];
- s_userToSellOrders[seller].pop();
+ delete sellOrders[orderId];
+ delete orderOwners[orderId];
emit OrderSelled(msg.sender, order.price);
// Transfer The Ether
(bool success, ) = payable(order.seller).call{value: (order.price - sellerFee)}("");
if(!success) {
revert TokenDivider__TransferFailed();
}
(bool taxSuccess, ) = payable(owner()).call{value: fee}("");
if(!taxSuccess) {
revert TokenDivider__TransferFailed();
}
IERC20(order.erc20Address).transfer(msg.sender, order.amount);
}