diff --git a/docs/how-arbitrum-works/timeboost/how-to-use-timeboost.mdx b/docs/how-arbitrum-works/timeboost/how-to-use-timeboost.mdx index 408d470ff5..1bac6502f7 100644 --- a/docs/how-arbitrum-works/timeboost/how-to-use-timeboost.mdx +++ b/docs/how-arbitrum-works/timeboost/how-to-use-timeboost.mdx @@ -5,20 +5,15 @@ author: jose-franco content_type: how-to --- -Timeboost is a new transaction ordering policy for Arbitrum chains. With Timeboost, anyone can bid for the right to access an express lane on the **Sequencer** for faster transaction inclusion. +Timeboost is a transaction ordering policy for Arbitrum chains. With Timeboost, anyone can bid for the right to access an express lane on the **Sequencer** for faster transaction inclusion. In this how-to, you'll learn how to bid for the right to use the express lane and submit transactions through the express lane. To learn more about Timeboost and the key terms used on this page, refer to the [gentle introduction](/how-arbitrum-works/timeboost/gentle-introduction.mdx). -This how-to assumes that you're familiar with the following: - -- [How Timeboost works](/how-arbitrum-works/timeboost/gentle-introduction.mdx) -- [viem](https://viem.sh/), since the snippets of code present in the how-to use this library +This how-to assumes that you're familiar with [How Timeboost works](/how-arbitrum-works/timeboost/gentle-introduction.mdx) -Please note that in the initial release of Timeboost, transferring control of the express lane via either the `setTransferor` or the `transferExpressLaneController` will not be supported by the Arbitrum Nitro node software at launch and may be implemented at a future date via a regular node upgrade. Calls made to these two functions on the auction contract will be successful, but the node software (including the Sequencer) will not recognize the actual rights transfer. - -A round's express lane controller, at their choice, can still send transactions signed by others on a per-transaction basis, as explained later in this guide. +A round's express lane controller, at their choice, can send transactions signed by others on a per-transaction basis, as explained later in this guide. @@ -45,7 +40,7 @@ Before we begin, make sure you have: The following table shows this information for the Arbitrum DAO-owned chains: -#### Table 1: Timeboost reference URLs and addresses +#### Timeboost reference URLs and addresses | Network | Auction contract | Autonomous auctioneer endpoint | | ---------------- | ----------------------------------------------------------------------------- | ------------------------------------------ | @@ -60,6 +55,8 @@ Before bidding on an auction, we need to deposit funds in the auction contract. To see the amount of tokens we have deposited in the auction contract, we can call the function `balanceOf` in the `Auction` contract: ```tsx +// Code example uses viem + const depositedBalance = await publicClient.readContract({ address: auctionContractAddress, abi: auctionContractAbi, @@ -72,6 +69,8 @@ console.log(`Current balance of ${userAddress} in auction contract: ${depositedB If we want to deposit more funds to the `Auction` contract, we first need to know what the bidding token is. To obtain the address of the bidding token, we can call the function `biddingToken` in the `Auction` contract: ```tsx +// Code example uses viem + const biddingTokenContractAddress = await publicClient.readContract({ address: auctionContractAddress, abi: auctionContractAbi, @@ -89,6 +88,8 @@ On Arbitrum One and Arbitrum Nova, the default bidding token is **WETH**. Once we know what the bidding token is, we can deposit funds to the auction contract by calling the function `deposit` of the contract after having it approved as spender of the amount we want to deposit: ```tsx +// Code example uses viem + // Approving spending tokens const approveHash = await walletClient.writeContract({ account, @@ -110,13 +111,45 @@ const depositHash = await walletClient.writeContract({ console.log(`Deposit transaction sent: ${depositHash}`); ``` -### Step 2: Submit bids +### Step 2: query the reserve pricer API + +Any auction participant submitting a bid must first know the minimum acceptable bid amount for the round being bid on. The Timeboost Reserve Pricer API exposes this data publicly over HTTP, returning the current reserve price and the round it applies to. Query this API before submitting any bid. + +The reserve price is updated at the 31st second of every minute. + +#### Endpoints + +The API exposes two endpoints over HTTPS: + +- `/api/latest` returns the newest reserve price and round. +- `/api/recent` returns the reserve price and rounds for the last two hours. + +The base URL differs per network: + +| Network | Base URL | +| ---------------- | ----------------------------------------------- | +| Arbitrum Sepolia | `https://arbsepolia-reserve-pricer.arbitrum.io` | +| Arbitrum One | `https://arb1-reserve-pricer.arbitrum.io` | + +For example, to fetch the latest reserve price and round: + +```shell +# Arbitrum One +curl https://arb1-reserve-pricer.arbitrum.io/api/latest + +# Arbitrum Sepolia +curl https://arbsepolia-reserve-pricer.arbitrum.io/api/latest +``` + +### Step 3: submit bids Once we have deposited funds into the auction contract, we can submit bids for the current auction round. We can obtain the current round by calling the function `currentRound` in the `Auction` contract: ```tsx +// Code example uses viem + const currentRound = await publicClient.readContract({ address: auctionContractAddress, abi: auctionContractAbi, @@ -128,6 +161,8 @@ console.log(`Current round: ${currentRound}`); The above shows the current round that's running. At the same time, the auction for the next round might be open. For example, if the `currentRound` is 10, the auction for round 11 is currently happening. To check whether or not that auction is open, we can call the function `isAuctionRoundClosed` of the `Auction` contract: ```tsx +// Code example uses viem + let currentAuctionRoundIsClosed = await publicClient.readContract({ address: auctionContractAddress, abi: auctionContractAbi, @@ -143,6 +178,12 @@ Remember that, by default, auctions for a given round open 60 seconds before tha Once we know the current round, we can bid for (`currentRound + 1`) and verify that the auction is still open (`!currentAuctionRoundIsClosed`), then we can submit a bid. +:::tip Fetching the minimum bid amount + +Before submitting a bid, query the reserve pricer API to retrieve the minimum acceptable bid amount for the round you're bidding on. + +::: + When bids get submitted to the autonomous auctioneer endpoint, we need to send an `auctioneer_submitBid` request with the following information: - chain id @@ -152,15 +193,11 @@ When bids get submitted to the autonomous auctioneer endpoint, we need to send a - the amount in `wei` of the deposit **ERC-20** token to bid - signature (explained below) - - -The amount to bid must be above the minimum reserve price at the moment you are bidding. This parameter is configurable per chain. You can obtain the minimum reserve price by calling the method `minReservePrice()(uint256)` in the `Auction` contract. - - - Let's see an example of a call to this RPC method: ```tsx +// Code example uses viem + const currentAuctionRound = currentRound + 1; const hexChainId: `0x${string}` = `0x${Number(publicClient.chain.id).toString(16)}`; @@ -195,6 +232,8 @@ The signature that needs to be sent is an [EIP-712](https://eips.ethereum.org/EI Here's an example to produce that signature with viem: ```tsx +// Code example uses viem + const currentAuctionRound = currentRound + 1; const signatureData = hashTypedData({ @@ -231,7 +270,7 @@ You can also call the function `getBidHash` in the auction contract to obtain th When sending the request, the autonomous auctioneer will return an empty result with an HTTP status `200` if received correctly. If the result returned contains an error message, something went wrong. Following are some of the error messages that can help us understand what's happening: -#### Table 2: Errors relating to bid submission +#### Errors relating to bid submission | Error | Description | | ----------------------- | ----------------------------------------------------------------------------------------------------------- | @@ -243,7 +282,7 @@ When sending the request, the autonomous auctioneer will return an empty result | `RESERVE_PRICE_NOT_MET` | Bid amount does not meet the minimum required reserve price onchain | | `INSUFFICIENT_BALANCE` | The bid amount specified in the request is higher than the deposit balance of the depositor in the contract | -### Step 3: find out the winner of the auction +### Step 4: find out the winner of the auction After the auction closes and before the round starts, the autonomous auctioneer will call the auction contract with the two highest bids received, allowing the contract to declare the winner and deduct the second-highest bid from the winner's deposited funds. After this, the contract will emit an event with the new Express Lane Controller address. @@ -263,6 +302,8 @@ event SetExpressLaneController( Here's an example to get the log from the auction contract to determine the new express lane controller: ```tsx +// Code example uses viem + const fromBlock = const logs = await publicClient.getLogs({ address: auctionContractAddress, @@ -305,6 +346,8 @@ Timeboost doesn't currently support the `eth_sendRawTransactionConditional` meth Let's see an example of a call to this RPC method: ```tsx +// Code example uses viem + const hexChainId: `0x${string}` = `0x${Number(publicClient.chain.id).toString(16)}`; const transaction = await walletClient.prepareTransactionRequest(...); @@ -344,6 +387,8 @@ The required signature is an Ethereum signature that needs to be sent with the f Here's an example to produce that signature: ```tsx +// Code example uses viem + const hexChainId: `0x${string}` = `0x${Number(publicClient.chain.id).toString(16)}`; const transaction = await walletClient.prepareTransactionRequest(...); @@ -364,7 +409,7 @@ const signature = await account.signMessage({ When sending the request, the sequencer will return an empty result with an HTTP status `200` if it received it correctly. If the result returned contains an error message, something went wrong. Following are some of the error messages that can help us understand what's happening: -#### Table 3: Errors relating to express lane transaction submission +#### Errors relating to express lane transaction submission Note that if you get any of the errors below, then the sequence number used in your express lane transaction was _not_ consumed. | Error | Description | @@ -385,58 +430,6 @@ If you are not the express lane controller and you try to submit a transaction t ::: - - ## How to withdraw funds deposited in the auction contract Funds are deposited in the auction contract to have the right to bid in auctions. Withdrawing funds is possible through a two-step process: initiate the withdrawal, wait for two rounds, and then finalize the withdrawal. @@ -444,6 +437,8 @@ Funds are deposited in the auction contract to have the right to bid in auctions To initiate a withdrawal, we can call the function `initiateWithdrawal` in the `Auction` contract: ```tsx +// Code example uses viem + const initWithdrawalTransaction = await walletClient.writeContract({ account, address: auctionContractAddress, @@ -468,6 +463,8 @@ In this event, the `account` is the address from which we will withdraw funds, ` After two rounds have passed, we can call the method `finalizeWithdrawal` in the `Auction` contract to finalize the withdrawal: ```tsx +// Code example uses viem + const finalizeWithdrawalTransaction = await walletClient.writeContract({ account, address: auctionContractAddress, @@ -512,7 +509,7 @@ In the sequencer feed, the `BroadcastFeedMessage` struct now contains a `blockMe In the current implementation, information about the winning bid for a resolved auction emits via the `AuctionResolved` event ([sample interface](https://github.com/OffchainLabs/nitro-contracts/blob/main/src/express-lane-auction/IExpressLaneAuction.sol#L95-L103)). Historical bid information, including the round number and bid amounts, are published to a public S3 bucket at a regular cadence. The domain for the S3 bucket where historical bids get saved is: -#### Table 4: S3 URLs for historical bid data +#### S3 URLs for historical bid data :::info URL updates for historical bid data On June 9 2025, at around 17:27 ET (UTC−05:00), the region in which the Amazon S3 bucket for historical bid data on Arbitrum One was _changed_ from `s3://timeboost-auctioneer-arb1/uw2/validated-timeboost-bids/` to `s3://timeboost-auctioneer-arb1/ue2/validated-timeboost-bids/`. The below table has been updated for the new, correct URL to access bid data after June 9, 2025 17:27 ET, but if you require data from before June 9, 2025 17:27 ET, then please use `s3://timeboost-auctioneer-arb1/uw2/validated-timeboost-bids/`.