NativeConverter
Inherits: Initializable, AccessControlUpgradeable, PausableUpgradeable, ReentrancyGuardUpgradeable, ERC20PermitUser, Versioned
Author: See https://github.com/agglayer/vault-bridge
Native Converter is an optional contract on Layer Ys that converts the underlying token (usually the bridge-wrapped version of the original underlying token from Layer X) to Custom Token, and vice versa, on demand. It can also migrate backing for Custom Token it has minted to Layer X, where vbToken will be minted and locked in LxLy Bridge. Please refer to migrateBackingToLayerX
for more information.
A base contract used to create Native Converters.
@note (ATTENTION) This contract MUST have mint and burn permission on Custom Token. Please refer to CustomToken.sol
for more information.
@note IMPORTANT: The underlying token MUST NOT be a rebasing token, and MUST NOT have transfer hooks (i.e., enable reentrancy); it MAY have a transfer fee.
State Variables
_NATIVE_CONVERTER_STORAGE
The storage slot at which Native Converter storage starts, following the EIP-7201 standard.
Calculated as keccak256(abi.encode(uint256(keccak256("agglayer.vault-bridge.NativeConverter.storage")) - 1)) & ~bytes32(uint256(0xff))
.
bytes32 private constant _NATIVE_CONVERTER_STORAGE =
hex"a14770e0debfe4b8406a01c33ee3a7bbe0acc66b3bde7c71854bf7d080a9c600";
MIGRATOR_ROLE
bytes32 public constant MIGRATOR_ROLE = keccak256("MIGRATOR_ROLE");
PAUSER_ROLE
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
Functions
__NativeConverter_init
The customToken
and underlyingToken
MUST have the same number of decimals. @note (ATTENTION) The decimals of the customToken
and underlyingToken
will default to 18
if they revert on decimals
.
function __NativeConverter_init(
address owner_,
address customToken_,
address underlyingToken_,
address lxlyBridge_,
uint32 layerXLxlyId_,
uint256 nonMigratableBackingPercentage_,
address migrationManager_
) internal onlyInitializing;
Parameters
Name | Type | Description |
---|---|---|
owner_ | address | (ATTENTION) This address will be granted the DEFAULT_ADMIN_ROLE , as well as all basic roles. Roles can be modified at any time. |
customToken_ | address | The upgraded version of the bridged vbToken. Native Converter must be able to mint and burn this token. Please refer to CustomToken.sol for more information. |
underlyingToken_ | address | The token that represents the original underlying token on Layer Y. @note IMPORTANT: This token MUST be either the bridge-wrapped version of the original underlying token, or the original underlying token must be custom mapped to this token on LxLy Bridge on Layer Y. |
lxlyBridge_ | address | |
layerXLxlyId_ | uint32 | |
nonMigratableBackingPercentage_ | uint256 | The percentage of backing that should remain in Native Converter when migrating backing to Layer X, based on the total supply of Custom Token. 1e18 is 100%. It is possible to game the system by manipulating the total supply of Custom Token, so this is more of a soft limit. |
migrationManager_ | address | The address of the Migration Manager on Layer X. |
customToken
The upgraded version of the bridged vbToken.
function customToken() public view returns (IERC20);
underlyingToken
The token that represent the original underlying token on Layer Y.
function underlyingToken() public view returns (IERC20);
backingOnLayerY
The amount of the underlying token that backs Custom Token minted by Native Converter on Layer Y that has not been migrated to Layer X.
The amount is used in accounting and may be different from Native Converter's underlying token balance. @note IMPORTANT: You may do as you wish with surplus underlying token balance, but you MUST NOT designate it as backing.
function backingOnLayerY() public view returns (uint256);
lxlyId
The LxLy ID of this network.
function lxlyId() public view returns (uint32);
lxlyBridge
LxLy Bridge, which connects AggLayer networks.
function lxlyBridge() public view returns (ILxLyBridge);
layerXLxlyId
The LxLy ID of Layer X.
function layerXLxlyId() public view returns (uint32);
nonMigratableBackingPercentage
The percentage of backing that should remain in Native Converter when migrating backing to Layer X, based on the total supply of Custom Token.
It is possible to game the system by manipulating the total supply of Custom Token, so this is more of a soft limit.
function nonMigratableBackingPercentage() public view returns (uint256);
Returns
Name | Type | Description |
---|---|---|
<none> | uint256 | 1e18 is 100%. |
migrationManager
The address of the Migration Manager on Layer X.
function migrationManager() public view returns (MigrationManager);
_getNativeConverterStorage
Returns a pointer to the ERC-7201 storage namespace.
function _getNativeConverterStorage() private pure returns (NativeConverterStorage storage $);
convert
Deposit a specific amount of the underlying token and get Custom Token.
function convert(uint256 assets, address receiver) external whenNotPaused nonReentrant returns (uint256 shares);
Parameters
Name | Type | Description |
---|---|---|
assets | uint256 | The amount of the underlying token to convert to Custom Token. |
receiver | address |
Returns
Name | Type | Description |
---|---|---|
shares | uint256 | The amount of Custom Token minted to the receiver. |
_convert
Deposit a specific amount of the underlying token and get Custom Token.
function _convert(uint256 assets, address receiver) internal returns (uint256 shares);
Parameters
Name | Type | Description |
---|---|---|
assets | uint256 | The amount of the underlying token to convert to Custom Token. |
receiver | address |
Returns
Name | Type | Description |
---|---|---|
shares | uint256 | The amount of Custom Token minted to the receiver. |
convertWithPermit
Deposit a specific amount of the underlying token and get Custom Token.
Uses EIP-2612 permit to transfer the underlying token from the sender to self.
function convertWithPermit(uint256 assets, address receiver, bytes calldata permitData)
external
whenNotPaused
nonReentrant
returns (uint256 shares);
Parameters
Name | Type | Description |
---|---|---|
assets | uint256 | The amount of the underlying token to convert to Custom Token. |
receiver | address | |
permitData | bytes |
Returns
Name | Type | Description |
---|---|---|
shares | uint256 | The amount of Custom Token minted to the receiver. |
maxDeconvert
How much Custom Token a specific user can burn. (Deconverting Custom Token burns it and unlocks the underlying token).
function maxDeconvert(address owner) external view returns (uint256 maxShares);
_simulateDeconvert
Calculates the amount of Custom Token that can be deconverted right now.
function _simulateDeconvert(uint256 shares, bool force) internal view returns (uint256 deconvertedShares);
Parameters
Name | Type | Description |
---|---|---|
shares | uint256 | The maximum amount of Custom Token to simulate deconversion for. |
force | bool | Whether to revert if the all of the shares would not be deconverted. |
deconvert
Burn a specific amount of Custom Token to unlock a respective amount of the underlying token.
function deconvert(uint256 shares, address receiver) external whenNotPaused nonReentrant returns (uint256 assets);
Parameters
Name | Type | Description |
---|---|---|
shares | uint256 | The amount of Custom Token to deconvert to the underlying token. |
receiver | address |
Returns
Name | Type | Description |
---|---|---|
assets | uint256 | The amount of the underlying token unlocked to the receiver. |
deconvertAndBridge
Burn a specific amount of Custom Token to unlock a respective amount of the underlying token, and bridge it to another network.
function deconvertAndBridge(
uint256 shares,
address receiver,
uint32 destinationNetworkId,
bool forceUpdateGlobalExitRoot
) external whenNotPaused nonReentrant returns (uint256 assets);
Parameters
Name | Type | Description |
---|---|---|
shares | uint256 | The amount of Custom Token to deconvert to the underlying token. |
receiver | address | |
destinationNetworkId | uint32 | |
forceUpdateGlobalExitRoot | bool |
Returns
Name | Type | Description |
---|---|---|
assets | uint256 | The amount of the underlying token unlocked to the receiver. |
_deconvert
Burn a specific amount of Custom Token to unlock a respective amount of the underlying token, and optionally bridge it to another network.
function _deconvert(uint256 shares, uint32 destinationNetworkId, address receiver, bool forceUpdateGlobalExitRoot)
internal
returns (uint256 assets);
Parameters
Name | Type | Description |
---|---|---|
shares | uint256 | The amount of Custom Token to deconvert to the underlying token. |
destinationNetworkId | uint32 | |
receiver | address | |
forceUpdateGlobalExitRoot | bool |
Returns
Name | Type | Description |
---|---|---|
assets | uint256 | The amount of the underlying token unlocked to the receiver. |
_convertToShares
Tells how much a specific amount of underlying token is worth in Custom Token.
The underlying token backs vbToken 1:1.
function _convertToShares(uint256 assets) internal pure returns (uint256 shares);
Parameters
Name | Type | Description |
---|---|---|
assets | uint256 | The amount of the underlying token. |
Returns
Name | Type | Description |
---|---|---|
shares | uint256 | The amount of Custom Token. |
_convertToAssets
Tells how much a specific amount of Custom Token is worth in the underlying token.
vbToken is backed by the underlying token 1:1.
function _convertToAssets(uint256 shares) internal pure returns (uint256 assets);
Parameters
Name | Type | Description |
---|---|---|
shares | uint256 | The amount of Custom Token. |
Returns
Name | Type | Description |
---|---|---|
assets | uint256 | The amount of the underlying token. |
migratableBacking
The maximum amount of backing that can be migrated to Layer X.
function migratableBacking() public view returns (uint256);
migrateBackingToLayerX
Migrates a specific amount of backing to Layer X.
This action provides vbToken liquidity on LxLy Bridge on Layer X.
The bridged asset and message must be claimed manually on LxLy Bridge on Layer X to complete the migration.
This function can be called by a migrator only.
The migration can be completed by anyone on Layer X.
Consider calling this function periodically; anyone can complete a migration on Layer X.
function migrateBackingToLayerX(uint256 assets) external whenNotPaused onlyRole(MIGRATOR_ROLE) nonReentrant;
setNonMigratableBackingPercentage
Sets the percentage of backing that should remain in Native Converter after a migration, based on the total supply of Custom Token.
It is possible to game the system by manipulating the total supply of Custom Token, so this is a soft limit.
This function can be called by the owner only.
function setNonMigratableBackingPercentage(uint256 nonMigratableBackingPercentage_)
external
onlyRole(DEFAULT_ADMIN_ROLE)
nonReentrant;
Parameters
Name | Type | Description |
---|---|---|
nonMigratableBackingPercentage_ | uint256 | 1e18 is 100%. |
_receiveUnderlyingToken
Transfers the underlying token from an external account to itself.
@note CAUTION! This function MUST NOT introduce reentrancy/cross-entrancy vulnerabilities.
function _receiveUnderlyingToken(address from, uint256 value) internal returns (uint256 receivedValue);
Returns
Name | Type | Description |
---|---|---|
receivedValue | uint256 | The amount of the underlying actually received (e.g., after transfer fees). |
_sendUnderlyingToken
Transfers the underlying token to an external account.
@note CAUTION! This function MUST NOT introduce reentrancy/cross-entrancy vulnerabilities.
function _sendUnderlyingToken(address to, uint256 value) internal;
pause
Prevents usage of functions with the whenNotPaused
modifier.
This function can be called by a pauser only.
function pause() external onlyRole(PAUSER_ROLE) nonReentrant;
unpause
Allows usage of functions with the whenNotPaused
modifier.
This function can be called by the owner only.
function unpause() external onlyRole(DEFAULT_ADMIN_ROLE) nonReentrant;
Events
MigrationStarted
event MigrationStarted(uint256 indexed mintedCustomToken, uint256 indexed migratedBacking);
NonMigratableBackingPercentageSet
event NonMigratableBackingPercentageSet(uint256 nonMigratableBackingPercentage);
Errors
InvalidOwner
error InvalidOwner();
InvalidCustomToken
error InvalidCustomToken();
InvalidUnderlyingToken
error InvalidUnderlyingToken();
InvalidLxLyBridge
error InvalidLxLyBridge();
InvalidLayerXLxlyId
error InvalidLayerXLxlyId();
InvalidMigrationManager
error InvalidMigrationManager();
NonMatchingTokenDecimals
error NonMatchingTokenDecimals(uint8 customTokenDecimals, uint8 underlyingTokenDecimals);
InvalidAssets
error InvalidAssets();
InvalidReceiver
error InvalidReceiver();
InvalidPermitData
error InvalidPermitData();
InvalidShares
error InvalidShares();
InvalidNonMigratableBackingPercentage
error InvalidNonMigratableBackingPercentage();
AssetsTooLarge
error AssetsTooLarge(uint256 availableAssets, uint256 requestedAssets);
InvalidDestinationNetworkId
error InvalidDestinationNetworkId();
OnlyMigrator
error OnlyMigrator();
Structs
NativeConverterStorage
Storage of Native Converter contract.
It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions when using with upgradeable contracts.
Note: storage-location: erc7201:agglayer.vault-bridge.NativeConverter.storage
struct NativeConverterStorage {
CustomToken customToken;
IERC20 underlyingToken;
uint256 backingOnLayerY;
uint32 lxlyId;
ILxLyBridge lxlyBridge;
uint32 layerXLxlyId;
uint256 nonMigratableBackingPercentage;
address migrationManager;
bool _underlyingTokenIsNotMintable;
}