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

[VEN-2404][VEN-2405][VEN-2406]: VAI bridge for Src and Dest #22

Open
wants to merge 25 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ff8f234
chore: directory restructure for better code maintainability
GitGuru7 Feb 19, 2024
8b40381
feat: add generic token bridge for src and destination
GitGuru7 Feb 19, 2024
007ee95
feat: prevent token transfers to blacklisted accounts in src bridge c…
GitGuru7 Feb 19, 2024
50eaa72
feat: support optional force minting
GitGuru7 Feb 20, 2024
52c53b9
chore: update contracts name to more meaningful
GitGuru7 Feb 20, 2024
f333995
feat: allow burning more than minting in token controller
GitGuru7 Feb 20, 2024
9a83ce7
feat: add decimals() functionalitity in Multichain token
GitGuru7 Feb 21, 2024
72bfb6f
refactor: add natspec and update function sequence
GitGuru7 Feb 21, 2024
b53cb6e
feat: add VAI bridge deployment scripts
GitGuru7 Feb 22, 2024
f8be21c
feat: updating deployment files
GitGuru7 Feb 22, 2024
191199b
chore: rename VAI token deployments file
GitGuru7 Feb 22, 2024
cf03959
feat: updating deployment files
GitGuru7 Feb 22, 2024
9175e05
fix: fix deployment scripts and adjust limits of transaction
GitGuru7 Feb 22, 2024
6d99b70
feat: add tests for vai bridge
GitGuru7 Feb 22, 2024
fefc614
feat: add test for inactive force mint
GitGuru7 Feb 23, 2024
8474b1f
refactor: make bridge test generic
GitGuru7 Feb 23, 2024
ba4e54a
refactor: add tokenBridgeController variable in bridge
GitGuru7 Feb 27, 2024
1d6a86c
fix: deployment scripts and add deployments
GitGuru7 Feb 27, 2024
b16e7f7
feat: updating deployment files
GitGuru7 Feb 27, 2024
63994bc
fix: add natspec and shift tokenBridgeController to MintableTokenBridge
GitGuru7 Feb 27, 2024
2396db7
refactor: update deployments
GitGuru7 Feb 27, 2024
e224883
feat: updating deployment files
GitGuru7 Feb 27, 2024
3f376d5
fix: typos
GitGuru7 Feb 27, 2024
24260fe
refactor: add NatSpec and update casing of immutable variables
GitGuru7 Mar 11, 2024
ea83571
refactor: immuatable variable casing
GitGuru7 Mar 12, 2024
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
511 changes: 511 additions & 0 deletions contracts/Bridge/BaseTokenBridge.sol

Large diffs are not rendered by default.

141 changes: 141 additions & 0 deletions contracts/Bridge/MintableTokenBridge.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.13;

import { IMultichainToken } from "./../interfaces/IMultichainToken.sol";
import { BaseTokenBridge } from "./BaseTokenBridge.sol";
import { ensureNonzeroAddress } from "@venusprotocol/solidity-utilities/contracts/validators.sol";

/**
* @title MintableTokenBridge
* @author Venus
* @notice MintableTokenBridge contract builds upon the functionality of its parent contract, BaseTokenBridge,
* and focuses on managing token transfers to the chain where token is mintable and burnable.
* It provides functions to check eligibility and perform the actual token transfers while maintaining strict access controls and pausing mechanisms.
*/

