Rook
Search…
⌃K

Coordinator WS API

The Rook Protocol WebSocket API is used by Keepers to communicate with the Coordinator in real-time.
Under Construction This documentation is being actively worked on. Use at your own risk, or consult Rook developers directly with questions.

Introduction

WS endpoint

This API is API key protected and currently only provided to Keepers. Contact us to gain access.

API Key

Each request requires a valid API key which must be provided in the message payload. Contact us to acquire an API key.
{
'apiKey': '1243-567-890',
}

ChainId

Please use chainId 1 when communicating with the Coordinator on Ethereum mainnet. Do note that many of these examples may contain a different chainId because they were copied from a test environment.

WebSocket API

Message Types

The message type is specified in the JSON payload using the type field.

Subscribe

Used to subscribe to a channel to receive messages indicating when a new auction of a particular type has started.
Example Request:
{
"apiKey": "1243-567-890",
"channel": "limitOrder",
"requestId": 38520892865339,
"type": "subscribe"
}
Example Success Response:
{
"type": "subscribe",
"channel": "limitOrder",
"message": "Successfully subscribed to channel",
}

Bid

Used to place a bid on one or more orders. See placing bids for how to structure the payment field. See the example below for the limit order bid format. The auctionId is the hiding book order hash.
Example Request:
{
"bids":[
{
"auctionId":"0x5a23000386c7b763f77e947bd64571afbde64d848d207fa981c32587eafaec00",
"chainId":31337,
"channel":"limitOrder",
"protocol":"zrxV4RFQ",
"takerTokenTargetFillAmount":222
}
],
"payment":{
"stakeAddress":"0xE91f7bB6C34315d50a739bb94bbDEaC916E17233",
"stakeSpent":66159044473427503,
"stakeNonce":1,
"channelNonce":0,
"previousCommitmentHash":"0x0000000000000000000000000000000000000000",
"stakeAddressSignature":"0x29b2daf2484ad20bf31d2f4e8da5656ccb14f4f0cfac1d9742d101bec79144a95ad7cbc3c8d039823dd38a2ca384dfc762041743e908ec514fccae4b3e293d0e1b",
"identityAddress":"0x6B2902918a10984875f7aC6Fd3eae87d1cDBb4A6",
"takerAddress":"0x26CbC5650944a0D0454291DCC105646c52c70b00"
},
"requestId":29234464271939,
"type":"bid",
"apiKey": "1243-567-890",
}

Bidding with bid protection

