Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

uses arb test mint token as example for updated contracts #4

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 104 additions & 23 deletions contracts/Dev.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,93 @@ import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IInbox} from "interfaces/IInbox.sol";
import {IOutbox} from "interfaces/IOutbox.sol";
import {IBridge} from "interfaces/IBridge.sol";
import {IL1CustomGateway} from "interfaces/IL1CustomGateway.sol";
import {L1MintableToken, ICustomToken} from "interfaces/ICustomToken.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-ERC20PermitUpgradeable.sol";
import {TransferAndCallToken} from "./TransferAndCallToken.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";

contract Dev is ERC20Upgradeable, ReentrancyGuardUpgradeable {
// https://github.com/OffchainLabs/arbitrum/blob/master/packages/arb-bridge-peripherals/contracts/tokenbridge/libraries/aeERC20.sol#L21
// removes _setupDecimals since it is no longer used in OZ 0.4

contract aeERC20 is ERC20PermitUpgradeable, TransferAndCallToken, ReentrancyGuardUpgradeable {
using AddressUpgradeable for address;

function _initialize(
string memory name_,
string memory symbol_
) internal initializer {
__ERC20Permit_init(name_);
__ERC20_init(name_, symbol_);
}
}

contract ArbDEVTokenL1 is aeERC20, ICustomToken {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need not to create this contract in upgradable?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this contract should be upgradeable. Because Arbitrum is still unstable, and the requirements may change.

using SafeERC20 for IERC20;

address public l2Token;
address public gateway;
address public inbox;
address public bridge;
bool private shouldRegisterGateway;
address public devAddress;
address public gateway;

event EscrowMint(address indexed minter, uint256 amount);
// uint8 public constant TEST = uint8(0xa4b1);

function initialize(address _l2TokenAddr, address _gatewayAddr, address _inbox, address _devAddress) public initializer {
__ERC20_init("Dev", "DEV");
l2Token = _l2TokenAddr;
gateway = _gatewayAddr;
inbox = _inbox;
constructor(address _bridge, address _devAddress, address _gateway) public {
bridge = _bridge;
aeERC20._initialize("Dev", "DEV");
devAddress = _devAddress;
gateway = _gateway;
}

function mint() external {
_mint(msg.sender, 50000000);
}
Comment on lines +50 to +52
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this function is needed. Maybe for testing or something?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If such an interface is required, we can upgrade this implementation contract later. Therefore, we can remove functions that we think are unnecessary.


function transferFrom(
address sender,
address recipient,
uint256 amount
) public virtual override(ERC20Upgradeable, ICustomToken) returns (bool) {
return ERC20Upgradeable.transferFrom(sender, recipient, amount);
}

function balanceOf(address account)
public
view
virtual
override(ERC20Upgradeable, ICustomToken)
returns (uint256)
{
return ERC20Upgradeable.balanceOf(account);
}

function escrowMint(uint256 amount) external {
address msgSender = _l2Sender();
require(msgSender == l2Token, "sender must be l2 token");
_mint(gateway, amount);
emit EscrowMint(msgSender, amount);
/// @dev we only set shouldRegisterGateway to true when in `registerTokenOnL2`
function isArbitrumEnabled() external view override returns (uint16) {
require(shouldRegisterGateway, "NOT_EXPECTED_CALL");
// uint8 public constant TEST = uint8(0xa4b1);
return uint16(0xa4b1);
// return TEST;
}

function _l2Sender() private view returns (address) {
IBridge _bridge = IInbox(inbox).bridge();
require(address(_bridge) != address(0), "bridge is zero address");
IOutbox outbox = IOutbox(_bridge.activeOutbox());
require(address(outbox) != address(0), "outbox is zero address");
return outbox.l2ToL1Sender();

function registerTokenOnL2(
address l2CustomTokenAddress,
uint256 maxSubmissionCost,
uint256 maxGas,
uint256 gasPriceBid
// address creditBackAddress
) public {
// we temporarily set `shouldRegisterGateway` to true for the callback in registerTokenToL2 to succeed
bool prev = shouldRegisterGateway;
shouldRegisterGateway = true;

IL1CustomGateway(bridge).registerTokenToL2(
l2CustomTokenAddress,
maxGas,
gasPriceBid,
maxSubmissionCost
);

shouldRegisterGateway = prev;
}

/**
Expand Down Expand Up @@ -73,4 +128,30 @@ contract Dev is ERC20Upgradeable, ReentrancyGuardUpgradeable {
IERC20(devAddress).transfer(msg.sender, _amount);
return true;
}
}
}

contract MintableArbDEVL1 is L1MintableToken, ArbDEVTokenL1 {

constructor(address _bridge, address _devAddress, address _gatewayAddress) public ArbDEVTokenL1(_bridge, _devAddress, _gatewayAddress) {}

function bridgeMint(address account, uint256 amount) public override(L1MintableToken) {
_mint(account, amount);
}

function balanceOf(address account)
public
view
override(ArbDEVTokenL1, ICustomToken)
returns (uint256 amount)
{
return super.balanceOf(account);
}

function transferFrom(
address sender,
address recipient,
uint256 amount
) public override(ArbDEVTokenL1, ICustomToken) returns (bool) {
return super.transferFrom(sender, recipient, amount);
}
}
52 changes: 52 additions & 0 deletions contracts/TransferAndCallToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: MPL-2.0
pragma solidity ^0.8.9;

// https://github.com/OffchainLabs/arbitrum/blob/master/packages/arb-bridge-peripherals/contracts/tokenbridge/libraries/TransferAndCallToken.sol

import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "../interfaces/ITransferAndCall.sol";

// Implementation from https://github.com/smartcontractkit/LinkToken/blob/master/contracts/v0.6/TransferAndCallToken.sol
/**
* @notice based on Implementation from https://github.com/smartcontractkit/LinkToken/blob/master/contracts/v0.6/ERC677Token.sol
* The implementation doesn't return a bool on onTokenTransfer. This is similar to the proposed 677 standard, but still incompatible - thus we don't refer to it as such.
*/
abstract contract TransferAndCallToken is ERC20Upgradeable, ITransferAndCall {
/**
* @dev transfer token to a contract address with additional data if the recipient is a contact.
* @param _to The address to transfer to.
* @param _value The amount to be transferred.
* @param _data The extra data to be passed to the receiving contract.
*/
function transferAndCall(
address _to,
uint256 _value,
bytes memory _data
) public virtual override returns (bool success) {
super.transfer(_to, _value);
emit Transfer(msg.sender, _to, _value, _data);
if (isContract(_to)) {
contractFallback(_to, _value, _data);
}
return true;
}

// PRIVATE

function contractFallback(
address _to,
uint256 _value,
bytes memory _data
) private {
ITransferAndCallReceiver receiver = ITransferAndCallReceiver(_to);
receiver.onTokenTransfer(msg.sender, _value, _data);
}

function isContract(address _addr) private view returns (bool hasCode) {
uint256 length;
assembly {
length := extcodesize(_addr)
}
return length > 0;
}
}
37 changes: 37 additions & 0 deletions interfaces/ICustomToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// SPDX-License-Identifier: MPL-2.0
pragma solidity ^0.8.0;

// taken from https://github.com/OffchainLabs/arbitrum/blob/001bdeecdefbc4eda9a824ef7b39452b46faeb86/packages/arb-bridge-peripherals/contracts/tokenbridge/ethereum/ICustomToken.sol

interface ArbitrumEnabledToken {
/// @notice should return `0xa4b1` if token is enabled for arbitrum gateways
function isArbitrumEnabled() external view returns (uint16);
}

/**
* @title Minimum expected interface for L1 custom token (see TestCustomTokenL1.sol for an example implementation)
*/
interface ICustomToken is ArbitrumEnabledToken {
/**
* @notice Should make an external call to EthERC20Bridge.registerCustomL2Token
*/
function registerTokenOnL2(
address l2CustomTokenAddress,
uint256 maxSubmissionCost,
uint256 maxGas,
uint256 gasPriceBid,
address creditBackAddress
) external;

function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);

function balanceOf(address account) external view returns (uint256);
}

interface L1MintableToken is ICustomToken {
function bridgeMint(address account, uint256 amount) external;
}
13 changes: 13 additions & 0 deletions interfaces/IL1CustomGateway.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: MPL-2.0
pragma solidity ^0.8.0;

/*
* @title Minimum expected interface for L1 custom gateway used https://github.com/OffchainLabs/arbitrum/blob/001bdeecdefbc4eda9a824ef7b39452b46faeb86/packages/arb-bridge-peripherals/contracts/tokenbridge/ethereum/gateway/L1CustomGateway.sol#L100
*/
interface IL1CustomGateway {
function registerTokenToL2(address _l2Address,
uint256 _maxGas,
uint256 _gasPriceBid,
uint256 _maxSubmissionCost
) external payable returns (uint256);
}
25 changes: 25 additions & 0 deletions interfaces/ITransferAndCall.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: MPL-2.0
pragma solidity ^0.8.9;

import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";

interface ITransferAndCall is IERC20Upgradeable {
function transferAndCall(
address to,
uint256 value,
bytes memory data
) external returns (bool success);

event Transfer(address indexed from, address indexed to, uint256 value, bytes data);
Copy link
Member

@Akira-Taniguchi Akira-Taniguchi Oct 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name is duplicated in the Transfer event of IERC20Upgradeable, is that OK?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the interface is declared duplicate, I think you need to remove one or the other.

}

/**
* @notice note that implementation of ITransferAndCallReceiver is not expected to return a success bool
*/
interface ITransferAndCallReceiver {
function onTokenTransfer(
address _sender,
uint256 _value,
bytes memory _data
) external;
}