contract MintableTokenBridge is BaseTokenBridge {
/**
* @notice Regulates the force minting; It should be true only if the token manage by this Bridge contract can be mintable on both source and destination chains.
*/
bool public immutable IS_FORCE_MINT_ACTIVE;

/**
* @notice Address of Token Bridge Controller. It can be zero address when token can be directly accessed.
*/
address public immutable TOKEN_BRIDGE_CONTROLLER;

/**
* @notice Emits when stored message dropped without successful retrying.
*/
event DropFailedMessage(uint16 srcChainId, bytes indexed srcAddress, uint64 nonce);
/**
* @notice Emits when token minted manually by owner in case of bridge failure and unsuccessful retrying.
*/
event ForceMint(uint16 srcChainId, address indexed to, uint256 amount);

constructor(
address tokenAddress_,
address tokenBridgeController_,
uint8 sharedDecimals_,
address lzEndpoint_,
address oracle_,
bool isForceMintActive_
) BaseTokenBridge(tokenAddress_, sharedDecimals_, lzEndpoint_, oracle_) {
IS_FORCE_MINT_ACTIVE = isForceMintActive_;
TOKEN_BRIDGE_CONTROLLER = tokenBridgeController_;
}

/**
* @notice Clear failed messages from the storage.
* @param srcChainId_ Chain id of source
* @param srcAddress_ Address of source followed by current bridge address
* @param nonce_ Nonce_ of the transaction
* @custom:access Only owner
* @custom:event Emits DropFailedMessage on clearance of failed message.
*/
function dropFailedMessage(uint16 srcChainId_, bytes memory srcAddress_, uint64 nonce_) external onlyOwner {
failedMessages[srcChainId_][srcAddress_][nonce_] = bytes32(0);
emit DropFailedMessage(srcChainId_, srcAddress_, nonce_);
}

/**
* @notice Only call it when there is no way to recover the failed message.
* `dropFailedMessage` must be called first if transaction is from remote->local chain to avoid double spending.
* @param srcChainId_ Chain ID from where transaction was initiated.
* @param to_ The address to mint to
* @param amount_ The amount of mint
* @custom:access Only owner.
* @custom:event Emits forceMint, once done with transfer.
*/
function forceMint(uint16 srcChainId_, address to_, uint256 amount_) external onlyOwner {
require(IS_FORCE_MINT_ACTIVE, "ProxyOFT: Force mint of token is not allowed on this chain");

ensureNonzeroAddress(to_);

// When controller is used to interact with token
if (TOKEN_BRIDGE_CONTROLLER != address(0)) {
IMultichainToken(TOKEN_BRIDGE_CONTROLLER).mint(to_, amount_);
} else {
IMultichainToken(address(INNER_TOKEN)).mint(to_, amount_);
}
emit ForceMint(srcChainId_, to_, amount_);
}

/**
* @notice Returns the total circulating supply of the token on the destination chain i.e (total supply).
* @return total circulating supply of the token on the destination chain.
*/
function circulatingSupply() public view override returns (uint256) {
return INNER_TOKEN.totalSupply();
}

/**
* @notice Debit tokens from the given address
* @param from_ Address from which tokens to be debited
* @param dstChainId_ Destination chain id
* @param amount_ Amount of tokens to be debited
* @return Actual amount debited
*/
function _debitFrom(
address from_,
uint16 dstChainId_,
bytes32,
uint256 amount_
) internal override whenNotPaused returns (uint256) {
require(from_ == _msgSender(), "ProxyOFT: owner is not send caller");
_isEligibleToSend(from_, dstChainId_, amount_);

// When controller is used to interact with token
if (TOKEN_BRIDGE_CONTROLLER != address(0)) {
IMultichainToken(TOKEN_BRIDGE_CONTROLLER).burn(from_, amount_);
} else {
IMultichainToken(address(INNER_TOKEN)).burn(from_, amount_);
}
return amount_;
}

/**
* @notice Credit tokens in the given account
* @param srcChainId_ Source chain id
* @param toAddress_ Address on which token will be credited
* @param amount_ Amount of tokens to be credited
* @return Actual amount credited
*/
function _creditTo(
uint16 srcChainId_,
address toAddress_,
uint256 amount_
) internal override whenNotPaused returns (uint256) {
_isEligibleToReceive(toAddress_, srcChainId_, amount_);

// When controller is used to interact with token
if (TOKEN_BRIDGE_CONTROLLER != address(0)) {
IMultichainToken(TOKEN_BRIDGE_CONTROLLER).mint(toAddress_, amount_);
} else {
IMultichainToken(address(INNER_TOKEN)).mint(toAddress_, amount_);
}
return amount_;
}

function _transferFrom(address from_, address to_, uint256 amount_) internal override returns (uint256) {}
}
142 changes: 142 additions & 0 deletions contracts/Bridge/TokenBridgeAdmin.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// SPDX-License-Identifier: BSD-3-Clause