We recommend that all keepers use the optional bidProtection property which allows you to specify exactly how much you're bidding for this individual trade. This way you don't have to rely on your math being correct when you calculated the new stakeSpent quantity. This is especially helpful when you're running a bot with multiple threads or processes, and also when you're running multiple bots using the same exact KSA. For those who are running multiple bots, we do recommend that keepers use separate KSAs, but in case you are sharing a KSA with more than one bot bidProtection is critical. Note: In a future update we will make bidProtection required as opposed to optional.
Example Request:
{
'bids': [
{
'channel': 'limitOrder',
'protocol': 'zrxV4RFQ',
'chainId': 1,
'auctionId': '0xcfdf615e14fbb77a3f6d0c5364bde873c2db2659560257441376414fe145e937',
'takerTokenTargetFillAmount': 6011820921771210752
}
],
'payment': {
'identityAddress': '0x211B6a1137BF539B2750e02b9E525CF5757A35aE',
'stakeAddress': '0x211B6a1137BF539B2750e02b9E525CF5757A35aE',
'takerAddress': '0x3D71d79C224998E608d03C5Ec9B405E7a38505F0',
'bidProtection': 113973492809359184,
'stakeSpent': 3809217775909198431191,
'stakeNonce': 7458,
'channelNonce': 0,
'previousCommitmentHash': '0x14a9ac553f3e816f04e0aaa1efb72c999e49d3be7068d6beef19c6fad0466230',
'stakeAddressSignature': '0x085f5f15e123ad2cc425d7103dd0681eed64cc0719b1f6f8952fdc6e63df382d2570876b780eaf5421029f0d3e063822155984225372700f0f35c047280d59011b'
},
'apiKey': '1243-567-890',
'requestId': 44747280850034,
'type': 'bid',
}
In this example, notice that the keeper specified they intend to bid exactly 113973492809359184 ROOK, and that the bid will increase their stakeSpent to 3809217775909198431191. When the coordinator processes this bid, it will determine how much it preceives that the keeper has bid based on the payment channel information combined with this bid. In the event this keeper has either a multithreading bug or a logic error, the coordinator will detect this mismatch and reject this bid on behalf the keeper citing an error. This error will be communicated back to the keeper as a clear indication that the keeper bidding logic is flawed.
Here is an example of how a keeper could have a logic flaw in this way. Let's say Ethereum bock 1000 is mined in and suddenly 3 arbitrage opportunities appear. The keeper decides to bid 1 ROOK on each of those 3 opportunities. They will be submitting three separate bids and each should be incrementing the stakeSpent by 1 ROOK each. It's common for keepers to submit each bid in its own thread or process. As long as the keeper sets each bid's bidProtection, the keeper can rest assured that if they have a bug as a result of the multithreading or multiprocessing, any bids containing a miscalculated stakeSpent will be rejected.
Refund
Used to issue any outstanding refunds. Must provide a stake address, channel nonce, the latest stake nonce, and a signature from the stake address in the payload. See refunds:
Example Request:
{
"payload":{
"channelNonce": 0,
"stakeAddress": "0x407EA0314F43E4cbBab28Bfd47f391e8968c9643",
"stakeAddressSignature": "0xf293586e495027b5893c00000016040b3041f4aef9d5ae000051fe2dac7c73aa64cc127bd156beb2f9b4d69c80ab3dd2cfc494b31cbb69b83844a78dc85859711c",
"stakeNonce": 1
},
"requestId": 33684210724642,
"type": "refund",
"apiKey": "1243-567-890",
}
Example Response:
{
"type": "refund",
"requestId": "33684210724642",
"payload": {
"refundCommitment": {
"stakeAddress": "0x407ea0314f43e4cbbab28bfd47f391e8968c9643",
"stakeSpent": 0,
"stakeNonce": 2,
"channelNonce": 0,
"previousCommitmentHash": "0x38fc7f3df34366e402d8b665cba162cde3508bfbb333fa77450ce1529a539ecb",
"stakeAddressSignature": null,
"coordinatorSignature": "0x5b8df00000e3a0539a042ffcd26d000002a8912e2d568a4a862126acc122aa342f12c56f4d7b01117cf6ac393464b3c4f52998c8f2b0b9725d0282c7f1e2f76d1c",
"stakeAddressInstantWithdrawalSignature": null,
"coordinatorInstantWithdrawalSignature": null
}
}
}

Greenlit auction

Used by the Coordinator to deliver information about who has been greenlit for an auction. Expect to see this message when an auction concludes and a keeper has been greenlit.
Example Greenlight Message:
{
"channel":"limitOrder",
"type":"auction",
"requestId":82091778811918,
"payload":{
"auctionIdList":[
"0xab29508897ee5a2714fa58705bd834390e8cf9979b5202680c05ca2e4459a05c"
],
"identityAddress":"0x6B2902918a10984875f7aC6Fd3eae87d1cDBb4A6",
"stakeAddress":"0xE91f7bB6C34315d50a739bb94bbDEaC916E17233",
"takerAddress":"0x26CbC5650944a0D0454291DCC105646c52c70b00",
"greenlit":1,
"bidState":"locked",
"actionDeadlineBlockNumber": 15077781
}
}
When parsing this message to determine relevance to you, check auctionIdList and the addresses to ensure that it's relevant to you. Also, check the actionDeadlineBlockNumber for when this order fill must be mined into the blockchain by to avoid taking a reputation hit. If you're using an MEV relay like Flashbots, you can lean on actionDeadlineBlockNumber to determine which blocks to broadcast your trade for. For example, say the current block is 1000 and actionDeadlineBlockNumber = 1003 . You should broadcast your transaction to blocks 1001, 1002, and 1003. Broadcasting to block 1004 is dangerous because if it fills, it will fill after the deadline has ended which will result in a reputation hit for bad behavior. And if you're broadcasting your transaction to mempool, you can include the deadline block number in your transaction and have it gracefully fail in the event the transaction is not mined in on time.

