> ## Documentation Index
> Fetch the complete documentation index at: https://seilabs-docs-evm-cookbook.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Distribution Precompile

> Manage staking rewards and validator commissions through Sei's distribution precompile. EVM applications can withdraw rewards, set withdrawal addresses, and interact with Cosmos SDK distribution functionality.

<Info>Watch the video walkthrough for this topic in the [Video Tutorials](/evm/videos) section.</Info>

**Address**: `0x0000000000000000000000000000000000001007`

The distribution precompile gives EVM access to the Cosmos SDK distribution module. Smart contracts can use it to manage staking rewards, validator commissions, and withdrawal addresses. DeFi dApps that need to handle staking rewards programmatically depend on this precompile.

## Key features

* Withdraw delegation rewards from validators.
* Withdraw earned commission (validators only).
* Withdraw rewards from multiple validators in one batch call.
* Set a custom withdrawal address.
* Query detailed reward information with `rewards(address)`.

## Interface overview

```solidity theme={null}
interface IDistr {
    // Events
    event WithdrawAddressSet(address indexed delegator, address withdrawAddr);
    event DelegationRewardsWithdrawn(address indexed delegator, string validator, uint256 amount);
    event MultipleDelegationRewardsWithdrawn(address indexed delegator, string[] validators, uint256[] amounts);
    event ValidatorCommissionWithdrawn(string indexed validator, uint256 amount);

    // Transaction Methods
    function setWithdrawAddress(address withdrawAddr) external returns (bool);
    function withdrawDelegationRewards(string memory validator) external returns (bool);
    function withdrawMultipleDelegationRewards(string[] memory validators) external returns (bool);
    function withdrawValidatorCommission() external returns (bool);

    // Query Methods
    function rewards(address delegatorAddress) external view returns (Rewards);
}
```

## Setup

Install ethers and the Sei EVM bindings, which include the precompile address and ABI:

```bash theme={null}
npm install ethers @sei-js/precompiles@^3
```

Import both constants where you create the contract instance:

```ts theme={null}
import { DISTRIBUTION_PRECOMPILE_ABI, DISTRIBUTION_PRECOMPILE_ADDRESS } from '@sei-js/precompiles';
```

## Events

The distribution precompile emits events for all state-changing operations. Off-chain services can use these events to track reward distributions and configuration changes.

### WithdrawAddressSet

Emitted when a delegator changes their reward withdrawal address.

```solidity theme={null}
event WithdrawAddressSet(address indexed delegator, address withdrawAddr);
```

**Parameters:**

* `delegator` (indexed): The EVM address of the delegator who changes the setting
* `withdrawAddr`: The new address that receives the rewards

**Example usage:**

```typescript theme={null}
// Listen for withdrawal address changes
distrContract.on('WithdrawAddressSet', (delegator, withdrawAddr) => {
  console.log(`${delegator} set withdrawal address to ${withdrawAddr}`);
});
```

### DelegationRewardsWithdrawn

Emitted when a delegator withdraws rewards from a single validator.

```solidity theme={null}
event DelegationRewardsWithdrawn(address indexed delegator, string validator, uint256 amount);
```

**Parameters:**

* `delegator` (indexed): The EVM address of the delegator who withdraws the rewards
* `validator`: The Sei validator address (for example, "seivaloper1...")
* `amount`: The amount of rewards withdrawn (in usei, 6-decimal precision)

**Example usage:**

```typescript theme={null}
// Track individual reward withdrawals
distrContract.on('DelegationRewardsWithdrawn', (delegator, validator, amount) => {
  const seiAmount = Number(amount) / 1e6;
  console.log(`${delegator} withdrew ${seiAmount} SEI from ${validator}`);
});
```

### MultipleDelegationRewardsWithdrawn

Emitted when a delegator withdraws rewards from multiple validators in a single transaction.

```solidity theme={null}
event MultipleDelegationRewardsWithdrawn(address indexed delegator, string[] validators, uint256[] amounts);
```

**Parameters:**

* `delegator` (indexed): The EVM address of the delegator who withdraws the rewards
* `validators`: An array of Sei validator addresses
* `amounts`: An array of reward amounts, one for each validator (in usei, 6-decimal precision)

