Universal Balance
Overview
The Universal Balance system is a user-facing token management layer within the Curvance Protocol, providing a flexible interface for users to manage their assets. It enables seamless transitions between idle holdings and yield-generating positions within Curvance's ecosystem, all while maintaining non-custodial control.
The system consists of two main contracts:
UniversalBalance: Core implementation for ERC20 tokens.
UniversalBalanceNative: Extension that adds support for native gas tokens (ETH, BERA, etc.).
Core Concepts
Dual Balance System
Universal Balance introduces a dual-balance accounting model for each user:
Sitting Balance: Tokens held in the contract but not deployed to lending markets.
Lent Balance: Tokens deployed into Curvance's lending markets to earn yield.
This dual-state approach allows users to maintain instant liquidity for a portion of their funds while earning yield on another portion, all through a single interface.
Architecture
Core Components
UniversalBalance: Base implementation for ERC20 tokens.
UniversalBalanceNative: Extension for native gas tokens with wrapping/unwrapping.
EToken: Corresponding lending market token that generates yield.
CentralRegistry: Provides system-wide configuration and permissions.
State Management
Universal Balance maintains the following key state variables:
Each Universal Balance contract is linked to:
A specific underlying token (e.g., USDC).
The corresponding EToken in Curvance (e.g., eUSDC).
Data Flows
Deposit Flow
User deposits tokens to UniversalBalance via
deposit()
ordepositFor()
.User specifies whether to keep as sitting balance or lend it.
If lending is chosen:
Tokens are transferred to the EToken contract via
mint()
.Resulting EToken shares are tracked in the user's
lentBalance
.
If keeping as sitting balance:
Tokens are held in the
UniversalBalance
contract.User's
sittingBalance
is increased.
Withdrawal Flow
User requests withdrawal via
withdraw()
orwithdrawFor()
.Contract checks if withdrawal can be fulfilled from sitting balance.
If sitting balance is insufficient:
Required ETokens are redeemed from lending market.
Underlying tokens are received.
Tokens are sent to the recipient.
User's balance (sitting or lent) is reduced accordingly.
Balance Conversion Flow
User requests to lend sitting balance via
lendAssets()
.Sitting balance is reduced.
Tokens are deployed to lending market.
Lent balance is increased.
User requests to unlend via
unlendAssets()
.Lent balance is reduced.
ETokens are redeemed from lending market.
Sitting balance is increased.
Native Token Flow (UniversalBalanceNative)
User sends native tokens (ETH) directly to contract.
Native tokens are wrapped (WETH).
Added to user's sitting balance.
User deposits native tokens via
depositNative()
.Similar to standard deposit with auto-wrapping.
User withdraws to native tokens via
withdrawNative()
.Wrapped tokens are unwrapped.
Native tokens are sent to recipient.
Integration with Curvance Ecosystem
EToken Interaction
Universal Balance contracts interact directly with their linked EToken contracts.
When lending, they call
mint()
on the EToken.When unlending, they call
redeem()
on the EToken.Yield accrues automatically through the EToken's interest model.
Oracle Integration
UniversalBalanceNative includes special support for oracle funding:
Oracle Manager can request funds via
useBalanceForOracleUpdate()
.User's balance is used to pay for oracle updates.
This enables "pull-based" oracle updates where users can fund oracle operations.
Security Features
Permission System: Implements delegated operations through the
PluginDelegable
contract.Reentrancy Protection: All critical functions include reentrancy guards.
Non-Custodial Design: Users maintain full control of their assets.
Permission Checks: Actions that affect user balances verify permissions.
Batch Operations: Multi-user functions to reduce gas costs and transaction volume.
User Features
Social Elements
Direct Transfers: Transfer portions of balance to other users.
Permission Delegation: Allow third-party operations on your balance.
Batch Operations: Efficient multi-user management.
Flexibility
Dynamic Allocation: Freely shift between sitting and lent states.
Multiple Recipients: Deposit or withdraw to different addresses.
Mixed Source Withdrawals: Pull from sitting first, then lent as needed.
Contract Interactions
UniversalBalance interacts with several Curvance contracts:
UniversalBalanceNative adds these interactions:
Implementation Notes
Each UniversalBalance contract is token-specific, managing only one underlying asset.
Deposit and withdrawal functions include options for immediate lending.
The system tracks balances via accounting rather than transferring tokens to users.
For native token operations, wrapping/unwrapping occurs transparently to the user.
User Interaction Functions
UniversalBalance Functions
deposit()
Description: Deposits underlying token into user's Universal Balance account, either to be held or lent out.
Contract: UniversalBalance
Function signature:
uint256
amount
The amount of underlying token to be deposited.
bool
willLend
Whether the deposited underlying tokens should be lent out inside Curvance Protocol.
Events:
depositFor()
Description: Deposits underlying token into recipient's Universal Balance account, either to be held or lent out. Requires that recipient has approved the caller previously to access their Universal Balance.
Contract: UniversalBalance
Function signature:
uint256
amount
The amount of underlying token to be deposited.
bool
willLend
Whether the deposited underlying tokens should be lent out inside Curvance Protocol.
address
recipient
The account who will receive the deposit.
Events:
multiDepositFor()
Description: Deposits underlying token into recipients Universal Balance accounts, either to be held or lent out. Requires that all recipients have approved the caller previously to access their Universal Balance.
Contract: UniversalBalance
Function signature:
uint256
depositSum
The total sum of underlying tokens being deposited.
uint256[]
amounts
An array containing the amount of underlying token to be deposited to each account.
bool[]
willLend
An array containing whether the deposited underlying tokens should be lent out inside Curvance Protocol for each account.
address[]
recipients
An array containing the accounts who will receive a deposit based on their matching amounts value.
Events:
withdraw
Description: Withdraws underlying token from user's Universal Balance account, currently held or lent out.
Contract: UniversalBalance
Function signature:
uint256
amount
The amount of underlying token to be withdrawn.
bool
forceLentRedemption
Whether the withdrawn underlying tokens should be pulled only from owner's lent position or the full account.
address
recipient
The account who will receive the underlying assets.
Return data:
uint256
The amount of underlying token actually withdrawn.
bool
Whether lent balance was used for the withdrawal.
Events:
withdrawFor
Description: Withdraws underlying token from owner's Universal Balance account, currently held or lent out. Requires that owner has approved the caller previously to access their Universal Balance.
Contract: UniversalBalance
Function signature:
uint256
amount
The amount of underlying token to be withdrawn.
bool
forceLentRedemption
Whether the withdrawn underlying tokens should be pulled only from owner's lent position or the full account.
address
recipient
The account who will receive the underlying assets.
address
owner
The account that will redeem from their universal balance.
Return data:
uint256
The amount of underlying token actually withdrawn.
bool
Whether lent balance was used for the withdrawal..
Events:
multiWithdrawFor
Description: Withdraws underlying token from owners Universal Balance accounts, currently held or lent out. Requires that each owners has approved the caller previously to access their Universal Balance.
Contract: UniversalBalance
Function signature:
uint256[]
amounts
An array containing the amount of underlying token to be withdrawn from each account.
bool[]
forceLentRedemption
An array containing whether the withdrawn underlying tokens should be pulled only from an owners lent position or the full account.
address
recipient
The account who will receive the underlying assets.
address[]
owners
An array containing the accounts that will redeem from their Universal Balance.
Events:
shiftBalance()
Description: Moves a user's Universal Balance between lent and sitting mode.
Contract: UniversalBalance
Function signature:
uint256
amount
The amount of underlying token to be shifted.
bool
fromLent
Whether the shifted underlying tokens should be pulled from the user's lent balance or the sitting balance.
Return data:
uint256
The amount of underlying token actually shifted
bool
Whether lent balance was used for the operation
Events:
transfer()
Description: Transfers amount from caller's Universal Balance, currently held or lent out to recipient.
Contract: UniversalBalance
Function signature:
uint256
amount
The amount of underlying token to be transferred.
bool
forceLentRedemption
Whether the transferred underlying tokens should be pulled only from the caller's lent position or the full account.
bool
willLend
Whether the deposited underlying tokens should be lent out inside Curvance Protocol.
address
recipient
The account who will receive the transferred balance.
Return data:
uint256
The amount of underlying token actually transferred.
bool
Whether lent balance was used for the transfer.
Events:
transferFor()
Description: Transfers amount from owner's Universal Balance, currently held or lent out to recipient. Requires that owner has approved the caller previously to access their Universal Balance.
Contract: UniversalBalance
Function signature:
uint256
amount
The amount of underlying token to be transferred.
bool
forceLentRedemption
Whether the transferred underlying tokens should be pulled only from owner's lent position or the full account.
bool
willLend
Whether the deposited underlying tokens should be lent out inside Curvance Protocol.
address
recipient
The account who will receive the transferred balance.
address
owner
The account that will transfer from their universal balance.
Return data:
uint256
The amount of underlying token actually transferred.
bool
Whether lent balance was used for the transfer.
Events:
UniversalBalanceNative Functions
depositNative()
Description: Deposits native gas token into user's Universal Balance account, either to be held or lent out.
Contract: UniversalBalanceNative
Function signature:
bool
isLent
Whether the deposited native tokens should be lent out inside Curvance Protocol (as wrapped native).
Events:
depositNativeFor()
Description: Deposits native gas token into recipient's Universal Balance account, either to be held or lent out. Requires that recipient has approved the caller previously to access their Universal Balance.
Contract: UniversalBalanceNative
Function signature:
bool
isLent
Whether the deposited native tokens should be lent out inside Curvance Protocol (as wrapped native).
address
recipient
The account who will receive the deposit.
Events:
multiDepositNativeFor()
Description: Deposits native gas token into recipient's Universal Balance account, either to be held or lent out. Requires that all recipients have approved the caller previously to access their Universal Balance.
Contract: UniversalBalanceNative
Function signature:
uint256[]
amounts
An array containing the amount of native token to be deposited to each account.
bool[]
willLend
An array containing whether the deposited native tokens should be lent out inside Curvance Protocol for each account.
address[]
recipients
An array containing the accounts who will receive a deposit based on their matching amounts value.
Events:
withdrawNative()
Description: Withdraws wrapped native token from user's Universal Balance account, either currently held or lent out and transfers it to the user in native form.
Contract: UniversalBalanceNative
Function signature:
uint256
amount
The amount of native token to be withdrawn.
bool
forceLentRedemption
Whether the withdrawn underlying tokens should be pulled only from owner's lent position or the full account.
address
recipient
The account who will receive the underlying assets.
Return data:
uint256
The amount of native token actually withdrawn.
bool
Whether lent balance was used for the withdrawal.
Events:
withdrawNativeFor()
Description: Withdraws wrapped native token from owner's universal balance account, either currently held or lent out and transfers it to the user in native form. Requires that owner has approved the caller previously to access their Universal Balance.
Contract: UniversalBalanceNative
Function signature:
uint256
amount
The amount of native token to be withdrawn.
bool
forceLentRedemption
Whether the withdrawn underlying tokens should be pulled only from owner's lent position or the full account.
address
recipient
The account who will receive the native token.
address
owner
The account that will redeem from their universal balance.
Return data:
uint256
The amount of native token actually withdrawn.
bool
Whether lent balance was used for the withdrawal.
Events:
multiWithdrawNativeFor()
Description: Withdraws native gas token from owners Universal Balance accounts, currently held or lent out. Requires that each owners has approved the caller previously to access their Universal Balance.
Contract: UniversalBalanceNative
Function signature:
uint256[]
amounts
An array containing the amount of native token to be withdrawn from each account.
bool[]
forceLentRedemption
An array containing whether the withdrawn underlying tokens should be pulled only from an owners lent position or the full account.
address
recipient
The account who will receive the native assets.
address[]
owners
An array containing the accounts that will redeem from their Universal Balance.
Events:
Last updated