Files
smom-dbis-138/contracts/dbis/DBIS_SignerRegistry.sol
defiQUG 1511f33857 chore: update DBIS contracts and integrate EIP-712 helper
- Updated DBIS_ConversionRouter and DBIS_SettlementRouter to utilize IDBIS_EIP712Helper for EIP-712 hashing and signature recovery, improving stack depth management.
- Refactored minting logic in DBIS_GRU_MintController to streamline recipient processing.
- Enhanced BUILD_NOTES.md with updated build instructions and test coverage details.
- Added new functions in DBIS_SignerRegistry for duplicate signer checks and active signer validation.
- Introduced a new submodule, DBIS_EIP712Helper, to encapsulate EIP-712 related functionalities.

Made-with: Cursor
2026-03-04 02:00:09 -08:00

177 lines
7.5 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/access/AccessControl.sol";
contract DBIS_SignerRegistry is AccessControl {
bytes32 public constant SIGNER_ADMIN_ROLE = keccak256("SIGNER_ADMIN");
uint8 public constant CATEGORY_OPS = 0;
uint8 public constant CATEGORY_COMPLIANCE = 1;
uint8 public constant CATEGORY_CUSTODY = 2;
uint8 public constant CATEGORY_RISK = 3;
uint8 public constant CATEGORY_AUDITOR = 4;
uint256 private constant NEVER_REVOKED = type(uint256).max;
struct SignerInfo {
uint8 category;
uint256 effectiveFromBlock;
uint256 revokedAtBlock;
bool exists;
}
mapping(address => SignerInfo) private _signers;
address[] private _signerList;
uint8 public requiredSignatures = 3;
uint256 public categoryMaskRequired = 1 << CATEGORY_COMPLIANCE;
uint256 public categoryMaskAllowed = (1 << CATEGORY_OPS) | (1 << CATEGORY_COMPLIANCE) | (1 << CATEGORY_CUSTODY) | (1 << CATEGORY_RISK) | (1 << CATEGORY_AUDITOR);
event SignerAdded(address indexed signer, uint8 category);
event SignerRemoved(address indexed signer);
event QuorumUpdated(uint8 requiredSigs, uint256 requiredMask, uint256 allowedMask);
constructor(address admin) {
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(SIGNER_ADMIN_ROLE, admin);
}
function addSigner(address signer, uint8 category) external onlyRole(SIGNER_ADMIN_ROLE) {
require(signer != address(0), "DBIS: zero signer");
require(!_signers[signer].exists, "DBIS: already signer");
require(category <= CATEGORY_AUDITOR, "DBIS: invalid category");
_signers[signer] = SignerInfo({
category: category,
effectiveFromBlock: block.number,
revokedAtBlock: NEVER_REVOKED,
exists: true
});
_signerList.push(signer);
emit SignerAdded(signer, category);
}
function removeSigner(address signer) external onlyRole(SIGNER_ADMIN_ROLE) {
require(_signers[signer].exists, "DBIS: not signer");
if (_signers[signer].revokedAtBlock == NEVER_REVOKED) {
_signers[signer].revokedAtBlock = block.number;
}
_signers[signer].exists = false;
for (uint256 i = 0; i < _signerList.length; i++) {
if (_signerList[i] == signer) {
_signerList[i] = _signerList[_signerList.length - 1];
_signerList.pop();
break;
}
}
emit SignerRemoved(signer);
}
function revokeSignerAtBlock(address signer) external onlyRole(SIGNER_ADMIN_ROLE) {
require(_signers[signer].exists, "DBIS: not signer");
_signers[signer].revokedAtBlock = block.number;
}
function setQuorum(uint8 requiredSigs, uint256 requiredMask, uint256 allowedMask) external onlyRole(SIGNER_ADMIN_ROLE) {
requiredSignatures = requiredSigs;
categoryMaskRequired = requiredMask;
categoryMaskAllowed = allowedMask;
emit QuorumUpdated(requiredSigs, requiredMask, allowedMask);
}
function isSigner(address signer) external view returns (bool) {
return _signers[signer].exists && _signers[signer].revokedAtBlock == NEVER_REVOKED;
}
function isSignerActiveAtBlock(address signer, uint256 blockNum) external view returns (bool) {
SignerInfo memory info = _signers[signer];
if (!info.exists) return false;
if (blockNum < info.effectiveFromBlock) return false;
if (info.revokedAtBlock != NEVER_REVOKED && blockNum >= info.revokedAtBlock) return false;
return true;
}
function areSignersActiveAtBlock(address[] calldata signers, uint256 blockNum) external view returns (bool) {
for (uint256 i = 0; i < signers.length; i++) {
if (!this.isSignerActiveAtBlock(signers[i], blockNum)) return false;
}
return true;
}
function hasDuplicateSigners(address[] calldata signers) external pure returns (bool) {
for (uint256 i = 0; i < signers.length; i++) {
for (uint256 j = i + 1; j < signers.length; j++) {
if (signers[i] == signers[j]) return true;
}
}
return false;
}
function getSignerInfo(address signer) external view returns (uint8 category, uint256 effectiveFromBlock, uint256 revokedAtBlock) {
SignerInfo memory info = _signers[signer];
require(info.exists, "DBIS: not signer");
return (info.category, info.effectiveFromBlock, info.revokedAtBlock);
}
function validateSigners(address[] calldata signers) external view returns (bool ok, string memory reason) {
if (signers.length < requiredSignatures) return (false, "insufficient count");
uint256 categoryMask = 0;
for (uint256 i = 0; i < signers.length; i++) {
address s = signers[i];
(bool exists, uint8 cat, uint256 revoked) = _getSignerInfo(s);
if (!exists) return (false, "not signer");
if (revoked != NEVER_REVOKED) return (false, "signer revoked");
if ((categoryMaskAllowed & (1 << cat)) == 0) return (false, "category not allowed");
if (_hasDuplicate(signers, i, s)) return (false, "duplicate signer");
categoryMask |= (1 << cat);
}
if ((categoryMask & categoryMaskRequired) != categoryMaskRequired) return (false, "required category missing");
return (true, "");
}
function _getSignerInfo(address signer) private view returns (bool exists, uint8 category, uint256 revokedAtBlock) {
SignerInfo memory info = _signers[signer];
return (info.exists, info.category, info.revokedAtBlock);
}
function _hasDuplicate(address[] calldata signers, uint256 currentIndex, address signer) private pure returns (bool) {
for (uint256 j = currentIndex + 1; j < signers.length; j++) {
if (signers[j] == signer) return true;
}
return false;
}
function getSignerCount() external view returns (uint256) {
return _signerList.length;
}
uint256 public largeSwapAmountThreshold;
uint8 public requiredSignaturesSmall = 2;
uint8 public requiredSignaturesLarge = 3;
function setSwapQuorum(uint256 largeThreshold, uint8 smallSigs, uint8 largeSigs) external onlyRole(SIGNER_ADMIN_ROLE) {
largeSwapAmountThreshold = largeThreshold;
requiredSignaturesSmall = smallSigs;
requiredSignaturesLarge = largeSigs;
}
function validateSignersForSwap(address[] calldata signers, uint256 amountIn) external view returns (bool ok, string memory reason) {
uint8 required = amountIn > largeSwapAmountThreshold ? requiredSignaturesLarge : requiredSignaturesSmall;
if (signers.length < required) return (false, "insufficient count");
uint256 categoryMask = 0;
for (uint256 i = 0; i < signers.length; i++) {
address s = signers[i];
(bool exists, uint8 cat, uint256 revoked) = _getSignerInfo(s);
if (!exists) return (false, "not signer");
if (revoked != NEVER_REVOKED) return (false, "signer revoked");
if ((categoryMaskAllowed & (1 << cat)) == 0) return (false, "category not allowed");
if (_hasDuplicate(signers, i, s)) return (false, "duplicate signer");
categoryMask |= (1 << cat);
}
if (amountIn > largeSwapAmountThreshold && (categoryMask & categoryMaskRequired) != categoryMaskRequired) {
return (false, "required category missing");
}
return (true, "");
}
}