Keep Alive

Used to keep the connection alive.
Example Request:
{
"type": "keepAlive",
"requestId":82091778811918,
"apiKey": "1243-567-890",
}

Get Info

Used to retrieve relevant contract addresses.
Example Request:
{
"type": "info",
"requestId": 33684210724642,
"apiKey": "1243-567-890",
}

Enumerated Types

Messages to and from the Coordinator may contain enumerated types. Please refer to this section to determine what each value corresponds with. For example, let's say a Keeper wants to place a bid to fill a limit order on zrxV4RFQ. They would use bid, zrxV4RFQ, and limitOrder when constructing their message payload.

Protocol

class Protocol(Enum):
zrxV4RFQ = "zrxV4RFQ"
keeperDAOSwapV0 = "swapV0"

BidState

class BidState(Enum):
awaitingBids = 1
locked = 2
softCanceled = 3

Channel

class Channel(Enum):
limitOrder = "limitOrder"
kCompound = "kCompound"
kAave = "kAave"
bProtocol = "bProtocol"

MessageType

class MessageType(Enum):
subscribe = "subscribe"
keepAlive = "keepAlive"
bid = "bid"
auction = "auction"
info = "info"
refund = "refund"

GreenlightResult

class GreenlightResult(Enum):
lose = 0
win = 1

Payment Channels Staking Contract

Structs

StakeCommitment

struct StakeCommitment {
address stakeAddress;
uint256 stakeSpent;
uint256 stakeNonce;
uint256 channelNonce;
bytes data;
bytes stakeAddressSignature;
bytes coordinatorSignature;
}
See Transacting Using the Payment Channel for an explanation of each field.

Methods

getStakerState

function getStakerState(address _stakeAddress) public view returns (
uint256 _stakedAmount,
uint256 _stakeNonce,
uint256 _stakeSpent,
uint256 _channelNonce,
uint256 _withdrawalTimelock
);
Used to retrieve the most recent settled state for a payment channel. _withdrawalTimelock will be non-zero if the payment channel is currently in the timelocked withdrawal process and will indicate the timestamp of the timelock's expiration.

stakeCommitmentHash

function stakeCommitmentHash(
address _stakeAddress,
uint256 _stakeSpent,
uint256 _stakeNonce,
uint256 _channelNonce,
bytes memory _data
) public pure returns (bytes32);
Used to calculate the hash of a payment channel commitment for signing.

getCurrentWithdrawalTimelock

function getCurrentWithdrawalTimelock(
address _stakeAddress
) public view returns (uint256);
Used to retrieve the current withdrawal timelock for the payment channel of the provided stake address. It will be zero if there is no timelock and will return the timestamp of the timelock's expiry otherwise.

stake

function stake(uint256 _amount) public;
Used to stake rook, adding rook to the active payment channel of msg.sender if there is one, and creating a new payment channel otherwise. Not that additional rook cannot be staked for a payment channel that is currently in a withdrawal timelock.

initiateTimelockedWithdrawal

function initiateTimelockedWithdrawal(
StakeCommitment memory _commitment
) external;
Used to initiate the timelocked withdrawal process.

executeTimelockedWithdrawal

function executeTimelockedWithdrawal(
address _stakeAddress
) public;
Used to complete the timelocked withdrawal process and withdraw the remaining rook in a payment channel.

executeInstantWithdrawal

function executeInstantWithdrawal(
StakeCommitment memory _commitment
) external;
Used to complete the instant withdrawal process and withdraw the remaining rook in a payment channel.