PaymentSplitter
Inherits: Initializable, Context
Title: PaymentSplitter
Author: OpenZeppelin; adapted by Golem Foundation
Splits ETH and ERC20 token payments proportionally among payees based on shares
Modified from OpenZeppelin to use initializable pattern instead of constructor. Payments split in proportion to number of shares held by each account. Pull payment model where payees call release() to claim their share. Not compatible with rebasing or fee-on-transfer tokens.
Notes:
-
security-contact: [email protected]
Quick Start — What Matters Most
The PaymentSplitter distributes yield shares proportionally to configured recipients using a pull-payment model.
Key functions to understand:
release— Payee claims their share (pull payment model)shares— Check a payee's share allocationreleased— Check how much a payee has already claimed
State Variables
_totalShares
Total shares across all payees
Sum of all individual payee shares. Used as denominator in distribution calculations
uint256 private _totalShares
_totalReleased
Total ETH released to all payees
Cumulative amount of ETH paid out. Used to calculate pending payments
uint256 private _totalReleased
_shares
Mapping of payee addresses to their share allocation
Set once during initialize(). Immutable after initialization
mapping(address => uint256) private _shares
_released
Mapping of payee addresses to ETH already released to them
Tracks cumulative ETH paid to each payee
mapping(address => uint256) private _released
_payees
Array of all payee addresses
Used for enumeration. Set during initialize(). Immutable
address[] private _payees
_erc20TotalReleased
Mapping of ERC20 tokens to total amount released
Tracks cumulative amount of each token paid out
mapping(IERC20 => uint256) private _erc20TotalReleased
_erc20Released
Nested mapping of tokens to payees to amounts released
Tracks cumulative amount of each token paid to each payee
mapping(IERC20 => mapping(address => uint256)) private _erc20Released
Functions
constructor
Constructor disables direct initialization
Prevents using this contract directly - must be used via proxy pattern Disables initializers so only proxy instances can be initialized
constructor() payable;
receive
Receives ETH payments and emits event
WARNING: Events are not fully reliable - ETH can be received without triggering this (e.g., via selfdestruct). Event reliability doesn't affect actual payment splitting See Solidity docs on fallback functions for details
receive() external payable virtual;
initialize
Initializes the payment splitter with payees and their shares
CRITICAL: Can only be called ONCE (enforced by initializer modifier) Sets immutable payee list and share allocations VALIDATION:
- Arrays must have same length
- Arrays must be non-empty
- All payee addresses must be non-zero
- All shares must be > 0
- No duplicate payees allowed EXAMPLE: payees = [alice, bob, charlie] shares = [50, 30, 20] Result: Alice gets 50%, Bob gets 30%, Charlie gets 20%
Notes:
-
security: Can only be called once via initializer
-
security: Payee list is immutable after initialization
function initialize(address[] memory payees, uint256[] memory shares_) public payable initializer;
Parameters
| Name | Type | Description |
|---|---|---|
payees | address[] | Array of payee addresses (cannot be zero addresses) |
shares_ | uint256[] | Array of proportional allocation shares for each payee (unitless, must all be > 0) |
totalShares
Returns the total shares across all payees
Used as denominator in payment calculations
function totalShares() public view returns (uint256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | total Sum of all payee proportional allocation shares (unitless) |
totalReleased
Returns total ETH released to all payees
Cumulative amount paid out since deployment
function totalReleased() public view returns (uint256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | total Total ETH released in wei |
totalReleased
Returns total amount of an ERC20 token released to all payees
Cumulative amount of specific token paid out
function totalReleased(IERC20 token) public view returns (uint256);
Parameters
| Name | Type | Description |
|---|---|---|
token | IERC20 | Address of the ERC20 token contract |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | total Total tokens released |
shares
Returns the share allocation for a payee
Share count, not percentage. Percentage = shares / totalShares
function shares(address account) public view returns (uint256);
Parameters
| Name | Type | Description |
|---|---|---|
account | address | Address of the payee |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | shares Number of proportional allocation shares assigned to payee (unitless) |
released
Returns ETH already released to a payee
Cumulative amount paid to this payee
function released(address account) public view returns (uint256);
Parameters
| Name | Type | Description |
|---|---|---|
account | address | Address of the payee |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | amount ETH released in wei |
released
Returns amount of an ERC20 token already released to a payee
Cumulative amount of specific token paid to this payee
function released(IERC20 token, address account) public view returns (uint256);
Parameters
| Name | Type | Description |
|---|---|---|
token | IERC20 | Address of the ERC20 token contract |
account | address | Address of the payee |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | amount Tokens released |
payee
Returns the payee address at a specific index
Used for enumerating all payees
function payee(uint256 index) public view returns (address);
Parameters
| Name | Type | Description |
|---|---|---|
index | uint256 | Index in the payees array (0 to payees.length - 1) |
Returns
| Name | Type | Description |
|---|---|---|
<none> | address | payee Payee address at the index |
releasable
Returns amount of ETH claimable by a payee
Formula: (totalReceived * payeeShares / totalShares) - alreadyReleased totalReceived = current balance + total released
function releasable(address account) public view returns (uint256);
Parameters
| Name | Type | Description |
|---|---|---|
account | address | Address of the payee |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | amount Claimable ETH in wei |
releasable
Returns amount of ERC20 tokens claimable by a payee
Formula: (totalReceived * payeeShares / totalShares) - alreadyReleased totalReceived = current balance + total released
function releasable(IERC20 token, address account) public view returns (uint256);
Parameters
| Name | Type | Description |
|---|---|---|
token | IERC20 | Address of the ERC20 token contract |
account | address | Address of the payee |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | amount Claimable tokens |
release
Releases owed ETH to a payee
Pull payment: payee calls this to claim their share of accumulated ETH CALCULATION: payment = (totalReceived * payeeShares / totalShares) - alreadyReleased REQUIREMENTS:
- Account must have shares > 0
- Payment must be > 0 EFFECTS:
- Updates _totalReleased and _released[account]
- Transfers ETH to account
- Emits PaymentReleased event
Note: security: Uses OpenZeppelin's Address.sendValue for safe ETH transfer
function release(address payable account) public virtual;
Parameters
| Name | Type | Description |
|---|---|---|
account | address payable | Address of the payee to release payment to |
release
Releases owed ERC20 tokens to a payee
Pull payment: payee calls this to claim their share of accumulated tokens CALCULATION: payment = (totalReceived * payeeShares / totalShares) - alreadyReleased REQUIREMENTS:
- Account must have shares > 0
- Payment must be > 0 COMPATIBILITY WARNING:
- Not compatible with rebasing tokens
- Not compatible with fee-on-transfer tokens
- Test with specific token before production use
Note: security: Uses SafeERC20 for safe token transfers
function release(IERC20 token, address account) public virtual;
Parameters
| Name | Type | Description |
|---|---|---|
token | IERC20 | Address of the ERC20 token contract |
account | address | Address of the payee to release payment to |
_pendingPayment
Calculates pending payment for a payee
function _pendingPayment(address account, uint256 totalReceived, uint256 alreadyReleased)
private
view
returns (uint256);
Parameters
| Name | Type | Description |
|---|---|---|
account | address | Payee address |
totalReceived | uint256 | Total received (balance + released) |
alreadyReleased | uint256 | Amount already paid to this payee |
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | payment Amount currently owed to payee |
_addPayee
function _addPayee(address account, uint256 shares_) private;
Parameters
| Name | Type | Description |
|---|---|---|
account | address | Payee address (cannot be zero) |
shares_ | uint256 | Number of shares to assign (must be > 0) |
Events
PayeeAdded
Emitted when a new payee is added during initialization
event PayeeAdded(address account, uint256 shares);
Parameters
| Name | Type | Description |
|---|---|---|
account | address | Address of the payee added |
shares | uint256 | Number of proportional allocation shares assigned to the payee (unitless) |
PaymentReleased
Emitted when ETH is released to a payee
event PaymentReleased(address to, uint256 amount);
Parameters
| Name | Type | Description |
|---|---|---|
to | address | Address receiving the payment |
amount | uint256 | Amount of ETH released in wei |
ERC20PaymentReleased
Emitted when ERC20 tokens are released to a payee
event ERC20PaymentReleased(IERC20 indexed token, address to, uint256 amount);
Parameters
| Name | Type | Description |
|---|---|---|
token | IERC20 | ERC20 token being released |
to | address | Address receiving the payment |
amount | uint256 | Amount of tokens released |
PaymentReceived
Emitted when ETH is received by the contract
event PaymentReceived(address from, uint256 amount);
Parameters
| Name | Type | Description |
|---|---|---|
from | address | Address sending the ETH |
amount | uint256 | Amount of ETH received in wei |