NativeConverter

Git Source

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

NameTypeDescription
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_addressThe 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_addressThe 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_uint256The 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_addressThe 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

NameTypeDescription
<none>uint2561e18 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

NameTypeDescription
assetsuint256The amount of the underlying token to convert to Custom Token.
receiveraddress

Returns

NameTypeDescription
sharesuint256The 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

NameTypeDescription
assetsuint256The amount of the underlying token to convert to Custom Token.
receiveraddress

Returns

NameTypeDescription
sharesuint256The 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

NameTypeDescription
assetsuint256The amount of the underlying token to convert to Custom Token.
receiveraddress
permitDatabytes

Returns

NameTypeDescription
sharesuint256The 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

NameTypeDescription
sharesuint256The maximum amount of Custom Token to simulate deconversion for.
forceboolWhether 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

NameTypeDescription
sharesuint256The amount of Custom Token to deconvert to the underlying token.
receiveraddress

Returns

NameTypeDescription
assetsuint256The 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

NameTypeDescription
sharesuint256The amount of Custom Token to deconvert to the underlying token.
receiveraddress
destinationNetworkIduint32
forceUpdateGlobalExitRootbool

Returns

NameTypeDescription
assetsuint256The 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

NameTypeDescription
sharesuint256The amount of Custom Token to deconvert to the underlying token.
destinationNetworkIduint32
receiveraddress
forceUpdateGlobalExitRootbool

Returns

NameTypeDescription
assetsuint256The 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

NameTypeDescription
assetsuint256The amount of the underlying token.

Returns

NameTypeDescription
sharesuint256The 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

NameTypeDescription
sharesuint256The amount of Custom Token.

Returns

NameTypeDescription
assetsuint256The 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

NameTypeDescription
nonMigratableBackingPercentage_uint2561e18 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

NameTypeDescription
receivedValueuint256The 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;
}