**Example usage:**

```typescript theme={null}
// Track batch reward withdrawals
distrContract.on('MultipleDelegationRewardsWithdrawn', (delegator, validators, amounts) => {
  for (let i = 0; i < validators.length; i++) {
    const seiAmount = Number(amounts[i]) / 1e6;
    console.log(`${delegator} withdrew ${seiAmount} SEI from ${validators[i]}`);
  }
});
```

### ValidatorCommissionWithdrawn

Emitted when a validator operator withdraws their earned commission.

```solidity theme={null}
event ValidatorCommissionWithdrawn(string indexed validator, uint256 amount);
```

**Parameters:**

* `validator` (indexed): The Sei validator address
* `amount`: The commission amount withdrawn (in usei, 6-decimal precision)

**Example usage:**

```typescript theme={null}
// Track validator commission withdrawals
distrContract.on('ValidatorCommissionWithdrawn', (validator, amount) => {
  const seiAmount = Number(amount) / 1e6;
  console.log(`Validator ${validator} withdrew ${seiAmount} SEI commission`);
});
```

<Info>**Event amounts**: All event amounts use 6-decimal precision (usei). This matches the withdrawn token amounts. In contrast, the `rewards()` query returns 18-decimal precision.</Info>

## Transaction methods

### setWithdrawAddress

Sets the withdrawal address for staking rewards. By default, rewards go to the delegator's address, but you can change it.

```solidity theme={null}
function setWithdrawAddress(address withdrawAddr) external returns (bool success);
```

**Parameters:**

* `withdrawAddr`: The EVM address that receives future rewards

**Gas cost**: \~30,000 gas

**Example:**

```solidity theme={null}
// Set rewards to go to a treasury contract
bool success = DISTR_CONTRACT.setWithdrawAddress(0x742d35Cc6634C0532925a3b8D4C9db96590c6C8C);
require(success, "Failed to set withdraw address");
```

<Warning>
  **Recipient validation**: Before you set the withdrawal address, make sure that it is associated and allowed to receive funds. The withdrawal address must be able to receive external funds. The precompile rejects an unassociated EVM address with the error `cannot use an unassociated address as withdraw address`. Separately, the bank module returns `ErrInvalidRecipient` for an address that it blocks from receiving external funds. For example, it blocks the cast address of an EVM account that was later associated with a different Sei address.

  If a withdrawal address that was set earlier becomes invalid, reward withdrawals automatically fall back to the delegator's own address. An address is invalid when it is blocked or can no longer receive external funds.
</Warning>

### withdrawDelegationRewards

Withdraws accumulated rewards from a specific validator.

```solidity theme={null}
function withdrawDelegationRewards(string memory validator) external returns (bool success);
```

**Parameters:**

* `validator`: The Sei validator address (for example, "seivaloper1...")

**Gas cost**: \~50,000-80,000 gas (varies with the reward amount)

**Example:**

```solidity theme={null}
string memory validator = "seivaloper1xyz...";
bool success = DISTR_CONTRACT.withdrawDelegationRewards(validator);
require(success, "Failed to withdraw rewards");
```

### withdrawMultipleDelegationRewards

Withdraws rewards from multiple validators in a single transaction.

```solidity theme={null}
function withdrawMultipleDelegationRewards(string[] memory validators) external returns (bool success);
```

**Parameters:**

* `validators`: An array of Sei validator addresses

**Gas cost**: \~40,000 + (30,000 × number of validators)

**Example:**

```solidity theme={null}
string[] memory validators = new string[](3);
validators[0] = "seivaloper1abc...";
validators[1] = "seivaloper1def...";
validators[2] = "seivaloper1ghi...";

bool success = DISTR_CONTRACT.withdrawMultipleDelegationRewards(validators);
require(success, "Failed to withdraw multiple rewards");
```

<Info>**Batch efficiency**: One `withdrawMultipleDelegationRewards` call is significantly more gas-efficient than multiple individual calls, especially when you withdraw from 3 or more validators.</Info>

