Skip to content

Commit

Permalink
[BatchExchangeViewer] Filter certain token symbol
Browse files Browse the repository at this point in the history
  • Loading branch information
fleupold committed Feb 12, 2020
1 parent 43a7c34 commit 110620e
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 21 deletions.
98 changes: 81 additions & 17 deletions contracts/BatchExchangeViewer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ contract BatchExchangeViewer {
using BytesLib for bytes;

uint8 public constant AUCTION_ELEMENT_WIDTH = 112;
uint16[] public ALL_TOKEN_FILTER;

BatchExchange batchExchange;

Expand All @@ -16,54 +17,77 @@ contract BatchExchangeViewer {
}

/** @dev Queries the orderbook for the auction that is still accepting orders
* @param tokenFilter all returned order will have buy *and* sell token from this list (leave empty for "no filter")
* @return encoded bytes representing orders
*/
function getOpenOrderBook() public view returns (bytes memory) {
(bytes memory elements, , ) = getOpenOrderBookPaginated(address(0), 0, uint16(-1));
function getOpenOrderBook(address[] memory tokenFilter) public view returns (bytes memory) {
(bytes memory elements, , ) = getOpenOrderBookPaginated(tokenFilter, address(0), 0, uint16(-1));
return elements;
}

/** @dev Queries a page of the orderbook for the auction that is still accepting orders
* @param tokenFilter all returned order will have buy *and* sell token from this list (leave empty for "no filter")
* @param previousPageUser address taken from nextPageUser return value from last page (address(0) for first page)
* @param previousPageUserOffset offset taken nextPageUserOffset return value from last page (0 for first page)
* @param pageSize count of elements to be returned per page (same value is used for subqueries on the exchange)
* @return encoded bytes representing orders and page information for next page
*/
function getOpenOrderBookPaginated(address previousPageUser, uint16 previousPageUserOffset, uint16 pageSize)
public
view
returns (bytes memory elements, address nextPageUser, uint16 nextPageUserOffset)
{
function getOpenOrderBookPaginated(
address[] memory tokenFilter,
address previousPageUser,
uint16 previousPageUserOffset,
uint16 pageSize
) public view returns (bytes memory elements, address nextPageUser, uint16 nextPageUserOffset) {
uint32 batch = batchExchange.getCurrentBatchId();
return getEncodedOrdersPaginated(batch, batch, previousPageUser, previousPageUserOffset, pageSize);
return
getEncodedOrdersPaginated(
batch,
batch,
getTokenIdsFromAdresses(tokenFilter),
previousPageUser,
previousPageUserOffset,
pageSize
);
}

/** @dev Queries the orderbook for the auction that is currently being solved
* @param tokenFilter all returned order will have buy *and* sell token from this list (leave empty for "no filter")
* @return encoded bytes representing orders
*/
function getFinalizedOrderBook() public view returns (bytes memory) {
(bytes memory elements, , ) = getFinalizedOrderBookPaginated(address(0), 0, uint16(-1));
function getFinalizedOrderBook(address[] memory tokenFilter) public view returns (bytes memory) {
(bytes memory elements, , ) = getFinalizedOrderBookPaginated(tokenFilter, address(0), 0, uint16(-1));
return elements;
}

/** @dev Queries a page of the orderbook for the auction that is currently being solved
* @param tokenFilter all returned order will have buy *and* sell token from this list (leave empty for "no filter")
* @param previousPageUser address taken from nextPageUser return value from last page (address(0) for first page)
* @param previousPageUserOffset offset taken nextPageUserOffset return value from last page (0 for first page)
* @param pageSize count of elements to be returned per page (same value is used for subqueries on the exchange)
* @return encoded bytes representing orders and page information for next page
*/
function getFinalizedOrderBookPaginated(address previousPageUser, uint16 previousPageUserOffset, uint16 pageSize)
public
view
returns (bytes memory elements, address nextPageUser, uint16 nextPageUserOffset)
{
function getFinalizedOrderBookPaginated(
address[] memory tokenFilter,
address previousPageUser,
uint16 previousPageUserOffset,
uint16 pageSize
) public view returns (bytes memory elements, address nextPageUser, uint16 nextPageUserOffset) {
uint32 batch = batchExchange.getCurrentBatchId();
return getEncodedOrdersPaginated(batch - 1, batch - 1, previousPageUser, previousPageUserOffset, pageSize);
return
getEncodedOrdersPaginated(
batch - 1,
batch - 1,
getTokenIdsFromAdresses(tokenFilter),
previousPageUser,
previousPageUserOffset,
pageSize
);
}

/** @dev Queries a page in the list of all orders
* @param maxValidFrom all returned orders will have a validFrom <= this value (they were placed at or before that batch)
* @param minValidUntil all returned orders will have a validUntil >= this value (validity ends at or after that batch)
* @param tokenFilter all returned order will have buy *and* sell token from this list (leave empty for "no filter")
* @param previousPageUser address taken from nextPageUser return value from last page (address(0) for first page)
* @param previousPageUserOffset offset taken nextPageUserOffset return value from last page (0 for first page)
* @param pageSize count of elements to be returned per page (same value is used for subqueries on the exchange)
Expand All @@ -72,6 +96,7 @@ contract BatchExchangeViewer {
function getEncodedOrdersPaginated(
uint32 maxValidFrom,
uint32 minValidUntil,
uint16[] memory tokenFilter,
address previousPageUser,
uint16 previousPageUserOffset,
uint16 pageSize
Expand All @@ -84,7 +109,11 @@ contract BatchExchangeViewer {
hasNextPage = unfiltered.length / AUCTION_ELEMENT_WIDTH == pageSize;
for (uint16 index = 0; index < unfiltered.length / AUCTION_ELEMENT_WIDTH; index++) {
bytes memory element = unfiltered.slice(index * AUCTION_ELEMENT_WIDTH, AUCTION_ELEMENT_WIDTH);
if (maxValidFrom >= getValidFrom(element) && minValidUntil <= getValidUntil(element)) {
if (
maxValidFrom >= getValidFrom(element) &&
minValidUntil <= getValidUntil(element) &&
matchesTokenFilter(getBuyToken(element), getSellToken(element), tokenFilter)
) {
elements = elements.concat(element);
}
// Update pagination info
Expand All @@ -104,11 +133,38 @@ contract BatchExchangeViewer {
return (elements, nextPageUser, nextPageUserOffset);
}

function matchesTokenFilter(uint16 buyToken, uint16 sellToken, uint16[] memory filter) public pure returns (bool) {
// An empty filter is interpreted as "select all"
if (filter.length == 0) {
return true;
}
(bool foundBuyToken, bool foundSellToken) = (false, false);
for (uint256 index = 0; index < filter.length; index++) {
if (filter[index] == buyToken) {
foundBuyToken = true;
}
if (filter[index] == sellToken) {
foundSellToken = true;
}
}
return foundBuyToken && foundSellToken;
}

function getUser(bytes memory element) public pure returns (address) {
bytes memory slice = element.slice(0, 20);
return slice.toAddress(0);
}

function getBuyToken(bytes memory element) public pure returns (uint16) {
bytes memory slice = element.slice(52, 2);
return slice.toUint16(0);
}

function getSellToken(bytes memory element) public pure returns (uint16) {
bytes memory slice = element.slice(54, 2);
return slice.toUint16(0);
}

function getValidFrom(bytes memory element) public pure returns (uint32) {
bytes memory slice = element.slice(56, 4);
return slice.toUint32(0);
Expand All @@ -118,4 +174,12 @@ contract BatchExchangeViewer {
bytes memory slice = element.slice(60, 4);
return slice.toUint32(0);
}

function getTokenIdsFromAdresses(address[] memory tokenIds) public view returns (uint16[] memory) {
uint16[] memory result = new uint16[](tokenIds.length);
for (uint256 index = 0; index < tokenIds.length; index++) {
result[index] = batchExchange.tokenAddressToIdMap(tokenIds[index]);
}
return result;
}
}
56 changes: 52 additions & 4 deletions test/stablex/batch_exchange_viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ contract("BatchExchangeViewer", accounts => {
)

const viewer = await BatchExchangeViewer.new(batchExchange.address)
const result = decodeAuctionElements(await viewer.getOpenOrderBook())
const result = decodeAuctionElements(await viewer.getOpenOrderBook([]))
assert.equal(result.filter(e => e.validFrom == batchId).length, 10)
})
it("can be queried with pagination", async () => {
Expand All @@ -64,11 +64,33 @@ contract("BatchExchangeViewer", accounts => {
)

const viewer = await BatchExchangeViewer.new(batchExchange.address)
const result = await viewer.getOpenOrderBookPaginated(zero_address, 0, 5)
const result = await viewer.getOpenOrderBookPaginated([], zero_address, 0, 5)
assert.equal(decodeAuctionElements(result.elements).filter(e => e.validFrom == batchId).length, 5)
assert.equal(result.nextPageUser, accounts[0])
assert.equal(result.nextPageUserOffset, 15)
})
it("can filter a token pair", async () => {
const batchId = await batchExchange.getCurrentBatchId()
await batchExchange.placeValidFromOrders(
Array(3).fill(0), //buyToken
Array(3).fill(1), //sellToken
Array(3).fill(batchId), //validFrom
Array(3).fill(batchId), //validTo
Array(3).fill(0), //buyAmounts
Array(3).fill(0) //sellAmounts
)
await batchExchange.placeValidFromOrders(
Array(5).fill(1), //buyToken
Array(5).fill(2), //sellToken
Array(5).fill(batchId), //validFrom
Array(5).fill(batchId), //validTo
Array(5).fill(0), //buyAmounts
Array(5).fill(0) //sellAmounts
)
const viewer = await BatchExchangeViewer.new(batchExchange.address)
const result = decodeAuctionElements(await viewer.getOpenOrderBook([token_1.address, token_2.address]))
assert.equal(result.filter(e => e.validFrom == batchId).length, 5)
})
})

describe("getFinalizedOrderBook", () => {
Expand All @@ -95,7 +117,7 @@ contract("BatchExchangeViewer", accounts => {
await closeAuction(batchExchange)

const viewer = await BatchExchangeViewer.new(batchExchange.address)
const result = decodeAuctionElements(await viewer.getFinalizedOrderBook())
const result = decodeAuctionElements(await viewer.getFinalizedOrderBook([]))
assert.equal(result.filter(e => e.validFrom == batchId).length, 10)
})
it("can be queried with pagination", async () => {
Expand All @@ -121,10 +143,36 @@ contract("BatchExchangeViewer", accounts => {
await closeAuction(batchExchange)

const viewer = await BatchExchangeViewer.new(batchExchange.address)
const result = await viewer.getFinalizedOrderBookPaginated(zero_address, 0, 5)
const result = await viewer.getFinalizedOrderBookPaginated([], zero_address, 0, 5)
assert.equal(decodeAuctionElements(result.elements).filter(e => e.validFrom == batchId).length, 5)
assert.equal(result.nextPageUser, accounts[0])
assert.equal(result.nextPageUserOffset, 15)
})
it("can filter a token pair", async () => {
const batchId = await batchExchange.getCurrentBatchId()
await batchExchange.placeValidFromOrders(
Array(3).fill(0), //buyToken
Array(3).fill(1), //sellToken
Array(3).fill(batchId), //validFrom
Array(3).fill(batchId), //validTo
Array(3).fill(0), //buyAmounts
Array(3).fill(0) //sellAmounts
)
await batchExchange.placeValidFromOrders(
Array(5).fill(1), //buyToken
Array(5).fill(2), //sellToken
Array(5).fill(batchId), //validFrom
Array(5).fill(batchId), //validTo
Array(5).fill(0), //buyAmounts
Array(5).fill(0) //sellAmounts
)

// finalize order book
await closeAuction(batchExchange)

const viewer = await BatchExchangeViewer.new(batchExchange.address)
const result = decodeAuctionElements(await viewer.getFinalizedOrderBook([token_1.address, token_2.address]))
assert.equal(result.filter(e => e.validFrom == batchId).length, 5)
})
})
})

0 comments on commit 110620e

Please sign in to comment.