Payment channels
Keepers transact with the Coordinator off-chain using payment channels.
In order to maximize throughput and minimize gas costs, Keepers transact with the Coordinator using off-chain payment channels. They will open this payment channel by staking ROOK into a contract and then pass signed commitments back and forth with the Coordinator. The payment channel state can be brought on-chain to settle in the event of a withdrawal, collection, or dispute. Keepers can submit bids for many auctions simultaneously. This gives Keepers the ability to perform all bids through a single KSA with high throughput rather than have to set up multiple KSAs to increase bidding throughput. Keepers may; however, choose to use mutliple KSAs to separate accounts or strategies. Having this payment channel off-chain, dramatically reduces gas costs for Keepers participating in the Rook Protocol.
Payment channels are opened and settled on-chain using the staking contract. To open a payment channel, Keepers should stake Rook into the staking contract using the
stake
function. They should do this from their KSA. function stake(uint256 _amount) public;
A Keeper that already has a payment channel open can call this function again to add more spendable Rook to their payment channel. The current amount of Rook staked by a KSA is stored in the public
stakedAmount
map on the contract.mapping (address => uint256) public stakedAmount;
Once the payment channel is open, the Keeper and Coordinator transact with this staked Rook by passing back and forth signed off-chain commitments. A commitment is defined in the staking contract as follows:
struct StakeCommitment {
address stakeAddress;
uint256 stakeSpent;
uint256 stakeNonce;
uint256 channelNonce;
bytes data;
bytes stakeAddressSignature;
bytes coordinatorSignature;
}
stakeAddress
is the KSA that the Keeper used to stake the Rook and that they plan to have sign commitments.stakeSpent
is how much of the Rook that the Keeper has staked has been spent. This will increase when the Keeper makes bids and decrease when the Keeper is issued refunds.stakeNonce
is an increasing number used to prevent replays of previously used commitments.channelNonce
is an increasing number that is incremented every time a Keeper closes their payment channel.data
will contain arbitrary information depending on the type of stake commitment.stakeAddressSignature
is an Ethereum signature from the KSA using the hash from the functionstakeCommitmentHash
as the message.coordinatorSignature
is an Ethereum signature from the Coordinator using the hash from the functionstakeCommitmentHash
as the message.
function stakeCommitmentHash(
address _stakeAddress,
uint256 _stakeSpent,
uint256 _stakeNonce,
uint256 _channelNonce,
bytes memory _data
) public pure returns (bytes32);

