Skip to content

Commit

Permalink
Merge branch 'development'
Browse files Browse the repository at this point in the history
  • Loading branch information
lykhonis committed Jan 9, 2025
2 parents c4b8d31 + 9f3969e commit b1fb945
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 33 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
| lukso-testnet | LSP7Marketplace | 0x61c3dd3476a88de7a2bae7e2bc55889185faea1e |
| lukso-testnet | LSP8Listings | 0x1dabeddbc94847b4ca9027073e545f67917a84f6 |
| lukso-testnet | LSP8Offers | 0x84c0b26747a4f997ab1bfe5110a9579de2c0aeaf |
| lukso-testnet | LSP8Orders | 0xf5e502235f1b21413ba96e8afb92004b7ac5b1f2 |
| lukso-testnet | LSP8Orders | 0x3e81a952670a5df4062296d644dd5bf05cd475cb |
| lukso-testnet | LSP8Auctions | 0xb20f814e55720e477640717bfbc139cf663e1ab4 |
| lukso-testnet | LSP8Marketplace | 0x6364738eb197115aece87591dff51d554535d1f8 |
| lukso-testnet | Points | 0x3582f474F6E9FB087651b135d6224500A89e6f44 |
Expand Down
20 changes: 20 additions & 0 deletions artifacts/abi/marketplace/lsp8/LSP8Orders.json
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,11 @@
"name": "buyer",
"type": "address",
"internalType": "address"
},
{
"name": "tokenIds",
"type": "bytes32[]",
"internalType": "bytes32[]"
}
],
"outputs": [
Expand All @@ -202,6 +207,11 @@
"name": "buyer",
"type": "address",
"internalType": "address"
},
{
"name": "tokenIds",
"type": "bytes32[]",
"internalType": "bytes32[]"
}
],
"outputs": [
Expand Down Expand Up @@ -576,6 +586,11 @@
"name": "buyer",
"type": "address",
"internalType": "address"
},
{
"name": "tokenIds",
"type": "bytes32[]",
"internalType": "bytes32[]"
}
]
},
Expand Down Expand Up @@ -673,6 +688,11 @@
"name": "buyer",
"type": "address",
"internalType": "address"
},
{
"name": "tokenIds",
"type": "bytes32[]",
"internalType": "bytes32[]"
}
]
},
Expand Down
9 changes: 7 additions & 2 deletions src/marketplace/lsp8/ILSP8Orders.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,19 @@ interface ILSP8Orders {
/// confirms an order has been placed by a buyer
/// @param asset asset address
/// @param buyer buyer
/// @param tokenIds token ids or empty for any tokens. Must be sorted in ascending order.
/// @return true if the order is placed
function isPlacedOrderOf(address asset, address buyer) external view returns (bool);
function isPlacedOrderOf(address asset, address buyer, bytes32[] calldata tokenIds) external view returns (bool);

/// retrieves an order for an asset made by a buyer or reverts if not placed
/// @param asset asset address
/// @param buyer buyer
/// @param tokenIds token ids or empty for any tokens. Must be sorted in ascending order.
/// @return order order
function orderOf(address asset, address buyer) external view returns (LSP8Order memory);
function orderOf(address asset, address buyer, bytes32[] calldata tokenIds)
external
view
returns (LSP8Order memory);

/// confirms an order has been placed by a buyer
/// @param id order id
Expand Down
89 changes: 74 additions & 15 deletions src/marketplace/lsp8/LSP8Orders.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ import {Module} from "../common/Module.sol";
import {ILSP8Orders, LSP8Order} from "./ILSP8Orders.sol";

contract LSP8Orders is ILSP8Orders, Module {
error NotPlacedOf(address asset, address buyer);
error NotPlacedOf(address asset, address buyer, bytes32[] tokenIds);
error NotPlaced(uint256 id);
error InvalidTokenCount(uint16 tokenCount);
error AlreadyPlaced(address asset, address buyer);
error AlreadyPlaced(address asset, address buyer, bytes32[] tokenIds);
error InvalidAmount(uint256 expected, uint256 actual);
error Unpaid(address buyer, uint256 amount);
error InsufficientTokenCount(uint16 orderCount, uint16 offeredCount);
error UnfulfilledToken(bytes32 tokenId);

uint256 public totalOrders;
mapping(address asset => mapping(address buyer => uint256 id)) private _orderIds;
mapping(address asset => mapping(address buyer => uint256[] ids)) private _orderIds;
mapping(uint256 id => LSP8Order) private _orders;

constructor() {
Expand All @@ -26,15 +26,47 @@ contract LSP8Orders is ILSP8Orders, Module {
Module._initialize(newOwner_);
}

function isPlacedOrderOf(address asset, address buyer) public view override returns (bool) {
return isPlacedOrder(_orderIds[asset][buyer]);
function _computeTokensKey(bytes32[] memory tokenIds) private pure returns (bytes32) {
bytes32 key = 0;
uint256 length = tokenIds.length;
for (uint256 i = 0; i < length; i++) {
key = keccak256(abi.encodePacked(key, tokenIds[i]));
}
return key;
}

function orderOf(address asset, address buyer) external view override returns (LSP8Order memory) {
if (!isPlacedOrderOf(asset, buyer)) {
revert NotPlacedOf(asset, buyer);
function isPlacedOrderOf(address asset, address buyer, bytes32[] memory tokenIds)
public
view
override
returns (bool)
{
uint256[] memory orders = _orderIds[asset][buyer];
bytes32 tokensKey = _computeTokensKey(tokenIds);
for (uint256 i = 0; i < orders.length; i++) {
LSP8Order memory order = _orders[orders[i]];
if (order.tokenCount > 0 && _computeTokensKey(order.tokenIds) == tokensKey) {
return true;
}
}
return false;
}

function orderOf(address asset, address buyer, bytes32[] memory tokenIds)
external
view
override
returns (LSP8Order memory)
{
uint256[] memory orders = _orderIds[asset][buyer];
bytes32 tokensKey = _computeTokensKey(tokenIds);
for (uint256 i = 0; i < orders.length; i++) {
LSP8Order memory order = _orders[orders[i]];
if (order.tokenCount > 0 && _computeTokensKey(order.tokenIds) == tokensKey) {
return order;
}
}
return _orders[_orderIds[asset][buyer]];
revert NotPlacedOf(asset, buyer, tokenIds);
}

function isPlacedOrder(uint256 id) public view override returns (bool) {
Expand Down Expand Up @@ -66,8 +98,20 @@ contract LSP8Orders is ILSP8Orders, Module {
}

address buyer = msg.sender;
if (isPlacedOrderOf(asset, buyer)) {
revert AlreadyPlaced(asset, buyer);

// verify buyer orders do not contain overlapping tokens
{
uint256[] memory orders = _orderIds[asset][buyer];
for (uint256 i = 0; i < orders.length; i++) {
LSP8Order memory order = _orders[orders[i]];
for (uint256 j = 0; j < order.tokenIds.length; j++) {
for (uint256 k = 0; k < tokenIds.length; k++) {
if (order.tokenIds[j] == tokenIds[k]) {
revert AlreadyPlaced(asset, buyer, tokenIds);
}
}
}
}
}

uint256 totalValue = tokenPrice * tokenCount;
Expand All @@ -78,7 +122,7 @@ contract LSP8Orders is ILSP8Orders, Module {
totalOrders += 1;
uint256 orderId = totalOrders;

_orderIds[asset][buyer] = orderId;
_orderIds[asset][buyer].push(orderId);
_orders[orderId] = LSP8Order({
id: orderId,
asset: asset,
Expand All @@ -97,11 +141,26 @@ contract LSP8Orders is ILSP8Orders, Module {

LSP8Order memory order = getOrder(id);
if (order.buyer != buyer) {
revert NotPlacedOf(order.asset, buyer);
revert NotPlacedOf(order.asset, buyer, order.tokenIds);
}

delete _orders[id];
delete _orderIds[order.asset][buyer];

// delete the order id from the buyer's orders
{
uint256[] storage orders = _orderIds[order.asset][order.buyer];
uint256 index = orders.length;
for (uint256 i = 0; i < orders.length; i++) {
if (orders[i] == id) {
index = i;
break;
}
}
if (index < orders.length) {
orders[index] = orders[orders.length - 1];
orders.pop();
}
}

uint256 remainingValue = order.tokenPrice * order.tokenCount;
(bool success,) = buyer.call{value: remainingValue}("");
Expand All @@ -112,7 +171,7 @@ contract LSP8Orders is ILSP8Orders, Module {
emit Canceled(id, order.asset, buyer, order.tokenPrice, order.tokenIds, order.tokenCount);
}

function fill(uint256 id, address seller, bytes32[] calldata tokenIds)
function fill(uint256 id, address seller, bytes32[] memory tokenIds)
external
override
whenNotPaused
Expand Down
Loading

0 comments on commit b1fb945

Please sign in to comment.