### withdrawValidatorCommission

Lets a validator withdraw its earned commission. Only the validator operator address can call this method.

```solidity theme={null}
function withdrawValidatorCommission() external returns (bool success);
```

**Parameters:** None. The precompile determines the validator automatically from the caller's associated Sei address.

**Gas cost**: \~60,000-90,000 gas

**Example:**

```solidity theme={null}
// Only works if caller is the validator operator
bool success = DISTR_CONTRACT.withdrawValidatorCommission();
require(success, "Failed to withdraw commission");
```

<Warning>**Validator only**: Call this method only from the validator's operator address. The precompile identifies the validator automatically from the caller's associated Sei address. If the caller is not a validator operator, the call fails.</Warning>

## Query methods

### rewards

Returns the reward information for a delegator across all validators.

```solidity theme={null}
function rewards(address delegatorAddress) external view returns (Rewards rewards);
```

**Data structures:**

```solidity theme={null}
struct Coin {
    uint256 amount;    // Token amount in 18 decimal precision (DecCoins)
    uint256 decimals;  // Always 18 - decimal precision for the amount
    string denom;      // Token denomination (e.g., "usei")
}

struct Reward {
    Coin[] coins;              // Reward coins from this validator
    string validator_address;  // Validator's Sei address
}

struct Rewards {
    Reward[] rewards;  // Per-validator breakdown
    Coin[] total;      // Total rewards across all validators
}
```

<Warning>
  **Critical: decimal precision for rewards**

  The `rewards()` query returns amounts with **18-decimal precision** (`DecCoins` from the Cosmos SDK).

  In contrast, withdrawn reward amounts use **6-decimal precision** (`sdk.Coins`).

  **To convert pending rewards to SEI for display:**

  ```javascript theme={null}
  const pendingRewardsSei = amount / 1e18; // rewards() query uses 18 decimals
  ```

  **Withdrawn rewards are in usei (6 decimals):**

  ```javascript theme={null}
  const withdrawnSei = withdrawnAmount / 1e6; // actual withdrawals use 6 decimals
  ```
</Warning>

**Example:**

```solidity theme={null}
Rewards memory userRewards = DISTR_CONTRACT.rewards(msg.sender);

// Check total rewards - uses 18 decimal precision
for (uint i = 0; i < userRewards.total.length; i++) {
    Coin memory coin = userRewards.total[i];
    // coin.decimals is always 18 for rewards
    uint256 displayAmount = coin.amount / (10 ** coin.decimals);
    // Process reward amount for coin.denom
}

// Check per-validator rewards
for (uint i = 0; i < userRewards.rewards.length; i++) {
    Reward memory reward = userRewards.rewards[i];
    // Process rewards from reward.validator_address
}
```

## Understanding decimal precision

Because of how the Cosmos SDK tracks rewards, the distribution precompile uses **different decimal precision for queries and withdrawals**:

| Function | Type | Decimal precision | Conversion to SEI |
| - | - | - | - |
| **rewards()** | Query (pending rewards) | 18 decimals (DecCoins) | `amount / 1e18` |
| **withdrawDelegationRewards()** | Withdrawn amount (event) | 6 decimals (usei) | `amount / 1e6` |
| **withdrawMultipleDelegationRewards()** | Withdrawn amount (event) | 6 decimals (usei) | `amount / 1e6` |
| **withdrawValidatorCommission()** | Withdrawn amount (event) | 6 decimals (usei) | `amount / 1e6` |

### Why different precisions?

1. **Pending rewards (18 decimals)**: The Cosmos SDK tracks pending rewards as `DecCoins` (decimal coins) with 18-decimal precision. This gives higher accuracy while rewards accumulate.

2. **Withdrawn rewards (6 decimals)**: When rewards are withdrawn, they are converted to `sdk.Coins` (usei) with 6-decimal precision. This matches Sei's native token units.

### Conversion helper functions