pragma solidity 0.8.13;

import { AccessControlledV8 } from "@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol";
import { ensureNonzeroAddress } from "@venusprotocol/solidity-utilities/contracts/validators.sol";
import { ITokenBridge } from "./../interfaces/ITokenBridge.sol";

/**
* @title TokenBridgeAdmin
* @author Venus
* @notice The TokenBridgeAdmin contract extends a parent contract AccessControlledV8 for access control, and it manages an external contract called TokenProxyOFT.
* It maintains a registry of function signatures and names,
* allowing for dynamic function handling i.e checking of access control of interaction with only owner functions.
*/
contract TokenBridgeAdmin is AccessControlledV8 {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
ITokenBridge public immutable TOKEN_BRIDGE;
/**
* @notice A mapping keeps track of function signature associated with function name string.
*/
mapping(bytes4 => string) public functionRegistry;

/**
* @notice emitted when function registry updated
*/
event FunctionRegistryChanged(string signature, bool active);

/// @custom:oz-upgrades-unsafe-allow constructor
constructor(address TokenBridge_) {
ensureNonzeroAddress(TokenBridge_);
TOKEN_BRIDGE = ITokenBridge(TokenBridge_);
_disableInitializers();
}

/**
* @param accessControlManager_ Address of access control manager contract.
*/
function initialize(address accessControlManager_) external initializer {
__AccessControlled_init(accessControlManager_);
}

/**
* @notice Invoked when called function does not exist in the contract.
* @return Response of low level call.
* @custom:access Controlled by AccessControlManager.
*/
fallback(bytes calldata data) external returns (bytes memory) {
string memory fun = _getFunctionName(msg.sig);
require(bytes(fun).length != 0, "Function not found");
_checkAccessAllowed(fun);
(bool ok, bytes memory res) = address(TOKEN_BRIDGE).call(data);
require(ok, "call failed");
return res;
}

/**
* @notice Sets trusted remote on particular chain.
* @param remoteChainId_ Chain Id of the destination chain.
* @param remoteAddress_ Address of the destination bridge.
* @custom:access Controlled by AccessControlManager.
* @custom:error ZeroAddressNotAllowed is thrown when remoteAddress_ contract address is zero.
*/
function setTrustedRemoteAddress(uint16 remoteChainId_, bytes calldata remoteAddress_) external {
_checkAccessAllowed("setTrustedRemoteAddress(uint16,bytes)");
require(remoteChainId_ != 0, "ChainId must not be zero");
ensureNonzeroAddress(bytesToAddress(remoteAddress_));
TOKEN_BRIDGE.setTrustedRemoteAddress(remoteChainId_, remoteAddress_);
}

/**
* @notice A setter for the registry of functions that are allowed to be executed from proposals.
* @param signatures_ Function signature to be added or removed.
* @param active_ bool value, should be true to add function.
* @custom:access Only owner.
* @custom:event Emits FunctionRegistryChanged if bool value of function changes.
*/
function upsertSignature(string[] calldata signatures_, bool[] calldata active_) external onlyOwner {
uint256 signatureLength = signatures_.length;
require(signatureLength == active_.length, "Input arrays must have the same length");
for (uint256 i; i < signatureLength; ) {
bytes4 sigHash = bytes4(keccak256(bytes(signatures_[i])));
bytes memory signature = bytes(functionRegistry[sigHash]);
if (active_[i] && signature.length == 0) {
functionRegistry[sigHash] = signatures_[i];
emit FunctionRegistryChanged(signatures_[i], true);
} else if (!active_[i] && signature.length != 0) {
delete functionRegistry[sigHash];
emit FunctionRegistryChanged(signatures_[i], false);
}
unchecked {
++i;
}
}
}

/**
* @notice This function transfers the ownership of the bridge from this contract to new owner.
* @param newOwner_ New owner of the Token Bridge.
* @custom:access Controlled by AccessControlManager.
*/
function transferBridgeOwnership(address newOwner_) external {
_checkAccessAllowed("transferBridgeOwnership(address)");
TOKEN_BRIDGE.transferOwnership(newOwner_);
}

/**
* @notice Returns true if remote address is trustedRemote corresponds to chainId_.
* @param remoteChainId_ Chain Id of the destination chain.
* @param remoteAddress_ Address of the destination bridge.
* @custom:error ZeroAddressNotAllowed is thrown when remoteAddress_ contract address is zero.
* @return Bool indicating whether the remote chain is trusted or not.
*/
function isTrustedRemote(uint16 remoteChainId_, bytes calldata remoteAddress_) external returns (bool) {
require(remoteChainId_ != 0, "ChainId must not be zero");
ensureNonzeroAddress(bytesToAddress(remoteAddress_));
return TOKEN_BRIDGE.isTrustedRemote(remoteChainId_, remoteAddress_);
}

/**
* @notice Empty implementation of renounce ownership to avoid any mishappening.
*/
function renounceOwnership() public override {}

/**
* @dev Returns function name string associated with function signature.
* @param signature_ Four bytes of function signature.
* @return Function signature corresponding to its hash.
*/
function _getFunctionName(bytes4 signature_) internal view returns (string memory) {
return functionRegistry[signature_];
}

/**
* @notice Converts given bytes into address.
* @param b Bytes to be converted into address.
* @return Converted address of given bytes.
*/
function bytesToAddress(bytes calldata b) private pure returns (address) {
return address(uint160(bytes20(b)));
}
}
68 changes: 68 additions & 0 deletions contracts/Token/MultichainToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.13;

