Files
smom-dbis-138/contracts/vault/VaultFactory.sol
defiQUG c336809676
Some checks failed
CI/CD Pipeline / Solidity Contracts (push) Failing after 1m3s
CI/CD Pipeline / Security Scanning (push) Successful in 2m18s
CI/CD Pipeline / Lint and Format (push) Failing after 34s
CI/CD Pipeline / Terraform Validation (push) Failing after 20s
CI/CD Pipeline / Kubernetes Validation (push) Successful in 22s
Deploy ChainID 138 / Deploy ChainID 138 (push) Failing after 40s
HYBX OMNL TypeScript & anchor / token-aggregation build + reconcile artifact (push) Failing after 49s
OMNL reconcile anchor / Run omnl:reconcile and upload artifacts (push) Failing after 21s
Validation / validate-genesis (push) Successful in 25s
Validation / validate-terraform (push) Failing after 21s
Validation / validate-kubernetes (push) Failing after 8s
Validation / validate-smart-contracts (push) Failing after 8s
Validation / validate-security (push) Failing after 1m11s
Validation / validate-documentation (push) Failing after 14s
Verify Deployment / Verify Deployment (push) Failing after 45s
Add mainnet checkpoint stack: ISO attestation, participant Etherscan surface, and services.
Ship AddressActivityRegistry V1/V2, ISO20022IntakeGateway, Chain138ParticipantSurface,
checkpoint hub contracts, checkpoint-core package, aggregator/indexer/sdk services,
relay profile guards, M00 diamond bridge facet, and OMNL compliance contracts.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-25 00:30:45 -07:00

