# Tip Streams

Tip streams are a mechanism designed to distribute fees among specific entities within a vault. These entities share a portion of the fees based on predefined rules. This process involves minting additional shares as tips, which slightly dilutes the overall value of existing shares. To keep the system fair and sustainable, the yield generated by the vault must outpace the fees distributed. For context, we charge a fee of 1% of the Assets Under Management (AUM), which forms the basis of the tips distributed.

## How Tip Streams Work

In any vault, the fee is set at the vault level rather than individually for entities. Tip streams define how this fee is divided among those entitled to a share of the tips. A key part of the process is setting the tip rate, which determines the proportion of the fee that will be distributed as tips. These tips are collected in a central pool known as the TipJar.

When tips are distributed, they are not taken directly from the vault’s assets. Instead, additional shares are minted to represent the tips. These newly minted shares are added to the TipJar. This increases the total number of shares in the vault, slightly reducing the value of each share since the same amount of assets is now split among a larger number of shares.

The 1% AUM fee ensures a steady and predictable source for tip distribution. This percentage is applied to the total assets managed by the vault, making the system straightforward and easy to calculate.

Note: The Ethereum Mainnet $ETH vault charges a 0.3% AUM fees, instead of the 1% for stablecoin vaults.

#### TipStream Management Functions

`addTipStream(TipStream memory tipStream)`

```solidity
function addTipStream(TipStream memory tipStream) external onlyGovernor returns (uint256 lockedUntilEpoch) {
    if (tipStream.recipient == address(0)) {
        revert InvalidTipStreamRecipient();
    }
    if (tipStreams[tipStream.recipient].recipient != address(0)) {
        revert TipStreamAlreadyExists(tipStream.recipient);
    }
    if (tipStream.lockedUntilEpoch > block.timestamp + MAX_ALLOWED_LOCKED_UNTIL_EPOCH) {
        revert TipStreamLockedForTooLong(tipStream.recipient);
    }
    _validateTipStreamAllocation(tipStream.allocation, toPercentage(0));

    tipStreams[tipStream.recipient] = tipStream;
    tipStreamRecipients.push(tipStream.recipient);

    emit TipStreamAdded(tipStream);

    return tipStream.lockedUntilEpoch;
}
```

#### Distribution Functions

`shake(address fleetCommander)`

```solidity
function _shake(address fleetCommander_) internal {
    if (!IHarborCommand(harborCommand()).activeFleetCommanders(fleetCommander_)) {
        revert InvalidFleetCommanderAddress();
    }

    IFleetCommander fleetCommander = IFleetCommander(fleetCommander_);
    uint256 shares = fleetCommander.balanceOf(address(this));
    
    if (shares == 0) return;

    uint256 withdrawnAssets = fleetCommander.redeem(
        Constants.MAX_UINT256,
        address(this),
        address(this)
    );

    if (withdrawnAssets == 0) return;

    IERC20 underlyingAsset = IERC20(fleetCommander.asset());
    uint256 totalDistributed = 0;
    Percentage totalAllocated = toPercentage(0);

    for (uint256 i = 0; i < tipStreamRecipients.length; i++) {
        address recipient = tipStreamRecipients[i];
        Percentage allocation = tipStreams[recipient].allocation;
        totalAllocated = totalAllocated + allocation;

        uint256 amount = (totalAllocated == PERCENTAGE_100) ? 
            withdrawnAssets - totalDistributed :
            withdrawnAssets.applyPercentage(allocation);

        if (amount > 0) {
            underlyingAsset.safeTransfer(recipient, amount);
            totalDistributed += amount;
        }
    }

    uint256 remaining = withdrawnAssets - totalDistributed;
    if (remaining > 0) {
        underlyingAsset.safeTransfer(treasury(), remaining);
    }
}
```

#### Query Functions

`getTotalAllocation()`

```solidity
function getTotalAllocation() public view returns (Percentage total) {
    total = toPercentage(0);
    for (uint256 i = 0; i < tipStreamRecipients.length; i++) {
        total = total + tipStreams[tipStreamRecipients[i]].allocation;
    }
}
```

The contract uses the OpenZeppelin SafeERC20 library for safe token transfers and includes comprehensive error handling for invalid operations.


---

# 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://docs.summer.fi/lazy-summer-protocol/governance/tip-streams.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.
