feat: Introduce MINTER_ROLE for minting control in CompliantFiatToken
- Added MINTER_ROLE constant to manage minting permissions. - Updated mint function to restrict access to addresses with MINTER_ROLE, enhancing security and compliance. - Granted MINTER_ROLE to the initial owner during contract deployment.
This commit is contained in:
196
contracts/dbis/DBIS_ConversionRouter.sol
Normal file
196
contracts/dbis/DBIS_ConversionRouter.sol
Normal file
@@ -0,0 +1,196 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "@openzeppelin/contracts/access/AccessControl.sol";
|
||||
import "@openzeppelin/contracts/utils/Pausable.sol";
|
||||
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
|
||||
import "./DBIS_RootRegistry.sol";
|
||||
import "./DBIS_SignerRegistry.sol";
|
||||
import "./StablecoinReferenceRegistry.sol";
|
||||
|
||||
interface IBlocklist {
|
||||
function isBlocked(address account) external view returns (bool);
|
||||
}
|
||||
|
||||
struct SwapAuth {
|
||||
bytes32 messageId;
|
||||
bytes32 lpaId;
|
||||
bytes32 venue;
|
||||
address tokenIn;
|
||||
address tokenOut;
|
||||
uint256 amountIn;
|
||||
uint256 minAmountOut;
|
||||
uint256 deadline;
|
||||
bytes32 quoteHash;
|
||||
address quoteIssuer;
|
||||
uint256 chainId;
|
||||
address verifyingContract;
|
||||
}
|
||||
|
||||
contract DBIS_ConversionRouter is AccessControl, Pausable, ReentrancyGuard {
|
||||
bytes32 public constant ROUTER_ADMIN_ROLE = keccak256("ROUTER_ADMIN");
|
||||
uint256 public constant CHAIN_ID = 138;
|
||||
|
||||
bytes32 public constant SIGNER_REGISTRY_KEY = keccak256("SignerRegistry");
|
||||
bytes32 public constant STABLECOIN_REGISTRY_KEY = keccak256("StablecoinReferenceRegistry");
|
||||
|
||||
bytes32 private constant EIP712_DOMAIN_TYPEHASH = keccak256(
|
||||
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
|
||||
);
|
||||
bytes32 private constant SWAPAUTH_TYPEHASH = keccak256(
|
||||
"SwapAuth(bytes32 messageId,bytes32 lpaId,bytes32 venue,address tokenIn,address tokenOut,uint256 amountIn,uint256 minAmountOut,uint256 deadline,bytes32 quoteHash,address quoteIssuer,uint256 chainId,address verifyingContract)"
|
||||
);
|
||||
|
||||
DBIS_RootRegistry public rootRegistry;
|
||||
mapping(bytes32 => bool) public usedSwapMessageIds;
|
||||
mapping(bytes32 => bool) public venueAllowlist;
|
||||
mapping(address => bool) public quoteIssuerAllowlist;
|
||||
address public blocklistContract;
|
||||
|
||||
event ConversionExecuted(
|
||||
bytes32 indexed messageId,
|
||||
bytes32 indexed quoteHash,
|
||||
bytes32 venue,
|
||||
address tokenIn,
|
||||
address tokenOut,
|
||||
uint256 amountIn,
|
||||
uint256 amountOut,
|
||||
address quoteIssuer
|
||||
);
|
||||
|
||||
constructor(address admin, address _rootRegistry) {
|
||||
_grantRole(DEFAULT_ADMIN_ROLE, admin);
|
||||
_grantRole(ROUTER_ADMIN_ROLE, admin);
|
||||
rootRegistry = DBIS_RootRegistry(_rootRegistry);
|
||||
}
|
||||
|
||||
function addVenue(bytes32 venue) external onlyRole(ROUTER_ADMIN_ROLE) {
|
||||
venueAllowlist[venue] = true;
|
||||
}
|
||||
|
||||
function removeVenue(bytes32 venue) external onlyRole(ROUTER_ADMIN_ROLE) {
|
||||
venueAllowlist[venue] = false;
|
||||
}
|
||||
|
||||
function addQuoteIssuer(address issuer) external onlyRole(ROUTER_ADMIN_ROLE) {
|
||||
quoteIssuerAllowlist[issuer] = true;
|
||||
}
|
||||
|
||||
function removeQuoteIssuer(address issuer) external onlyRole(ROUTER_ADMIN_ROLE) {
|
||||
quoteIssuerAllowlist[issuer] = false;
|
||||
}
|
||||
|
||||
function setBlocklist(address _blocklist) external onlyRole(ROUTER_ADMIN_ROLE) {
|
||||
blocklistContract = _blocklist;
|
||||
}
|
||||
|
||||
function pause() external onlyRole(ROUTER_ADMIN_ROLE) {
|
||||
_pause();
|
||||
}
|
||||
|
||||
function unpause() external onlyRole(ROUTER_ADMIN_ROLE) {
|
||||
_unpause();
|
||||
}
|
||||
|
||||
function _domainSeparator() private view returns (bytes32) {
|
||||
return keccak256(
|
||||
abi.encode(
|
||||
EIP712_DOMAIN_TYPEHASH,
|
||||
keccak256("DBISConversionRouter"),
|
||||
keccak256("1"),
|
||||
CHAIN_ID,
|
||||
address(this)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function _hashSwapAuth(SwapAuth calldata auth) private pure returns (bytes32) {
|
||||
return keccak256(
|
||||
abi.encode(
|
||||
SWAPAUTH_TYPEHASH,
|
||||
auth.messageId,
|
||||
auth.lpaId,
|
||||
auth.venue,
|
||||
auth.tokenIn,
|
||||
auth.tokenOut,
|
||||
auth.amountIn,
|
||||
auth.minAmountOut,
|
||||
auth.deadline,
|
||||
auth.quoteHash,
|
||||
auth.quoteIssuer,
|
||||
auth.chainId,
|
||||
auth.verifyingContract
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function getSwapAuthDigest(SwapAuth calldata auth) external view returns (bytes32) {
|
||||
return keccak256(abi.encodePacked("\x19\x01", _domainSeparator(), _hashSwapAuth(auth)));
|
||||
}
|
||||
|
||||
function submitSwapAuth(SwapAuth calldata auth, bytes[] calldata signatures, uint256 amountOut) external nonReentrant whenNotPaused {
|
||||
require(auth.chainId == CHAIN_ID, "DBIS: wrong chain");
|
||||
require(auth.verifyingContract == address(this), "DBIS: wrong contract");
|
||||
require(block.timestamp <= auth.deadline, "DBIS: expired");
|
||||
require(!usedSwapMessageIds[auth.messageId], "DBIS: replay");
|
||||
require(venueAllowlist[auth.venue], "DBIS: venue not allowed");
|
||||
require(quoteIssuerAllowlist[auth.quoteIssuer], "DBIS: quote issuer not allowed");
|
||||
require(amountOut >= auth.minAmountOut, "DBIS: slippage");
|
||||
_requireStablecoinActive(auth.tokenOut);
|
||||
_requireNotBlocked();
|
||||
address[] memory signers = _recoverSwapSigners(auth, signatures);
|
||||
DBIS_SignerRegistry signerReg = DBIS_SignerRegistry(rootRegistry.getComponent(SIGNER_REGISTRY_KEY));
|
||||
(bool ok, ) = signerReg.validateSignersForSwap(signers, auth.amountIn);
|
||||
require(ok, "DBIS: quorum not met");
|
||||
usedSwapMessageIds[auth.messageId] = true;
|
||||
emit ConversionExecuted(
|
||||
auth.messageId,
|
||||
auth.quoteHash,
|
||||
auth.venue,
|
||||
auth.tokenIn,
|
||||
auth.tokenOut,
|
||||
auth.amountIn,
|
||||
amountOut,
|
||||
auth.quoteIssuer
|
||||
);
|
||||
}
|
||||
|
||||
function _requireStablecoinActive(address tokenOut) private view {
|
||||
StablecoinReferenceRegistry stableReg = StablecoinReferenceRegistry(rootRegistry.getComponent(STABLECOIN_REGISTRY_KEY));
|
||||
if (address(stableReg) != address(0)) require(stableReg.isActive(tokenOut), "DBIS: tokenOut not active");
|
||||
}
|
||||
|
||||
function _requireNotBlocked() private view {
|
||||
if (blocklistContract != address(0)) require(!IBlocklist(blocklistContract).isBlocked(msg.sender), "DBIS: blocked");
|
||||
}
|
||||
|
||||
function _recoverSwapSigners(SwapAuth calldata auth, bytes[] calldata signatures) private view returns (address[] memory signers) {
|
||||
DBIS_SignerRegistry signerReg = DBIS_SignerRegistry(rootRegistry.getComponent(SIGNER_REGISTRY_KEY));
|
||||
require(address(signerReg) != address(0), "DBIS: no signer registry");
|
||||
bytes32 digest = keccak256(abi.encodePacked("\x19\x01", _domainSeparator(), _hashSwapAuth(auth)));
|
||||
signers = new address[](signatures.length);
|
||||
for (uint256 i = 0; i < signatures.length; i++) {
|
||||
require(signatures[i].length == 65, "DBIS: bad sig len");
|
||||
address signer = _recover(digest, signatures[i]);
|
||||
require(signer != address(0), "DBIS: invalid sig");
|
||||
require(signerReg.isSignerActiveAtBlock(signer, block.number), "DBIS: signer not active");
|
||||
for (uint256 j = 0; j < i; j++) require(signers[j] != signer, "DBIS: duplicate signer");
|
||||
signers[i] = signer;
|
||||
}
|
||||
}
|
||||
|
||||
function _recover(bytes32 digest, bytes calldata signature) private pure returns (address) {
|
||||
require(signature.length == 65, "DBIS: sig length");
|
||||
bytes32 r;
|
||||
bytes32 s;
|
||||
uint8 v;
|
||||
assembly {
|
||||
r := calldataload(signature.offset)
|
||||
s := calldataload(add(signature.offset, 32))
|
||||
v := byte(0, calldataload(add(signature.offset, 64)))
|
||||
}
|
||||
if (v < 27) v += 27;
|
||||
require(v == 27 || v == 28, "DBIS: invalid v");
|
||||
return ecrecover(digest, v, r, s);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user