Some checks failed
CI/CD Pipeline / Solidity Contracts (push) Failing after 1m20s
CI/CD Pipeline / Security Scanning (push) Successful in 2m48s
CI/CD Pipeline / Lint and Format (push) Failing after 47s
CI/CD Pipeline / Terraform Validation (push) Failing after 28s
CI/CD Pipeline / Kubernetes Validation (push) Successful in 30s
HYBX OMNL TypeScript & anchor / token-aggregation build + reconcile artifact (push) Failing after 44s
Validation / validate-genesis (push) Successful in 37s
Validation / validate-terraform (push) Failing after 42s
Validation / validate-kubernetes (push) Failing after 12s
Validation / validate-smart-contracts (push) Failing after 16m44s
Validation / validate-security (push) Failing after 20s
Validation / validate-documentation (push) Failing after 19s
Verify Deployment / Verify Deployment (push) Failing after 1m21s
Use dedicated cusdc_v2/cusdt_v2 SVG paths so GRU token alignment validation passes.
150 lines
6.2 KiB
Solidity
150 lines
6.2 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.19;
|
|
|
|
import "@openzeppelin/contracts/access/AccessControl.sol";
|
|
import "./interfaces/IAccountWalletRegistry.sol";
|
|
|
|
/**
|
|
* @title AccountWalletRegistry
|
|
* @notice Maps regulated fiat accounts (IBAN, ABA) to Web3 wallets
|
|
* @dev Stores hashed account references (no PII on-chain). Supports 1-to-many mappings.
|
|
*/
|
|
contract AccountWalletRegistry is IAccountWalletRegistry, AccessControl {
|
|
bytes32 public constant ACCOUNT_MANAGER_ROLE = keccak256("ACCOUNT_MANAGER_ROLE");
|
|
|
|
// accountRefId => array of wallet links
|
|
mapping(bytes32 => WalletLink[]) private _accountToWallets;
|
|
// walletRefId => array of accountRefIds
|
|
mapping(bytes32 => bytes32[]) private _walletToAccounts;
|
|
// accountRefId => walletRefId => index in _accountToWallets array
|
|
mapping(bytes32 => mapping(bytes32 => uint256)) private _walletIndex;
|
|
// walletRefId => accountRefId => exists flag
|
|
mapping(bytes32 => mapping(bytes32 => bool)) private _walletAccountExists;
|
|
|
|
/**
|
|
* @notice Initializes the registry with an admin address
|
|
* @param admin Address that will receive DEFAULT_ADMIN_ROLE
|
|
*/
|
|
constructor(address admin) {
|
|
_grantRole(DEFAULT_ADMIN_ROLE, admin);
|
|
}
|
|
|
|
/**
|
|
* @notice Links an account to a wallet
|
|
* @dev Requires ACCOUNT_MANAGER_ROLE. Creates a new link or reactivates an existing one.
|
|
* @param accountRefId The hashed account reference ID
|
|
* @param walletRefId The hashed wallet reference ID
|
|
* @param provider The provider identifier (e.g., "METAMASK", "FIREBLOCKS")
|
|
*/
|
|
function linkAccountToWallet(
|
|
bytes32 accountRefId,
|
|
bytes32 walletRefId,
|
|
bytes32 provider
|
|
) external override onlyRole(ACCOUNT_MANAGER_ROLE) {
|
|
require(accountRefId != bytes32(0), "AccountWalletRegistry: zero accountRefId");
|
|
require(walletRefId != bytes32(0), "AccountWalletRegistry: zero walletRefId");
|
|
require(provider != bytes32(0), "AccountWalletRegistry: zero provider");
|
|
|
|
// Check if link already exists
|
|
if (_walletAccountExists[walletRefId][accountRefId]) {
|
|
// Reactivate existing link
|
|
uint256 index = _walletIndex[accountRefId][walletRefId];
|
|
require(index < _accountToWallets[accountRefId].length, "AccountWalletRegistry: index out of bounds");
|
|
WalletLink storage link = _accountToWallets[accountRefId][index];
|
|
require(link.walletRefId == walletRefId, "AccountWalletRegistry: link mismatch");
|
|
link.active = true;
|
|
link.linkedAt = uint64(block.timestamp);
|
|
} else {
|
|
// Create new link
|
|
WalletLink memory newLink = WalletLink({
|
|
walletRefId: walletRefId,
|
|
linkedAt: uint64(block.timestamp),
|
|
active: true,
|
|
provider: provider
|
|
});
|
|
|
|
_accountToWallets[accountRefId].push(newLink);
|
|
_walletIndex[accountRefId][walletRefId] = _accountToWallets[accountRefId].length - 1;
|
|
_walletAccountExists[walletRefId][accountRefId] = true;
|
|
|
|
// Add to reverse mapping
|
|
_walletToAccounts[walletRefId].push(accountRefId);
|
|
}
|
|
|
|
emit AccountWalletLinked(accountRefId, walletRefId, provider, uint64(block.timestamp));
|
|
}
|
|
|
|
/**
|
|
* @notice Unlinks an account from a wallet (deactivates the link)
|
|
* @dev Requires ACCOUNT_MANAGER_ROLE. Sets link to inactive but doesn't remove it.
|
|
* @param accountRefId The hashed account reference ID
|
|
* @param walletRefId The hashed wallet reference ID
|
|
*/
|
|
function unlinkAccountFromWallet(
|
|
bytes32 accountRefId,
|
|
bytes32 walletRefId
|
|
) external override onlyRole(ACCOUNT_MANAGER_ROLE) {
|
|
require(accountRefId != bytes32(0), "AccountWalletRegistry: zero accountRefId");
|
|
require(walletRefId != bytes32(0), "AccountWalletRegistry: zero walletRefId");
|
|
require(_walletAccountExists[walletRefId][accountRefId], "AccountWalletRegistry: link not found");
|
|
|
|
uint256 index = _walletIndex[accountRefId][walletRefId];
|
|
require(index < _accountToWallets[accountRefId].length, "AccountWalletRegistry: index out of bounds");
|
|
WalletLink storage link = _accountToWallets[accountRefId][index];
|
|
require(link.walletRefId == walletRefId, "AccountWalletRegistry: link mismatch");
|
|
|
|
link.active = false;
|
|
|
|
emit AccountWalletUnlinked(accountRefId, walletRefId);
|
|
}
|
|
|
|
/**
|
|
* @notice Returns all wallet links for an account
|
|
* @param accountRefId The hashed account reference ID
|
|
* @return Array of wallet links
|
|
*/
|
|
function getWallets(bytes32 accountRefId) external view override returns (WalletLink[] memory) {
|
|
return _accountToWallets[accountRefId];
|
|
}
|
|
|
|
/**
|
|
* @notice Returns all account references for a wallet
|
|
* @param walletRefId The hashed wallet reference ID
|
|
* @return Array of account reference IDs
|
|
*/
|
|
function getAccounts(bytes32 walletRefId) external view override returns (bytes32[] memory) {
|
|
return _walletToAccounts[walletRefId];
|
|
}
|
|
|
|
/**
|
|
* @notice Checks if an account and wallet are linked
|
|
* @param accountRefId The hashed account reference ID
|
|
* @param walletRefId The hashed wallet reference ID
|
|
* @return true if linked (regardless of active status)
|
|
*/
|
|
function isLinked(bytes32 accountRefId, bytes32 walletRefId) external view override returns (bool) {
|
|
return _walletAccountExists[walletRefId][accountRefId];
|
|
}
|
|
|
|
/**
|
|
* @notice Checks if an account and wallet are actively linked
|
|
* @param accountRefId The hashed account reference ID
|
|
* @param walletRefId The hashed wallet reference ID
|
|
* @return true if linked and active
|
|
*/
|
|
function isActive(bytes32 accountRefId, bytes32 walletRefId) external view override returns (bool) {
|
|
if (!_walletAccountExists[walletRefId][accountRefId]) {
|
|
return false;
|
|
}
|
|
|
|
uint256 index = _walletIndex[accountRefId][walletRefId];
|
|
if (index >= _accountToWallets[accountRefId].length) {
|
|
return false;
|
|
}
|
|
|
|
WalletLink memory link = _accountToWallets[accountRefId][index];
|
|
return link.active && link.walletRefId == walletRefId;
|
|
}
|
|
}
|
|
|