import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

import { MultichainTokenController } from "./utils/MultichainTokenController.sol";

/**
* @title MultichainToken
* @author Venus
* @notice MultichainToken contract serves as a customized ERC-20 token with additional minting and burning functionality.
* It also incorporates access control features provided by the "MultichainTokenController" contract to ensure proper governance and
* restrictions on minting and burning operations.
*/

contract MultichainToken is ERC20, MultichainTokenController {
constructor(
address accessControlManager_,
string memory name_,
string memory symbol_
) ERC20(name_, symbol_) MultichainTokenController(accessControlManager_) {}

/**
* @notice Creates `amount_` tokens and assigns them to `account_`, increasing
* the total supply. Checks access and eligibility.
* @param account_ Address to which tokens are assigned.
* @param amount_ Amount of tokens to be assigned.
* @custom:access Controlled by AccessControlManager.
* @custom:event Emits MintLimitDecreased with new available limit.
* @custom:error MintLimitExceed is thrown when minting amount exceeds the maximum cap.
*/
function mint(address account_, uint256 amount_) external whenNotPaused {
_ensureAllowed("mint(address,uint256)");
_isEligibleToMint(msg.sender, amount_);
_mint(account_, amount_);
}

/**
* @notice Destroys `amount_` tokens from `account_`, reducing the
* total supply. Checks access and eligibility.
* @param account_ Address from which tokens be destroyed.
* @param amount_ Amount of tokens to be destroyed.
* @custom:access Controlled by AccessControlManager.
* @custom:event Emits MintLimitIncreased with new available limit.
*/
function burn(address account_, uint256 amount_) external whenNotPaused {
_ensureAllowed("burn(address,uint256)");
_burn(account_, amount_);
_increaseMintLimit(msg.sender, amount_);
}

/**
* @notice Hook that is called before any transfer of tokens. This includes
* minting and burning.
* @param from_ Address of account from which tokens are to be transferred.
* @param to_ Address of the account to which tokens are to be transferred.
* @param amount_ The amount of tokens to be transferred.
* @custom:error AccountBlacklisted is thrown when either `from` or `to` address is blacklisted.
*/
function _beforeTokenTransfer(address from_, address to_, uint256 amount_) internal override whenNotPaused {
if (_blacklist[to_]) {
revert AccountBlacklisted(to_);
}
if (_blacklist[from_]) {
revert AccountBlacklisted(from_);
}
}
}
Loading
Loading