The ZEUR protocol uses a sophisticated tokenization system to represent user positions and enable composability. All user deposits and debts are tokenized into ERC20-compatible tokens that can be tracked, transferred (where applicable), and integrated with other DeFi protocols.
Token Types
1. ColTokens (Collateral Tokens)
ColTokens represent user deposits of collateral assets. They are minted 1:1 with the underlying collateral and serve as proof of deposit.
contract ColToken is ERC20Upgradeable, AccessManagedUpgradeable, UUPSUpgradeable
function mint(address account, uint256 value) external restricted;
function burn(address account, uint256 value) external restricted;
function decimals() public pure override returns (uint8) { return 18; }
contract DebtEUR is ERC20Upgradeable, AccessManagedUpgradeable, UUPSUpgradeable
function transfer(address to, uint256 amount) public override returns (bool) {
revert DebtEUR_OperationNotAllowed();
}
function transferFrom(address from, address to, uint256 amount) public override returns (bool) {
revert DebtEUR_OperationNotAllowed();
}
function approve(address spender, uint256 amount) public override returns (bool) {
revert DebtEUR_OperationNotAllowed();
}
contract ColEUR is ERC4626Upgradeable, ERC20PermitUpgradeable, AccessManagedUpgradeable, UUPSUpgradeable
function deposit(uint256 assets, address receiver) external returns (uint256 shares);
function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
function mint(uint256 shares, address receiver) external returns (uint256 assets);
function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
function transferTokenTo(address to, uint256 amount) external; // For Pool contract
function supply(address asset, uint256 amount, address from) external {
// For ETH/LINK collateral
if (collateralAssetList.contains(asset)) {
// Transfer asset to vault
vault.lockCollateral(from, amount);
// Mint colToken 1:1
colToken.mint(from, amount);
}
}
function withdraw(address asset, uint256 amount, address to) external {
// Burn colToken from user
colToken.burn(msg.sender, amount);
// Unlock collateral from vault
vault.unlockCollateral(to, amount);
// Check health factor remains > 1
require(getUserHealthFactor(msg.sender) >= HEALTH_FACTOR_BASE);
}
User Deposit → ColToken Mint → Vault Stakes → LST Rewards → User Benefits
function borrow(address asset, uint256 amount, address to) external {
// Check borrowing capacity
require(availableBorrowsValue >= debtValue);
// Mint debtEUR to track debt
debtToken.mint(msg.sender, amount);
// Transfer EUR from ColEUR vault
colEUR.transferTokenTo(to, amount);
}
function repay(address asset, uint256 amount, address from) external {
// Transfer EUR back to ColEUR vault
assetToken.safeTransferFrom(msg.sender, colEUR, amount);
// Burn debtEUR tokens
debtToken.burn(from, amount);
}
// Shares to assets
function convertToAssets(uint256 shares) public view returns (uint256) {
return shares * totalAssets() / totalSupply();
}
// Assets to shares
function convertToShares(uint256 assets) public view returns (uint256) {
return assets * totalSupply() / totalAssets();
}
// Pool contract has minter role for all tokens
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
// Setup in deployment
accessManager.grantRole(MINTER_ROLE, poolAddress);
1. User → Pool.supply(ETH, 5 ether, user)
2. Pool → VaultETH.lockCollateral(user, 5 ether)
3. VaultETH → Stake across LST protocols
4. Pool → ColETH.mint(user, 5 ether)
5. User receives 5 colETH tokens
1. User → Pool.borrow(EURC, 3000e6, user)
2. Pool → Check borrowing capacity
3. Pool → DebtEUR.mint(user, 3000e6)
4. Pool → ColEUR.transferTokenTo(user, 3000e6)
5. User receives 3000 EURC, owes 3000 debtEUR
1. User → Pool.supply(EURC, 10000e6, user)
2. Pool → EURC.transferFrom(user, pool, 10000e6)
3. Pool → ColEUR.deposit(10000e6, user)
4. User receives ColEUR shares
1. Liquidator → Pool.liquidate(ETH, EURC, 2000e6, liquidatedUser)
2. Pool → EURC.transferFrom(liquidator, colEUR, 2000e6)
3. Pool → DebtEUR.burn(liquidatedUser, 2000e6)
4. Pool → ColETH.burn(liquidatedUser, collateralAmount)
5. Pool → VaultETH.unlockCollateral(liquidator, collateralAmount)
interface IERC4626 {
function asset() external view returns (address);
function totalAssets() external view returns (uint256);
function convertToShares(uint256 assets) external view returns (uint256);
function convertToAssets(uint256 shares) external view returns (uint256);
function maxDeposit(address receiver) external view returns (uint256);
function previewDeposit(uint256 assets) external view returns (uint256);
function deposit(uint256 assets, address receiver) external returns (uint256);
// ... other functions
}
// ColETH & ColLINK
function decimals() public pure override returns (uint8) {
return 18;
}
// DebtEUR
function decimals() public pure override returns (uint8) {
return 6;
}
// ColEUR
function decimals() public view override returns (uint8) {
return IERC20Metadata(asset()).decimals(); // 6 for EURC
}