Bid and Refund Sequence Diagram
Keepers will spend Rook when they submit bids to the Coordinator. Here is an example of the bid JSON:
{
"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"
}
- The commitment information is under the
payment
field. The relevant fields arestakeAddress
,stakeSpent
,stakeNonce
,channelNonce
,previousCommitmentHash
, andstakeAddressSignature
. - Set the bid amount using
stakeSpent
. It is initialized to 0 and should be incremented relative to the latest value by an amount equal to the desired bid. So if the current value ofstakeSpent
is 100, to create a payment that bids 50,stakeSpent
should be set to 150.stakeSpent
can only be set as high the amount of Rook the keeper has staked. The Coordinator will reject any bids that attempt to bid higher than the staked amount. - The information you need to prepare this commitment is obtained from the keeperStakeChannel API. You can also track it yourself locally, but we recommend regularly syncing up with the Coordinator to ensure you don't get out of sync.
stakeNonce
must be incremented by 1 in each new commitment.channelNonce
should always match the current on-chain valuepreviousCommitmentHash
is used by the Coordinator as a validation mechanism. This is thedata
field in the commitment definition in the staking contract. When creating a new bid commitment,previousCommitmentHash
must be set to the output ofstakeCommitmentHash
on the preceding commitment. Important: If it is the first bid commitment and there is no preceding commitment,previousCommitmentHash
should be set to"0x0000000000000000000000000000000000000000"
as in the example above.stakeAddressSignature
should be an Ethereum signature from the KSA over the output ofstakeCommitmentHash
on the new commitment.
Upon receiving a bid commitment from a Keeper, the Coordinator will validate the bid commitment according to the rules above. If it is valid, the Coordinator will record the bid commitment and sign it. The latest commitment the Coordinator has validated and recorded can be checked using the
/keeperStakeChannel
endpoint.Keepers will accumulate refunds for any bids they did not win according to the Bid Outcome logic TODO Create this page. The refundable ROOK will accumulate for every auction they lose. ROOK bids will also be refunded to Keepers who win an auction but fail to fill the orders bid upon, although their reputation will suffer. The amount of refundable ROOK that has been accumulated can be checked using the
/keeperStakeChannel
endpoint.Request a refund by sending a refund request websocket message. In the payload, the KSA must include the latest
stakeAddress
, channelNonce
, and stakeNonce
for their payment channel, and a stakeAddressSignature
. The stakeAddressSignature
is an Ethereum signature on the message hash returned by the /refundRequestMessageHash endpoint. The coordinator will create a new commitment using the same rules the Keeper uses to create a bid commitment except that the value for stakeSpent
is determined by subtracting the total refundable ROOK from the current latest commitment's stakeSpent
. In the event a refund is requested and issued, the
stakeSpent
will be reduced. This means the Keeper must generate new bid commitments using the refund commitment as the latest commitment state. For example: say a keeper's stakeSpent
is 150 and they have accumulated a refund of 50. They request and are issued a refund. Their stakeSpent
is now reduced to 100. Now let's say another profit opportunity appears and they place another bid. The keeper must bid based on the most recent commitment, which is the refund commitment. So if the keeper wants to bid 25, they will bid with a stakeSpent of 125.
To close a payment channel and withdraw their unspent ROOK, Keepers will submit the latest commitment to the staking contract. The staking contract offers a timelocked and an instant withdrawal.
A Keeper will initiate the timelocked withdrawal process using the
initiateTimelockedWithdrawal
function.function initiateTimelockedWithdrawal(
StakeCommitment memory _commitment
) external;
The keeper should use the latest commitment in the payment channel and if they have any outstanding refunds, they should request a refund from the Coordinator first. The Keeper can retrieve the latest commitment using the
/keeperStakeChannel
endpoint. The commitment may or may not have the KSA signature on it already depending on if it's a bid or a refund commitment. Since the Coordinator generates the refund commitment, it will not have the KSA signature on it. The Keeper should use previousCommitmentHash
for the data
field in StakeCommitment
. This will begin a 7 day timelock where the Coordinator can challenge the withdrawal by submitting a commitment with a higher stake nonce using the same function. Once the 7 day timelock has concluded, the Keeper can complete their withdrawal using executeTimelockedWithdrawal
.function executeTimelockedWithdrawal(
address _stakeAddress
) public;
A Keeper can initiate an instant withdrawal by retrieving a message hash using the
/instantWithdrawalRequestMessageHash
endpoint, generating an Ethereum signature on it using their KSA, and then submitting it to the /initiateInstantWithdrawal
endpoint. Once the Coordinator sees this instant withdrawal request, it will no longer allow the KSA to bid on auctions and then it will wait some amount of time to allow all active auctions containing a bid from the KSA to conclude. After the auctions have concluded, it will issue any outstanding refunds and generate an instant withdrawal signature. The latest commitment and the instant withdrawal signature on it can be retrieved using the /keeperStakeChannel
endpoint. The Keeper should then generate their own instant withdrawal signature. They should generate an Ethereum signature on the output of stakePaymentHash
using the latest commitment and the stake contract constant INSTANT_WITHDRAWAL_COMMITMENT_DATA
as the value for the data
parameter. They can then complete the withdrawal using the executeInstantWithdrawal
function on the stake contract.function executeInstantWithdrawal(
StakeCommitment memory _commitment
) external;
- The signatures passed in should be the instant withdrawal signatures.
- The value for
data
should be the stake contract constantINSTANT_WITHDRAWAL_COMMITMENT_DATA
.
Note that the on-chain payment channel state will more often than not be stale since transacting using the payment channel occurs off-chain. There will only be on-chain payment channel interactions in certain circumstances: when a keeper stakes Rook to the payment channel, when a coordinator performs a partial settlement of spent Rook to accumulate claimable Rook, and when a keeper performs a withdrawal. The Coordinator is the source of truth for the most up to date payment channel state and this state should be retrieved using the
/keeperStakeChannel
endpoint.Last modified 1yr ago