Skip to content

Commit

Permalink
[Viewer] Fetch users once per page (#647)
Browse files Browse the repository at this point in the history
We currently fetch the next user "on demand" whenever we exhaust the order for the current one. #641 noticed that the act of getting a single user is querying the `size()` of the allUser struct und thus actually iterates the list of all users in the system. 

Consequently, getting all users is just twice as expensive as getting a single user (once for checking the size and once for getting them) mod the memory of storing them.

For a single unfiltered page we can have at most `pageSize` users (a user is only in the system if they have at least on order).

This PR therefore replaces the "fetch n times one user when needed" logic with "fetch once n users at the beginning" and then read the next user from that list.

### Test Plan

No logic is changed (unit tests keep running). Notice, that now we can query the open orderbook with a page size of 1000 on a standard infura node:

`npx truffle console --network mainnet`

```js
let instance = await BatchExchangeViewer.at("0xd7a78A333BAe9AAdDD5ebE41C209Fb5226eA155B")
let page = {nextPageUser: "0x0000000000000000000000000000000000000000", nextPageUserOffset:0}
page = await instance.contract.methods.getOpenOrderBookPaginated([], page.nextPageUser, page.nextPageUserOffset, 1000).call()
// ... continue last call until nextPage = false
```
  • Loading branch information
fleupold authored Apr 9, 2020
1 parent 4511897 commit 8e82c74
Showing 1 changed file with 17 additions and 11 deletions.
28 changes: 17 additions & 11 deletions contracts/BatchExchangeViewer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ contract BatchExchangeViewer {
using SafeMath for uint256;

uint8 public constant AUCTION_ELEMENT_WIDTH = 112;
uint8 public constant ADDRESS_WIDTH = 20;
uint16 public constant LARGE_PAGE_SIZE = 5000;
// Can be used by external contracts to indicate no filter as it doesn't seem possible
// to create an empty memory array in solidity.
Expand Down Expand Up @@ -164,26 +165,28 @@ contract BatchExchangeViewer {
returns (bytes memory)
{
bytes memory elements = new bytes(pageSize * AUCTION_ELEMENT_WIDTH);
bytes memory users = batchExchange.getUsersPaginated(previousPageUser, uint16(pageSize));
uint16 currentOffset = previousPageUserOffset;
uint256 index = 0;
address currentUser = previousPageUser;
while (index < pageSize) {
uint256 orderIndex = 0;
uint256 userIndex = 0;
while (orderIndex < pageSize) {
bytes memory element = batchExchange.getEncodedUserOrdersPaginated(currentUser, currentOffset, 1);
if (element.length > 0) {
currentOffset += 1;
copyInPlace(element, elements, index * AUCTION_ELEMENT_WIDTH);
index += 1;
copyInPlace(element, elements, orderIndex * AUCTION_ELEMENT_WIDTH);
orderIndex += 1;
} else {
currentOffset = 0;
bytes memory nextUser = batchExchange.getUsersPaginated(currentUser, 1);
if (nextUser.length > 0) {
currentUser = nextUser.toAddress(0);
if (users.length >= (userIndex * ADDRESS_WIDTH) + ADDRESS_WIDTH) {
currentUser = getUser(users.slice(userIndex * ADDRESS_WIDTH, ADDRESS_WIDTH));
userIndex += 1;
} else {
break;
}
}
}
setLength(elements, index * AUCTION_ELEMENT_WIDTH);
setLength(elements, orderIndex * AUCTION_ELEMENT_WIDTH);
return elements;
}

Expand All @@ -205,17 +208,20 @@ contract BatchExchangeViewer {
}

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

function getSellTokenBalance(bytes memory element) public pure returns (uint256) {
bytes memory slice = element.slice(20, 52);
bytes memory slice = element.slice(ADDRESS_WIDTH, 52);
return slice.toUint(0);
}

function updateSellTokenBalance(bytes memory element, uint256 amount) public pure returns (bytes memory) {
return element.slice(0, 20).concat(abi.encodePacked(amount)).concat(element.slice(52, AUCTION_ELEMENT_WIDTH - 52));
return
element.slice(0, ADDRESS_WIDTH).concat(abi.encodePacked(amount)).concat(
element.slice(52, AUCTION_ELEMENT_WIDTH - 52)
);
}

function getBuyToken(bytes memory element) public pure returns (uint16) {
Expand Down

0 comments on commit 8e82c74

Please sign in to comment.