```typescript theme={null}
// Convert pending rewards (18 decimals) to SEI
function pendingRewardsToSei(amount: bigint): number {
  return Number(amount) / 1e18;
}

// Convert withdrawn rewards (6 decimals) to SEI
function withdrawnRewardsToSei(amount: bigint): number {
  return Number(amount) / 1e6;
}

// Example usage
const pendingRewards = await distrContract.rewards(delegatorAddress);
const pendingSei = pendingRewardsToSei(pendingRewards.total[0].amount);
console.log(`Pending rewards: ${pendingSei} SEI`);
```

<Info>When you reconcile pending rewards with withdrawals, remember the 12-decimal difference (10^12 factor) between query results and withdrawal amounts.</Info>

## Practical examples

### DeFi yield aggregator

```solidity theme={null}
contract YieldAggregator {
    IDistr constant DISTR = IDistr(0x0000000000000000000000000000000000001007);

    mapping(address => string[]) public userValidators;

    function harvestRewards() external {
        string[] memory validators = userValidators[msg.sender];
        require(validators.length > 0, "No validators to harvest from");

        // Efficiently withdraw from all validators
        bool success = DISTR.withdrawMultipleDelegationRewards(validators);
        require(success, "Harvest failed");

        // Additional logic to compound or distribute rewards
    }

    function checkPendingRewards(address user) external view returns (uint256 totalSei) {
        Rewards memory rewards = DISTR.rewards(user);

        for (uint i = 0; i < rewards.total.length; i++) {
            if (keccak256(bytes(rewards.total[i].denom)) == keccak256(bytes("usei"))) {
                totalSei = rewards.total[i].amount;
                break;
            }
        }
    }
}
```

### Validator commission manager

```solidity theme={null}
contract ValidatorManager {
    IDistr constant DISTR = IDistr(0x0000000000000000000000000000000000001007);

    address public treasury;

    constructor(address _treasury) {
        treasury = _treasury;

        // Set commission withdrawals to go to treasury
        DISTR.setWithdrawAddress(treasury);
    }

    // Only callable by the validator operator
    function withdrawCommission() external {
        bool success = DISTR.withdrawValidatorCommission();
        require(success, "Commission withdrawal failed");
    }
}
```

## Error handling

This example shows common error scenarios and how to handle them:

```solidity theme={null}
contract SafeDistribution {
    IDistr constant DISTR = IDistr(0x0000000000000000000000000000000000001007);

    function safeWithdrawRewards(string memory validator) external returns (bool) {
        // Check if there are rewards to withdraw first
        Rewards memory rewards = DISTR.rewards(msg.sender);

        bool hasRewards = false;
        for (uint i = 0; i < rewards.rewards.length; i++) {
            if (keccak256(bytes(rewards.rewards[i].validator_address)) ==
                keccak256(bytes(validator))) {
                hasRewards = rewards.rewards[i].coins.length > 0;
                break;
            }
        }

        if (!hasRewards) {
            return false; // No rewards to withdraw
        }

        try DISTR.withdrawDelegationRewards(validator) returns (bool success) {
            return success;
        } catch {
            return false; // Handle withdrawal failure gracefully
        }
    }
}
```

## Gas optimization tips

1. To withdraw from multiple validators, use one `withdrawMultipleDelegationRewards` batch call.
2. Query rewards before you withdraw, to avoid unnecessary transactions.
3. Set the withdrawal address once. Avoid repeated `setWithdrawAddress` calls.
4. Withdraw validator commission when the amounts are substantial.

## Integration patterns

### Auto-compounding strategy

```solidity theme={null}
// Automatically reinvest rewards back into staking
function autoCompound() external {
    // 1. Withdraw rewards
    DISTR.withdrawMultipleDelegationRewards(getMyValidators());

    // 2. Use staking precompile to re-delegate
    // (Implementation depends on staking precompile integration)
}
```

### Treasury management

```solidity theme={null}
// Route all rewards to a DAO treasury
function setupTreasuryWithdrawals(address treasury) external onlyOwner {
    DISTR.setWithdrawAddress(treasury);
}
```

<Info>View the complete distribution precompile ABI at the [Sei Chain v6.6.1 snapshot](https://github.com/sei-protocol/sei-chain/blob/v6.6.1/precompiles/distribution/legacy/v66/abi.json).</Info>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.