I am working on an XC20 implementation of an existing ERC20. I am basing the contract off of the Astar here.
I noticed there is some code inherited from the ERC20 contract via the ERC20Wrapper contract that is not needed for this implementation. The ERC20Wrapper is usually used to create a wrapped version of an existing token, but in the case with XC20, the wrapped token already exists and just needs to be bridged to the underlying token. I am curious why the example still has this unused code.
I implemented it with removing all of the extra ERC20 code and was able to get a successful run, being able to mint/burn my xcToken by depositing/withdrawing my underlying token.
Here is my contract code for reference.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import {IERC20} from './Dependencies/IERC20.sol';
import {BAIToken} from './BAIToken.sol';
import {Ownable} from "./Dependencies/Ownable.sol";
// This xBai interface was recieved from the astar XCM documentation
// (https://github.com/AstarNetwork/astar-frame/blob/674356e7b611e561aaf9bf581452cab965cf8e87/examples/assets-erc20/XcBurrito.sol#L12)
interface IERC20Plus is IERC20 {
function mint(address beneficiary, uint256 amount) external returns (bool);
function burn(address who, uint256 amount) external returns (bool);
}
contract XBaiWrapper is Ownable{
IERC20 public immutable underlyingToken;
IERC20Plus public crossChainToken;
constructor(IERC20 _underlyingToken, IERC20Plus _crossChainToken) {
underlyingToken = _underlyingToken;
crossChainToken = _crossChainToken;
}
// --- Events ---
event xBaiMinted(address to, uint256 amount);
event xBaiBurned(address from, uint256 amount);
event baiSupplyChanged(uint256 newTotalSupply);
// --- Admin functions ---
function mintXBai(address _to, uint256 _amount) public onlyOwner {
require( // Make sure amount to mint is greater than 0
_amount != 0,
"Mint amount must be greater than 0"
);
_mintCrossChainToken(_to, _amount);
emit xBaiMinted(_to, _amount);
}
function burnXBai(address _from, uint256 _amount) public onlyOwner {
require( // Make sure amount to burn is greater than 0
_amount != 0,
"Burn amount must be greater than 0"
);
_burnCrossChainToken(_from, _amount);
emit xBaiBurned(_from, _amount);
}
//=====================================================================
// This functiontion is to be used if the total supply of xBai is
// greater than the amount of Bai in this contract. The caller should
// supply this contract with Bai so that the total supply of xBai is
// equal to this contract's balance of Bai.
//=====================================================================
function supplyBai(address _from, uint256 _amount) public onlyOwner {
require( // Make sure amount to recover is greater than 0
_amount != 0,
"Burn amount must be greater than 0"
);
underlyingToken.transferFrom(_from, address(this), _amount);
uint256 newBaiSupply = underlyingToken.balanceOf(address(this));
emit baiSupplyChanged(newBaiSupply);
}
//=====================================================================
// This function is to be used if the total supply of xBai is less than
// the amount of Bai in this contract. The call should recover enough
// Bai so this contract's balance of Bai is equal to the total supply
// of xBai.
//=====================================================================
function recoverBai(address _to, uint256 _amount) public onlyOwner {
require( // Make sure amount to recover is greater than 0
_amount != 0,
"Burn amount must be greater than 0"
);
underlyingToken.transfer(_to, _amount);
uint256 newBaiSupply = underlyingToken.balanceOf(address(this));
emit baiSupplyChanged(newBaiSupply);
}
// --- Public functions ---
//=====================================================================
// Allow a user to deposit underlying tokens and mint the corresponding
// number of wrapped tokens.
//=====================================================================
function depositFor(address _account, uint256 _amount) public returns (bool) {
require( // Make sure amount to deposit is greater than 0
_amount != 0,
"Deposit amount must be greater than 0"
);
underlyingToken.transferFrom(msg.sender, address(this), _amount);
_mintCrossChainToken(_account, _amount);
return true;
}
//=====================================================================
// Allow a user to burn a number of wrapped tokens and withdraw the
// corresponding number of underlying tokens.
//=====================================================================
function withdrawTo(address _account, uint256 _amount) public returns (bool) {
require( // Make sure amount to withdraw is greater than 0
_amount != 0,
"Withdrawal amount must be greater than 0"
);
_burnCrossChainToken(msg.sender, _amount);
underlyingToken.transfer(_account, _amount);
return true;
}
// --- Internal functions ---
function _mintCrossChainToken(address _to, uint256 _amount) internal {
// add here your pre-mint hooks hooks if needed
require(
crossChainToken.mint(_to, _amount),
"Minting xc token failed"
);
// add here your post-mint hooks hooks if needed
emit xBaiMinted(_to, _amount);
}
function _burnCrossChainToken(address _from, uint256 _amount) internal {
// add here your pre-burn hooks hooks if needed
require(
crossChainToken.burn(_from, _amount),
"Burning xc token failed"
);
// add here your post-burn hooks hooks if needed
emit xBaiBurned(_from, _amount);
}
}