329 lines
11 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import "./Vault.sol";
import "./tokens/DepositToken.sol";
import "./tokens/DebtToken.sol";
import "./interfaces/ILedger.sol";
/**
* @title VaultFactory
* @notice Factory for creating vault instances with associated tokens
* @dev Creates Vault, DepositToken, and DebtToken instances
*/
contract VaultFactory is AccessControl {
bytes32 public constant VAULT_DEPLOYER_ROLE = keccak256("VAULT_DEPLOYER_ROLE");
address public immutable vaultImplementation;
address public immutable depositTokenImplementation;
address public immutable debtTokenImplementation;
ILedger public ledger;
address public entityRegistry;
address public collateralAdapter;
address public eMoneyJoin;
address public gruVaultIndex;
mapping(address => address[]) public vaultsByEntity; // entity => vaults[]
mapping(address => address) public vaultToEntity; // vault => entity
event VaultCreated(
address indexed vault,
address indexed entity,
address indexed owner,
address depositToken,
address debtToken
);
constructor(
address admin,
address vaultImplementation_,
address depositTokenImplementation_,
address debtTokenImplementation_,
address ledger_,
address entityRegistry_,
address collateralAdapter_,
address eMoneyJoin_
) {
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(VAULT_DEPLOYER_ROLE, admin);
vaultImplementation = vaultImplementation_;
depositTokenImplementation = depositTokenImplementation_;
debtTokenImplementation = debtTokenImplementation_;
ledger = ILedger(ledger_);
entityRegistry = entityRegistry_;
collateralAdapter = collateralAdapter_;
eMoneyJoin = eMoneyJoin_;
}
/**
* @notice Wire GRU vault index (optional). Grants FACTORY_ROLE on index to this factory.
*/
function setGruVaultIndex(address index) external onlyRole(DEFAULT_ADMIN_ROLE) {
gruVaultIndex = index;
}
function _recordGruVault(
address vault,
address entity,
address baseToken,
address depositToken,
address debtToken,
uint8 gruTier,
bytes32 ibanHash,
bytes32 policyProfileKey
) internal {
if (gruVaultIndex == address(0)) return;
(bool ok, bytes memory data) = gruVaultIndex.call(
abi.encodeWithSignature(
"recordVault(address,address,address,address,address,uint8,bytes32,bytes32)",
vault,
entity,
baseToken,
depositToken,
debtToken,
gruTier,
ibanHash,
policyProfileKey
)
);
ok;
data;
}
/**
* @notice Create a new vault for a regulated entity
* @param owner Vault owner address
* @param entity Regulated entity address
* @param asset Collateral asset address (for deposit token)
* @param currency eMoney currency address (for debt token)
* @return vault Address of created vault
* @return depositToken Address of deposit token
* @return debtToken Address of debt token
*/
function createVault(
address owner,
address entity,
address asset,
address currency
) external onlyRole(VAULT_DEPLOYER_ROLE) returns (
address vault,
address depositToken,
address debtToken
) {
require(owner != address(0), "VaultFactory: zero owner");
require(entity != address(0), "VaultFactory: zero entity");
// Deploy vault directly (not using proxy for simplicity)
// In production, could use proxy pattern for upgradeability
Vault vaultContract = new Vault(
owner,
entity,
address(ledger),
entityRegistry,
collateralAdapter,
eMoneyJoin
);
vault = address(vaultContract);
// Deploy deposit token (factory as admin so it can grant MINTER/BURNER to vault)
bytes memory depositTokenInitData = abi.encodeWithSelector(
DepositToken.initialize.selector,
string(abi.encodePacked("Deposit ", _getAssetSymbol(asset))),
string(abi.encodePacked("d", _getAssetSymbol(asset))),
vault,
asset,
address(this)
);
ERC1967Proxy depositTokenProxy = new ERC1967Proxy(depositTokenImplementation, depositTokenInitData);
depositToken = address(depositTokenProxy);
// Grant minter/burner roles to vault
DepositToken(depositToken).grantRole(keccak256("MINTER_ROLE"), vault);
DepositToken(depositToken).grantRole(keccak256("BURNER_ROLE"), vault);
// Deploy debt token (factory as admin so it can grant MINTER/BURNER to vault)
bytes memory debtTokenInitData = abi.encodeWithSelector(
DebtToken.initialize.selector,
string(abi.encodePacked("Debt ", _getCurrencySymbol(currency))),
string(abi.encodePacked("debt", _getCurrencySymbol(currency))),
vault,
currency,
address(this)
);
ERC1967Proxy debtTokenProxy = new ERC1967Proxy(debtTokenImplementation, debtTokenInitData);
debtToken = address(debtTokenProxy);
// Grant minter/burner roles to vault
DebtToken(debtToken).grantRole(keccak256("MINTER_ROLE"), vault);
DebtToken(debtToken).grantRole(keccak256("BURNER_ROLE"), vault);
// Configure vault with tokens (cast via payable since Vault has receive())
Vault(payable(vault)).setDepositToken(asset, depositToken);
Vault(payable(vault)).setDebtToken(currency, debtToken);
// Grant vault role in ledger
ledger.registerVault(vault);
// Track vault
vaultsByEntity[entity].push(vault);
vaultToEntity[vault] = entity;
emit VaultCreated(vault, entity, owner, depositToken, debtToken);
}
/**
* @notice Create a vault with explicit decimals and debt transferability (DEX-ready)
* @param depositDecimals Deposit token decimals (e.g. 6 for stablecoins; 0 = 18)
* @param debtDecimals Debt token decimals (0 = 18)
* @param debtTransferable If true, debt token is freely transferable (DEX-ready)
*/
function createVaultWithDecimals(
address owner,
address entity,
address asset,
address currency,
uint8 depositDecimals,
uint8 debtDecimals,
bool debtTransferable
) external onlyRole(VAULT_DEPLOYER_ROLE) returns (
address vault,
address depositToken,
address debtToken
) {
return _deployVaultWithDecimals(
owner, entity, asset, currency, depositDecimals, debtDecimals, debtTransferable
);
}
function _deployVaultWithDecimals(
address owner,
address entity,
address asset,
address currency,
uint8 depositDecimals,
uint8 debtDecimals,
bool debtTransferable
) internal returns (
address vault,
address depositToken,
address debtToken
) {
require(owner != address(0), "VaultFactory: zero owner");
require(entity != address(0), "VaultFactory: zero entity");
Vault vaultContract = new Vault(
owner,
entity,
address(ledger),
entityRegistry,
collateralAdapter,
eMoneyJoin
);
vault = address(vaultContract);
uint8 dDec = depositDecimals > 0 ? depositDecimals : 18;
bytes memory depositTokenInitData = abi.encodeWithSelector(
DepositToken.initializeWithDecimals.selector,
string(abi.encodePacked("Deposit ", _getAssetSymbol(asset))),
string(abi.encodePacked("d", _getAssetSymbol(asset))),
vault,
asset,
address(this),
dDec
);
ERC1967Proxy depositTokenProxy = new ERC1967Proxy(depositTokenImplementation, depositTokenInitData);
depositToken = address(depositTokenProxy);
DepositToken(depositToken).grantRole(keccak256("MINTER_ROLE"), vault);
DepositToken(depositToken).grantRole(keccak256("BURNER_ROLE"), vault);
uint8 debtDec = debtDecimals > 0 ? debtDecimals : 18;
bytes memory debtTokenInitData = abi.encodeWithSelector(
DebtToken.initializeFull.selector,
string(abi.encodePacked("Debt ", _getCurrencySymbol(currency))),
string(abi.encodePacked("debt", _getCurrencySymbol(currency))),
vault,
currency,
address(this),
debtDec,
debtTransferable
);
ERC1967Proxy debtTokenProxy = new ERC1967Proxy(debtTokenImplementation, debtTokenInitData);
debtToken = address(debtTokenProxy);
DebtToken(debtToken).grantRole(keccak256("MINTER_ROLE"), vault);
DebtToken(debtToken).grantRole(keccak256("BURNER_ROLE"), vault);
Vault(payable(vault)).setDepositToken(asset, depositToken);
Vault(payable(vault)).setDebtToken(currency, debtToken);
ledger.registerVault(vault);
vaultsByEntity[entity].push(vault);
vaultToEntity[vault] = entity;
emit VaultCreated(vault, entity, owner, depositToken, debtToken);
}
/**
* @notice Create vault with explicit GRU tier, IBAN hash, and policy profile key.
*/
function createVaultWithDecimalsGRU(
address owner,
address entity,
address asset,
address currency,
uint8 depositDecimals,
uint8 debtDecimals,
bool debtTransferable,
uint8 gruTier,
bytes32 ibanHash,
bytes32 policyProfileKey
) external onlyRole(VAULT_DEPLOYER_ROLE) returns (address vault, address depositToken, address debtToken) {
(vault, depositToken, debtToken) = _deployVaultWithDecimals(
owner, entity, asset, currency, depositDecimals, debtDecimals, debtTransferable
);
_recordGruVault(vault, entity, asset, depositToken, debtToken, gruTier, ibanHash, policyProfileKey);
}
/**
* @notice Get asset symbol (helper)
* @param asset Asset address
* @return symbol Asset symbol
*/
function _getAssetSymbol(address asset) internal pure returns (string memory symbol) {
if (asset == address(0)) {
return "ETH";
}
// In production, would fetch from ERC20
return "ASSET";
}
/**
* @notice Get currency symbol (helper)
* @param currency Currency address
* @return symbol Currency symbol
*/
function _getCurrencySymbol(address currency) internal pure returns (string memory symbol) {
// In production, would fetch from eMoney token
return "CURRENCY";
}
/**
* @notice Get vaults for an entity
* @param entity Entity address
* @return vaults Array of vault addresses
*/
function getVaultsByEntity(address entity) external view returns (address[] memory vaults) {
return vaultsByEntity[entity];
}
}