NGOLis
Last updated
Last updated
The NGOLis contract manages staking and withdrawal functionalities for Non-Governmental Organizations (NGOs). It enables users to stake ETH, stETH, or wstETH while allocating a portion to NGO operations. The contract interacts with the Lido Finance protocol for staking and the withdrawal queue for managing withdrawals. It incorporates percentage-based sharing mechanisms, reward distributions, and comprehensive security controls. It includes various functions and modifiers to manage NGO operations, such as creating and updating NGO details, handling donations, and ensuring compliance with specific rules. The notBanned
modifier is used to restrict access to stake, ensuring that only non-banned entities can stake while everyone can withdraw their staked funds. Supports staking and withdrawal support for ETH, stETH, wstETH tokens.
The contract implements the UUPS (Universal Upgradeable Proxy Standard) pattern:
Uses OpenZeppelin's UUPSUpgradeable
framework
Upgrades are controlled through the _authorizeUpgrade
function
Only the contract owner can initiate upgrades
State variables are preserved during upgrades through proxy delegation
The upgrade process is controlled through two main components:
Authorization Function
Authorizes contract upgrades in the UUPS pattern.
Access Control:
Restricted to contract owner
Parameters:
newImplementation: Address of new implementation contract
Security Considerations:
Critical function for contract upgradeability
Must be called through proxy
Requires careful implementation validation
Should maintain contract invariants
Usage:
Part of upgrade process
Called automatically by upgrade mechanism
Validates upgrade permissions
Proxy Pattern
Uses ERC1967 proxy standard
Maintains separation of concerns
Preserves contract state
Owner initiates upgrade with new implementation
_authorizeUpgrade
validates permission
Proxy updates implementation pointer
State remains intact in proxy storage
Percentage Calculation
NGO share percentage uses a base of 10000 for precise calculations
100% = 10000 and 1% = 100
Valid range: MIN_SHARE_PERCENT (100 = 1%) to MAX_SHARE_PERCENT (10000 = 100%)
Example: 5000 represents 50% share to NGO
Minimum Stake Requirements
Minimum stake amount: 1000 wei (defined by MIN_AMOUNT constant)
All stake operations require user not to be banned
Stakes cannot be made after NGO is marked as finished
Asset Distribution
User portion: (100% - NGO percentage) of staked amount
NGO portion: NGO percentage of staked amount
Rewards are calculated based on the User portion and donations on NGO portion
Token Support
Native ETH (stake
)
Liquid staked ETH (stakeStEth
)
Wrapped staked ETH (stakeWStEth
)
Reward Calculation
Rewards tracked through lastNGOBalance
pendingRewardsCalculation() updates pending rewards by:
Converting current NGO assets to stETH value
Comparing with last recorded balance
Converting difference to wstETH
Adding to pendingNGORewards
Reward Distribution
Handled by oracle through handleNGOShareDistribution
Calculate pending rewards
Take platform fee (5% - LIS_FEE)
Transfer fee to platform address
Transfer remaining rewards to rewardsOwner
Update lastNGOBalance
Request Process
Users initiate withdrawals through requestWithdrawals
Amount constraints:
Minimum: Set by Lido withdrawal queue
Maximum: Set by Lido withdrawal queue
Cannot exceed user's total balance (direct assets + NGO share)
Minimum withdrawal amount: 100 wei (MIN_WITHDRAWAL_AMOUNT)
Withdrawal Methods
Queue-based ETH withdrawal
Request withdrawal via requestWithdrawals
Claim via claimWithdrawal after finalization
Requires withdrawal request ID
Direct token withdrawal
claimWithdrawInStEth: Withdraw in stETH
claimWithdrawInWStEth: Withdraw in wstETH
No queue waiting period
Minimum withdrawal: 100 wei
Asset Recalculation
Withdrawals trigger proportional reduction of:
User's direct assets
NGO shares
Total NGO assets
Type of eth in function for event
Struct representing initial stake information for a user.
constructor
Disables initializers at deployment.
initialize
Initializes the NGO contract.
Parameters
lidoSCAddress
: Lido staking contract
_rewardOwnerAddress
: Rewards recipient
withdrawalSCAddress
: Withdrawal queue contract
owner
: NGO owner address
oracle
: Oracle address
_wstETHSC
: Wrapped stETH contract
Validation
No zero addresses allowed
Effects
Initializes base contracts
Sets up initial configuration
stake
Stakes native ETH in the NGO contract.
Parameters
_ngoPercent
: NGO share percentage (100 = 1%, 10000 = 100%)
Constraints
Must not be banned
NGO must not be finished
Minimal amount to stake: 1000 wei
Percentage must be between MIN_SHARE_PERCENT and MAX_SHARE_PERCENT
stakeStEth
Stakes stETH tokens in the NGO contract.
Parameters
amount
: Amount of stETH to stake (in wei)
_ngoPercent
: NGO share percentage (100 = 1%, 10000 = 100%)
Requirements
Caller must have approved contract for stETH transfer
Same constraints as stake
stakeWStEth
Stakes wstETH tokens directly in the NGO contract.
Parameters
amount
: Amount of wstETH to stake (in wei)
_ngoPercent
: NGO share percentage (100 = 1%, 10000 = 100%)
Requirements
User must approve contract for wstETH transfer before calling
Directly uses wstETH without wrapping
Same constraints as other stake functions
requestWithdrawals
Initiates a withdrawal request.
Parameters
_amount
: Amount of ETH to withdraw (in wei)
_id
: Stake identifier
Constraints
Amount must be within Lido's min/max limits
Range: min - 100 wei , max - 1000 ETH
Amount cannot exceed user's balance for given stake ID
claimWithdrawal
Claims completed withdrawal request.
Parameters
_requestId
: Withdrawal request identifier
Requirements
Request must be finalized in Lido queue
Caller must be original requester
claimWithdrawInStEth
Claims stETH withdrawal. Note: expected amount to withdraw: min - 100 wei.
Parameters
_amount
: Amount to withdraw
_id
: Stake identifier
Effects
Transfers stETH
Updates state
claimWithdrawInWStEth
Claims wstETH withdrawal.
Note: expected amount to withdraw: min - 100 wei.
Parameters
_amount
: Amount to withdraw
_id
: Stake identifier
Effects
Transfers wstETH
Updates state
withdrawCalculation
Internal function for processing withdrawal mathematics.
Parameters
_amount
: Amount to withdraw in stETH
_id
: Stake identifier
Returns
_amountWstETH
: Converted amount in wstETH
Calculation Steps With Explanation
Calculate User's NGO Share
Retrieves the user's share of NGO tokens for the specific stake ID
This represents the user's portion of the total NGO shares
Convert NGO Share to Assets
Converts user's NGO shares to actual wstETH tokens
Formula: user_ngo_share * total_ngo_assets / total_ngo_shares
This determines how many wstETH tokens user's NGO shares represent
Calculate Total User Assets
Combines user's direct assets and NGO share assets
assets[msg.sender][_id]
: Direct wstETH holdings
_ngoAssets
: Converted NGO share in wstETH
Represents total user's withdrawable wstETH
Convert Withdrawal Amount to wstETH
Converts requested stETH withdrawal amount to wstETH
Uses Lido's conversion rate
Necessary because internal accounting uses wstETH
Calculate Withdrawal Ratio
Determines what fraction of user's total assets is being withdrawn
DIVIDER
(10 * 10**17) used for precision
Formula: (withdrawal_amount * DIVIDER) / total_user_assets
This ratio is used to proportionally reduce both direct assets and NGO shares
State Updates Based on Calculations
Update User's Direct Assets
Reduces user's direct assets proportionally based on withdrawal ratio
Maintains correct balance proportion
Update NGO Assets
Calculates and removes withdrawn portion from NGO assets
Maintains NGO asset accounting
Update NGO Shares
Reduces total NGO shares
Updates user's NGO share balance
Maintains share proportions
Update NGO Balance
Updates the tracked stETH balance
Used for reward calculations
Example Calculation Let's say:
User has 100 wstETH direct assets
User has NGO shares worth 50 wstETH
Total assets = 150 wstETH
User wants to withdraw 30 stETH
Convert 30 stETH to wstETH (say 25 wstETH)
Calculate ratio: 25 * DIVIDER / 150 = 0.1667 * DIVIDER
Reduce direct assets: 100 * 0.1667 = -16.67 wstETH
Reduce NGO assets: 50 * 0.1667 = -8.33 wstETH
Final balances:
Direct assets: 83.33 wstETH
NGO share: 41.67 wstETH
Total withdrawn: 25 wstETH
Important Considerations
All calculations maintain proportional relationships
Precision is maintained using DIVIDER
Both user assets and NGO shares are reduced proportionally
Conversion between stETH and wstETH is handled appropriately
This function is critical for maintaining correct balances and proportions during withdrawals while ensuring both user assets and NGO shares are properly accounted for.
Calculates and updates pending rewards for the NGO based on asset value changes.
Process
Converts current NGO assets to stETH value using wstETHSC.getStETHByWstETH()
Compares current balance with lastNGOBalance
If current balance is higher:
Calculates the difference
Converts difference to wstETH
Adds to pendingNGORewards
Reduces totalNGOAssets by the reward amount
State Changes
Updates pendingNGORewards
Modifies totalNGOAssets
Usage
Called before stake asset calculations
Called during withdrawal processing
Called during reward distribution
Dependencies:
Requires wstETHSC interface for token conversions
Relies on lastNGOBalance for change detection
Calculates and distributes staked assets between user and NGO portions.
Parameters:
_amount: Amount being staked in wstETH
_percent: NGO share percentage (100-10000)
Process:
NGO Asset Calculation
Calculates NGO portion using percentage
Assigns remaining amount to user's direct assets
Share Distribution
Creates stake information record
Calculates NGO shares based on total assets
State Updates
Updates user's asset record
Increments total NGO assets
Updates share allocations
Updates lastNGOBalance
State Changes:
Updates assets[msg.sender][id]
Updates _userToStakeInfo mapping
Modifies totalNGOAssets
Updates totalNGOShares
Updates ngoShares mapping
Sets lastNGOBalance
Usage:
Called by stake()
Called by stakeStEth()
Called by stakeWStEth()
handleNGOShareDistribution
Manages the distribution of NGO rewards.
Access Control
Only callable by oracle
Calculations
Converts current NGO assets to stETH value
Calculates reward difference since last distribution
Takes LIS platform fee (5%)
Effects
Updates NGO asset totals
Transfers fees to platform
Transfers remaining rewards to NGO
Updates last balance tracking
setOracle
Manages oracle permissions.
Parameters
_newOracle
: Address to modify oracle status
_state
: True to grant oracle role, false to revoke
setRewardsOwner
Updates rewards recipient.
Parameters
_newRewOwner
: New recipient address
endNGO
Terminates the NGO contract.
Effects
Sets isFinish
to true
Prevents new stakes
Allows existing withdrawals
setUserBan
Controls user access to staking functionality.
Parameters
userAddress
: Address to ban/unban
isBan
: True to ban, false to unban
Access Control
Only callable by owner
Effects
Updates user's ban status
Banned users can withdraw but cannot stake
emitEvent
Emits NGO metadata for indexing.
Parameters
_name
: NGO name
_imageLink
: NGO image URL
_description
: NGO description
_link
: NGO website/resource link
_location
: NGO location
Access Control
Only callable by owner
Usage
Used for updating NGO information in external systems
Enables graph indexing of NGO details
receive
Fallback function to receive ETH.
Usage
Enables direct ETH transfers to contract
Required for stake operations with native ETH
No additional logic executed
getUserBalance
Returns user's total balance including NGO share.
Parameters
_user
: User address
_id
: Stake identifier
Returns
Total balance in stETH
getUserStakeInfo
Returns stake details for given user and ID.
Parameters
_user
: User address
_id
: Stake identifier
Returns
Struct containing:
percent
: NGO share percentage
amount
: Original stake amount
startDate
: Stake timestamp
onlyOracle
Modifier to restrict access to only oracle.
notFinished
Modifier to restrict access to only oracle.
validStake
Modifier to check valid stake info.
notBanned
Modifier to check is user is banned.
validAmount
Modifier to check valid minimum amount for ETH and stETH.
validWithdrawalAmount
Modifier to check valid minimum amount for ETH and stETH.
Staked
Emitted when a user stakes funds in the NGO.
Parameters
_id
: Unique identifier for the stake
_staker
: Address of the staking user
_amountStaked
: Amount of tokens staked
_percentShare
: Percentage shared with NGO (100-10000)
_ngo
: Address of the NGO contract
_timestamp
: Block timestamp of staking
_blockNumber
: Block number of staking
_ethType
: Type of staked token (Native/StEth/WStEth)
Tracks: stake ID, staker address, amount, NGO share percentage, timestamp
Usage: Track new stakes and type of tokens used
RewardsUpdated
Emitted when NGO rewards are distributed.
Parameters
totalNGOAssets
: Amount of tokens in the NGO pool.
_timestamp
: Block timestamp of distribution
_blockNumber
: Block number of distribution
Tracks: total tokens in NGO pool, share distribution timestamp and block number.
Usage: Monitor reward distributions and track NGO earnings
WithdrawRequested
Emitted when a withdrawal request is initiated.
Parameters
_staker
: The address of the user requesting withdrawal
_ngo
: The address of the NGO contract
_requestId
: The ID of the withdrawal request
_timestamp
: The block timestamp when withdraw was requested
_blockNumber
: The block number when withdraw was requested
_stakeId
: The id of the stake
Tracks: user address, request ID, stake ID
Usage: Track withdrawal requests in the queue
WithdrawClaimed
Emitted when a user claims a withdrawal.
Parameters
_claimer
: The address of the user claiming withdrawal
_ngo
: The address of the NGO contract
_amount
: The amount requested for withdrawal
_requestId
: The ID of the withdrawal request
_timestamp
: The block timestamp when withdraw was claimed
_blockNumber
: The block number when withdraw was claimed
Tracks: claimer, amount, request ID
Usage: Track completed ETH withdrawals
WithdrawERC20Claimed
Emitted when stETH or wstETH withdrawal is claimed.
Parameters
_claimer
: The address of the user claiming withdrawal
_ngo
: The address of the NGO contract
_amount
: The amount of stEth or WStEth claimed
_timestamp
: The block timestamp when withdraw was claimed
_blockNumber
: The block number when withdraw was claimed
_stakeId
: The id of the stake
_ethType
: Type of Eth (stETH/wstETH)
Note: if _ethType = 1 then amount of stETH is passed. if _ethType = 2 then amount of wstETH is passed.
Tracks: claimer, amount, token type
Usage: Track completed stETH or wstETH withdrawals
GraphEvent
Emitted when NGO metadata is updated.
Parameters
_name
: The name of the NGO
_imageLink
: The link to the image associated with the NGO
_description
: A description of the NGO
_link
: A link associated with the NGO
_location
: Physical location of NGO
_ngo
: The address of the NGO contract
_timestamp
: The block timestamp when withdraw was claimed
Tracks: NGO details and location
Usage: Track NGO metadata updates for indexing
NGOFinished
Emitted when NGO operations are terminated.
Parameters
_ngo
: The address of the NGO contract
_timestamp
: The timestamp when the NGO was finished
_blockNumber
: The block number when the NGO was finished
Tracks: termination timestamp and block number and address of NGO Contract.
Usage: Signal end of NGO contract.
OracleChanged
Emitted when the oracle was added or status was changed.
Parameters
_newOracle
: New oracle address
_state
:Current oracle state
Note: If state = true - current address active. If state = false - current address inactive.
Tracks: New oracle address and current oracle state.
Usage: Signals change of an Oracle.
RewardsOwnerChanged
Emitted when the rewards owner was changed.
Parameters
_newRewOwner
: New reward owner address
Tracks: New Reward Owner address.
Usage: Signals change of Reward Owner.
BannedUser
Emitted when the state of user access is changed.
Parameters
_userAddress
: User address
_isBan
: Current user state
Tracks: User address and access state of user.
Usage: Signals change of User access status.
InvalidPercent
Trigger: Share percentage outside valid range (100-10000)
Prevention: Validate percentage before staking
Impact: Prevents invalid share calculations
InvalidStakeAmount
Trigger: Invalid Stake amount maybe less than 1000 wei
Prevention: Make sure the stake amount is greater than 1000
Impact: Ensures economic viability of staking
InvalidWithdrawalAmount
Trigger: Withdrawal request below minimum limit 100 wei
Prevention: Make sure the withdrawal amount is greater than 100 wei
Impact: Ensures economic viability of withdrawals
InvalidWithdrawAmount
Trigger: Withdrawal request below minimum limit 1 percent of stake
Prevention: Make sure the withdrawal amount is greater than 1% of your stake
Impact: Ensures economic viability of withdrawals
RequestAmountTooSmall
Trigger: Withdrawal request below minimum limit
Parameter: _amount
- Requested amount
Prevention: Check Lido's minimum withdrawal amount
Impact: Ensures economic viability of withdrawals
RequestAmountTooLarge
Trigger: Withdrawal request above maximum limit or user balance
Parameter: _amount
- Requested amount
Prevention: Check balance and Lido's maximum withdrawal amount
Impact: Prevents overdraw attempts
MinimumWstEthStakeError
Trigger: Amount of stake lower than minimum for wstETH
Prevention: Check balance and Lido's minimum stake amount
Impact: Prevents failed stake attempts
MinimumWstEthWithdrawError
Trigger: Amount of withdrawal lower than minimum for wstETH
Prevention: Check balance and Lido's minimum withdrawal amount
Impact: Prevents failed withdraw attempts
OnlyOracle
Trigger: Non-oracle address calling oracle-restricted function
Parameter: _sender
- Unauthorized caller address
Prevention: Use correct oracle address
Impact: Protects reward distribution mechanism
UserBanned
Trigger: Banned user attempting to stake
Prevention: Check ban status before staking
Impact: Enforces user restrictions
NgoFinished
Trigger: Attempting operations after NGO termination
Prevention: Check NGO status before operations
Impact: Prevents post-termination activities
NotFinalizedStatus
Trigger: Claiming withdrawal before finalization
Prevention: Wait for withdrawal finalization
Impact: Ensures proper withdrawal sequence
RewardError
Trigger: Attempting reward distribution with no rewards
Prevention: Check reward balance before distribution
Impact: Prevents empty reward distributions
NullAddress
Trigger: Providing zero address for critical parameters
Prevention: Validate addresses before use
Impact: Prevents contract initialization with invalid addresses
InvalidRequestIdForUser
Trigger: Claiming withdrawal for wrong request ID
Parameters:
_claimer
: Address attempting claim
_requestId
: Invalid request ID
Prevention: Verify request ownership
Impact: Protects withdrawal ownership
ZeroAmount
Trigger: Attempting operation with zero amount
Prevention: Validate amount before operation
Impact: Prevents meaningless transactions
FeeError
Trigger: Attempting to transfer zero fee
Prevention: Ensure fee is not zero
Impact: Handles failed fee transfers
Administrative Controls
Owner-only administrative functions
Contract upgrades (_authorizeUpgrade)
Oracle management (setOracle)
Rewards owner management (setRewardsOwner)
User banning (setUserBan)
NGO termination (endNGO)
Oracle-only functions
Reward distribution (handleNGOShareDistribution)
User Restrictions
Ban system for malicious users
Banned users can withdraw but cannot stake
Safety Checks
Reentrancy protection on withdrawals
Amount validation
Balance verification
Null address checks
Upgradability
UUPS proxy pattern
Owner-controlled upgrades
State preservation during upgrades