NGOLis

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.

Contract Architecture

Upgrade Mechanism

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

Upgrade Control

The upgrade process is controlled through two main components:

  1. Authorization Function

function _authorizeUpgrade(address newImplementation) internal override onlyOwner

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

  1. Proxy Pattern

  • Uses ERC1967 proxy standard

  • Maintains separation of concerns

  • Preserves contract state

Upgrade Process

  1. Owner initiates upgrade with new implementation

  2. _authorizeUpgrade validates permission

  3. Proxy updates implementation pointer

  4. State remains intact in proxy storage

Core Logic and Operations

Staking Mechanism

  1. 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

  2. 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

  3. 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

  4. Token Support

    • Native ETH (stake)

    • Liquid staked ETH (stakeStEth)

    • Wrapped staked ETH (stakeWStEth)

Reward Management

  1. 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

  2. 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

Withdrawal System

  1. 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)

  2. 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

  3. Asset Recalculation

    • Withdrawals trigger proportional reduction of:

      • User's direct assets

      • NGO shares

      • Total NGO assets

Inheritance Structure

Sequential Diagram

Contract Storage Structure

State Variables

Key Storage Mappings

Important Constants

Data Structures

Enums

Type of eth in function for event

Structs

Struct representing initial stake information for a user.

Function Documentation

Initialization

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

Staking Functions

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

Withdrawal 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

  1. 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

  2. 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

  3. 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

  4. Convert Withdrawal Amount to wstETH

    • Converts requested stETH withdrawal amount to wstETH

    • Uses Lido's conversion rate

    • Necessary because internal accounting uses wstETH

  5. 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

  1. Update User's Direct Assets

    • Reduces user's direct assets proportionally based on withdrawal ratio

    • Maintains correct balance proportion

  2. Update NGO Assets

    • Calculates and removes withdrawn portion from NGO assets

    • Maintains NGO asset accounting

  3. Update NGO Shares

    • Reduces total NGO shares

    • Updates user's NGO share balance

    • Maintains share proportions

  4. 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

  1. Convert 30 stETH to wstETH (say 25 wstETH)

  2. Calculate ratio: 25 * DIVIDER / 150 = 0.1667 * DIVIDER

  3. Reduce direct assets: 100 * 0.1667 = -16.67 wstETH

  4. Reduce NGO assets: 50 * 0.1667 = -8.33 wstETH

  5. Final balances:

    • Direct assets: 83.33 wstETH

    • NGO share: 41.67 wstETH

    • Total withdrawn: 25 wstETH

Important Considerations

  1. All calculations maintain proportional relationships

  2. Precision is maintained using DIVIDER

  3. Both user assets and NGO shares are reduced proportionally

  4. 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.

Internal Calculation Functions

pendingRewardsCalculation

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

assetsCalculation

Calculates and distributes staked assets between user and NGO portions.

  • Parameters:

    • _amount: Amount being staked in wstETH

    • _percent: NGO share percentage (100-10000)

  • Process:

    1. NGO Asset Calculation

      • Calculates NGO portion using percentage

      • Assigns remaining amount to user's direct assets

    2. Share Distribution

      • Creates stake information record

      • Calculates NGO shares based on total assets

    3. 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()

Administrative Functions

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

System Functions

receive

Fallback function to receive ETH.

  • Usage

    • Enables direct ETH transfers to contract

    • Required for stake operations with native ETH

    • No additional logic executed

View Functions

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

Modifiers

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.

Events

Staking Events

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

Reward Events

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

Withdrawal Events

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

Administrative Events

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.

Error Conditions

Validation Errors

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

Access Control Errors

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

State Errors

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

Technical Errors

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

Security Features

  1. 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)

  2. User Restrictions

    • Ban system for malicious users

    • Banned users can withdraw but cannot stake

  3. Safety Checks

    • Reentrancy protection on withdrawals

    • Amount validation

    • Balance verification

    • Null address checks

  4. Upgradability

    • UUPS proxy pattern

    • Owner-controlled upgrades

    • State preservation during upgrades

Minimum Withdrawal Limit Protection

When withdrawing assets from the smart contract, there is a built-in protection mechanism that enforces a minimum withdrawal amount based on the total staked amount. This prevents potential attacks through micro-withdrawals.

Example:

  • Total Stake: 1000 ETH

  • Attempted Withdrawal: 999 wei

  • Result: Transaction reverts

Due to Solidity's integer division rules, the ratio calculation for very small withdrawals relative to the stake amount will round to 0, making such withdrawals impossible.

Key Point: The minimum withdrawal amount automatically scales with the size of the stake. For a 1000 ETH stake, the minimum withdrawal is 1000 wei.

Last updated