diff --git a/docs/build-decentralized-apps/bridging/configure-token-gateway/custom.mdx b/docs/build-decentralized-apps/bridging/configure-token-gateway/custom.mdx
index dc9353d177..7f4744273b 100644
--- a/docs/build-decentralized-apps/bridging/configure-token-gateway/custom.mdx
+++ b/docs/build-decentralized-apps/bridging/configure-token-gateway/custom.mdx
@@ -2,20 +2,39 @@
title: 'Configure custom gateway bridging'
description: 'Guide to creating and deploying a custom gateway for specialized token bridging between Ethereum and Arbitrum. For advanced use cases not covered by the standard or generic-custom gateways.'
user_story: 'As a developer, I want to deploy a custom token bridge gateway so I can implement specialized bridging logic for my token between Ethereum and Arbitrum.'
-reader_audience: developers who want to build on Ethereum/Arbitrum and bridge tokens between parent and child chains
content_type: how-to
+author: anegg0
+sme: TBD
displayed_sidebar: buildAppsSidebar
---
-:::caution Do you really need a custom gateway?
+import TokenBridgingPrereqs from '../partials/_token-bridging-prereqs.mdx';
+import TokenBridgingNextSteps from '../partials/_token-bridging-next-steps.mdx';
+import TokenBridgingResources from '../partials/_token-bridging-resources.mdx';
-Before implementing and deploying a custom gateway, it is strongly encouraged to analyze the current solutions that Arbitrum's token bridge provides: the [standard gateway](/build-decentralized-apps/bridging/configure-token-gateway/standard.mdx) and the [generic-custom gateway](/build-decentralized-apps/bridging/configure-token-gateway/generic-custom.mdx). These solutions provide enough functionality to solve the majority of bridging needs from projects. And if you are in doubt about your current approach, you can always ask for assistance on our [Discord server](https://discord.gg/arbitrum).
+
-:::
+**Do you really need a custom gateway?** Before deploying one, evaluate Arbitrum's built-in options: the [standard gateway](/build-decentralized-apps/bridging/configure-token-gateway/standard.mdx) and the [generic-custom gateway](/build-decentralized-apps/bridging/configure-token-gateway/generic-custom.mdx) cover the majority of bridging needs. If in doubt, ask on the [Arbitrum Discord](https://discord.gg/arbitrum).
-In this how-to, you'll learn how to bridge your own token between Ethereum (the parent chain) and Arbitrum (the child chain), using a custom gateway. For alternative ways of bridging tokens, check out the [token bridging overview](/build-decentralized-apps/bridging/overview.mdx).
+
-Familiarity with [Arbitrum's token bridge system](/how-arbitrum-works/deep-dives/token-bridging.mdx), smart contracts, and decentralized application development is expected. If you're new to developing on Arbitrum, consider reviewing our [Quickstart: Build a dApp with Arbitrum (Solidity, Remix)](/build-decentralized-apps/01-quickstart-solidity-remix.mdx) before proceeding. We'll use [Arbitrum's SDK](https://github.com/OffchainLabs/arbitrum-sdk) throughout this how-to, although no prior knowledge is required.
+In this how-to, you'll learn how to bridge your own token between Ethereum (parent chain) and Arbitrum (child chain) using a custom gateway. For alternative ways of bridging tokens, see the [token bridging overview](/build-decentralized-apps/bridging/overview.mdx).
+
+For other gateway types, see:
+
+- [Standard gateway](/build-decentralized-apps/bridging/configure-token-gateway/standard.mdx) — for standard **ERC-20** tokens with no custom logic.
+- [Generic-custom gateway](/build-decentralized-apps/bridging/configure-token-gateway/generic-custom.mdx) — when you only need a custom child chain token, not custom bridge mechanics.
+
+
+
+**Audience and scope.** This guide covers deploying a custom gateway on Arbitrum One. The contracts and SDK calls generalize to any Arbitrum chain, but the registration step differs:
+
+- **Arbitrum One or Nova:** custom gateway registration must go through Arbitrum DAO governance.
+- **An Arbitrum chain you operate:** as the chain owner, register your custom gateway directly through your chain's `UpgradeExecutor`. No DAO vote is required.
+
+
+
+
We will go through all the steps involved in the process. However, if you want to jump straight to the code, we have created a [custom gateway bridging tutorial script](https://github.com/OffchainLabs/arbitrum-tutorials/tree/master/packages/custom-gateway-bridging) that encapsulates the entire process.
@@ -48,11 +67,11 @@ If you are deploying custom gateways, you will likely want to support your custo
## Step 1: Create a gateway and deploy it on the parent chain
-:::caution This code is for testing purposes
+
The code in the following sections is intended for testing purposes only and doesn't guarantee any level of security. It hasn't undergone any formal audit or security analysis, so it isn't ready for production use. Exercise caution and due diligence while using this code in any environment.
-:::
+
We'll begin by creating our custom gateway and deploying it to the parent chain. A good example of a custom gateway is [Arbitrum’s generic-custom gateway](https://github.com/OffchainLabs/token-bridge-contracts/blob/main/contracts/tokenbridge/ethereum/gateway/L1CustomGateway.sol). It implements all required methods and adds additional methods, enabling the gateway to support a wide variety of tokens for bridging.
@@ -363,43 +382,42 @@ abstract contract L1CrosschainMessenger {
We now deploy that gateway to the parent chain.
-```tsx
-const { ethers } = require('hardhat');
-const { providers, Wallet, BigNumber } = require('ethers');
-const { getArbitrumNetwork, ParentToChildMessageStatus } = require('@arbitrum/sdk');
-const { AdminErc20Bridger, Erc20Bridger } = require('@arbitrum/sdk/dist/lib/assetBridger/erc20Bridger');
-require('dotenv').config();
+```ts
+import { ethers } from 'hardhat';
+import { providers, Wallet } from 'ethers';
+import { getArbitrumNetwork, AdminErc20Bridger, Erc20Bridger } from '@arbitrum/sdk';
+import 'dotenv/config';
/**
- * Set up: instantiate L1 / L2 wallets connected to providers
+ * Set up: instantiate parent and child chain wallets connected to providers.
*/
const walletPrivateKey = process.env.DEVNET_PRIVKEY;
-const l1Provider = new providers.JsonRpcProvider(process.env.L1RPC);
-const l2Provider = new providers.JsonRpcProvider(process.env.L2RPC);
-const l1Wallet = new Wallet(walletPrivateKey, l1Provider);
-const l2Wallet = new Wallet(walletPrivateKey, l2Provider);
+const parentProvider = new providers.JsonRpcProvider(process.env.PARENT_RPC);
+const childProvider = new providers.JsonRpcProvider(process.env.CHILD_RPC);
+const parentWallet = new Wallet(walletPrivateKey, parentProvider);
+const childWallet = new Wallet(walletPrivateKey, childProvider);
const main = async () => {
/**
- * Use l2Network to create an Arbitrum SDK AdminErc20Bridger instance
- * We'll use AdminErc20Bridger for its convenience methods around registering tokens to a custom gateway
+ * Use the Arbitrum network info to create an AdminErc20Bridger instance.
+ * We'll use AdminErc20Bridger for its convenience methods around registering tokens to a custom gateway.
*/
- const l2Network = await getArbitrumNetwork(l2Provider);
- const erc20Bridger = new Erc20Bridger(l2Network);
- const adminTokenBridger = new AdminErc20Bridger(l2Network);
- const l1Router = l2Network.tokenBridge.parentGatewayRouter;
- const l2Router = l2Network.tokenBridge.childGatewayRouter;
- const inbox = l2Network.ethBridge.inbox;
+ const arbitrumNetwork = await getArbitrumNetwork(childProvider);
+ const erc20Bridger = new Erc20Bridger(arbitrumNetwork);
+ const adminTokenBridger = new AdminErc20Bridger(arbitrumNetwork);
+ const parentGatewayRouter = arbitrumNetwork.tokenBridge.l1GatewayRouter;
+ const childGatewayRouter = arbitrumNetwork.tokenBridge.l2GatewayRouter;
+ const inbox = arbitrumNetwork.ethBridge.inbox;
/**
- * Deploy our custom gateway to L1
+ * Deploy our custom gateway to the parent chain.
*/
- const L1CustomGateway = await await ethers.getContractFactory('L1CustomGateway', l1Wallet);
- console.log('Deploying custom gateway to L1');
- const l1CustomGateway = await L1CustomGateway.deploy(l1Router, inbox);
- await l1CustomGateway.deployed();
- console.log(`Custom gateway is deployed to L1 at ${l1CustomGateway.address}`);
- const l1CustomGatewayAddress = l1CustomGateway.address;
+ const L1CustomGateway = await ethers.getContractFactory('L1CustomGateway', parentWallet);
+ console.log('Deploying custom gateway to the parent chain');
+ const parentCustomGateway = await L1CustomGateway.deploy(parentGatewayRouter, inbox);
+ await parentCustomGateway.deployed();
+ console.log(`Custom gateway is deployed to the parent chain at ${parentCustomGateway.address}`);
+ const parentCustomGatewayAddress = parentCustomGateway.address;
};
main()
@@ -682,42 +700,41 @@ abstract contract L2CrosschainMessenger {
We now deploy that gateway to the child chain.
-```tsx
-const { ethers } = require('hardhat');
-const { providers, Wallet, BigNumber } = require('ethers');
-const { getArbitrumNetwork, ParentToChildMessageStatus, AdminErc20Bridger, Erc20Bridger } = require('@arbitrum/sdk');
-require('dotenv').config();
+```ts
+import { ethers } from 'hardhat';
+import { providers, Wallet } from 'ethers';
+import { getArbitrumNetwork, AdminErc20Bridger, Erc20Bridger } from '@arbitrum/sdk';
+import 'dotenv/config';
/**
- * Set up: instantiate L1 / L2 wallets connected to providers
+ * Set up: instantiate parent and child chain wallets connected to providers.
*/
const walletPrivateKey = process.env.DEVNET_PRIVKEY;
-const l1Provider = new providers.JsonRpcProvider(process.env.L1RPC);
-const l2Provider = new providers.JsonRpcProvider(process.env.L2RPC);
-const l1Wallet = new Wallet(walletPrivateKey, l1Provider);
-const l2Wallet = new Wallet(walletPrivateKey, l2Provider);
+const parentProvider = new providers.JsonRpcProvider(process.env.PARENT_RPC);
+const childProvider = new providers.JsonRpcProvider(process.env.CHILD_RPC);
+const parentWallet = new Wallet(walletPrivateKey, parentProvider);
+const childWallet = new Wallet(walletPrivateKey, childProvider);
const main = async () => {
/**
- * Use l2Network to create an Arbitrum SDK AdminErc20Bridger instance
- * We'll use AdminErc20Bridger for its convenience methods around registering tokens to a custom gateway
+ * Use the Arbitrum network info to create an AdminErc20Bridger instance.
*/
- const l2Network = await getArbitrumNetwork(l2Provider);
- const erc20Bridger = new Erc20Bridger(l2Network);
- const adminTokenBridger = new AdminErc20Bridger(l2Network);
- const l1Router = l2Network.tokenBridge.l1GatewayRouter;
- const l2Router = l2Network.tokenBridge.l2GatewayRouter;
- const inbox = l2Network.ethBridge.inbox;
+ const arbitrumNetwork = await getArbitrumNetwork(childProvider);
+ const erc20Bridger = new Erc20Bridger(arbitrumNetwork);
+ const adminTokenBridger = new AdminErc20Bridger(arbitrumNetwork);
+ const parentGatewayRouter = arbitrumNetwork.tokenBridge.l1GatewayRouter;
+ const childGatewayRouter = arbitrumNetwork.tokenBridge.l2GatewayRouter;
+ const inbox = arbitrumNetwork.ethBridge.inbox;
/**
- * Deploy our custom gateway to L2
+ * Deploy our custom gateway to the child chain.
*/
- const L2CustomGateway = await await ethers.getContractFactory('L2CustomGateway', l2Wallet);
- console.log('Deploying custom gateway to L2');
- const l2CustomGateway = await L2CustomGateway.deploy(l2Router);
- await l2CustomGateway.deployed();
- console.log(`Custom gateway is deployed to L2 at ${l2CustomGateway.address}`);
- const l2CustomGatewayAddress = l2CustomGateway.address;
+ const L2CustomGateway = await ethers.getContractFactory('L2CustomGateway', childWallet);
+ console.log('Deploying custom gateway to the child chain');
+ const childCustomGateway = await L2CustomGateway.deploy(childGatewayRouter);
+ await childCustomGateway.deployed();
+ console.log(`Custom gateway is deployed to the child chain at ${childCustomGateway.address}`);
+ const childCustomGatewayAddress = childCustomGateway.address;
};
main()
@@ -738,24 +755,24 @@ We won't go through the process of deploying custom tokens in this how-to, but y
This step will also depend on your setup. In this case, our simplified gateway requires the method `setTokenBridgeInformation` to be called on both gateways to set the addresses of the counterpart gateway and both custom tokens.
-```tsx
+```ts
/**
- * Set the token bridge information on the custom gateways
+ * Set the token bridge information on the custom gateways.
* (This is an optional step that depends on your configuration. In this example, we've added one-shot
* functions on the custom gateways to set the token bridge addresses in a second step. This could be
- * avoided if you are using proxies or the opcode CREATE2 for example)
+ * avoided if you are using proxies or the opcode CREATE2, for example.)
*/
console.log('Setting token bridge information on L1CustomGateway:');
-const setTokenBridgeInfoOnL1 = await l1CustomGateway.setTokenBridgeInformation(l1CustomToken.address, l2CustomToken.address, l2CustomGatewayAddress);
+const setTokenBridgeInfoOnParent = await parentCustomGateway.setTokenBridgeInformation(parentCustomToken.address, childCustomToken.address, childCustomGatewayAddress);
-const setTokenBridgeInfoOnL1Rec = await setTokenBridgeInfoOnL1.wait();
-console.log(`Token bridge information set on L1CustomGateway! L1 receipt is: ${setTokenBridgeInfoOnL1Rec.transactionHash}`);
+const setTokenBridgeInfoOnParentRec = await setTokenBridgeInfoOnParent.wait();
+console.log(`Token bridge information set on L1CustomGateway! Receipt: ${setTokenBridgeInfoOnParentRec.transactionHash}`);
console.log('Setting token bridge information on L2CustomGateway:');
-const setTokenBridgeInfoOnL2 = await l2CustomGateway.setTokenBridgeInformation(l1CustomToken.address, l2CustomToken.address, l1CustomGatewayAddress);
+const setTokenBridgeInfoOnChild = await childCustomGateway.setTokenBridgeInformation(parentCustomToken.address, childCustomToken.address, parentCustomGatewayAddress);
-const setTokenBridgeInfoOnL2Rec = await setTokenBridgeInfoOnL2.wait();
-console.log(`Token bridge information set on L2CustomGateway! L2 receipt is: ${setTokenBridgeInfoOnL2Rec.transactionHash}`);
+const setTokenBridgeInfoOnChildRec = await setTokenBridgeInfoOnChild.wait();
+console.log(`Token bridge information set on L2CustomGateway! Receipt: ${setTokenBridgeInfoOnChildRec.transactionHash}`);
```
## Step 5: Register the custom token with your custom gateway
@@ -770,51 +787,75 @@ In this case, when using this function, a single action occurs:
To simplify the process, we’ll use Arbitrum’s SDK and call the method [`registerCustomToken`](https://github.com/OffchainLabs/arbitrum-sdk/blob/main/packages/sdk/src/lib/assetBridger/erc20Bridger.ts) of the [`AdminErc20Bridger`](https://github.com/OffchainLabs/arbitrum-sdk/blob/main/packages/sdk/src/lib/assetBridger/erc20Bridger.ts) class, which will call the `registerTokenOnL2` method of the token passed by parameter.
-```tsx
+```ts
/**
- * Register the custom gateway as the gateway of our custom token
+ * Register the custom gateway as the gateway of our custom token.
*/
-console.log('Registering custom token on L2:');
-const registerTokenTx = await adminTokenBridger.registerCustomToken(l1CustomToken.address, l2CustomToken.address, l1Wallet, l2Provider);
+console.log('Registering custom token on the child chain:');
+const registerTokenTx = await adminTokenBridger.registerCustomToken(parentCustomToken.address, childCustomToken.address, parentWallet, childProvider);
const registerTokenRec = await registerTokenTx.wait();
-console.log(`Registering token txn confirmed on L1! 🙌 L1 receipt is: ${registerTokenRec.transactionHash}.`);
-console.log(`Waiting for L2 retryable (takes 10-15 minutes); current time: ${new Date().toTimeString()})`);
+console.log(`Registering token txn confirmed on the parent chain! 🙌 Receipt: ${registerTokenRec.transactionHash}.`);
+console.log(`Waiting for child chain retryable (takes 10-15 minutes); current time: ${new Date().toTimeString()})`);
/**
- * The L1 side is confirmed; now we listen and wait for the L2 side to be executed; we can do this by computing the expected txn hash of the L2 transaction.
- * To compute this txn hash, we need our message's "sequence numbers", unique identifiers of each L1 to L2 message.
- * We'll fetch them from the event logs with a helper method.
+ * The parent chain side is confirmed; now we wait for the child chain side to execute.
+ * Each parent-to-child message has a unique sequence number; we fetch them from the event logs via a helper.
*/
-const l1ToL2Msgs = await registerTokenRec.getParentToChildMessages(l2Provider);
+const parentToChildMsgs = await registerTokenRec.getParentToChildMessages(childProvider);
/**
- * In this case, the registerTokenOnL2 method creates 1 L1-to-L2 messages to set the L1 token to the Custom Gateway via the Router
- * Here, We check if that message is redeemed on L2
+ * `registerTokenOnL2` produces a single parent-to-child message that sets the parent chain token to the custom gateway via the router.
+ * We confirm it's redeemed on the child chain.
*/
-expect(l1ToL2Msgs.length, 'Should be 1 message.').to.eq(1);
+expect(parentToChildMsgs.length, 'Should be 1 message.').to.eq(1);
-const setGateways = await l1ToL2Msgs[0].waitForStatus();
+const setGateways = await parentToChildMsgs[0].waitForStatus();
expect(setGateways.status, 'Set gateways not redeemed.').to.eq(ParentToChildMessageStatus.REDEEMED);
console.log('Your custom token and gateways are now registered on the token bridge 🥳!');
```
+### Verify the registration
+
+Once the retryable has redeemed, confirm the new mapping on both chains. If the router returns the zero address for your token, registration didn't land:
+
+```ts
+import { Contract } from 'ethers';
+
+const routerAbi = ['function l1TokenToGateway(address) view returns (address)'];
+
+const parentRouter = new Contract(arbitrumNetwork.tokenBridge.l1GatewayRouter, routerAbi, parentProvider);
+const childRouter = new Contract(arbitrumNetwork.tokenBridge.l2GatewayRouter, routerAbi, childProvider);
+
+console.log('Parent router → gateway:', await parentRouter.l1TokenToGateway(parentCustomToken.address));
+console.log('Child router → gateway: ', await childRouter.l1TokenToGateway(parentCustomToken.address));
+```
+
+If either call returns `0x0000…0000`, the retryable likely failed. Inspect its status with `parentToChildMsgs[0].status()` and manually redeem if needed; see [Parent-to-child messaging](/how-arbitrum-works/deep-dives/l1-to-l2-messaging.mdx) for the recovery procedure.
+
## Conclusion
Upon completion of all the steps, registration of your parent chain and child chain gateways in the token bridge will be complete, and both tokens will have connections through your custom gateway.
The full code for this how-to and other more extensive deployment and testing scripts is available in the [custom gateway bridging tutorial package](https://github.com/OffchainLabs/arbitrum-tutorials/tree/master/packages/custom-gateway-bridging) of our tutorials repository.
+## Frequently asked questions
+
+### The retryable in Step 5 didn't redeem. How do I recover?
+
+`registerCustomToken` produces a single parent-to-child retryable that updates the child chain router mapping. If it fails to auto-redeem — for example, because the supplied gas wasn't enough — the verification call in [Step 5](#verify-the-registration) will return `0x0000…0000`. Manually redeem the failed retryable from the child chain by calling `redeem()` on the message; see [Parent-to-child messaging](/how-arbitrum-works/deep-dives/l1-to-l2-messaging.mdx) for the procedure. Registration succeeds as soon as the retryable is redeemed.
+
+### Do I have to register through Arbitrum DAO governance?
+
+Only on Arbitrum One and Nova. If you operate your own Arbitrum chain, your chain owner can register the custom gateway directly through the chain's `UpgradeExecutor` without going through any DAO.
+
## Next steps
Your custom gateway is now configured! Users can:
-- [Deposit tokens to the child chain](/build-decentralized-apps/bridging/deposit/tokens.mdx)
-- [Withdraw tokens back to the parent chain](/build-decentralized-apps/bridging/withdraw/tokens.mdx)
+
## Resources
-1. [Concept page: Token Bridge](/how-arbitrum-works/deep-dives/token-bridging.mdx)
-2. [Arbitrum SDK](https://github.com/OffchainLabs/arbitrum-sdk)
-3. [Token bridge contract addresses](/build-decentralized-apps/reference/02-contract-addresses.mdx)
+
diff --git a/docs/build-decentralized-apps/bridging/configure-token-gateway/generic-custom.mdx b/docs/build-decentralized-apps/bridging/configure-token-gateway/generic-custom.mdx
index 812cefa3cf..f9631a5437 100644
--- a/docs/build-decentralized-apps/bridging/configure-token-gateway/generic-custom.mdx
+++ b/docs/build-decentralized-apps/bridging/configure-token-gateway/generic-custom.mdx
@@ -3,12 +3,32 @@ title: 'Configure generic-custom gateway bridging'
description: "Guide to configuring Arbitrum's generic-custom gateway for token bridging with custom child chain token functionality. Enables custom ERC-20 behavior while using Arbitrum's built-in bridge infrastructure."
user_story: 'As a developer, I want to configure the generic-custom gateway so I can bridge my token with custom functionality on the Arbitrum child chain.'
content_type: how-to
+author: anegg0
+sme: TBD
displayed_sidebar: buildAppsSidebar
---
-In this how-to, you'll learn how to bridge your own token between Ethereum (parent chain) and Arbitrum (child chain), using [Arbitrum's generic-custom gateway](/how-arbitrum-works/deep-dives/token-bridging.mdx#the-arbitrum-generic-custom-gateway). For alternative ways of bridging tokens, check out the [token bridging overview](/how-arbitrum-works/deep-dives/token-bridging.mdx).
+import TokenBridgingPrereqs from '../partials/_token-bridging-prereqs.mdx';
+import TokenBridgingNextSteps from '../partials/_token-bridging-next-steps.mdx';
+import TokenBridgingResources from '../partials/_token-bridging-resources.mdx';
-Familiarity with [Arbitrum's token bridge system](/how-arbitrum-works/deep-dives/token-bridging.mdx), smart contracts, and blockchain development is expected. If you're new to blockchain development, consider reviewing our [Quickstart: Build a dApp with Arbitrum (Solidity, Hardhat)](/build-decentralized-apps/01-quickstart-solidity-remix.mdx) before proceeding. We'll use [Arbitrum's SDK](https://github.com/OffchainLabs/arbitrum-sdk) throughout this how-to, although no prior knowledge is required.
+In this how-to, you'll learn how to bridge your own token between Ethereum (parent chain) and Arbitrum (child chain), using [Arbitrum's generic-custom gateway](/how-arbitrum-works/deep-dives/token-bridging.mdx#the-arbitrum-generic-custom-gateway). For alternative ways of bridging tokens, check out the [token bridging overview](/build-decentralized-apps/bridging/overview.mdx).
+
+For other gateway types, see:
+
+- [Standard gateway](/build-decentralized-apps/bridging/configure-token-gateway/standard.mdx) — for standard **ERC-20** tokens with no custom logic.
+- [Custom gateway](/build-decentralized-apps/bridging/configure-token-gateway/custom.mdx) — for advanced bridge-level customization.
+
+
+
+
+
+**Audience and scope.** This guide covers registration on Arbitrum One. The Solidity interfaces, SDK calls, and on-chain mechanics generalize to any Arbitrum chain—including Arbitrum Nova and chains you operate yourself—but the registration fallback for non-upgradeable parent chain tokens differs:
+
+- **Arbitrum One or Nova:** register through Arbitrum DAO governance, or wrap your token (see [Step 1](#step-1-review-the-prerequisites)).
+- **An Arbitrum chain you operate:** as the chain owner, call `forceRegisterTokenToL2` and `setGateways` directly through your chain's `UpgradeExecutor`. No DAO vote is involved. You will also need to register your chain with the SDK via [`registerCustomArbitrumNetwork`](/sdk/dataEntities/networks.md) before calling `getArbitrumNetwork`.
+
+
We'll go through all the steps involved in the process. However, if you want to jump straight to the code, we've created a [custom token bridging tutorial script](https://github.com/OffchainLabs/arbitrum-tutorials/tree/master/packages/custom-token-bridging) that encapsulates the entire process.
@@ -21,12 +41,13 @@ First of all, the **parent chain counterpart of the token** must conform to the
- It must have an `isArbitrumEnabled` method that returns `0xb1`
- It must have a method that makes an external call to `L1CustomGateway.registerCustomL2Token` specifying the address of the child chain contract, and to `L1GatewayRouter.setGateway` specifying the address of the custom gateway. Make these calls only once to configure the gateway.
-These methods are needed to register the token via the gateway contract. If your parent chain contract does not include these methods and it is not upgradeable, you could register in one of these ways:
+These methods are needed to register the token via the gateway contract. If your parent chain contract does not include these methods and is not upgradeable, you can register through one of these alternative paths:
-- As a chain owner, register via an [Arbitrum DAO](https://forum.arbitrum.foundation/) proposal.
-- By wrapping your parent chain token and registering the wrapped version of your token.
+- **On Arbitrum One or Nova:** submit an [Arbitrum DAO proposal using the standardized token registrations template](https://docs.arbitrum.foundation/how-tos/register-token-via-dao-governance). The DAO's `UpgradeExecutor` routes the privileged `forceRegisterTokenToL2` and `setGateways` calls on your behalf.
+- **On an Arbitrum chain you operate:** as the chain owner, call `forceRegisterTokenToL2` on the parent chain custom gateway and `setGateways` on the parent chain gateway router directly through your chain's `UpgradeExecutor`. No DAO vote is required.
+- **Any Arbitrum chain:** deploy a new upgradeable wrapper contract that holds your existing token and itself implements `ICustomToken`. Register the wrapper instead of the original.
-Note that registration is a one-time event.
+Registration is a one-time event for any given parent chain token address.
Also, the **child chain counterpart of the token** must conform to the [`IArbToken`](https://github.com/OffchainLabs/token-bridge-contracts/blob/main/contracts/tokenbridge/arbitrum/IArbToken.sol) interface, meaning that:
@@ -35,6 +56,8 @@ Also, the **child chain counterpart of the token** must conform to the [`IArbTok
import TokenCompatibilityPartial from '../partials/_token-compatibility.mdx';
+The interfaces above also place a few constraints on how your token implements standard **ERC-20** behavior. Review these before continuing:
+
## Step 2: Create a token and deploy it on the parent chain
@@ -157,44 +180,44 @@ contract L1Token is Ownable, ICustomToken, ERC20 {
We now deploy that token to the parent chain.
```typescript
-const { ethers } = require('hardhat');
-const { providers, Wallet } = require('ethers');
-const { getArbitrumNetwork } = require('@arbitrum/sdk');
-require('dotenv').config();
+import { ethers } from 'hardhat';
+import { providers, Wallet } from 'ethers';
+import { getArbitrumNetwork } from '@arbitrum/sdk';
+import 'dotenv/config';
const walletPrivateKey = process.env.DEVNET_PRIVKEY;
-const l1Provider = new providers.JsonRpcProvider(process.env.L1RPC);
-const l2Provider = new providers.JsonRpcProvider(process.env.L2RPC);
-const l1Wallet = new Wallet(walletPrivateKey, l1Provider);
+const parentProvider = new providers.JsonRpcProvider(process.env.PARENT_RPC);
+const childProvider = new providers.JsonRpcProvider(process.env.CHILD_RPC);
+const parentWallet = new Wallet(walletPrivateKey, parentProvider);
/**
- * For the purpose of our tests, here we deploy an standard ERC20 token (L1Token) to L1
- * It sends its deployer (us) the initial supply of 1000
+ * For the purpose of our tests, here we deploy a standard **ERC-20** token (`L1Token`) to the parent chain.
+ * It sends its deployer (us) the initial supply of 1000.
*/
const main = async () => {
/**
- * Use l2Network to get the token bridge addresses needed to deploy the token
+ * Use the Arbitrum network info to get the token bridge addresses needed to deploy the token.
*/
- const l2Network = await getArbitrumNetwork(l2Provider);
+ const arbitrumNetwork = await getArbitrumNetwork(childProvider);
- const l1Gateway = l2Network.tokenBridge.l1CustomGateway;
- const l1Router = l2Network.tokenBridge.l1GatewayRouter;
+ const parentCustomGateway = arbitrumNetwork.tokenBridge.l1CustomGateway;
+ const parentGatewayRouter = arbitrumNetwork.tokenBridge.l1GatewayRouter;
/**
- * Deploy our custom token smart contract to L1
- * We give the custom token contract the address of l1CustomGateway and l1GatewayRouter as well as the initial supply (premine)
+ * Deploy our custom token smart contract to the parent chain.
+ * We give the custom token contract the address of the parent custom gateway and gateway router, plus the initial supply (premine).
*/
- console.log('Deploying the test L1Token to L1:');
- const L1Token = await (await ethers.getContractFactory('L1Token')).connect(l1Wallet);
- const l1Token = await L1Token.deploy(l1Gateway, l1Router, 1000);
+ console.log('Deploying the test L1Token to the parent chain:');
+ const L1Token = await (await ethers.getContractFactory('L1Token')).connect(parentWallet);
+ const parentToken = await L1Token.deploy(parentCustomGateway, parentGatewayRouter, 1000);
- await l1Token.deployed();
- console.log(`L1Token is deployed to L1 at ${l1Token.address}`);
+ await parentToken.deployed();
+ console.log(`L1Token is deployed to the parent chain at ${parentToken.address}`);
/**
- * Get the deployer token balance
+ * Get the deployer token balance.
*/
- const tokenBalance = await l1Token.balanceOf(l1Wallet.address);
+ const tokenBalance = await parentToken.balanceOf(parentWallet.address);
console.log(`Initial token balance of deployer: ${tokenBalance}`);
};
@@ -254,37 +277,37 @@ contract L2Token is ERC20, IArbToken {
We now deploy that token to the child chain.
```typescript
-const { ethers } = require('hardhat');
-const { providers, Wallet } = require('ethers');
-const { getArbitrumNetwork } = require('@arbitrum/sdk');
-require('dotenv').config();
+import { ethers } from 'hardhat';
+import { providers, Wallet } from 'ethers';
+import { getArbitrumNetwork } from '@arbitrum/sdk';
+import 'dotenv/config';
const walletPrivateKey = process.env.DEVNET_PRIVKEY;
-const l2Provider = new providers.JsonRpcProvider(process.env.L2RPC);
-const l2Wallet = new Wallet(walletPrivateKey, l2Provider);
+const childProvider = new providers.JsonRpcProvider(process.env.CHILD_RPC);
+const childWallet = new Wallet(walletPrivateKey, childProvider);
-const l1TokenAddress = '
';
+const parentTokenAddress = '';
/**
- * For the purpose of our tests, here we deploy an standard ERC20 token (L2Token) to L2
+ * For the purpose of our tests, here we deploy a standard **ERC-20** token (`L2Token`) to the child chain.
*/
const main = async () => {
/**
- * Use l2Network to get the token bridge addresses needed to deploy the token
+ * Use the Arbitrum network info to get the token bridge addresses needed to deploy the token.
*/
- const l2Network = await getArbitrumNetwork(l2Provider);
- const l2Gateway = l2Network.tokenBridge.childCustomGateway;
+ const arbitrumNetwork = await getArbitrumNetwork(childProvider);
+ const childCustomGateway = arbitrumNetwork.tokenBridge.childCustomGateway;
/**
- * Deploy our custom token smart contract to L2
- * We give the custom token contract the address of childCustomGateway as well as the address of the counterpart L1 token
+ * Deploy our custom token smart contract to the child chain.
+ * We give the custom token contract the address of the child custom gateway, plus the address of its counterpart parent chain token.
*/
- console.log('Deploying the test L2Token to L2:');
- const L2Token = await (await ethers.getContractFactory('L2Token')).connect(l2Wallet);
- const l2Token = await L2Token.deploy(l2Gateway, l1TokenAddress);
+ console.log('Deploying the test L2Token to the child chain:');
+ const L2Token = await (await ethers.getContractFactory('L2Token')).connect(childWallet);
+ const childToken = await L2Token.deploy(childCustomGateway, parentTokenAddress);
- await l2Token.deployed();
- console.log(`L2Token is deployed to L2 at ${l2Token.address}`);
+ await childToken.deployed();
+ console.log(`L2Token is deployed to the child chain at ${childToken.address}`);
};
main()
@@ -310,46 +333,70 @@ To simplify the process, we’ll use Arbitrum’s SDK. We’ll call the [`regist
```typescript
/**
- * Register custom token on our custom gateway
+ * Register the custom token on the generic-custom gateway.
*/
-const adminTokenBridger = new AdminErc20Bridger(l2Network);
-const registerTokenTx = await adminTokenBridger.registerCustomToken(l1CustomToken.address, l2CustomToken.address, l1Wallet, l2Provider);
+const adminTokenBridger = new AdminErc20Bridger(arbitrumNetwork);
+const registerTokenTx = await adminTokenBridger.registerCustomToken(parentToken.address, childToken.address, parentWallet, childProvider);
const registerTokenRec = await registerTokenTx.wait();
-console.log(`Registering token txn confirmed on L1! 🙌 L1 receipt is: ${registerTokenRec.transactionHash}`);
+console.log(`Registering token txn confirmed on the parent chain! 🙌 Receipt: ${registerTokenRec.transactionHash}`);
/**
- * The L1 side is confirmed; now we listen and wait for the L2 side to be executed; we can do this by computing the expected txn hash of the L2 transaction.
- * To compute this txn hash, we need our message's "sequence numbers", unique identifiers of each L1 to L2 message.
- * We'll fetch them from the event logs with a helper method.
+ * The parent chain side is confirmed; now we wait for the child chain side to execute.
+ * Each parent-to-child message has a unique sequence number; we fetch them from the event logs via a helper.
*/
-const l1ToL2Msgs = await registerTokenRec.getParentToChildMessages(l2Provider);
+const parentToChildMsgs = await registerTokenRec.getParentToChildMessages(childProvider);
/**
- * In principle, a single L1 txn can trigger any number of L1-to-L2 messages (each with its own sequencer number).
- * In this case, the registerTokenOnL2 method created 2 L1-to-L2 messages;
- * - (1) one to set the L1 token to the Custom Gateway via the Router, and
- * - (2) another to set the L1 token to its L2 token address via the Generic-Custom Gateway
- * Here, We check if both messages are redeemed on L2
+ * A single parent chain transaction can trigger any number of parent-to-child messages.
+ * `registerTokenOnL2` produces exactly two:
+ * (1) set the parent chain token to the custom gateway via the router
+ * (2) set the parent chain token to its child chain counterpart via the generic-custom gateway
+ * We confirm both are redeemed on the child chain.
*/
-expect(l1ToL2Msgs.length, 'Should be 2 messages.').to.eq(2);
+expect(parentToChildMsgs.length, 'Should be 2 messages.').to.eq(2);
-const setTokenTx = await l1ToL2Msgs[0].waitForStatus();
+const setTokenTx = await parentToChildMsgs[0].waitForStatus();
expect(setTokenTx.status, 'Set token not redeemed.').to.eq(ParentToChildMessageStatus.REDEEMED);
-const setGateways = await l1ToL2Msgs[1].waitForStatus();
+const setGateways = await parentToChildMsgs[1].waitForStatus();
expect(setGateways.status, 'Set gateways not redeemed.').to.eq(ParentToChildMessageStatus.REDEEMED);
-console.log('Your custom token is now registered on our custom gateway 🥳 Go ahead and make the deposit!');
+console.log('Your custom token is now registered on the generic-custom gateway 🥳 Go ahead and make the deposit!');
```
+### Verify the registration
+
+Once both retryables have redeemed, confirm the new mappings on both chains. The router and gateway expose view methods that return the registered counterpart for any given token; if they return the zero address, registration didn't land:
+
+```typescript
+import { Contract } from 'ethers';
+
+const routerAbi = ['function l1TokenToGateway(address) view returns (address)'];
+const gatewayAbi = ['function l1ToL2Token(address) view returns (address)'];
+
+const parentRouter = new Contract(arbitrumNetwork.tokenBridge.l1GatewayRouter, routerAbi, parentProvider);
+const parentGateway = new Contract(arbitrumNetwork.tokenBridge.l1CustomGateway, gatewayAbi, parentProvider);
+const childRouter = new Contract(arbitrumNetwork.tokenBridge.l2GatewayRouter, routerAbi, childProvider);
+const childGateway = new Contract(arbitrumNetwork.tokenBridge.childCustomGateway, gatewayAbi, childProvider);
+
+console.log('Parent router → gateway: ', await parentRouter.l1TokenToGateway(parentToken.address));
+console.log('Parent gateway → child token: ', await parentGateway.l1ToL2Token(parentToken.address));
+console.log('Child router → gateway: ', await childRouter.l1TokenToGateway(parentToken.address));
+console.log('Child gateway → child token: ', await childGateway.l1ToL2Token(parentToken.address));
+```
+
+If any of these calls returns `0x0000…0000`, the corresponding retryable likely failed. Inspect the message status with `parentToChildMsgs[i].status()` and manually redeem it if needed; see [Parent-to-child messaging](/how-arbitrum-works/deep-dives/l1-to-l2-messaging.mdx) for the recovery procedure.
+
## Conclusion
Upon completion, the parent and child chain tokens are connected via the generic-custom gateway.
You can bridge tokens between the parent and child chain using the origin parent chain token and the custom token deployed on the child chain, along with the router and gateway contracts from each layer.
-Suppose you want to see an example of bridging a token from the parent to the child chain using Arbitrum’s SDK. In that case, you can check out [How to bridge tokens via Arbitrum’s standard **ERC-20** gateway](/build-decentralized-apps/bridging/deposit/tokens.mdx), specifically in Steps 2-5.
+For a fully working end-to-end implementation of every step on this page—contract sources, deployment scripts, registration call, and post-registration checks—see the [`custom-token-bridging` tutorial package](https://github.com/OffchainLabs/arbitrum-tutorials/tree/master/packages/custom-token-bridging).
+
+If you want to see an example of bridging a token from the parent to the child chain using Arbitrum's SDK, check out [How to bridge tokens via Arbitrum's standard **ERC-20** gateway](/build-decentralized-apps/bridging/deposit/tokens.mdx), specifically Steps 2-5.
## Frequently asked questions
@@ -359,7 +406,15 @@ No, you can only register once a child chain token for the same parent chain tok
### What can I do if my parent chain token is not upgradable?
-As mentioned on the concept page, token registration can also be completed as a chain owner registration via an **[Arbitrum DAO](https://forum.arbitrum.foundation/)** proposal.
+It depends on which Arbitrum chain you're targeting:
+
+- **Arbitrum One or Nova:** registration can be completed through an Arbitrum DAO proposal using the standardized template. See [Register a custom gateway token via Arbitrum DAO governance](https://docs.arbitrum.foundation/how-tos/register-token-via-dao-governance) for the full process.
+- **An Arbitrum chain you operate:** as the chain owner, call `forceRegisterTokenToL2` on the parent chain custom gateway and `setGateways` on the parent chain gateway router directly through your chain's `UpgradeExecutor`.
+- **Any Arbitrum chain:** deploy a new upgradeable wrapper contract that holds your existing token and itself implements `ICustomToken`, then register the wrapper instead.
+
+### One of the retryables didn't redeem. How do I recover?
+
+`registerCustomToken` produces two parent-to-child retryables (one for the router, one for the gateway). If either fails to auto-redeem—for example, because the supplied gas wasn't enough — the corresponding child chain mapping won't update and the verification calls in [Step 4](#verify-the-registration) will return `0x0000…0000`. Manually redeem the failed retryable from the child chain by calling `redeem()` on the message; see [Parent-to-child messaging](/how-arbitrum-works/deep-dives/l1-to-l2-messaging.mdx) for the procedure. Registration succeeds as soon as both retryables are redeemed.
### Can I set up the generic-custom gateway after a standard **ERC-20** token has been deployed on the child chain?
@@ -369,11 +424,8 @@ Yes, if your token has a standard **ERC-20** counterpart on the child chain, you
Your token is now configured for bridging! Users can:
-- [Deposit tokens to the child chain](/build-decentralized-apps/bridging/deposit/tokens.mdx)
-- [Withdraw tokens back to the parent chain](/build-decentralized-apps/bridging/withdraw/tokens.mdx)
+
## Resources
-1. [Concept page: Token Bridge](/how-arbitrum-works/deep-dives/token-bridging.mdx)
-2. [Arbitrum SDK](https://github.com/OffchainLabs/arbitrum-sdk)
-3. [Token bridge contract addresses](/build-decentralized-apps/reference/02-contract-addresses.mdx)
+
diff --git a/docs/build-decentralized-apps/bridging/configure-token-gateway/standard.mdx b/docs/build-decentralized-apps/bridging/configure-token-gateway/standard.mdx
index 94783f0902..22625753ed 100644
--- a/docs/build-decentralized-apps/bridging/configure-token-gateway/standard.mdx
+++ b/docs/build-decentralized-apps/bridging/configure-token-gateway/standard.mdx
@@ -3,9 +3,18 @@ title: Configure standard gateway bridging
description: "Guide to configuring ERC-20 token bridging using Arbitrum's standard gateway, which automatically creates a standard token representation on the child chain with no custom configuration required."
user_story: 'As a developer, I want to set up standard gateway bridging for my ERC-20 token so it can be transferred between Ethereum and Arbitrum.'
content_type: how-to
+author: anegg0
+sme: TBD
+displayed_sidebar: buildAppsSidebar
---
-This guide explains how to configure your **ERC-20** token to work with Arbitrum's standard gateway. The standard gateway is the simplest option—it automatically creates a standard **ERC-20** token on the child chain with no configuration required.
+This guide explains how to configure your **ERC-20** token to work with Arbitrum's standard gateway. The standard gateway is the simplest option — it automatically creates a standard **ERC-20** token on the child chain with no configuration required.
+
+
+
+**Audience and scope.** This guide covers registration on Arbitrum One. The same mechanics apply on Arbitrum Nova and on Arbitrum chains you operate yourself — the standard gateway is permissionless on every Arbitrum chain, so no extra steps are needed if you operate the chain. The first deposit triggers automatic child chain token deployment in all cases.
+
+
## When to use the standard gateway
@@ -57,9 +66,9 @@ contract DappToken is ERC20 {
Deploy it to the parent chain:
-```javascript
-const { ethers } = require('hardhat');
-const { providers, Wallet } = require('ethers');
+```ts
+import { ethers } from 'hardhat';
+import { providers, Wallet } from 'ethers';
const parentProvider = new providers.JsonRpcProvider(process.env.PARENT_RPC);
const wallet = new Wallet(process.env.PRIVATE_KEY, parentProvider);
@@ -98,7 +107,7 @@ The child chain token is created automatically on the first deposit. You can tri
### Using the Arbitrum SDK
-```javascript
+```ts
import { getArbitrumNetwork, Erc20Bridger } from '@arbitrum/sdk';
import { providers, Wallet } from 'ethers';
@@ -135,7 +144,7 @@ After the first deposit, find your token's child chain address:
### Using the SDK
-```javascript
+```ts
const childTokenAddress = await erc20Bridge.getChildErc20Address(parentTokenAddress, parentProvider);
console.log(`L2 token address: ${childTokenAddress}`);
@@ -162,12 +171,14 @@ The automatically deployed token is an instance of [`StandardArbERC20`](https://
You can verify this on [Arbiscan](https://arbiscan.io/) by viewing the token contract.
+import TokenBridgingNextSteps from '../partials/_token-bridging-next-steps.mdx';
+import TokenBridgingResources from '../partials/_token-bridging-resources.mdx';
+
## Configuration complete
Your token is now bridgeable! Users can:
-- [Deposit tokens to the child chain](/build-decentralized-apps/bridging/deposit/tokens.mdx)
-- [Withdraw tokens back to parent chain](/build-decentralized-apps/bridging/withdraw/tokens.mdx)
+
## Important considerations
@@ -190,16 +201,9 @@ Once the first deposit occurs, the token is permanently assigned to the standard
The gateway controls the automatically deployed child chain token—you can't modify or upgrade it. For control over the child chain token, use the generic-custom gateway.
-## Next steps
-
-- [Deposit tokens](/build-decentralized-apps/bridging/deposit/tokens.mdx)
-- [Withdraw tokens](/build-decentralized-apps/bridging/withdraw/tokens.mdx)
-- [Understand token bridge architecture](/how-arbitrum-works/deep-dives/token-bridging.mdx)
-- [View example code](https://github.com/OffchainLabs/arbitrum-tutorials/tree/master/packages/token-deposit)
-
## Resources
-- [Token bridge conceptual overview](/how-arbitrum-works/deep-dives/token-bridging.mdx)
+
+
- [Standard **ERC-20** bridging details](/how-arbitrum-works/deep-dives/token-bridging.mdx#default-standard-bridging)
-- [Arbitrum SDK documentation](/sdk)
-- [Contract addresses](/build-decentralized-apps/reference/02-contract-addresses.mdx)
+- [Example code: token deposit](https://github.com/OffchainLabs/arbitrum-tutorials/tree/master/packages/token-deposit)
diff --git a/docs/build-decentralized-apps/bridging/partials/_token-bridging-next-steps.mdx b/docs/build-decentralized-apps/bridging/partials/_token-bridging-next-steps.mdx
new file mode 100644
index 0000000000..7a8143a8d7
--- /dev/null
+++ b/docs/build-decentralized-apps/bridging/partials/_token-bridging-next-steps.mdx
@@ -0,0 +1,2 @@
+- [Deposit tokens to the child chain](/build-decentralized-apps/bridging/deposit/tokens.mdx)
+- [Withdraw tokens back to the parent chain](/build-decentralized-apps/bridging/withdraw/tokens.mdx)
diff --git a/docs/build-decentralized-apps/bridging/partials/_token-bridging-prereqs.mdx b/docs/build-decentralized-apps/bridging/partials/_token-bridging-prereqs.mdx
new file mode 100644
index 0000000000..88c6b72ebd
--- /dev/null
+++ b/docs/build-decentralized-apps/bridging/partials/_token-bridging-prereqs.mdx
@@ -0,0 +1 @@
+Familiarity with [Arbitrum's token bridge system](/how-arbitrum-works/deep-dives/token-bridging.mdx), smart contracts, and decentralized application development is required. If you're new to developing on Arbitrum, consider reviewing the [Quickstart: Build an app with Arbitrum](/build-decentralized-apps/01-quickstart-solidity-remix.mdx) before proceeding. We'll use [Arbitrum's SDK](https://github.com/OffchainLabs/arbitrum-sdk) throughout this how-to, although no prior knowledge is required.
diff --git a/docs/build-decentralized-apps/bridging/partials/_token-bridging-resources.mdx b/docs/build-decentralized-apps/bridging/partials/_token-bridging-resources.mdx
new file mode 100644
index 0000000000..9122218e95
--- /dev/null
+++ b/docs/build-decentralized-apps/bridging/partials/_token-bridging-resources.mdx
@@ -0,0 +1,3 @@
+- [Token bridge conceptual overview](/how-arbitrum-works/deep-dives/token-bridging.mdx)
+- [Arbitrum SDK](https://github.com/OffchainLabs/arbitrum-sdk)
+- [Token bridge contract addresses](/build-decentralized-apps/reference/02-contract-addresses.mdx)
diff --git a/docs/how-arbitrum-works/timeboost/timeboost-reserve-originator.mdx b/docs/how-arbitrum-works/timeboost/timeboost-reserve-originator.mdx
new file mode 100644
index 0000000000..7d5b737e0b
--- /dev/null
+++ b/docs/how-arbitrum-works/timeboost/timeboost-reserve-originator.mdx
@@ -0,0 +1,38 @@
+---
+title: 'Timeboost - Reserve Originator'
+description: Learn how to use timeboost
+author: alxdca
+sme: alxdca
+content_type: how-to
+sidebar_position: 2
+target_audience: Developers writing Stylus contracts in Rust using Stylus
+---
+
+## What’s the Reserve Originator ?
+
+Timeboost is an auction-based mechanism to gain exclusive access to the Arbitrum sequencer express lane for a given amount of time. It was recently noticed that auction participation is low and collusion may be happening to lower the winning bid price.
+
+The reserve originator is part of a new design that introduces a reserve price, a minimum per round fair access price to the auction. The reserve originator basically acts as a participant to the auction, fetches the reserve price from a dedicated service and submits a bid to the Auctioneer service. If the reserve originator ends up being the winner of the auction, the auctioneer will nullify the round, meaning no one gains the express lane. In that case, the reserve originator doesn’t pay anything.
+
+## Links
+
+Problem definition and requirements: [[PRD] Timeboost Dynamic Reserve Price](https://www.notion.so/PRD-Timeboost-Dynamic-Reserve-Price-30c01a3f59f880c8abdaf6be9e3846f5?pvs=21)
+
+Timeboost docs: https://docs.arbitrum.io/how-arbitrum-works/timeboost/gentle-introduction
+
+Repository: https://github.com/OffchainLabs/timeboost-virtual-bidder
+
+Reserve pricer: https://github.com/OffchainLabs/timeboost-reserve-pricer
+
+Auctioneer code: https://github.com/OffchainLabs/nitro/tree/master/cmd/autonomous-auctioneer
+
+## How to use it?
+
+Any auction participant looking to submit a valid bid will need to access the Timeboost Reserve Pricer API to get the round and minimum bid amount. There are two API endpoints that can be access using the following command
+
+`curl -H "Authorization: Bearer replace_with_api_token" ip_addr:port/api/latest`
+
+- `/api/latest` which returns the newest reserve price and round
+- `/api/recent` which returns the reserve price and rounds of the last two hours
+
+The price gets updated at the 31st second of every minute.
diff --git a/sidebars.js b/sidebars.js
index 59156561c0..a59f60b03c 100644
--- a/sidebars.js
+++ b/sidebars.js
@@ -1036,6 +1036,11 @@ const sidebars = {
label: 'AnyTrust',
id: 'how-arbitrum-works/deep-dives/anytrust-protocol',
},
+ {
+ type: 'doc',
+ label: 'Token bridging',
+ id: 'how-arbitrum-works/deep-dives/token-bridging',
+ },
{
type: 'doc',
label: 'ArbOS',
@@ -1144,6 +1149,11 @@ const sidebars = {
id: 'how-arbitrum-works/timeboost/gentle-introduction',
label: 'How Timeboost works',
},
+ {
+ type: 'doc',
+ label: 'Reserve originator',
+ id: 'how-arbitrum-works/timeboost/timeboost-reserve-originator',
+ },
{
type: 'doc',
label: 'Use Timeboost',