# Bit Token Migration Analysis (Living Research Document) ## **Analysis of comparable tokens** #### **Arbitrum** <table> <tr> <td colspan="2" ><strong>L2 Token Components (Native Token) -<a href="https://arbiscan.io/address/0xc4ed0a9ea70d5bcc69f748547650d32cc219d882#code"> Implementation Contract</a></strong> </td> </tr> <tr> <td>ERC20 Modules </td> <td>ERC20Upgradeable, ERC20BurnableUpgradeable, ERC20PermitUpgradeable </td> </tr> <tr> <td>Governance Modules </td> <td>ERC20VotesUpgradeable </td> </tr> <tr> <td>Access Control Modules </td> <td>OwnableUpgradeable </td> </tr> <tr> <td>Additional Functionality </td> <td>TransferAndCallToken, L2ArbitrumToken </td> </tr> <tr> <td>Is Upgradable? </td> <td>Yes </td> </tr> </table> The Arbitrum L2 token is a base upgradable ERC20 token with added functionality for voting and delegation, permits (EIP 2612 signed approvals), burning, minting, and access control by a single party. It includes some additional features in the TransferAndCallToken contract that are similar to the ERC677 standard allowing tokens to be transferred and a contract to be called atomically. <table> <tr> <td colspan="2" ><strong>Modified/Additional Functionality (TransferAndCallToken, L2ArbitrumToken)</strong> </td> </tr> <tr> <td>mint(address recipient, uint256 amount) external onlyOwner <p> From L2ArbitrumToken </td> <td>Mint has been modified to only allow the owner to mint new tokens with the following additional restrictions: <ol> <li>The amount of tokens to be minted is below a certain inflation cap, defined as MINT_CAP_NUMERATOR divided by MINT_CAP_DENOMINATOR. This cap is set to a maximum of 0% of the total token supply. <li>That the current block timestamp is equal to or greater than the nextMint variable, which ensures that the minting can only occur once per year subject to governance changing the mintInterval variable. </li> </ol> </td> </tr> <tr> <td>transferAndCall(address _to, uint256 _value, bytes memory _data) <p> From TransferAndCallToken </td> <td>The transferAndCall function allows a user to transfer tokens to a contract address along with additional data. The function takes three parameters: the recipient address, the amount of tokens to transfer, and the extra data to be passed to the receiving contract. The function first calls the transfer function of the ERC20Upgradeable contract to transfer the tokens to the recipient address. It then emits a Transfer event with the sender address, recipient address, amount of tokens transferred, and the extra data. Next, the function checks if the recipient address is a contract by calling the isContract function, which returns a Boolean value indicating if the recipient is a contract. If the recipient is a contract, the function calls the contractFallback function to handle the extra data passed to the contract. Finally, the function returns a Boolean value indicating the success of the transfer and call operation. Note that the implementation is not exactly the same as the ERC677 standard because the contract does not expect a boolean to be returned with the onTokenTransfer function. </td> </tr> </table> <table> <tr> <td colspan="2" ><strong>L1 Token Components (Bridged Token) -<a href="https://etherscan.io/address/0xad0c361ef902a7d9851ca7dcc85535da2d3c6fc7#code"> Implementation Contract</a></strong> </td> </tr> <tr> <td>ERC20 Modules </td> <td>ArbitrumEnabledToken, ERC20Upgradeable, ERC20PermitUpgradeable </td> </tr> <tr> <td>Governance Modules </td> <td>None </td> </tr> <tr> <td>Access Control Modules </td> <td>None, but have gated modifiers in L1ArbitrumToken (see: onlyArbGateway ) </td> </tr> <tr> <td>Additional Functionality </td> <td>L1ArbitrumToken, ArbitrumEnabledToken, TransferAndCallToken </td> </tr> <tr> <td>Is Upgradable? </td> <td>Yes </td> </tr> </table> The Arbitrum L1 token is a base upgradable ERC20 token that serves as a representation of the corresponding native L2 token. It has added functionality for permits (EIP 2612 signed approvals), with added functionality for voting and delegation, permits (EIP 2612 signed approvals), burning (bridge gated), minting (bridge gated). It includes some additional features in the TransferAndCallToken contract that are similar to the ERC677 standard allowing tokens to be transferred and a contract to be called atomically. There’s also additional interfaces linking the L1 token representation to the L2 token representation after it has been registered. <table> <tr> <td colspan="2" ><strong>Modified/Additional Functionality (L1ArbitrumToken, ArbitrumEnabledToken, TransferAndCallToken)</strong> </td> </tr> <tr> <td>function bridgeMint(address account, uint256 amount) public override(INovaArbOneReverseToken) onlyArbOneGateway <p> From L1ArbitrumToken </td> <td>The bridgeMint function allows the arbitrum bridge, as specified by the arbOneGateway to mint arbitrum tokens on L1. </td> </tr> <tr> <td>function bridgeBurn(address account, uint256 amount) public override(INovaArbOneReverseToken) onlyArbOneGateway <p> From L1ArbitrumToken </td> <td>The bridgeBurn function allows the arbitrum bridge, as specified by the arbOneGateway to burn tokens on L1. </td> </tr> <tr> <td>function registerTokenOnL2(RegistrationParams memory novaParams) public payable <p> From L1ArbitrumToken </td> <td>Registers the token on both Arb One and Nova. Can only be called once once after the contract is deployed. </td> </tr> <tr> <td>modifier onlyArbOneGateway() <p> From L1ArbitrumToken </td> <td>Modifier that restricts the function to be called only by the Arb One gateway. </td> </tr> <tr> <td>function isArbitrumEnabled() external view override returns (uint8) <p> From ArbitrumEnabledToken </td> <td>Returns a uint8 indicating that the token is Arbitrum-enabled. </td> </tr> <tr> <td>transferAndCall(address _to, uint256 _value, bytes memory _data) <p> From TransferAndCallToken </td> <td>See function in L2 table </td> </tr> </table> #### **Optimism** <table> <tr> <td colspan="2" ><strong>L2 Token Components (Native Token) -<a href="https://optimistic.etherscan.io/token/0x4200000000000000000000000000000000000042#code"> Implementation Contract</a></strong> </td> </tr> <tr> <td>ERC20 Modules </td> <td>ERC20, ERC20Permitl, ERC20Burnable, EIP712 </td> </tr> <tr> <td>Governance Modules </td> <td>ERC20Votes </td> </tr> <tr> <td>Access Control Modules </td> <td>Ownable </td> </tr> <tr> <td>Additional Functionality </td> <td>GovernanceToken </td> </tr> <tr> <td>Is Upgradable? </td> <td>No </td> </tr> </table> The Optimism L2 token is a base ERC20 token with added functionality for voting and delegation, permits (signed approvals), burning, minting, and access control. <table> <tr> <td colspan="2" ><strong>Modified/Additional Functionality (GovernanceToken)</strong> </td> </tr> <tr> <td>function mint(address recipient, uint256 amount) external onlyOwner </td> <td>Mint has been modified to only allow the owner of the contract to mint additional supply. </td> </tr> </table> Optimism appears to be a L2 based token without an official L1 equivalent. #### **Boba** <table> <tr> <td colspan="2" ><strong>L1 Token Components (Native Token) -<a href="https://etherscan.io/token/0x42bbfa2e77757c645eeaad1655e0911a7553efbc#code"> Implementation Contract</a></strong> </td> </tr> <tr> <td>ERC20 Modules </td> <td>ERC20, ERC20Burnable, ERC20Permit </td> </tr> <tr> <td>Governance Modules </td> <td>ERC20Votes, ERC20VotesComp </td> </tr> <tr> <td>Access Control Modules </td> <td>None </td> </tr> <tr> <td>Additional Functionality </td> <td>BOBA </td> </tr> <tr> <td>Is Upgradable? </td> <td>No </td> </tr> </table> The BOBA L1 token is a base ERC20 token with added functionality for voting and delegation (exactly the same as compound), burning (not exposed), minting (not exposed), and permits (EIP 2612 signed approvals). The BOBA L1 Token contract does not contain any additional functionality beyond what has been imported that has been exposed. <table> <tr> <td colspan="2" ><strong>L2 Token Components -<a href="https://bobascan.com/token/0xa18bf3994c0cc6e3b63ac420308e5383f53120d7#code"> Implementation Contract</a></strong> </td> </tr> <tr> <td>ERC20 Modules </td> <td>ERC20, ERC20Permit </td> </tr> <tr> <td>Governance Modules </td> <td>ERC20Votes, ERC20VotesComp </td> </tr> <tr> <td>Access Control Modules </td> <td>None, but have gated modifiers in L2GovernanceERC20 (see: onlyBridge ) </td> </tr> <tr> <td>Additional Functionality </td> <td>L2GovernanceERC20 </td> </tr> <tr> <td>Is Upgradable? </td> <td>No </td> </tr> </table> <table> <tr> <td colspan="2" ><strong>Modified/Additional Functionality (L2GovernanceERC20)</strong> </td> </tr> <tr> <td>function mint(address recipient, uint256 amount) external onlyOwner </td> <td>Mint has been modified to only allow the owner of the contract to mint additional supply. </td> </tr> </table> ## **Feature Set Considerations** #### **Upgradability** Though upgradability can be controversial due to the risk of introducing bugs and unwanted features in upgrades I believe that if we handle ownership of the contracts properly there should not be an issue. Immediately upon deploying the contract we should transfer the ownership of the L1 instance of the token to BitDAO so that any upgrade changes are gated through governance - L2 token instance upgrades can be done by upgrading the BVM_BIT precompile at the protocol level through governance votes as well - though this would not have a smart contract guarantee. This allows for us to add additional features down the road (eg natively integrating One-To-Many delegation, enabling proper cross chain governance and so on) without having to perform another costly token migration. #### **Cross-chain Governance** We can minimally enable crosschain Governance through including the ERC20Votes or ERC20VotesComp on both the L1 and L2 instances of the new BIT token. Though it does not have to be enabled upon launch we should be able to upgrade the bridge itself to lock tokens with functionality introduced by adding transferAndCall to the contract itself with the sendMessage function in the xDomainMessageSender. To enable it we would need to make a couple of changes to whatever onchain governance contracts we decided to use. Specifically changing gating of functions from checking specifically for token owners to something resembling the following code snippet. ```=solidity require( msg.sender == address(bitL2CrossDomainMessenger) && bitL2CrossDomainMessenger.xDomainMessageSender() == owner ); ``` We should also make it clear that tokens delegated through L2 messages to L1 (as we presume that we will be keeping governance votes on L1 for the time being even if we move to on-chain governance) should be delegated at least N days before the deadline, where N is the number of days in our challenge period - as the message will not be relayed until the end of the challenge period. #### **Redelegate** One to many delegation being natively integrated in the contract is unnecessary at the moment and may also be unnecessary in the future. We propose keeping mass delegation a separate contract for optionality and looking into the Default Framework ([https://github.com/fullyallocated/Default](https://github.com/fullyallocated/Default)) to unify all BIT and Mantle affiliated contracts in a unified composable and upgradable manner. #### **Future EIP1559 compatibility** In order to minimally enable EIP1559 compatibility down the road we will need to enable burning functionality gated by the bridge on both the L1 and L2 versions of the BIT contract. We will also need to consider how we want to approach integrating the EIP1559 token burning mechanic. There are two bridge based approaches we can take: 1. We can transmit the amount to be burned to the bridge and burn it cross message on a per transaction basis. 2. We can have a governance gated function to mass burn an accumulated bitToBurn amount on the bridge. The second approach (which we find preferable) would be more gas efficient and enabled by a basic bridgeBurn function on the new L1 token. It would involve adding a variable to track the amount of bit to be burned bitToBurn that is incremented on a per transaction basis to the bridge and a function that may look something like the following code snippet. ```=solidity function burnBitToBeBurned() onlyGovernance { uint burnAmount = bitToBeBurned; bitToBeBurned = 0; L1BitToken.bridgeBurn(burnAmount); } ``` #### **Access Control** To enable the necessary functionality we will need to take one of two approaches to the bridge. Adding the OpenZeppelin AccessControl contract to the token contract and expressing granular role controls (for example minter role, governance role, bridge role etc) Or manually creating modifiers attached to address identifiers explicitly stored as variables in the contract to gate functions. #### **Burning and Minting** Our L1 token currently does not have burning or minting functionality. We propose at the minimum adding burning functionality gated by governance in order to make proposals to burn token amounts more viable and later on enable EIP1559 to burn the native BIT token as needed. A constrained mint function could also be added in order to enable features such as inflation down the road. #### **Inflation** We also propose adding a constrained mint function to potentially enable future inflation similar to arbitrum's `mint`. We suggest initially setting the inflation amount to `0` in order to ensure that it would require a two step governance process in order to modify the token's mechanics. The function would look something as follows: ``` ``` ## **Recommended New Bit Token** The following is the proposed token to migrate to that is a hybrid of the Optimism and Arbitrum model. We are unable to follow either exactly as our native token is on L1 and has predefined parameters we need to follow, this is an approach somewhat similar to that Boba has taken. Similar to arbitrum we recommend putting this contract behind an OpenZeppelin TransparentUpgradableProxy. This contract is primarily using openzeppelin libraries and is therefore not the most optimized, however it will require minimal auditing as a result of being built from entirely already audited contracts. <table> <tr> <td colspan="2" ><strong>L1 Token Components</strong> </td> </tr> <tr> <td>ERC20 Modules </td> <td>ERC20Upgradeable, ERC20BurnableUpgradeable, ERC20PermitUpgradeable </td> </tr> <tr> <td>Governance Modules </td> <td>ERC20VotesUpgradeable or ERC20VotesCompUpgradeable </td> </tr> <tr> <td>Access Control Modules </td> <td>Ownable and Bridge gated modifiers </td> </tr> <tr> <td>Additional Features </td> <td>TransferAndCallToken, gated burn modifiers, bridge references </td> </tr> <tr> <td>Is Upgradable? </td> <td>Yes </td> </tr> </table> In the L1 Token we propose converting the token into an upgradable contract that includes the following modules <table> <tr> <td colspan="2" ><strong>Modified/Additional Functionality (BIT)</strong> </td> </tr> <tr> <td>modifier onlyL1MantleBridge() </td> <td>We should add a modifier to the L1 token gating functions to the Mantle bridge. </td> </tr> <tr> <td>function bridgeBurn(address account, uint256 amount) public onlyL1MantleBridge </td> <td>We should add a function to allow for the burning of L1 BIT tokens only by the Mantle Bridge. This would allow for the enabling of EIP1559 fee burning as tracked by the bridge at a later date without having to upgrade the BIT token again. </td> </tr> <tr> <td>function burn(address account, uint256 amount) public onlyOwner </td> <td>We should add a function to allow for the burning of L1 BIT tokens by the owner of the token contract (which should be set to governance) to better enable governance proposals like buy and burn to actually programmatically decrease the token supply cap rather than sending to an alternate burn address. </td> </tr> <tr> <td>address l1MantleBridge </td> <td>We should store a reference to the MantleBridge in the contract for gating purposes </td> </tr> </table> The contract would look something like the following stub: ```=solikdity contract Bit is Initializable, ERC20Upgradeable, ERC20BurnableUpgradeable, ERC20PermitUpgradeable, ERC20VotesCompUpgradeable, OwnableUpgradeable, TransferAndCallToken { address public l1MantleBridge; // add empty constructor // add initialization function **do not forget to initialize on deploy** modifier onlyL1MantleBridge() { require(msg.sender == l1MantleBridge, "err"); _; } function bridgeBurn(address account, uint256 amount) public onlyL1MantleBridge { _burn(account, amount); } function burn(address account, uint256 amount) public onlyOwner { _burn(account, amount); } // add nessessary function overrides } ``` This token would be entirely compatible with the existing functionality of the current bit token. For the L2 token we propose making a few changes making the token compatible with then newly proposed L1 version of the token. The token itself does not need to be made upgradable as it is a precompile, and upgrading it would be done on the protocol level rather than through typical contract upgrade methods. <table> <tr> <td colspan="2" ><strong>L2 Token Components</strong> </td> </tr> <tr> <td>ERC20 Modules </td> <td>ERC20, ERC20Burnable, ERC20Permit </td> </tr> <tr> <td>Governance Modules </td> <td>ERC20Votes or ERC20VotesComp </td> </tr> <tr> <td>Access Control Modules </td> <td>Bridge gated modifiers </td> </tr> <tr> <td>Additional Features </td> <td>TransferAndCallToken </td> </tr> <tr> <td>Is Explicitly Upgradable? </td> <td>No </td> </tr> </table> <table> <tr> <td colspan="2" ><strong>Modified/Additional Functionality (L2BIT)</strong> </td> </tr> <tr> <td>modifier onlyL2MantleBridge() </td> <td>We should add a modifier to the L2 token gating functions to the L2 Mantle bridge. </td> </tr> <tr> <td>modifier onlyGovernance() </td> <td>We should add a modifier to the L2 token gating functions to governance. </td> </tr> <tr> <td>function bridgeBurn(address account, uint256 amount) public onlyL2MantleBridge </td> <td>We should add a function to allow for the burning of L2 BIT tokens only by the Mantle Bridge. This would allow for the enabling of EIP1559 fee burning as tracked by the bridge at a later date without having to upgrade the BIT token again. </td> </tr> <tr> <td>function burn(address account, uint256 amount) onlyGovernance public </td> <td>We should add a function to allow for the burning of L1 BIT tokens by governance </td> </tr> <tr> <td>address Governance </td> <td>We should store a reference to the governance multisig in the L2 token as as a precompile it does not have an owner. </td> </tr> <tr> <td>address l2MantleBridge </td> <td>We should store a reference to the MantleBridge in the contract as well </td> </tr> <tr> <td>address l1Bit </td> <td>We should store a reference to the L1 bit address in the L2 contract </td> </tr> </table> The contract would look something like the following stub: ```=solidity contract L2Bit is IL2StandardERC20, ERC20, ERC20Permit, ERC20Votes, ERC20VotesComp { // empty contructor address public l2MantleBridge; address l1Bit; address public governance; modifier onlyMantleBridge() { require(msg.sender == l2MantleBridge, "err"); _; } modifier onlyGovernance() { require(msg.sender == governance, "err"); _; } function bridgeBurn(address account, uint256 amount) public onlyL2MantleBridge { _burn(account, amount); } function burn (address account, uint256 amount) public onlyGovernance { _burn(account, amount); } // add nessessary overrides } ``` ### **Considerations** **ERC20VotesUpgradeable vs ERC20VotesCompUpgradeable** We need to consider if we want to match the exact Compound delegation functionality of the current BIT token (keeping compatibility with Governor Alpha and Governor Bravo) or if we want to extend and genericize functionality with the ERC20VotesUpgradable module. Note that ERC20VotesUpgradable supports a token supply cap of 2^224^ - 1 vs 2^96^ - 1 should we choose to make the token inflatable later on as well. See:[ ERC20VotesCompUpgradeable](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/token/ERC20/extensions/ERC20VotesCompUpgradeable.sol),[ ERC20VotesUpgradeable](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/token/ERC20/extensions/ERC20VotesUpgradeable.sol) **Minting by governance and bridge on the L1 and L2 token** We also want to consider if we want to ship with “mint” and “bridgeMint” functions or if we want to keep the feature set as minimal as possible and not add the possibility to exceed the total supply cap of bit. We do not presently need either with the existing bridging mechanics. ### **Migration Strategy Analysis** We must first note that the migration must be gradual and that we must give users a fair amount of time to migrate from the BITV1 to BITV2 tokens and still providing ample liquidity so they are unable to unwind any built up positions overtime. We’ve identified two possible strategies with associated examples for how we can perform the token migration: A smart contract enabled token swap, or a snapshot + airdrop #### **One Way Liquidity Pool/Token Swap** A token swap would involve creating a smart contract and interface for users to interact with to swap their BitV1 tokens for a predetermined ratio of BitV2 tokens. We would mint the maximum supply of BitV2 into the contract for the users to interact with. The token swap contract address and official site must be clearly communicated to users to avoid potential lookalike scams and we must try to provide all hands on deck support during the migration push to ensure users We would also need to collaborate with exchanges to update the tokens held by users to the novel token and having them redeem their held tokens against the contract to distribute to users. The interface of the TokenSwap contract would look something like this: Example: Olympus v1 to Olympus v2 Upgrade [https://docs.olympusdao.finance/main/basics/migration/](https://docs.olympusdao.finance/main/basics/migration/) #### **Snapshot + Airdrop or claim** This process would involve taking a snapshot of user token balances and airdropping the equivalent amount of bitV2 to each address. Users would need to withdraw any LP during the snapshot time period. This would be a rather gas intensive process. Example: Gala v1 to v2 upgrade [https://medium.com/nerd-for-tech/gala-airdrop-incoming-details-on-the-token-upgrade-979e5e62d525](https://medium.com/nerd-for-tech/gala-airdrop-incoming-details-on-the-token-upgrade-979e5e62d525) ### **Recommended Migration Strategy** Overall we would recommend using the One Way Liquidity Pool/Token Swap strategy to migrate the token as it will be much less cost intensive to us.