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

* [Source code](https://github.com/Launchnodes-Ltd/LIS/blob/main/contracts/NGOLis.sol)
* [Deployed contract](https://holesky.etherscan.io/address/0xce4235b8d424323ed68b2eed84ccfcfe61835dfa#code)

## 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**

```solidity
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

2. **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&#x20;
     * 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

```solidity
NGOLis is
    Initializable,
    ERC721HolderUpgradeable,
    ReentrancyGuardUpgradeable,
    UUPSUpgradeable,
    OwnableUpgradeable
```

## Sequential Diagram

<figure><img src="/files/IJebaeJ6c4G4IUJins9B" alt=""><figcaption></figcaption></figure>

### Contract Storage Structure

#### State Variables

```solidity
uint256 private id;                        // Stake ID counter
address private _lis;                      // LIS token address
ILido public lidoSC;                       // Lido protocol interface
IWithdrawalQueue public withdrawalSC;      // Withdrawal queue interface
IWstEth public wstETHSC;                   // Wrapped stETH interface
address public rewardsOwner;               // Rewards recipient
bool public isFinish;                      // NGO status flag
uint256 private pendingNGORewards;         // Storage variable for pending rewards
uint256 totalNGOAssets;                    // Total wstETH with NGO
uint256 totalNGOShares;                    // Total Shares with User
uint256 lastNGOBalance;                    // stETH balance at the time of the last NGO reward distribution

```

#### Key Storage Mappings

```solidity
mapping(address => mapping(uint256 => StakeInfo)) private _userToStakeInfo;
mapping(address => bool) private _oracles;
mapping(uint256 => address) private _requestIdToUser;
mapping(address => mapping(uint256 => uint256)) public ngoShares;
mapping(address => bool) public isBanned;
mapping(address => mapping(uint256 => uint)) public assets;
```

#### Important Constants

```solidity
uint8 constant MIN_SHARE_PERCENT = 100;    // 1%
uint16 constant MAX_SHARE_PERCENT = 10000;  // 100%
uint16 constant PERCENT_DIVIDER = 10000;      // Base for percentage calculations
uint256 constant DIVIDER = 10 * 10 ** 17;      // Precision divider
uint16 constant LIS_FEE = 500;                // 5% platform fee
uint8 constant MIN_WITHDRAWAL_AMOUNT = 100;   // minimum withdrawal amount
uint16 constant MIN_AMOUNT = 1000;            // minimum amount to stake
uint8 constant WITHDRAW_GAP = 100;           //If during withdrawal user's balance < WITHDRAW_GAP
```

#### Data Structures

### Enums

```solidity
enum EthType {
    Native,
    StEth,
    WStEth
}
```

Type of eth in function for event

### Structs

```solidity
struct StakeInfo {
    uint16 percent;    // Share percentage
    uint256 amount;       // Staked amount
    uint256 startDate;    // Timestamp
}
```

Struct representing initial stake information for a user.

### Function Documentation

#### Initialization

### `constructor`

```solidity
constructor() {
    _disableInitializers();
}
```

Disables initializers at deployment.

### `initialize`

```solidity
function initialize(
    address lidoSCAddress,
    address _rewardOwnerAddress,
    address withdrawalSCAddress,
    address owner,
    address oracle,
    address _wstETHSC
) public initializer
```

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`

```solidity
function stake(uint16 _ngoPercent) public payable
```

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`

```solidity
function stakeStEth(uint256 amount, uint16 _ngoPercent) public
```

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`

```solidity
function stakeWStEth(uint256 amount, uint16 _ngoPercent) public
```

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`

```solidity
function requestWithdrawals(uint256 _amount, uint256 _id) public
```

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`

```solidity
function claimWithdrawal(uint256 _requestId) public nonReentrant
```

Claims completed withdrawal request.

* **Parameters**
  * `_requestId`: Withdrawal request identifier
* **Requirements**
  * Request must be finalized in Lido queue
  * Caller must be original requester

### `claimWithdrawInStEth`

```solidity
function claimWithdrawInStEth(uint256 _amount, uint256 _id) public
```

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`

```solidity
function claimWithdrawInWStEth(uint256 _amount, uint256 _id) public
```

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`

```solidity
function withdrawCalculation(
    uint256 _amount,
    uint256 _id
) private returns (uint256 _amountWstETH)
```

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**

   ```solidity
   _ngoShare = ngoShares[msg.sender][_id]
   ```

   * 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**

   ```solidity
   _ngoAssets = _ngoShare.mulDiv(totalNGOAssets, totalNGOShares)
   ```

   * 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**

   ```solidity
   _totalUserWstETH = assets[msg.sender][_id] + _ngoAssets
   ```

   * 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**

   ```solidity
   _amountWstETH = wstETHSC.getWstETHByStETH(_amount)
   ```

   * Converts requested stETH withdrawal amount to wstETH
   * Uses Lido's conversion rate
   * Necessary because internal accounting uses wstETH
5. **Calculate Withdrawal Ratio**

   ```solidity
   _ratio = _amountWstETH.mulDiv(DIVIDER, _totalUserWstETH)
   ```

   * 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**

   ```solidity
   assets[msg.sender][_id] -= _ratio.mulDiv(assets[msg.sender][_id], DIVIDER)
   ```

   * Reduces user's direct assets proportionally based on withdrawal ratio
   * Maintains correct balance proportion
2. **Update NGO Assets**

   ```solidity
   uint256 withdrawnNgoAssets = _ngoAssets.mulDiv(_ratio, DIVIDER)
   totalNGOAssets -= withdrawnNgoAssets
   ```

   * Calculates and removes withdrawn portion from NGO assets
   * Maintains NGO asset accounting
3. **Update NGO Shares**

   ```solidity
   totalNGOShares -= _ngoShare.mulDiv(_ratio, DIVIDER)
   ngoShares[msg.sender][_id] -= _ngoShare.mulDiv(_ratio, DIVIDER)
   ```

   * Reduces total NGO shares
   * Updates user's NGO share balance
   * Maintains share proportions
4. **Update NGO Balance**

   ```solidity
   lastNGOBalance -= wstETHSC.getStETHByWstETH(withdrawnNgoAssets)
   ```

   * 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

```solidity
function pendingRewardsCalculation() private
```

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

```solidity
function assetsCalculation(uint256 _amount, uint16 _percent) private
```

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`

```solidity
function handleNGOShareDistribution() public onlyOracle
```

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`

```solidity
function setOracle(address _newOracle, bool _state) public onlyOwner
```

Manages oracle permissions.

* **Parameters**
  * `_newOracle`: Address to modify oracle status
  * `_state`: True to grant oracle role, false to revoke

### `setRewardsOwner`

```solidity
function setRewardsOwner(address _newRewOwner) public onlyOwner
```

Updates rewards recipient.

* **Parameters**
  * `_newRewOwner`: New recipient address

### `endNGO`

```solidity
function endNGO() public notFinished onlyOwner
```

Terminates the NGO contract.

* **Effects**
  * Sets `isFinish` to true
  * Prevents new stakes
  * Allows existing withdrawals

### `setUserBan`

```solidity
function setUserBan(address userAddress, bool isBan) public onlyOwner
```

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`

```solidity
function emitEvent(
    string memory _name,
    string calldata _imageLink,
    string calldata _description,
    string calldata _link,
    string calldata _location
) public onlyOwner
```

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`

```solidity
receive() external payable
```

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`

```solidity
function getUserBalance(address _user, uint256 _id) public view returns (uint256)
```

Returns user's total balance including NGO share.

* **Parameters**
  * `_user`: User address
  * `_id`: Stake identifier
* **Returns**
  * Total balance in stETH

#### `getUserStakeInfo`

```solidity
function getUserStakeInfo(address _user, uint256 _id) public view returns (StakeInfo)
```

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`**

```solidity
modifier onlyOracle() {
        if (!_oracles[msg.sender]) {
            revert OnlyOracle(msg.sender);
        }
        _;
    }
```

Modifier to restrict access to only oracle.

**`notFinished`**

```solidity
modifier notFinished() {
        if (isFinish) {
            revert NgoFinished();
        }
        _;
    }
```

Modifier to restrict access to only oracle.

**`validStake`**

```solidity
modifier validStake(uint16 _ngoPercent) {
        if (
            _ngoPercent < MIN_SHARE_PERCENT || _ngoPercent > MAX_SHARE_PERCENT
        ) {
            revert InvalidPercent();
        }

        _;
    }
```

Modifier to check valid stake info.

**`notBanned`**

```solidity
modifier notBanned() {
        if (_isBanned[msg.sender]) {
            revert UserBanned();
        }
        _;
    }
```

Modifier to check is user is banned.

**`validAmount`**

```solidity
modifier validAmount(uint256 _amount) {
        if (_amount < MIN_AMOUNT) {
            revert InvalidStakeAmount();
        }
        _;
    }
```

Modifier to check valid minimum amount for ETH and stETH.

**`validWithdrawalAmount`**

```solidity
modifier validWithdrawalAmount(uint256 _amount) {
        if (_amount < MIN_WITHDRAWAL_AMOUNT) {
            revert InvalidWithdrawalAmount();
        }
        _;
    }
```

Modifier to check valid minimum amount for ETH and stETH.

### Events&#x20;

#### Staking Events

**`Staked`**

```solidity
event Staked(
    uint256 _id,
    address _staker,
    uint256 _amountStaked,
    uint16 _percentShare,
    address _ngo,
    uint256 _timestamp,
    uint256 _blockNumber,
    EthType _ethType
);
```

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`**

```solidity
event RewardsUpdated(
    uint256 _totalNGOAssets,
    uint256 _timestamp,
    uint256 _blockNumber
);
```

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`**

```solidity
event WithdrawRequested(
    address _staker,
    address _ngo,
    uint256 _requestId,
    uint256 _timestamp,
    uint256 _blockNumber,
    uint256 _stakeId
);
```

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`**

```solidity
event WithdrawClaimed(
    address _claimer,
    address _ngo,
    uint256 _amount,
    uint256 _requestId,
    uint256 _timestamp,
    uint256 _blockNumber
);
```

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`**

```solidity
event WithdrawERC20Claimed(
    address _claimer,
    address _ngo,
    uint256 _amount,
    uint256 _timestamp,
    uint256 _blockNumber,
    uint256 _stakeId,
    EthType _ethType
);
```

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.\
  &#x20;           if \_ethType = 2 then amount of wstETH is passed.
* **Tracks:** claimer, amount, token type
* **Usage**: Track completed stETH or wstETH withdrawals

#### Administrative Events

**`GraphEvent`**

```solidity
event GraphEvent(
    string _name,
    string _imageLink,
    string _description,
    string _link,
    string _location,
    address _ngo,
    uint256 _timestamp
);
```

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`**

```solidity
event NGOFinished(
    address _ngo,
    uint256 _timestamp,
    uint256 _blockNumber
);
```

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`**

```solidity
event OracleChanged(
    address _newOracle,
    bool _state
);
```

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`**

```solidity
event RewardsOwnerChanged(
    address _newRewOwner
);
```

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`**

```solidity
event BannedUser(
    address _userAddress, 
    bool _isBan
);
```

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`**

```solidity
error InvalidPercent();
```

* **Trigger**: Share percentage outside valid range (100-10000)
* **Prevention**: Validate percentage before staking
* **Impact**: Prevents invalid share calculations

**`InvalidStakeAmount`**

```solidity
error 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`**

```solidity
error 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`**

```solidity
error 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`**

```solidity
error RequestAmountTooSmall(uint256 _amount);
```

* **Trigger**: Withdrawal request below minimum limit
* **Parameter**: `_amount` - Requested amount
* **Prevention**: Check Lido's minimum withdrawal amount
* **Impact**: Ensures economic viability of withdrawals

**`RequestAmountTooLarge`**

```solidity
error RequestAmountTooLarge(uint256 _amount);
```

* **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`**

```solidity
error 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`**

```solidity
error 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`**

```solidity
error OnlyOracle(address _sender);
```

* **Trigger**: Non-oracle address calling oracle-restricted function
* **Parameter**: `_sender` - Unauthorized caller address
* **Prevention**: Use correct oracle address
* **Impact**: Protects reward distribution mechanism

**`UserBanned`**

```solidity
error UserBanned();
```

* **Trigger**: Banned user attempting to stake
* **Prevention**: Check ban status before staking
* **Impact**: Enforces user restrictions

#### State Errors

**`NgoFinished`**

```solidity
error NgoFinished();
```

* **Trigger**: Attempting operations after NGO termination
* **Prevention**: Check NGO status before operations
* **Impact**: Prevents post-termination activities

**`NotFinalizedStatus`**

```solidity
error NotFinalizedStatus();
```

* **Trigger**: Claiming withdrawal before finalization
* **Prevention**: Wait for withdrawal finalization
* **Impact**: Ensures proper withdrawal sequence

**`RewardError`**

```solidity
error RewardError();
```

* **Trigger**: Attempting reward distribution with no rewards
* **Prevention**: Check reward balance before distribution
* **Impact**: Prevents empty reward distributions

#### Technical Errors

**`NullAddress`**

```solidity
error NullAddress();
```

* **Trigger**: Providing zero address for critical parameters
* **Prevention**: Validate addresses before use
* **Impact**: Prevents contract initialization with invalid addresses

**`InvalidRequestIdForUser`**

```solidity
error InvalidRequestIdForUser(address _claimer, uint256 _requestId);
```

* **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`**

```solidity
error ZeroAmount();
```

* **Trigger**: Attempting operation with zero amount
* **Prevention**: Validate amount before operation
* **Impact**: Prevents meaningless transactions

**`FeeError`**

```solidity
error 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

{% hint style="info" %}
**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.
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://launchnodes.gitbook.io/lido-impact-staking-lis/contracts/ngolis.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
