diff --git a/src/assets/images/developers/L1GatewayWHITE.png b/src/assets/images/developers/L1GatewayWHITE.png new file mode 100644 index 000000000..8e31b1712 Binary files /dev/null and b/src/assets/images/developers/L1GatewayWHITE.png differ diff --git a/src/assets/images/developers/txFeesProjectStructure.png b/src/assets/images/developers/txFeesProjectStructure.png new file mode 100644 index 000000000..2acc38758 Binary files /dev/null and b/src/assets/images/developers/txFeesProjectStructure.png differ diff --git a/src/assets/images/developers/withdrawWHITE.png b/src/assets/images/developers/withdrawWHITE.png new file mode 100644 index 000000000..7a6f485d5 Binary files /dev/null and b/src/assets/images/developers/withdrawWHITE.png differ diff --git a/src/content/docs/en/developers/guides/bridge-an-erc721-nft-through-a-custom-gateway.md b/src/content/docs/en/developers/guides/bridge-an-erc721-nft-through-a-custom-gateway.md new file mode 100644 index 000000000..9df7793df --- /dev/null +++ b/src/content/docs/en/developers/guides/bridge-an-erc721-nft-through-a-custom-gateway.md @@ -0,0 +1,178 @@ +--- +section: developers +date: Last Modified +title: "Bridge an ERC721 NFT through a Custom Gateway" +lang: "en" +permalink: "TODO" +excerpt: "TODO" +--- + +Whenever you want to bridge an ERC721 NFT, you may interact with the Gateway and NFT contracts on Sepolia and Scroll testnet. In this guide, we'll cover different ways of doing so. + +## Step 1: Launch an NFT on Sepolia + +If you already have an existing token on Sepolia, feel free to skip this step. If you don't have an NFT on L1, you can grab the following minimal example of an ERC721 and launch it on L1. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; + +contract MockNFT is ERC721 { + constructor() ERC721("Mock NFT", "MNFT") {} + + function mint(address to, uint nftId) public { + _mint(to, nftId); + } +} +``` + +## Step 2: Launch the Gateway on Sepolia + +This step is needed only if you want to launch your own Gateway. Launching your own gateway is fully permissionless and also allows you to have custom logic called every time a token is deposited. You can skip this step if you use the Scroll ERC721 bridge launched at `TODO: 0x1C441Dfc5C2eD7A2AA8636748A664E59CB029157.` More information is available [here](https://github.com/scroll-tech/token-list). This contract will allow you to send NFTs from Sepolia to Scroll testnet. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import "@scroll-tech/contracts@0.0.10/L1/gateways/L1ERC721Gateway.sol"; + +contract MyL1ERC721Gateway is L1ERC721Gateway { + function _depositERC721( + address _token, + address _to, + uint256 _tokenId, + uint256 _gasLimit + ) internal override nonReentrant { + super._depositERC721(_token, _to, _tokenId, _gasLimit); + /*custom logic goes here*/ + } + + function _batchDepositERC721( + address _token, + address _to, + uint256[] calldata _tokenIds, + uint256 _gasLimit + ) internal override nonReentrant { + super._batchDepositERC721(_token, _to, _tokenIds, _gasLimit); + /*custom logic goes here*/ + } +} +``` + +## Step 3: Launch the Gateway on Scroll Alpha + +You can also skip this step if you use the Scroll ERC721 Gateway launched at `TODO: 0x8Fee20e0C0Ef16f2898a8073531a857D11b9C700`. This contract lets you bridge tokens from Scroll testnet back to Sepolia. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import "@scroll-tech/contracts@0.0.10/L2/gateways/L2ERC721Gateway.sol"; + +contract MyL2ERC721Gateway is L2ERC721Gateway { + function _withdrawERC721( + address _token, + address _to, + uint256 _tokenId, + uint256 _gasLimit + ) internal override nonReentrant { + super._withdrawERC721(_token, _to, _tokenId, _gasLimit); + /*custom logic goes here*/ + } + + function _batchWithdrawERC721( + address _token, + address _to, + uint256[] calldata _tokenIds, + uint256 _gasLimit + ) internal override nonReentrant { + super._batchWithdrawERC721(_token, _to, _tokenIds, _gasLimit); + /*custom logic goes here*/ + } +} +``` + +## Step 4: Launch the custom token on Scroll Alpha + +This contract has to follow the IScrollERC721 standard interface. It has to allow the gateway to mint tokens on deposit and burn on withdrawal. The following example shows a sample implementation by passing as constructor parameters the L2 gateway (either the one you just launched or Scroll's at `TODO: 0x1C441Dfc5C2eD7A2AA8636748A664E59CB029157`) and your token address on Sepolia. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "@scroll-tech/contracts@0.0.10/libraries/token/IScrollERC721.sol"; + +contract MockNFT is ERC721, IScrollERC721 { + address GATEWAY; + address COUNTERPART; + + constructor(address _gateway, address _counterpart) ERC721("Mock NFT", "MNFT") { + GATEWAY = _gateway; + COUNTERPART = _counterpart; + } + + /// @notice Return the address of Gateway the token belongs to. + function gateway() public view returns (address) { + return GATEWAY; + } + + /// @notice Return the address of counterpart token. + function counterpart() public view returns (address) { + return COUNTERPART; + } + + /// @notice Mint some token to recipient's account. + /// @dev Gateway Utilities, only gateway contract can call + /// @param _to The address of recipient. + /// @param _tokenId The token id to mint. + function mint(address _to, uint256 _tokenId) external { + require(msg.sender == gateway(), "Only gateway can mint"); + _mint(_to, _tokenId); + } + + /// @notice Burn some token from account. + /// @dev Gateway Utilities, only gateway contract can call + /// @param _tokenId The token id to burn. + function burn(uint256 _tokenId) external { + require(msg.sender == gateway(), "Only gateway can mint"); + _burn(_tokenId); + } +} +``` + +## Step 5: Initialize the Gateways + +This step is only needed if you're launching your own custom Gateways. If you're using Scroll's Gateway, the Scroll team will take care of this. + +On Sepolia, first call the `initialize` function on the Sepolia L1 Gateway by passing the following params: + +- `counterpart` : L2 gateway you just launched on Scroll testnet. +- messenger: The messenger contract on Sepolia at `TODO: 0x5260e38080BFe97e6C4925d9209eCc5f964373b6` + +Next, you will need to call the `updateTokenMapping` to `bind the L1 and L2 tokens.` + +- `l1 token`: Token launched on Sepolia +- `l2 token`: Token launched on Scroll testnet + +Now let's move to Scroll testnet and call the initialize function on your Scroll testnet L2 Gateway: + +- `counterpart`: L1 gateway you just launched on Sepolia. +- `messenger`: The messenger contract on Scroll testnet at `TODO: 0xb75d7e84517e1504C151B270255B087Fd746D34C` + +Finally, let's bind the tokens on Scroll testnet by calling `updateTokenMapping` on the same contract: + +- `l2 token` : Token launched on Scroll testnet +- `l1 Token`: Token launched on Sepolia + +## Step 6: Deposit from Sepolia to Scroll testnet + +Deposits can be made by first approving the Gateway on L1 and then calling the `depositERC721` function by passing the following parameters. Notice that this is a payable function, so if you send 0.0001 ETH, it should be more than enough to confirm your transaction on L2: + +1. `token`: token address on Sepolia +2. `token id`: token id that you want to deposit +3. `gas limit`: 4000 should be enough + +After confirming first on L1 and then waiting around 20mins for your confirmation on L2, you should be able to see your NFT on any block explorer. diff --git a/src/content/docs/en/developers/guides/bridge-erc1155-through-the-custom-gateway.md b/src/content/docs/en/developers/guides/bridge-erc1155-through-the-custom-gateway.md new file mode 100644 index 000000000..ca9ab81e4 --- /dev/null +++ b/src/content/docs/en/developers/guides/bridge-erc1155-through-the-custom-gateway.md @@ -0,0 +1,200 @@ +--- +section: developers +date: Last Modified +title: "Bridge ERC1155 through the Custom Gateway" +lang: "en" +permalink: "TODO" +excerpt: "TODO" +--- + +Whenever you want to bridge an ERC1155 NFT, you may interact with the Gateway and NFT contracts on Sepolia and Scroll testnet. In this guide, we'll cover different approaches to doing so. + +## Step 1: Launch an 1155 Contract on Sepolia + +If you already have an existing token on Sepolia, feel free to skip this step. If you don't have an ERC1155 on L1, you can grab the following minimal example and launch it on L1. + +```solidity +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; + +contract MockNFT is ERC1155 { + constructor() ERC1155("") {} + + function mint(address to, uint256 id, uint256 amount) public { + _mint(to, id, amount, ""); + } +} +``` + +## Step 2: Launch the Gateway on Sepolia + +This step is needed only if you want to launch your own Gateway. Launching your own gateway is fully permissionless and also allows you to have custom logic called every time a token is deposited. You can skip this step if you use the Scroll ERC1155 bridge launched at `TODO: TODO: 0xd1bE599aaCBC21448fD6373bbc7c1b4c7806f135`. More information is available [here](https://github.com/scroll-tech/token-list). This contract will allow you to send ERC1155 tokens from Sepolia to Scroll testnet. + +
// SPDX-License-Identifier: MIT
+pragma solidity 0.8.19;
+
+import "@scroll-tech/contracts@0.0.10/L1/gateways/L1ERC1155Gateway.sol";
+
+contract MyL1ERC1155Gateway is L1ERC1155Gateway {
+    function _depositERC1155(
+        address _token,
+        address _to,
+        uint256 _tokenId,
+        uint256 _amount,
+        uint256 _gasLimit
+    ) internal override nonReentrant {
+        super._depositERC1155(_token, _to, _tokenId, _amount, _gasLimit);
+        /*custom logic goes here*/
+    }
+
+    function _batchDepositERC1155(
+        address _token,
+        address _to,
+        uint256[] calldata _tokenIds,
+        uint256[] calldata _amounts,
+        uint256 _gasLimit
+    ) internal override nonReentrant {
+        super._batchDepositERC1155(_token, _to, _tokenIds, _amounts, _gasLimit);
+        /*custom logic goes here*/
+    }
+}
+
+ +## Step 3: Launch the Gateway on Scroll + +This step is needed only if you want to launch your own Gateway. Launching your own gateway is fully permissionless and also allows you to have custom logic called every time a token is deposited. You can skip this step if you use the Scroll ERC1155 bridge launched at `TODO: 0xfe5Fc32777646bD123564C41f711FF708Dd48360`. More information is available [here](https://github.com/scroll-tech/token-list). This contract will allow you to send ERC1155 tokens from Sepolia to Scroll testnet. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import "@scroll-tech/contracts@0.0.10/L2/gateways/L2ERC1155Gateway.sol"; + +contract MyL2ERC1155Gateway is L2ERC1155Gateway { + function _withdrawERC1155( + address _token, + address _to, + uint256 _tokenId, + uint256 _amount, + uint256 _gasLimit + ) internal override nonReentrant { + super._withdrawERC1155(_token, _to, _tokenId, _amount, _gasLimit); + /*custom logic goes here*/ + } + + function _batchWithdrawERC1155( + address _token, + address _to, + uint256[] calldata _tokenIds, + uint256[] calldata _amounts, + uint256 _gasLimit + ) internal override nonReentrant { + super._batchWithdrawERC1155(_token, _to, _tokenIds, _amounts, _gasLimit); + /*custom logic goes here*/ + } +} +``` + +## Step 3: Launch the custom token on Scroll testnet + +This contract has to follow the IScrollERC1155 standard interface. It has to allow the gateway to mint tokens on deposit and burn on withdrawal. The following example shows a sample implementation by passing as constructor parameters the L2 gateway (either the one you just launched or Scroll's at `TODO: 0xd1bE599aaCBC21448fD6373bbc7c1b4c7806f135`) and your token address on Sepolia. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; +import "@scroll-tech/contracts@0.0.10/libraries/token/IScrollERC1155.sol"; + +contract MockNFT is ERC1155, IScrollERC1155 { + address GATEWAY; + address COUNTERPART; + + constructor(address _gateway, address _counterpart) ERC1155("") { + GATEWAY = _gateway; + COUNTERPART = _counterpart; + } + + /// @notice Return the address of Gateway the token belongs to. + function gateway() public view returns (address) { + return GATEWAY; + } + + /// @notice Return the address of counterpart token. + function counterpart() public view returns (address) { + return COUNTERPART; + } + + /// @notice Mint some token to recipient's account. + /// @dev Gateway Utilities, only gateway contract can call + /// @param _to The address of recipient. + /// @param _tokenId The token id to mint. + function mint(address _to, uint256 _tokenId, uint256 _amount, bytes memory _data) external { + require(msg.sender == gateway(), "Only gateway can mint"); + _mint(_to, _tokenId, _amount, _data); + } + + /// @notice Burn some token from account. + /// @dev Gateway Utilities, only gateway contract can call + /// @param _tokenId The token id to burn. + function burn(address _from, uint256 _tokenId, uint256 _amount) external { + require(msg.sender == gateway(), "Only gateway can mint"); + _burn(_from, _tokenId, _amount); + } + + function batchMint( + address _to, + uint256[] calldata _tokenIds, + uint256[] calldata _amounts, + bytes calldata _data + ) public { + require(msg.sender == gateway(), "Only gateway can mint"); + _mintBatch(_to, _tokenIds, _amounts, _data); + } + + /// @notice Batch burn some token from account. + /// @dev Gateway Utilities, only gateway contract can call + /// @param _from The address of account to burn token. + /// @param _tokenIds The list of token ids to burn. + /// @param _amounts The list of corresponding amount of token to burn. + function batchBurn(address _from, uint256[] calldata _tokenIds, uint256[] calldata _amounts) public { + require(msg.sender == gateway(), "Only gateway can Burn"); + _burnBatch(_from, _tokenIds, _amounts); + } +} +``` + +## Step 4: Initialize the Gateways + +This step is only needed if you're launching your own custom Gateways. If you're using Scroll's Gateway, the Scroll team will take care of this. + +On Sepolia, first call the `initialize` function on the Sepolia L1 Gateway by passing the following params: + +- `counterpart` : L2 gateway you just launched on Scroll testnet. +- messenger: The messenger contract on Sepolia at `TODO: 0x5260e38080BFe97e6C4925d9209eCc5f964373b6` + +Next, you will need to call the `updateTokenMapping` to `bind the L1 and L2 tokens.` + +- `l1 token`: Token launched on Sepolia +- `l2 token`: Token launched on Scroll testnet + +Now let's move to Scroll testnet and call the initialize function on your Scroll testnet L2 Gateway: + +- `counterpart`: L1 gateway you just launched on Sepolia. +- `messenger`: The messenger contract on Scroll testnet at `TODO: 0xb75d7e84517e1504C151B270255B087Fd746D34C` + +Finally, let's bind the tokens on Scroll testnet by calling `updateTokenMapping` on the same contract: + +- `l2 token` : Token launched on Scroll testnet +- `l1 Token`: Token launched on Sepolia + +## Step 5: Deposit from Sepolia to Scroll testnet + +Deposits can be made by first approving the Gateway on L1 and then calling the `depositERC1155` function by passing the following parameters. Notice that this is a payable function, so if you send 0.0001 ETH, it should be more than enough to confirm your transaction on L2: + +1. `token`: token address on Sepolia +2. `token id`: token id that you want to deposit +3. `amount`: amount of tokens you want to deposit +4. `gas limit`: 4000 should be enough diff --git a/src/content/docs/en/developers/guides/bridge-erc20-through-the-custom-gateway.md b/src/content/docs/en/developers/guides/bridge-erc20-through-the-custom-gateway.md new file mode 100644 index 000000000..fef9e3c60 --- /dev/null +++ b/src/content/docs/en/developers/guides/bridge-erc20-through-the-custom-gateway.md @@ -0,0 +1,186 @@ +--- +section: developers +date: Last Modified +title: "Bridge ERC20 through the Custom Gateway" +lang: "en" +permalink: "TODO" +excerpt: "TODO" +--- + +This guide will walk through how to use Scroll's bridge for ERC20s that need custom functionality using the Custom Gateway. + +## Step 1: Launch a token on Sepolia + +There is no need for a particular implementation for a token to be compatible with L2. If you already have a token, feel free to skip this step. If you want to deploy a new token, you can use the following contract of a simple ERC20 token that mints 1 million tokens to the deployer when launched. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract L1Token is ERC20 { + constructor() ERC20("My Token L1", "MTL1") { + _mint(msg.sender, 1_000_000 ether); + } +} +``` + +## Step 2: Launch the counterpart token on Scroll testnet + +The next step is launching the token on Scroll testnet which represents the original token on Sepolia. This token can implement custom logic to match the same logic as the L1 token or even add more features on top of it. + +For this to work: + +- The token must implement the IScrollStandardERC20 interface in order to be compatible with the bridge. +- The contract should provide the gateway address and the counterpart token addresses (the L1 token we just launched) under the `gateway()` and `counterpart()` functions. It should also allow the L2 gateway to call the token `mint()` and `burn()` functions that will be called when a token is deposited and withdrawn. + +The following is a complete example of a token compatible with the bridge. As the constructor, you should pass the `TODO: 0xa07Cb742657294C339fB4d5d6CdF3fdBeE8C1c68` address as the official Scroll gateway and the address of the token we just launched on Sepolia. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "@scroll-tech/contracts@0.0.10/libraries/token/IScrollERC20Extension.sol"; + +contract L2Token is ERC20, IScrollERC20Extension { + // We store the gateway and the L1 token address to provide the gateway() and counterpart() functions which are needed from the Scroll Standard ERC20 interface + address _gateway; + address _counterpart; + + // In the constructor we pass as parameter the Custom L2 Gateway and the L1 token address as parameters + constructor(address gateway_, address counterpart_) ERC20("My Token L2", "MTL2") { + _gateway = gateway_; + _counterpart = counterpart_; + } + + function gateway() public view returns (address) { + return _gateway; + } + + function counterpart() external view returns (address) { + return _counterpart; + } + + // We allow minting only to the Gateway so it can mint new tokens when bridged from L1 + function transferAndCall(address receiver, uint256 amount, bytes calldata data) external returns (bool success) { + transfer(receiver, amount); + data; + return true; + } + + // We allow minting only to the Gateway so it can mint new tokens when bridged from L1 + function mint(address _to, uint256 _amount) external onlyGateway { + _mint(_to, _amount); + } + + // Similarly to minting, the Gateway is able to burn tokens whem bridged from L2 to L1 + function burn(address _from, uint256 _amount) external onlyGateway { + _burn(_from, _amount); + } + + modifier onlyGateway() { + require(gateway() == _msgSender(), "Ownable: caller is not the gateway"); + _; + } +} +``` + +## Step 3: Add the token to the Scroll Bridge + +You need to contact the Scroll team to add the token to `L2CustomERC20Gateway` contract in Scroll and `L1CustomERC20Gateway` contract in L1. In addition, follow the instructions on the [token lists](https://github.com/scroll-tech/token-list) repository to add your token to the Scroll official frontend. + +## Step 4: Deposit tokens + +Once your token has been approved by the Scroll team, you should be able to deposit tokens from L1. To do so you must approve the `TODO: 0x920f906B814597cF5DC76F95100F09CBAF9c5748` address that hosts the L1CustomGateway contract on Sepolia. Then, deposit tokens by calling the `depositERC20` function from the `L1CustomGateway` contract. You can do this using [the bridge](TODO: https://scroll.io/sepolia/bridge), [sepolia scan](TODO: https://sepolia.etherscan.io/address/0x920f906B814597cF5DC76F95100F09CBAF9c5748#writeProxyContract), or a smart contract. + +## Step 5: Withdraw tokens + +You will follow similar steps to send tokens back from L2 to L1. First, approve the L2CustomGateway on `TODO: 0xa07Cb742657294C339fB4d5d6CdF3fdBeE8C1c68` and then withdraw the tokens calling the `withdrawERC20` from the `L2CustomGateway` contract. + +## Alternative Approach: Launch and set up a custom L1 gateway contract + +Adding your token to the Scroll official bridge is the recommended way of bridging tokens. This will make them easier to discover and safer for holders. However, it will still need approval from the Scroll team. If you want to launch a custom token without going through the official approval process, you can launch a custom gateway yourself. To do so, you would need to launch a `L1CustomERC20Gateway` contract on L1 and a `L2CustomERC20Gateway` on L2. + +### Launch an L1 Custom Gateway + +Let’s start by launching the following contract on Sepolia. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import "@scroll-tech/contracts@0.0.10/L1/gateways/L1CustomERC20Gateway.sol"; + +contract MyL1Gateway is L1CustomERC20Gateway { + function _deposit( + address _token, + address _to, + uint256 _amount, + bytes memory _data, + uint256 _gasLimit + ) internal override nonReentrant { + super._deposit(_token, _to, _amount, _data, _gasLimit); + /*custom logic goes here*/ + } +} +``` + +### Launch an L2 Custom Gateway + +Now let’s launch the counterpart contract on Scroll testnet. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import "@scroll-tech/contracts@0.0.10/L2/gateways/L2CustomERC20Gateway.sol"; + +contract MyL2Gateway is L2CustomERC20Gateway { + function _withdraw( + address _token, + address _to, + uint256 _amount, + bytes memory _data, + uint256 _gasLimit + ) internal override nonReentrant { + super._withdraw(_token, _to, _amount, _data, _gasLimit); + /*custom logic goes here*/ + } +} +``` + +### Setup your Gateway contract on Sepolia + +Once the contracts are launched, call the following functions to initialize the contracts and bind them to the corresponding tokens and gateway on the other side of the bridge. + +First, call the `initialize` function on the `MyL1Gateway` contract with the following parameters: + +- `_counterpart`: The address of `MyL2Gateway` we just launched on Scroll testnet. +- `_router`: Set it to `TODO: 0xe5E30E7c24e4dFcb281A682562E53154C15D3332`, the Scroll router contract on Sepolia. +- `_messenger`: Set it t[^1]o `TODO: 0x5260e38080BFe97e6C4925d9209eCc5f964373b6`, the Scroll messenger contract on Sepolia. + +A custom gate can host multiple token bridges. In this case, we will only be allowing bridging between L1Token and L2Token by calling the `updateTokenMapping` function on the `MyL1Gateway` contract with the following parameters: + +- `_l1Token`: The address of the `L1Token` contract we previously launched on Sepolia. +- `_l2Token`: The address of the `L2Token` contract we previously launched on Scroll testnet. + +### Setup your Gateway contract Scroll testnet + +Now let’s switch to the Scroll testnet chain and initialize `MyL2Gateway` in a similar way. + +First, we call the `initialize` function from `MyL2Gateway`: + +- `_counterpart`: The address of `MyL1Gateway` we just launched on Sepolia. +- `_router`: Set it to `TODO: 0x6d79Aa2e4Fbf80CF8543Ad97e294861853fb0649`, the Scroll router contract on Scroll testnet. +- `_messenger`: Set it `TODO: 0xb75d7e84517e1504C151B270255B087Fd746D34C`, the Scroll messenger contract on Scroll testnet. + +And then call the `updateTokenMapping` on the `MyL2Gateway` contract: + +- `_l2Token`: The address of the `L2Token` contract we previously launched on Scroll testnet. +- `_l1Token`: The address of the `L1Token` contract we previously launched on Sepolia. + +### Bridging tokens + +We can now call `depositERC20` from `MyL1Gateway` and `withdrawERC20` from `MyL2Gateway` in a similar way to the official Scroll bridge. diff --git a/src/content/docs/en/developers/guides/estimating-gas-and-tx-fees.mdx b/src/content/docs/en/developers/guides/estimating-gas-and-tx-fees.mdx new file mode 100644 index 000000000..0fe662a84 --- /dev/null +++ b/src/content/docs/en/developers/guides/estimating-gas-and-tx-fees.mdx @@ -0,0 +1,221 @@ +--- +section: developers +date: Last Modified +title: "Estimating Gas and Tx Fees" +lang: "en" +permalink: "TODO" +excerpt: "TODO" +--- + +import Aside from "../../../../../components/Aside.astro" +import ClickToZoom from "../../../../../components/ClickToZoom.astro" +import txFeesProjectStructure from "../../../../../assets/images/developers/txFeesProjectStructure.png" + +Since Scroll is an L2 rollup, part of the transaction lifecycle is committing some data to L1 for security. To pay for this, all transaction incurs an additional fee called the _L1 fee_. + +In this guide, we will go over how to estimate the L1 fee on Scroll and calculate final transaction fee estimations through a hands-on code example you can find [here](https://github.com/scroll-tech/scroll-guides/tree/main/gas-estimation-demo). + +## The formula + +In short, the transaction fee on Scroll, at any given moment, can be calculated as + +``` +totalFee = (l2GasPrice * l2GasUsed) + l1Fee +``` + +An important distinction we need to make is that `l1Fee` is separated from the `l2Fee`. + +For a more comprehensive explanation and details on the formula, check the documentation on [how Transaction Fees work on Scroll](../developers/transaction-fees-on-scroll-l2/). + +## Interfacing with values + +To fetch these values and calculate the final fee, we’ll interact with Scroll’s public RPC and pre-deployed Smart Contract L1GasOracle.sol, which is deployed at [`TODO: 0x5300000000000000000000000000000000000002`](TODO: https://blockscout.scroll.io/address/0x5300000000000000000000000000000000000002) + +For our example codebase, we make a Hardhat project and fetch values using the [Ethers.js](https://docs.ethers.org/v6/) library. + +`l2GasUsed` - We get this using the [estimateGas](https://docs.ethers.org/v6/api/providers/#Provider-estimateGas) method, which queries our RPC with the TX data to get an estimate of the gas to be used. + +`l2GasPrice` - For this, we’ll use the [getFeeData](https://docs.ethers.org/v6/api/providers/#Provider-getFeeData) method, which will get the current market conditions on the L2 + +The Gas Oracle smart contract exposes multiple key fields we need to figure out the cost of the transaction. `overhead()` , `scalar()` , `l1BaseFee()` and `getL1GasUsed(bytes memory data)` + +But it also exposes the `getL1Fee(bytes memory data)` function, which abstracts all these complexities and allows us to get the fee in just one function call. + +## Hands-on example + +### Project structure + +First of all, let’s quickly go over the key folders inside our project structure. + +It’s a standard Hardhat project, but most of our work is inside the c*ontracts* and _scripts_ folders. + + + + + +### The smart contract + +First, we need a Smart Contract to interact with to showcase the gas estimation. For that, we’ll create a `ExampleContract.sol`. + +```solidity +pragma solidity ^0.8.17; + +contract ExampleContract { + uint public exampleVariable; + + function setExampleVariable(uint _exampleVariable) external { + exampleVariable = _exampleVariable; + } +} +``` + + + +Once deployed, we fill the `EXAMPLE_CONTRACT_ADDRESS` value in our `.env` file. For the example project, it’s already deployed at `TODO: 0xc37ee92c73753B46eB865Ee156d89741ad0a8777` and pre-filled, so nothing has to be done here. + +### Estimating the fees + +The central part of the example lives in the `/scripts/gasEstimation.ts` file. + +We’ll do just four things: + +1. Create a dummy transaction using the ExampleContract +2. Estimate the L2 fee of that transaction +3. Estimate the L1 fee of that transaction +4. Emit an actual transaction to Scroll and compare the values + +#### Creating the dummy transaction + +The goal of this step is to create an **(RLP) Serialized Unsigned Transaction** to be used later on as the parameter for calling the oracle gas estimation method. + +1. Let’s populate our transaction with the needed values to estimate its cost by calling `buildPopulatedExampleContractTransaction`. This will fill the `data`, `to`, `gasPrice`, `type` and `gasLimit` fields: + +```typescript +export async function buildPopulatedExampleContractTransaction( + exampleContractAddress: string, + newValueToSet: number +): Promise { + const exampleContract = await ethers.getContractAt("ExampleContract", exampleContractAddress) + + return exampleContract.setExampleVariable.populateTransaction(newValueToSet) +} +``` + +2. Now, let’s trim it down to the basic fields using `buildUnsignedTransaction`. We won’t be signing it. + +```typescript +export async function buildUnsignedTransaction(signer: HardhatEthersSigner, populatedTransaction: ContractTransaction): Promise { + const nonce = await signer.getNonce(); + + return { + data: populatedTransaction.data, + to: populatedTransaction.to, + gasPrice: populatedTransaction.gasPrice, + type: populatedTransaction.type, + gasLimit: populatedTransaction.gasLimit, + nonce, + }; +``` + + + +3. With the output of the previous function, we serialize it using `getSerializedTransaction` + +```typescript +export function getSerializedTransaction(tx: UnsignedTransaction) { + return serialize(tx) +} +``` + +#### Estimating the L2 fee + +This step is pretty standard and the same as on Ethereum. We’ll use the `estimateL2Fee` function which takes the populated transaction as an input and returns an estimate of the total gas it will use by multiplying the current gas price and gas needed to be used. + +```typescript +export async function estimateL2Fee(tx: ContractTransaction): Promise { + const gasToUse = await ethers.provider.estimateGas(tx) + const feeData = await ethers.provider.getFeeData() + const gasPrice = feeData.gasPrice + + return gasToUse * gasPrice +} +``` + +#### Estimating the L1 fee of that transaction + +This step is very straightforward. Using the output of the `getSerializedTransaction` function, we query the oracle and get the estimated fee in return. + +```typescript +export async function estimateL1Fee( + gasOraclePrecompileAddress: string, + unsignedSerializedTransaction: string +): Promise { + const l1GasOracle = await ethers.getContractAt("IL1GasPriceOracle", gasOraclePrecompileAddress) + + return l1GasOracle.getL1Fee(unsignedSerializedTransaction) +} +``` + +#### Emitting the transaction and comparing estimated vs actual values + +**Emitting the transaction** + +We’ll create a call to our contract using the same values used for the dummy transaction. + +```typescript +const tx = await exampleContract.setExampleVariable(newValueToSetOnExampleContract) +const txReceipt = await tx.wait(5) +``` + +**Calculating the L2 fee** + +Using the transaction receipt we can see the amount of gas used by the transaction + +```typescript +const l2Fee = txReceipt.gasUsed * txReceipt.gasPrice +``` + +**Getting the amount used to pay for the L1 fee** + +To do this, we’ll compare the _balance of the account before_ the transaction execution, the _account balance after_ the execution and then subtract the L2 fee. + +```typescript +const totalFee = signerBalanceBefore - signerBalanceAfter +const l1Fee = totalFee - l2Fee +``` + +**Comparing the values** + +Fee markets are constantly moving and unpredictable. Since the values estimated may differ from the actual execution, let’s check how much they differ. + +We can run all the code that we previously wrote by typing `yarn gas:estimate` command in our [project](https://github.com/scroll-tech/scroll-guides/tree/main/gas-estimation-demo) that will run the [`gasEstimation.ts`](https://github.com/scroll-tech/scroll-guides/blob/main/gas-estimation-demo/scripts/gasEstimation.ts) script and give us the output below: + +```bash +Estimated L1 fee (wei): 208705598167252 +Estimated L2 fee (wei): 26416000000 +Estimated total fee (wei): 208732014167252 + +Actual L1 fee (wei): 210830909757550 +Actual L2 fee (wei): 26416000000 +Actual total fee (wei): 210857325757550 + +(actual fee - estimated fee) +Difference L1 fee (wei): 2125311590298 (1.0183299389002531%) +Difference L2 fee (wei): 0 (0%) +Difference total fee (wei): 2125311590298 (1.0182010645453987%) +``` + +We can see above that the estimated values (in wei) differ by around 1%, but keep in mind that during spikes in gas prices, this difference may increase. Be sure to account for this in your front end for users! + + diff --git a/src/content/docs/en/developers/guides/fake-guide.md b/src/content/docs/en/developers/guides/fake-guide.md deleted file mode 100644 index ed518ab0f..000000000 --- a/src/content/docs/en/developers/guides/fake-guide.md +++ /dev/null @@ -1,84 +0,0 @@ ---- -section: developers -date: Last Modified -title: "Fake Guide" -lang: "en" -permalink: "developers/guides/fake-guide" -excerpt: "Our Alpha Testnet allows the community to deploy smart contracts on Scroll. In this tutorial, we will teach you how to deploy a contract on the Scroll Testnet." ---- - -Our Alpha Testnet allows the community to deploy smart contracts on Scroll. In this tutorial, we will teach you how to deploy a contract on the Scroll Testnet. This [demo repo](https://github.com/scroll-tech/scroll-contract-deploy-demo) illustrates contract deployment with [Hardhat](https://hardhat.org/) and [Foundry](https://github.com/foundry-rs/foundry). - -:::note[Note] -Before you start deploying the contract, you need to request test tokens from a Goerli faucet and use the [bridge](https://scroll.io/alpha/bridge) to transfer some test ETH from _Goerli_ to _Scroll Alpha_. -::: - -### Deploy contracts with Hardhat - -1. If you haven't already, install [nodejs](https://nodejs.org/en/download/) and [yarn](https://classic.yarnpkg.com/lang/en/docs/install). -2. Clone the repo and install dependencies - -```shell -git clone https://github.com/scroll-tech/scroll-contract-deploy-demo.git -cd scroll-contract-deploy-demo -yarn install -``` - -3\. Create a `.env` file following the example `.env.example` in the root directory. Change `PRIVATE_KEY` to your own account private key in the `.env`. - -4\. Run `yarn compile` to compile the contract. - -5\. Run `yarn deploy:scrollTestnet` to deploy the contract on the Scroll Alpha Testnet. - -6\. Run `yarn test` for hardhat tests. - -### Deploy contracts with Foundry - -1. Clone the repo. - -```shell -git clone https://github.com/scroll-tech/scroll-contract-deploy-demo.git -cd scroll-contract-deploy-demo -``` - -2\. Install Foundry. - -```shell -curl -L https://foundry.paradigm.xyz | bash -foundryup -``` - -3\. Run `forge build` to build the project. - -4\. Deploy your contract with Foundry - -```bash -forge create --rpc-url https://alpha-rpc.scroll.io/l2 \ - --value \ - --constructor-args \ - --private-key \ - --legacy \ - contracts/Lock.sol:Lock -``` - -- `` is the amount of test `ETH` to be locked in the contract. Try setting this to some small amount, like `0.0000001ether`. -- `` is the Unix timestamp after which the funds locked in the contract will become available for withdrawal. Try setting this to some Unix timestamp in the future, like `1696118400` (this Unix timestamp corresponds to October 1, 2023). - -For example: - -```bash -forge create --rpc-url https://alpha-rpc.scroll.io/l2 \ - --value 0.00000000002ether \ - --constructor-args 1696118400 \ - --private-key 0xabc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc1 \ - --legacy contracts/Lock.sol:Lock -``` - -### Questions and Feedback - -Thank you for participating in and developing on the Scroll Alpha Testnet. If you encounter any issues, join our [Discord](https://discord.gg/scroll) and ask us in the `developers` channel. - -#### Developer Notes - -1. The `SELFDESTRUCT` opcode is disabled and will not be supported in Scroll, as it is slated to be removed from the EVM at a later date. -2. For now, we have set Layer 2 gas prices to be the same as on Ethereum Layer 1. However, these gas prices are subject to change and will be set in the future to match proving costs. We will endeavor to minimize these changes, primarily applying them to ZK-unfriendly precompiles when necessary for security. diff --git a/src/content/docs/en/developers/guides/greeting-contract-with-cross-chain-interaction.md b/src/content/docs/en/developers/guides/greeting-contract-with-cross-chain-interaction.md new file mode 100644 index 000000000..9719e2e38 --- /dev/null +++ b/src/content/docs/en/developers/guides/greeting-contract-with-cross-chain-interaction.md @@ -0,0 +1,78 @@ +--- +section: developers +date: Last Modified +title: "Greeting Contract with Cross-chain Interaction" +lang: "en" +permalink: "TODO" +excerpt: "TODO" +--- + +In this example, we will launch a dummy smart contract on either Sepolia or Scroll testnet and interact with it from the opposite chain. We will be using the `ScrollMessenger` that is deployed on both Sepolia and Scroll testnet. + +#### Target Smart Contract + +Let’s start by deploying the target smart contract. We will use the Greeter smart contract for this example, but you can use any other contract. Deploy it to either Sepolia or Scroll testnet — L1 and L2 use the same API, so it’s up to you. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +// This Greeter contract will be interacted with through the ScrollMessenger across the bridge +contract Greeter { + string public greeting = "Hello World!"; + + // This function will be called by executeFunctionCrosschain on the Operator Smart Contract + function setGreeting(string memory greeting_) public { + greeting = greeting_; + } +} +``` + +We will now execute the `setGreeting` in a cross-chain way. + +#### Operator Smart Contract + +Now switch to the other chain and deploy the `GreeterOperator`. If you deployed the `Greeter` contract on L1, deploy the `GreeterOperator` from L2 or vice versa. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +// The Scroll Messenger interface is the same on both L1 and L2, it allows sending cross-chain transactions +// Let's import it directly from the Scroll Contracts library +import "@scroll-tech/contracts@0.0.10/libraries/IScrollMessenger.sol"; + +// The GreeterOperator is capable of executing the Greeter function through the bridge +contract GreeterOperator { + // This function will execute setGreeting on the Greeter contract + function executeFunctionCrosschain( + address scrollMessengerAddress, + address targetAddress, + uint256 value, + string memory greeting, + uint32 gasLimit + ) public payable { + IScrollMessenger scrollMessenger = IScrollMessenger(scrollMessengerAddress); + // sendMessage is able to execute any function by encoding the abi using the encodeWithSignature function + scrollMessenger.sendMessage{ value: msg.value }( + targetAddress, + value, + abi.encodeWithSignature("setGreeting(string)", greeting), + gasLimit, + msg.sender + ); + } +} +``` + +We pass the message by executing the `executeFunctionCrosschain` by passing the following parameters. + +- `scrollMessengerAddress`: This will depend on where you deployed the `GreeterOperator` contract. If you deployed it on L1 use `TODO: 0x5260e38080BFe97e6C4925d9209eCc5f964373b6`. If you deployed on L2 use `TODO: 0xb75d7e84517e1504C151B270255B087Fd746D34C`. +- `targetAddress`: The address of the `Greeter` contract on the opposite chain. +- `value`: In this case, it is `0` because the `setGreeting`is not payable. +- `greeting`: This is the parameter that will be sent through the message. Try passing `“This message was crosschain!”` +- `gasLimit`: If you are sending the message from L1 to L2, around `5000` gas limit should be more than enough. If you are sending this from L2 to L1, you can pass `0` because this is an optional parameter. + +After executing and confirming the transaction on both L1 and L2, the new state of `greeting` on the `Greeter` contract should be `“This message was crosschain!”`. Sending a message from one chain to the other should take around 20mins after the transactions are confirmed on the origin chain. + +Congratulations, you now executed a transaction from one chain to the other! diff --git a/src/content/docs/en/developers/l1-and-l2-bridging.mdx b/src/content/docs/en/developers/l1-and-l2-bridging.mdx new file mode 100644 index 000000000..58cad2e19 --- /dev/null +++ b/src/content/docs/en/developers/l1-and-l2-bridging.mdx @@ -0,0 +1,34 @@ +--- +section: developers +date: Last Modified +title: "L1 and L2 Bridging" +lang: "en" +permalink: "TODO" +excerpt: "TODO" +--- + +import ClickToZoom from "../../../../components/ClickToZoom.astro" +import L1GatewayWHITE from "../../../../assets/images/developers/L1GatewayWHITE.png" +import withdrawWHITE from "../../../../assets/images/developers/withdrawWHITE.png" + +The Scroll bridge enables the transfer of ETH, ERC20 tokens, NFTs, and arbitrary messages between L1 and L2. It serves as a secure mechanism for moving various digital assets across L1 and L2. + +To facilitate the transfer of ETH and ERC20 tokens, the Scroll bridge utilizes the Gateway Router. This contract ensures the smooth passage of these assets between L1 and L2, allowing users to transfer their Ethereum-based tokens seamlessly. + +The ERC721 and ERC1155 Gateway enables the transfer of non-fungible assets between the two networks, allowing users to move their NFTs across L1 and L2. + +In addition to token transfers, the Scroll Messenger contract enables cross-chain contract interaction. This means that contracts on one network can interact with contracts on the other network through the Scroll Messenger contract. This functionality expands the possibilities for decentralized applications and smart contracts to operate seamlessly across both networks. + +## L1 Gateway architecture + + + +There are many entry points from the user to the Scroll bridge. This will depend on what you want to do and how you want to do it. If you want to send ETH or ERC20 tokens, you should use the `GatewayRouter` . If you want to send NFTs, you should use the `L1ERC721Gateway` or `L1ERC1155Gateway`. If you want to send arbitrary data, you should use the `L1ScrollMessenger`. All Gateway transfers use the Scroll Messenger to send assets cross-chain, whose job is to append the transactions to the Message Queue for L2 inclusion. + +## L2 Gateway architecture + + + +Regarding possible permissionlessly callable entry points, the L2 Gateway Architecture is very similar to L1. The difference is that when sending a message from L2, calling the `appendMessage` function will store the message in an append-only binary merkle tree (aka withdraw tree) in the `L2MessageQueue`. When a new message is sent to the `L2MessageQueue`, the relayer will detect it and store it in the database. When the block is finalized, it will generate a proof of the new merkle path and pass it to the L1geth node to execute on `L1ScrollMessenger` . All finalized withdraw roots will be stored in the rollup contract so we can verify the proof against them. In the next Scroll versions, the Relayer won't be needed since all users will be able to finalize the transaction on L1. + +In the upcoming sections, we will explore the technical aspects of the bridge, including the smart contract API required to utilize its capabilities. Detailed guides with code examples are provided in the Developer Guides section to assist developers and users in understanding and implementing these functionalities. diff --git a/src/content/docs/en/developers/l1-and-l2-bridging/erc1155-token-bridge.mdx b/src/content/docs/en/developers/l1-and-l2-bridging/erc1155-token-bridge.mdx new file mode 100644 index 000000000..38eb4b608 --- /dev/null +++ b/src/content/docs/en/developers/l1-and-l2-bridging/erc1155-token-bridge.mdx @@ -0,0 +1,161 @@ +--- +section: developers +date: Last Modified +title: "ERC1155 token bridge" +lang: "en" +permalink: "TODO" +excerpt: "TODO" +--- + +import Aside from "../../../../../components/Aside.astro" + +## Deposit ERC1155 tokens from L1 + +ERC1155 bridging from L1 to L2 is done via the L1ERC1155Gateway. Similarly to ERC721 bridging, we don't use a router but the `depositERC1155` function on the Gateway directly. + + + +### Creating an ERC1155 token on L2 + +Similar to ERC721 bridging, in order to bridge ERC1155 tokens, a contract compatible with the `IScrollERC1155` standard has to be launched and mapped on a `L1ERC1155Gateway` and `L2ERC1155Gateway` on both L1 and L2 respectively. This contract has to grant permission to the Gateway on L2 to mint when a token is deposited and burn when the token is withdrawn. + +The following interface is the `IScrollERC1155` needed for deploying ERC1155 tokens compatible with the `L2ERC1155Gateway` on L2. + +```solidity +interface IScrollERC1155 { + /// @notice Return the address of Gateway the token belongs to. + function gateway() external view returns (address); + + /// @notice Return the address of counterpart token. + function counterpart() external view returns (address); + + /// @notice Mint some token to recipient's account. + /// @dev Gateway Utilities, only gateway contract can call + /// @param _to The address of recipient. + /// @param _tokenId The token id to mint. + /// @param _amount The amount of token to mint. + /// @param _data The data passed to recipient + function mint(address _to, uint256 _tokenId, uint256 _amount, bytes memory _data) external; + + /// @notice Burn some token from account. + /// @dev Gateway Utilities, only gateway contract can call + /// @param _from The address of account to burn token. + /// @param _tokenId The token id to burn. + /// @param _amount The amount of token to burn. + function burn(address _from, uint256 _tokenId, uint256 _amount) external; + + /// @notice Batch mint some token to recipient's account. + /// @dev Gateway Utilities, only gateway contract can call + /// @param _to The address of recipient. + /// @param _tokenIds The token id to mint. + /// @param _amounts The list of corresponding amount of token to mint. + /// @param _data The data passed to recipient + function batchMint( + address _to, + uint256[] calldata _tokenIds, + uint256[] calldata _amounts, + bytes calldata _data + ) external; + + /// @notice Batch burn some token from account. + /// @dev Gateway Utilities, only gateway contract can call + /// @param _from The address of account to burn token. + /// @param _tokenIds The list of token ids to burn. + /// @param _amounts The list of corresponding amount of token to burn. + function batchBurn(address _from, uint256[] calldata _tokenIds, uint256[] calldata _amounts) external; +} +``` + +### Adding an ERC1155 token to the Scroll Bridge + +All assets can be bridged securely and permissionlessly through Gateway contracts deployed by any developer. However, Scroll also manages an ERC1155 Gateway contract where all NFTs created by the community are welcome. Being part of the Scroll-managed Gateway means you won't need to deploy the Gateway contracts, and your token will appear in the Scroll frontend. To be part of the Scroll Gateway, you must contact the Scroll team to add the token to both L1 and L2 Gateway contracts. To do so, follow the instructions on the [token lists](https://github.com/scroll-tech/token-list) repository to add your token to the Scroll official frontend. + +## Withdraw ERC1155 tokens from L2 + +The `L2ERC1155Gateway` contract is used to send tokens from L2 to L1. Before bridging, the `L2ERC1155Gateway` contract has to be approved by the token contract. Once that is done, `withdrawERC1155` can be called to perform the asset bridge. + + + + + +## L1ERC1155Gateway API + +Please visit the [npm library](https://www.npmjs.com/package/@scroll-tech/contracts?activeTab=code) for the complete Scroll contract API documentation. + +### depositERC1155 + +```solidity +function depositERC1155( + address _token, + address _to, + uint256 _tokenId, + uint256 _amount, + uint256 _gasLimit +) external payable; +``` + +Deposit an ERC1155 token from L1 to a recipient's account on L2. + +| Parameter | Description | +| --------- | ------------------------------------------------- | +| token | The address of ERC1155 token contract on L1. | +| to | The address of recipient's account on L2. | +| tokenId | The NFT id to deposit. | +| amount | The amount of tokens to deposit. | +| gasLimit | Gas limit required to complete the deposit on L2. | + +### updateTokenMapping + +```solidity +function updateTokenMapping(address _l1Token, address _l2Token) external; +``` + +Update the mapping that connects an ERC1155 token contract from L1 to L2. + +| Parameter | Description | +| --------- | ------------------------------------------------- | +| \_l1Token | The address of the ERC1155 token in L1. | +| \_l2Token | The address of corresponding ERC1155 token in L2. | + +## L2ERC1155Gateway API + +### withdrawERC1155 + +```solidity +function withdrawERC1155(address token, address to, uint256 tokenId, uint256 amount, uint256 gasLimit) external payable; +``` + +Send ERC1155 tokens from L2 to a recipient's account on L1. + +| Parameter | Description | +| --------- | ------------------------------------------------------------------------ | +| token | The address of ERC1155 token contract on L2. | +| to | The address of recipient's account on L1. | +| tokenId | The NFT id to withdraw. | +| amount | The amount of tokens to withdraw. | +| gasLimit | Unused, but included for potential forward compatibility considerations. | + +### updateTokenMapping + +```solidity +function updateTokenMapping(address _l1Token, address _l2Token) external; +``` + +Update the mapping that connects an ERC1155 token contract from L2 to L1. + +| Parameter | Description | +| --------- | ------------------------------------------------- | +| \_l1Token | The address of th ERC1155 token in L1. | +| \_l2Token | The address of corresponding ERC1155 token in L2. | diff --git a/src/content/docs/en/developers/l1-and-l2-bridging/erc721-nft-bridge.mdx b/src/content/docs/en/developers/l1-and-l2-bridging/erc721-nft-bridge.mdx new file mode 100644 index 000000000..a0f4928e9 --- /dev/null +++ b/src/content/docs/en/developers/l1-and-l2-bridging/erc721-nft-bridge.mdx @@ -0,0 +1,131 @@ +--- +section: developers +date: Last Modified +title: "ERC721 NFT Bridge" +lang: "en" +permalink: "TODO" +excerpt: "TODO" +--- + +import Aside from "../../../../../components/Aside.astro" + +## Deposit ERC721 tokens from L1 + +NFT bridging from L1 to L2 is done via the `L1ERC721Gateway` contract instead of using a router. Similarly to bridging ERC20 tokens, we use the `depositERC721` function to send tokens to L2, and we can later retrieve them back to L1 using `withdrawERC721` on the `L2ERC721Gateway` contract deployed on L2. + +NFT contracts on both L1 and l2 must be launched and connected through the Gateways to enable bridging. This means deposit and withdraw transactions will revert if a contract on either L1 or L2 is missing or not mapped through the `updateTokenMapping` function. + + + +### Creating a ScrollERC721 token on L2 + +To deposit an ERC721 token to L2, a token contract compatible with the `IScrollERC721` standard has to be launched and mapped on a `L1ERC721Gateway` and `L2ERC721Gateway` on both L1 and L2, respectively. This contract has to grant permission to the Gateway on L2 to mint when a token is deposited and burn when the token is withdrawn. + +The following interface is the `IScrollERC721` needed for deploying ERC721 tokens compatible with the `L2ERC721Gateway` on L2. + +```solidity +interface IScrollERC721 { + /// @notice Return the address of Gateway the token belongs to. + function gateway() external view returns (address); + + /// @notice Return the address of counterpart token. + function counterpart() external view returns (address); + + /// @notice Mint some token to recipient's account. + /// @dev Gateway Utilities, only gateway contract can call + /// @param _to The address of recipient. + /// @param _tokenId The token id to mint. + function mint(address _to, uint256 _tokenId) external; + + /// @notice Burn some token from account. + /// @dev Gateway Utilities, only gateway contract can call + /// @param _tokenId The token id to burn. + function burn(uint256 _tokenId) external; +} +``` + +### Adding ERC721 NFTs to the Scroll Bridge + +All assets can be bridged securely and permissionlessly through Gateway contracts deployed by any developer. However, Scroll also manages an ERC721 Gateway contract where all NFTs created by the community are welcome. Being part of the Scroll-managed Gateway means you won't need to deploy the Gateway contracts, and your token will appear in the Scroll frontend. To be part of the Scroll Gateway, you must contact the Scroll team to add the NFT on both L1 and L2 Gateway contracts. To do so, follow the instructions on the [token lists](https://github.com/scroll-tech/token-list) repository to add a new token to the Scroll official frontend. + +## Withdraw ERC721 tokens from Scroll + +The `L2ERC721Gateway` contract is used to send tokens from L2 to L1. Before bridging, the `L2ERC721Gateway` contract has to be approved by the NFT contract. Once that is done, `withdrawERC721` can be called to perform the asset bridge. + + + + + +## L1ERC721Gateway API + +Please visit the [npm library](https://www.npmjs.com/package/@scroll-tech/contracts?activeTab=code) for the complete Scroll contract API documentation. + +### depositERC721 + +```solidity +function depositERC721(address _token, address _to, uint256 _tokenId, uint256 _gasLimit) external payable; +``` + +Deposit an ERC721 NFT from L1 to a recipient's account on L2. + +| Parameter | Description | +| --------- | ------------------------------------------------- | +| token | The address of ERC721 NFT contract on L1. | +| to | The address of recipient's account on L2. | +| tokenId | The NFT id to deposit. | +| gasLimit | Gas limit required to complete the deposit on L2. | + +### updateTokenMapping + +```solidity +function updateTokenMapping(address _l1Token, address _l2Token) external; +``` + +Update the mapping that connects an NFT contract from L1 to L2. + +| Parameter | Description | +| --------- | ------------------------------------------------ | +| \_l1Token | The address of ERC721 token in L1. | +| \_l2Token | The address of corresponding ERC721 token in L2. | + +## L2ERC721Gateway API + +### withdrawERC721 + +```solidity +function depositERC721(address _token, address _to, uint256 _tokenId, uint256 _gasLimit) external payable; +``` + +Send an ERC721 NFT from L2 to a recipient's account on L1. + +| Parameter | Description | +| --------- | ------------------------------------------------------------------------ | +| token | The address of ERC721 NFT token contract on L2. | +| to | The address of recipient's account on L1. | +| tokenId | The NFT id to deposit. | +| gasLimit | Unused, but included for potential forward compatibility considerations. | + +### updateTokenMapping + +```solidity +function updateTokenMapping(address _l1Token, address _l2Token) external; +``` + +Update the mapping that connects an NFT contract from L2 to L1. + +| Parameter | Description | +| --------- | ------------------------------------------------ | +| \_l2Token | The address of ERC721 token in L2. | +| \_l1Token | The address of corresponding ERC721 token in L1. | diff --git a/src/content/docs/en/developers/l1-and-l2-bridging/eth-and-erc20-token-bridge.mdx b/src/content/docs/en/developers/l1-and-l2-bridging/eth-and-erc20-token-bridge.mdx new file mode 100644 index 000000000..caeb38476 --- /dev/null +++ b/src/content/docs/en/developers/l1-and-l2-bridging/eth-and-erc20-token-bridge.mdx @@ -0,0 +1,203 @@ +--- +section: developers +date: Last Modified +title: "ETH and ERC20 token bridge" +lang: "en" +permalink: "TODO" +excerpt: "TODO" +--- + +import Aside from "../../../../../components/Aside.astro" + +## Deposit ETH and ERC20 tokens from L1 + +The Gateway Router allows ETH and ERC20 token bridging from L1 to L2 using the `depositETH` and `depositERC20` functions respectively. It is a permissionless bridge deployed on L1. Notice that ERC20 tokens will have a different address on L2, you can use the `getL2ERC20Address` function to query the new address. + + + +When bridging ERC20 tokens, you don’t have to worry about selecting the right Gateway. This is because the `L1GatewayRouter` will choose the correct underlying entry point to send the message: + +- `L1StandardERC20Gateway`: This Gateway permits any ERC20 deposit and will be selected as the default by the L1GatewayRouter for an ERC20 token that doesn’t need custom logic on L2. On the very first token bridging, a new token will be created on L2 that implements the ScrollStandardERC20. To bridge a token, call the `depositERC20` function on the `L1GatewayRouter`. +- `L1CustomERC20Gateway`: This Gateway will be selected by the `L1GatewayRouter` for tokens with custom logic. For an L1/L2 token pair to work on the Scroll Custom ERC20 Bridge, the L2 token contract has to implement `IScrollStandardERC20`. Additionally, the token should grant `mint` or `burn` capability to the `L2CustomERC20Gateway`. Visit the [bridge-erc20-through-the-custom-gateway.md](../../developer-guides/bridge-erc20-through-the-custom-gateway.md "mention") guide for a step-by-step example of how to bridge a custom token. + +All Gateway contracts will form the message and send it to the `L1ScrollMessenger` which can send arbitrary messages to L2. The `L1ScrollMessenger` passes the message to the `L1MessageQueue`. Any user can send messages directly to the Messenger to execute arbitrary data on L2. This means they can execute any function on L2 from a transaction made on L1 via the bridge. Although an application could directly pass messages to existing token contracts, the Gateway abstracts the specifics and simplifies making transfers and calls. + + + +When a new block gets created on L1, the Watcher will detect the message on the `L1MessageQueue` and will pass it to the Relayer service, which will submit the transaction to the L2 via the l2geth node. Finally, the l2geth node will pass the transaction to the `L2ScrollMessagner` contract for execution on L2. + +## Withdraw ETH and ERC20 tokens from L2 + +The L2 Gateway is very similar to the L1 Gateway. We can withdraw ETH and ERC20 tokens back to L1 using the `withdrawETH` and `withdrawERC20` functions. The contract address is deployed on L2. We use the `getL1ERC20Address` to retrieve the token address on L1. + + + + + +## Creating an ERC20 token with custom logic on L2 + +If a token needs custom logic on L2, it will need to be bridged through a `L1CustomERC20Gateway` and `L2CustomERC20Gateway` respectively. The custom token on L2 will need to give permission to the Gateway to mint new tokens when a deposit occurs and to burn when tokens are withdrawn + +The following interface is the `IScrollStandardERC20` needed for deploying tokens compatible with the `L2CustomERC20Gateway` on L2. + +```solidity +interface IScrollStandardERC20 { + /// @notice Return the address of Gateway the token belongs to. + function gateway() external view returns (address); + + /// @notice Return the address of counterpart token. + function counterpart() external view returns (address); + + /// @dev ERC677 Standard, see https://github.com/ethereum/EIPs/issues/677 + /// Defi can use this method to transfer L1/L2 token to L2/L1, + /// and deposit to L2/L1 contract in one transaction + function transferAndCall(address receiver, uint256 amount, bytes calldata data) external returns (bool success); + + /// @notice Mint some token to recipient's account. + /// @dev Gateway Utilities, only gateway contract can call + /// @param _to The address of recipient. + /// @param _amount The amount of token to mint. + function mint(address _to, uint256 _amount) external; + + /// @notice Mint some token from account. + /// @dev Gateway Utilities, only gateway contract can call + /// @param _from The address of account to burn token. + /// @param _amount The amount of token to mint. + function burn(address _from, uint256 _amount) external; +} +``` + +### Adding a Custom L2 ERC20 token to the Scroll Bridge + +Tokens can be bridged securely and permissionlessly through Gateway contracts deployed by any developer. However, Scroll also manages an ERC20 Router and a Gateway where all tokens created by the community are welcome. Being part of the Scroll-managed Gateway means you won't need to deploy the Gateway contracts, and your token will appear in the Scroll frontend. To be part of the Scroll Gateway, you must contact the Scroll team to add the token to both L1 and L2 bridge contracts. To do so, follow the instructions on the [token lists](https://github.com/scroll-tech/token-list) repository to add your new token to the official Scroll frontend. + +## L1 Gateway API + +Please visit the [npm library](https://www.npmjs.com/package/@scroll-tech/contracts?activeTab=code) for the complete Scroll contract API documentation. + +### depositETH + +```solidity +function depositETH(address _to, uint256 _amount, uint256 _gasLimit) public payable; +``` + +Sends ETH from L1 to L2. + +| Parameter | Description | +| --------- | --------------------------------------------------------------------------------------------------------------------------- | +| to | The address of recipient's account on L2. | +| amount | The amount of token to transfer, in wei. | +| gasLimit | Gas limit required to complete the deposit on L2. From 4000 to 10000 gas limit should be enough to process the transaction. | + +### depositERC20 + +```solidity +function depositERC20(address _token, address _to, uint256 _amount, uint256 _gasLimit) payable; +``` + +Sends ERC20 tokens from L1 to L2. + +| Parameter | Description | +| --------- | --------------------------------------------------------------------------------------------------------------------------- | +| token | The token address on L1. | +| to | The address of recipient's account on L2. | +| amount | The amount of token to transfer, in wei. | +| gasLimit | Gas limit required to complete the deposit on L2. From 4000 to 10000 gas limit should be enough to process the transaction. | + +### getL2ERC20Address + +```solidity +function getL2ERC20Address(address _l1Token) external view returns (address); +``` + +Returns the corresponding L2 token address given L1 token address. + +| Parameter | Description | +| --------- | ------------------------ | +| \_l1Token | The address of l1 token. | + +### updateTokenMapping + +```solidity +function updateTokenMapping(address _l1Token, address _l2Token) external; +``` + +Update the mapping that connects an ERC20 token from L1 to L2. + +| Parameter | Description | +| --------- | ----------------------------------------------- | +| \_l1Token | The address of the ERC20 token in L1. | +| \_l2Token | The address of corresponding ERC20 token in L2. | + +## L2 Gateway API + +### withdrawETH + +```solidity +function withdrawETH(address to, uint256 amount, uint256 gasLimit) external payable; +``` + +Sends ETH from L2 to L1. + +| Parameter | Description | +| --------- | ------------------------------------------------------------------------------------------------------- | +| to | The address of recipient's account on L1. | +| amount | The amount of token to transfer, in wei. | +| gasLimit | Gas limit required to complete the deposit on L1. This is optional, send 0 if you don’t want to set it. | + +### withdrawERC20 + +```solidity +function withdrawERC20(address token, address to, uint256 amount, uint256 gasLimit) external payable; +``` + +Sends ERC20 tokens from L2 to L1. + +| Parameter | Description | +| --------- | ------------------------------------------------------------------------------------------------------- | +| token | The token address on L2. | +| to | The address of recipient's account on L1. | +| amount | The amount of token to transfer, in wei. | +| gasLimit | Gas limit required to complete the deposit on L1. This is optional, send 0 if you don’t want to set it. | + +### getL1ERC20Address + +```solidity +function getL1ERC20Address(address l2Token) external view returns (address); +``` + +Returns the corresponding L1 token address given an L2 token address. + +| Parameter | Description | +| --------- | ---------------------------- | +| l2Token | The address of the L2 token. | + +### updateTokenMapping + +```solidity +function updateTokenMapping(address _l1Token, address _l2Token) external; +``` + +Update the mapping that connects an ERC20 contract from L2 to L1. + +| Parameter | Description | +| --------- | ----------------------------------------------- | +| \_l2Token | The address of the ERC20 token in L2. | +| \_l1Token | The address of corresponding ERC20 token in L1. | diff --git a/src/content/docs/en/developers/l1-and-l2-bridging/the-scroll-messenger.mdx b/src/content/docs/en/developers/l1-and-l2-bridging/the-scroll-messenger.mdx new file mode 100644 index 000000000..dfedacbc1 --- /dev/null +++ b/src/content/docs/en/developers/l1-and-l2-bridging/the-scroll-messenger.mdx @@ -0,0 +1,71 @@ +--- +section: developers +date: Last Modified +title: "The Scroll Messenger" +lang: "en" +permalink: "todo" +excerpt: "The Scroll Messenger documentation for arbitrary message passing between L1 and L2." +--- + +import Aside from "../../../../../components/Aside.astro" + +You can send arbitrary messages from L1 to L2 or vice versa through the Scroll Messenger contracts. This means we can execute functions on another chain in a secure and permissionless way. If you want to send a message from L1 to L2, use the messenger smart contract deployed on L1. If you want to send a message from L2 to L1, use the contract deployed on L1. + + + +## Messenger API + +Please visit the [npm library](https://www.npmjs.com/package/@scroll-tech/contracts?activeTab=code) for the complete Scroll contract API documentation. + +### sendMessage + +```solidity +function sendMessage( + address target, + uint256 value, + bytes calldata message, + uint256 gasLimit, + address refundAddress +) external payable; +``` + +Sends arbitrary data from one chain to another. It allows us to execute functions cross-chain. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterDescription
target + The address of account who receive the message. The receiver can be either a smart contract or a EOA wallet. +
valueThe amount of ether passed when call target contract.
messageThe content of the message. This is the arbitrary calldata to be executed.
gasLimitGas limit required to complete the message relay on corresponding chain.
refundAddressThe address of account who will receive the refunded fee.