Quick Solidity walk through

The following code was taken from Open Zeppelin library:

We will be navigating contract that:

Disclaimer: this is a quick first dive and by no means is extensive or covers all the different features, design patterns or security best practices that need to be considered when developing in Solidity.

Solidity version and imports

Whenever we start a solidity file we need to declare the Solidity version we are going to use:

  • Solidity has evolved quite fast so new features were added making previous versions not to be compatible
  • Remember everything is compiled to EVM bytecode so unless there is an upgrade, the compile code is compatible.

Also libraries (like OZ) can be used helping quite a lot the development process.

PaymentSplitter.sol

_214
// SPDX-License-Identifier: MIT
_214
// OpenZeppelin Contracts (last updated v4.7.0) (finance/PaymentSplitter.sol)
_214
_214
pragma solidity ^0.8.0;
_214
_214
import "../token/ERC20/utils/SafeERC20.sol";
_214
import "../utils/Address.sol";
_214
import "../utils/Context.sol";
_214
_214
/**
_214
* @title PaymentSplitter
_214
* @dev This contract allows to split Ether payments among a group of accounts. The sender does not need to be aware
_214
* that the Ether will be split in this way, since it is handled transparently by the contract.
_214
*
_214
* The split can be in equal parts or in any other arbitrary proportion. The way this is specified is by assigning each
_214
* account to a number of shares. Of all the Ether that this contract receives, each account will then be able to claim
_214
* an amount proportional to the percentage of total shares they were assigned. The distribution of shares is set at the
_214
* time of contract deployment and can't be updated thereafter.
_214
*
_214
* `PaymentSplitter` follows a _pull payment_ model. This means that payments are not automatically forwarded to the
_214
* accounts but kept in this contract, and the actual transfer is triggered as a separate step by calling the {release}
_214
* function.
_214
*
_214
* NOTE: This contract assumes that ERC20 tokens will behave similarly to native tokens (Ether). Rebasing tokens, and
_214
* tokens that apply fees during transfers, are likely to not be supported as expected. If in doubt, we encourage you
_214
* to run tests before sending real value to this contract.
_214
*/
_214
contract PaymentSplitter is Context {
_214
event PayeeAdded(address account, uint256 shares);
_214
event PaymentReleased(address to, uint256 amount);
_214
event ERC20PaymentReleased(IERC20 indexed token, address to, uint256 amount);
_214
event PaymentReceived(address from, uint256 amount);
_214
_214
uint256 private _totalShares;
_214
uint256 private _totalReleased;
_214
_214
mapping(address => uint256) private _shares;
_214
mapping(address => uint256) private _released;
_214
address[] private _payees;
_214
_214
mapping(IERC20 => uint256) private _erc20TotalReleased;
_214
mapping(IERC20 => mapping(address => uint256)) private _erc20Released;
_214
_214
/**
_214
* @dev Creates an instance of `PaymentSplitter` where each account in `payees` is assigned the number of shares at
_214
* the matching position in the `shares` array.
_214
*
_214
* All addresses in `payees` must be non-zero. Both arrays must have the same non-zero length, and there must be no
_214
* duplicates in `payees`.
_214
*/
_214
constructor(address[] memory payees, uint256[] memory shares_) payable {
_214
require(payees.length == shares_.length, "PaymentSplitter: payees and shares length mismatch");
_214
require(payees.length > 0, "PaymentSplitter: no payees");
_214
_214
for (uint256 i = 0; i < payees.length; i++) {
_214
_addPayee(payees[i], shares_[i]);
_214
}
_214
}
_214
_214
/**
_214
* @dev The Ether received will be logged with {PaymentReceived} events. Note that these events are not fully
_214
* reliable: it's possible for a contract to receive Ether without triggering this function. This only affects the
_214
* reliability of the events, and not the actual splitting of Ether.
_214
*
_214
* To learn more about this see the Solidity documentation for
_214
* https://solidity.readthedocs.io/en/latest/contracts.html#fallback-function[fallback
_214
* functions].
_214
*/
_214
receive() external payable virtual {
_214
emit PaymentReceived(_msgSender(), msg.value);
_214
}
_214
_214
/**
_214
* @dev Getter for the total shares held by payees.
_214
*/
_214
function totalShares() public view returns (uint256) {
_214
return _totalShares;
_214
}
_214
_214
/**
_214
* @dev Getter for the total amount of Ether already released.
_214
*/
_214
function totalReleased() public view returns (uint256) {
_214
return _totalReleased;
_214
}
_214
_214
/**
_214
* @dev Getter for the total amount of `token` already released. `token` should be the address of an IERC20
_214
* contract.
_214
*/
_214
function totalReleased(IERC20 token) public view returns (uint256) {
_214
return _erc20TotalReleased[token];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of shares held by an account.
_214
*/
_214
function shares(address account) public view returns (uint256) {
_214
return _shares[account];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of Ether already released to a payee.
_214
*/
_214
function released(address account) public view returns (uint256) {
_214
return _released[account];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of `token` tokens already released to a payee. `token` should be the address of an
_214
* IERC20 contract.
_214
*/
_214
function released(IERC20 token, address account) public view returns (uint256) {
_214
return _erc20Released[token][account];
_214
}
_214
_214
/**
_214
* @dev Getter for the address of the payee number `index`.
_214
*/
_214
function payee(uint256 index) public view returns (address) {
_214
return _payees[index];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of payee's releasable Ether.
_214
*/
_214
function releasable(address account) public view returns (uint256) {
_214
uint256 totalReceived = address(this).balance + totalReleased();
_214
return _pendingPayment(account, totalReceived, released(account));
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of payee's releasable `token` tokens. `token` should be the address of an
_214
* IERC20 contract.
_214
*/
_214
function releasable(IERC20 token, address account) public view returns (uint256) {
_214
uint256 totalReceived = token.balanceOf(address(this)) + totalReleased(token);
_214
return _pendingPayment(account, totalReceived, released(token, account));
_214
}
_214
_214
/**
_214
* @dev Triggers a transfer to `account` of the amount of Ether they are owed, according to their percentage of the
_214
* total shares and their previous withdrawals.
_214
*/
_214
function release(address payable account) public virtual {
_214
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
_214
_214
uint256 payment = releasable(account);
_214
_214
require(payment != 0, "PaymentSplitter: account is not due payment");
_214
_214
// _totalReleased is the sum of all values in _released.
_214
// If "_totalReleased += payment" does not overflow, then "_released[account] += payment" cannot overflow.
_214
_totalReleased += payment;
_214
unchecked {
_214
_released[account] += payment;
_214
}
_214
_214
Address.sendValue(account, payment);
_214
emit PaymentReleased(account, payment);
_214
}
_214
_214
/**
_214
* @dev Triggers a transfer to `account` of the amount of `token` tokens they are owed, according to their
_214
* percentage of the total shares and their previous withdrawals. `token` must be the address of an IERC20
_214
* contract.
_214
*/
_214
function release(IERC20 token, address account) public virtual {
_214
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
_214
_214
uint256 payment = releasable(token, account);
_214
_214
require(payment != 0, "PaymentSplitter: account is not due payment");
_214
_214
// _erc20TotalReleased[token] is the sum of all values in _erc20Released[token].
_214
// If "_erc20TotalReleased[token] += payment" does not overflow, then "_erc20Released[token][account] += payment"
_214
// cannot overflow.
_214
_erc20TotalReleased[token] += payment;
_214
unchecked {
_214
_erc20Released[token][account] += payment;
_214
}
_214
_214
SafeERC20.safeTransfer(token, account, payment);
_214
emit ERC20PaymentReleased(token, account, payment);
_214
}
_214
_214
/**
_214
* @dev internal logic for computing the pending payment of an `account` given the token historical balances and
_214
* already released amounts.
_214
*/
_214
function _pendingPayment(
_214
address account,
_214
uint256 totalReceived,
_214
uint256 alreadyReleased
_214
) private view returns (uint256) {
_214
return (totalReceived * _shares[account]) / _totalShares - alreadyReleased;
_214
}
_214
_214
/**
_214
* @dev Add a new payee to the contract.
_214
* @param account The address of the payee to add.
_214
* @param shares_ The number of shares owned by the payee.
_214
*/
_214
function _addPayee(address account, uint256 shares_) private {
_214
require(account != address(0), "PaymentSplitter: account is the zero address");
_214
require(shares_ > 0, "PaymentSplitter: shares are 0");
_214
require(_shares[account] == 0, "PaymentSplitter: account already has shares");
_214
_214
_payees.push(account);
_214
_shares[account] = shares_;
_214
_totalShares = _totalShares + shares_;
_214
emit PayeeAdded(account, shares_);
_214
}
_214
}

Comments

Developing easy to follow, well tested and with proper comments is extremely important as most of the time code that can be abused is written in solidity.

Comments should be written in Ethereum Natural Language Specification Format (NatSpec)

PaymentSplitter.sol

_214
// SPDX-License-Identifier: MIT
_214
// OpenZeppelin Contracts (last updated v4.7.0) (finance/PaymentSplitter.sol)
_214
_214
pragma solidity ^0.8.0;
_214
_214
import "../token/ERC20/utils/SafeERC20.sol";
_214
import "../utils/Address.sol";
_214
import "../utils/Context.sol";
_214
_214
/**
_214
* @title PaymentSplitter
_214
* @dev This contract allows to split Ether payments among a group of accounts. The sender does not need to be aware
_214
* that the Ether will be split in this way, since it is handled transparently by the contract.
_214
*
_214
* The split can be in equal parts or in any other arbitrary proportion. The way this is specified is by assigning each
_214
* account to a number of shares. Of all the Ether that this contract receives, each account will then be able to claim
_214
* an amount proportional to the percentage of total shares they were assigned. The distribution of shares is set at the
_214
* time of contract deployment and can't be updated thereafter.
_214
*
_214
* `PaymentSplitter` follows a _pull payment_ model. This means that payments are not automatically forwarded to the
_214
* accounts but kept in this contract, and the actual transfer is triggered as a separate step by calling the {release}
_214
* function.
_214
*
_214
* NOTE: This contract assumes that ERC20 tokens will behave similarly to native tokens (Ether). Rebasing tokens, and
_214
* tokens that apply fees during transfers, are likely to not be supported as expected. If in doubt, we encourage you
_214
* to run tests before sending real value to this contract.
_214
*/
_214
contract PaymentSplitter is Context {
_214
event PayeeAdded(address account, uint256 shares);
_214
event PaymentReleased(address to, uint256 amount);
_214
event ERC20PaymentReleased(IERC20 indexed token, address to, uint256 amount);
_214
event PaymentReceived(address from, uint256 amount);
_214
_214
uint256 private _totalShares;
_214
uint256 private _totalReleased;
_214
_214
mapping(address => uint256) private _shares;
_214
mapping(address => uint256) private _released;
_214
address[] private _payees;
_214
_214
mapping(IERC20 => uint256) private _erc20TotalReleased;
_214
mapping(IERC20 => mapping(address => uint256)) private _erc20Released;
_214
_214
/**
_214
* @dev Creates an instance of `PaymentSplitter` where each account in `payees` is assigned the number of shares at
_214
* the matching position in the `shares` array.
_214
*
_214
* All addresses in `payees` must be non-zero. Both arrays must have the same non-zero length, and there must be no
_214
* duplicates in `payees`.
_214
*/
_214
constructor(address[] memory payees, uint256[] memory shares_) payable {
_214
require(payees.length == shares_.length, "PaymentSplitter: payees and shares length mismatch");
_214
require(payees.length > 0, "PaymentSplitter: no payees");
_214
_214
for (uint256 i = 0; i < payees.length; i++) {
_214
_addPayee(payees[i], shares_[i]);
_214
}
_214
}
_214
_214
/**
_214
* @dev The Ether received will be logged with {PaymentReceived} events. Note that these events are not fully
_214
* reliable: it's possible for a contract to receive Ether without triggering this function. This only affects the
_214
* reliability of the events, and not the actual splitting of Ether.
_214
*
_214
* To learn more about this see the Solidity documentation for
_214
* https://solidity.readthedocs.io/en/latest/contracts.html#fallback-function[fallback
_214
* functions].
_214
*/
_214
receive() external payable virtual {
_214
emit PaymentReceived(_msgSender(), msg.value);
_214
}
_214
_214
/**
_214
* @dev Getter for the total shares held by payees.
_214
*/
_214
function totalShares() public view returns (uint256) {
_214
return _totalShares;
_214
}
_214
_214
/**
_214
* @dev Getter for the total amount of Ether already released.
_214
*/
_214
function totalReleased() public view returns (uint256) {
_214
return _totalReleased;
_214
}
_214
_214
/**
_214
* @dev Getter for the total amount of `token` already released. `token` should be the address of an IERC20
_214
* contract.
_214
*/
_214
function totalReleased(IERC20 token) public view returns (uint256) {
_214
return _erc20TotalReleased[token];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of shares held by an account.
_214
*/
_214
function shares(address account) public view returns (uint256) {
_214
return _shares[account];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of Ether already released to a payee.
_214
*/
_214
function released(address account) public view returns (uint256) {
_214
return _released[account];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of `token` tokens already released to a payee. `token` should be the address of an
_214
* IERC20 contract.
_214
*/
_214
function released(IERC20 token, address account) public view returns (uint256) {
_214
return _erc20Released[token][account];
_214
}
_214
_214
/**
_214
* @dev Getter for the address of the payee number `index`.
_214
*/
_214
function payee(uint256 index) public view returns (address) {
_214
return _payees[index];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of payee's releasable Ether.
_214
*/
_214
function releasable(address account) public view returns (uint256) {
_214
uint256 totalReceived = address(this).balance + totalReleased();
_214
return _pendingPayment(account, totalReceived, released(account));
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of payee's releasable `token` tokens. `token` should be the address of an
_214
* IERC20 contract.
_214
*/
_214
function releasable(IERC20 token, address account) public view returns (uint256) {
_214
uint256 totalReceived = token.balanceOf(address(this)) + totalReleased(token);
_214
return _pendingPayment(account, totalReceived, released(token, account));
_214
}
_214
_214
/**
_214
* @dev Triggers a transfer to `account` of the amount of Ether they are owed, according to their percentage of the
_214
* total shares and their previous withdrawals.
_214
*/
_214
function release(address payable account) public virtual {
_214
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
_214
_214
uint256 payment = releasable(account);
_214
_214
require(payment != 0, "PaymentSplitter: account is not due payment");
_214
_214
// _totalReleased is the sum of all values in _released.
_214
// If "_totalReleased += payment" does not overflow, then "_released[account] += payment" cannot overflow.
_214
_totalReleased += payment;
_214
unchecked {
_214
_released[account] += payment;
_214
}
_214
_214
Address.sendValue(account, payment);
_214
emit PaymentReleased(account, payment);
_214
}
_214
_214
/**
_214
* @dev Triggers a transfer to `account` of the amount of `token` tokens they are owed, according to their
_214
* percentage of the total shares and their previous withdrawals. `token` must be the address of an IERC20
_214
* contract.
_214
*/
_214
function release(IERC20 token, address account) public virtual {
_214
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
_214
_214
uint256 payment = releasable(token, account);
_214
_214
require(payment != 0, "PaymentSplitter: account is not due payment");
_214
_214
// _erc20TotalReleased[token] is the sum of all values in _erc20Released[token].
_214
// If "_erc20TotalReleased[token] += payment" does not overflow, then "_erc20Released[token][account] += payment"
_214
// cannot overflow.
_214
_erc20TotalReleased[token] += payment;
_214
unchecked {
_214
_erc20Released[token][account] += payment;
_214
}
_214
_214
SafeERC20.safeTransfer(token, account, payment);
_214
emit ERC20PaymentReleased(token, account, payment);
_214
}
_214
_214
/**
_214
* @dev internal logic for computing the pending payment of an `account` given the token historical balances and
_214
* already released amounts.
_214
*/
_214
function _pendingPayment(
_214
address account,
_214
uint256 totalReceived,
_214
uint256 alreadyReleased
_214
) private view returns (uint256) {
_214
return (totalReceived * _shares[account]) / _totalShares - alreadyReleased;
_214
}
_214
_214
/**
_214
* @dev Add a new payee to the contract.
_214
* @param account The address of the payee to add.
_214
* @param shares_ The number of shares owned by the payee.
_214
*/
_214
function _addPayee(address account, uint256 shares_) private {
_214
require(account != address(0), "PaymentSplitter: account is the zero address");
_214
require(shares_ > 0, "PaymentSplitter: shares are 0");
_214
require(_shares[account] == 0, "PaymentSplitter: account already has shares");
_214
_214
_payees.push(account);
_214
_shares[account] = shares_;
_214
_totalShares = _totalShares + shares_;
_214
emit PayeeAdded(account, shares_);
_214
}
_214
}

Contract declaration

Contracts resemble to OOP:

  • Multiple inheritance
  • Interfaces, Abstract Contracts
PaymentSplitter.sol

_214
// SPDX-License-Identifier: MIT
_214
// OpenZeppelin Contracts (last updated v4.7.0) (finance/PaymentSplitter.sol)
_214
_214
pragma solidity ^0.8.0;
_214
_214
import "../token/ERC20/utils/SafeERC20.sol";
_214
import "../utils/Address.sol";
_214
import "../utils/Context.sol";
_214
_214
/**
_214
* @title PaymentSplitter
_214
* @dev This contract allows to split Ether payments among a group of accounts. The sender does not need to be aware
_214
* that the Ether will be split in this way, since it is handled transparently by the contract.
_214
*
_214
* The split can be in equal parts or in any other arbitrary proportion. The way this is specified is by assigning each
_214
* account to a number of shares. Of all the Ether that this contract receives, each account will then be able to claim
_214
* an amount proportional to the percentage of total shares they were assigned. The distribution of shares is set at the
_214
* time of contract deployment and can't be updated thereafter.
_214
*
_214
* `PaymentSplitter` follows a _pull payment_ model. This means that payments are not automatically forwarded to the
_214
* accounts but kept in this contract, and the actual transfer is triggered as a separate step by calling the {release}
_214
* function.
_214
*
_214
* NOTE: This contract assumes that ERC20 tokens will behave similarly to native tokens (Ether). Rebasing tokens, and
_214
* tokens that apply fees during transfers, are likely to not be supported as expected. If in doubt, we encourage you
_214
* to run tests before sending real value to this contract.
_214
*/
_214
contract PaymentSplitter is Context {
_214
event PayeeAdded(address account, uint256 shares);
_214
event PaymentReleased(address to, uint256 amount);
_214
event ERC20PaymentReleased(IERC20 indexed token, address to, uint256 amount);
_214
event PaymentReceived(address from, uint256 amount);
_214
_214
uint256 private _totalShares;
_214
uint256 private _totalReleased;
_214
_214
mapping(address => uint256) private _shares;
_214
mapping(address => uint256) private _released;
_214
address[] private _payees;
_214
_214
mapping(IERC20 => uint256) private _erc20TotalReleased;
_214
mapping(IERC20 => mapping(address => uint256)) private _erc20Released;
_214
_214
/**
_214
* @dev Creates an instance of `PaymentSplitter` where each account in `payees` is assigned the number of shares at
_214
* the matching position in the `shares` array.
_214
*
_214
* All addresses in `payees` must be non-zero. Both arrays must have the same non-zero length, and there must be no
_214
* duplicates in `payees`.
_214
*/
_214
constructor(address[] memory payees, uint256[] memory shares_) payable {
_214
require(payees.length == shares_.length, "PaymentSplitter: payees and shares length mismatch");
_214
require(payees.length > 0, "PaymentSplitter: no payees");
_214
_214
for (uint256 i = 0; i < payees.length; i++) {
_214
_addPayee(payees[i], shares_[i]);
_214
}
_214
}
_214
_214
/**
_214
* @dev The Ether received will be logged with {PaymentReceived} events. Note that these events are not fully
_214
* reliable: it's possible for a contract to receive Ether without triggering this function. This only affects the
_214
* reliability of the events, and not the actual splitting of Ether.
_214
*
_214
* To learn more about this see the Solidity documentation for
_214
* https://solidity.readthedocs.io/en/latest/contracts.html#fallback-function[fallback
_214
* functions].
_214
*/
_214
receive() external payable virtual {
_214
emit PaymentReceived(_msgSender(), msg.value);
_214
}
_214
_214
/**
_214
* @dev Getter for the total shares held by payees.
_214
*/
_214
function totalShares() public view returns (uint256) {
_214
return _totalShares;
_214
}
_214
_214
/**
_214
* @dev Getter for the total amount of Ether already released.
_214
*/
_214
function totalReleased() public view returns (uint256) {
_214
return _totalReleased;
_214
}
_214
_214
/**
_214
* @dev Getter for the total amount of `token` already released. `token` should be the address of an IERC20
_214
* contract.
_214
*/
_214
function totalReleased(IERC20 token) public view returns (uint256) {
_214
return _erc20TotalReleased[token];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of shares held by an account.
_214
*/
_214
function shares(address account) public view returns (uint256) {
_214
return _shares[account];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of Ether already released to a payee.
_214
*/
_214
function released(address account) public view returns (uint256) {
_214
return _released[account];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of `token` tokens already released to a payee. `token` should be the address of an
_214
* IERC20 contract.
_214
*/
_214
function released(IERC20 token, address account) public view returns (uint256) {
_214
return _erc20Released[token][account];
_214
}
_214
_214
/**
_214
* @dev Getter for the address of the payee number `index`.
_214
*/
_214
function payee(uint256 index) public view returns (address) {
_214
return _payees[index];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of payee's releasable Ether.
_214
*/
_214
function releasable(address account) public view returns (uint256) {
_214
uint256 totalReceived = address(this).balance + totalReleased();
_214
return _pendingPayment(account, totalReceived, released(account));
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of payee's releasable `token` tokens. `token` should be the address of an
_214
* IERC20 contract.
_214
*/
_214
function releasable(IERC20 token, address account) public view returns (uint256) {
_214
uint256 totalReceived = token.balanceOf(address(this)) + totalReleased(token);
_214
return _pendingPayment(account, totalReceived, released(token, account));
_214
}
_214
_214
/**
_214
* @dev Triggers a transfer to `account` of the amount of Ether they are owed, according to their percentage of the
_214
* total shares and their previous withdrawals.
_214
*/
_214
function release(address payable account) public virtual {
_214
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
_214
_214
uint256 payment = releasable(account);
_214
_214
require(payment != 0, "PaymentSplitter: account is not due payment");
_214
_214
// _totalReleased is the sum of all values in _released.
_214
// If "_totalReleased += payment" does not overflow, then "_released[account] += payment" cannot overflow.
_214
_totalReleased += payment;
_214
unchecked {
_214
_released[account] += payment;
_214
}
_214
_214
Address.sendValue(account, payment);
_214
emit PaymentReleased(account, payment);
_214
}
_214
_214
/**
_214
* @dev Triggers a transfer to `account` of the amount of `token` tokens they are owed, according to their
_214
* percentage of the total shares and their previous withdrawals. `token` must be the address of an IERC20
_214
* contract.
_214
*/
_214
function release(IERC20 token, address account) public virtual {
_214
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
_214
_214
uint256 payment = releasable(token, account);
_214
_214
require(payment != 0, "PaymentSplitter: account is not due payment");
_214
_214
// _erc20TotalReleased[token] is the sum of all values in _erc20Released[token].
_214
// If "_erc20TotalReleased[token] += payment" does not overflow, then "_erc20Released[token][account] += payment"
_214
// cannot overflow.
_214
_erc20TotalReleased[token] += payment;
_214
unchecked {
_214
_erc20Released[token][account] += payment;
_214
}
_214
_214
SafeERC20.safeTransfer(token, account, payment);
_214
emit ERC20PaymentReleased(token, account, payment);
_214
}
_214
_214
/**
_214
* @dev internal logic for computing the pending payment of an `account` given the token historical balances and
_214
* already released amounts.
_214
*/
_214
function _pendingPayment(
_214
address account,
_214
uint256 totalReceived,
_214
uint256 alreadyReleased
_214
) private view returns (uint256) {
_214
return (totalReceived * _shares[account]) / _totalShares - alreadyReleased;
_214
}
_214
_214
/**
_214
* @dev Add a new payee to the contract.
_214
* @param account The address of the payee to add.
_214
* @param shares_ The number of shares owned by the payee.
_214
*/
_214
function _addPayee(address account, uint256 shares_) private {
_214
require(account != address(0), "PaymentSplitter: account is the zero address");
_214
require(shares_ > 0, "PaymentSplitter: shares are 0");
_214
require(_shares[account] == 0, "PaymentSplitter: account already has shares");
_214
_214
_payees.push(account);
_214
_shares[account] = shares_;
_214
_totalShares = _totalShares + shares_;
_214
emit PayeeAdded(account, shares_);
_214
}
_214
}

Events

Events can be emitted when executing transactions.

PaymentSplitter.sol

_214
// SPDX-License-Identifier: MIT
_214
// OpenZeppelin Contracts (last updated v4.7.0) (finance/PaymentSplitter.sol)
_214
_214
pragma solidity ^0.8.0;
_214
_214
import "../token/ERC20/utils/SafeERC20.sol";
_214
import "../utils/Address.sol";
_214
import "../utils/Context.sol";
_214
_214
/**
_214
* @title PaymentSplitter
_214
* @dev This contract allows to split Ether payments among a group of accounts. The sender does not need to be aware
_214
* that the Ether will be split in this way, since it is handled transparently by the contract.
_214
*
_214
* The split can be in equal parts or in any other arbitrary proportion. The way this is specified is by assigning each
_214
* account to a number of shares. Of all the Ether that this contract receives, each account will then be able to claim
_214
* an amount proportional to the percentage of total shares they were assigned. The distribution of shares is set at the
_214
* time of contract deployment and can't be updated thereafter.
_214
*
_214
* `PaymentSplitter` follows a _pull payment_ model. This means that payments are not automatically forwarded to the
_214
* accounts but kept in this contract, and the actual transfer is triggered as a separate step by calling the {release}
_214
* function.
_214
*
_214
* NOTE: This contract assumes that ERC20 tokens will behave similarly to native tokens (Ether). Rebasing tokens, and
_214
* tokens that apply fees during transfers, are likely to not be supported as expected. If in doubt, we encourage you
_214
* to run tests before sending real value to this contract.
_214
*/
_214
contract PaymentSplitter is Context {
_214
event PayeeAdded(address account, uint256 shares);
_214
event PaymentReleased(address to, uint256 amount);
_214
event ERC20PaymentReleased(IERC20 indexed token, address to, uint256 amount);
_214
event PaymentReceived(address from, uint256 amount);
_214
_214
uint256 private _totalShares;
_214
uint256 private _totalReleased;
_214
_214
mapping(address => uint256) private _shares;
_214
mapping(address => uint256) private _released;
_214
address[] private _payees;
_214
_214
mapping(IERC20 => uint256) private _erc20TotalReleased;
_214
mapping(IERC20 => mapping(address => uint256)) private _erc20Released;
_214
_214
/**
_214
* @dev Creates an instance of `PaymentSplitter` where each account in `payees` is assigned the number of shares at
_214
* the matching position in the `shares` array.
_214
*
_214
* All addresses in `payees` must be non-zero. Both arrays must have the same non-zero length, and there must be no
_214
* duplicates in `payees`.
_214
*/
_214
constructor(address[] memory payees, uint256[] memory shares_) payable {
_214
require(payees.length == shares_.length, "PaymentSplitter: payees and shares length mismatch");
_214
require(payees.length > 0, "PaymentSplitter: no payees");
_214
_214
for (uint256 i = 0; i < payees.length; i++) {
_214
_addPayee(payees[i], shares_[i]);
_214
}
_214
}
_214
_214
/**
_214
* @dev The Ether received will be logged with {PaymentReceived} events. Note that these events are not fully
_214
* reliable: it's possible for a contract to receive Ether without triggering this function. This only affects the
_214
* reliability of the events, and not the actual splitting of Ether.
_214
*
_214
* To learn more about this see the Solidity documentation for
_214
* https://solidity.readthedocs.io/en/latest/contracts.html#fallback-function[fallback
_214
* functions].
_214
*/
_214
receive() external payable virtual {
_214
emit PaymentReceived(_msgSender(), msg.value);
_214
}
_214
_214
/**
_214
* @dev Getter for the total shares held by payees.
_214
*/
_214
function totalShares() public view returns (uint256) {
_214
return _totalShares;
_214
}
_214
_214
/**
_214
* @dev Getter for the total amount of Ether already released.
_214
*/
_214
function totalReleased() public view returns (uint256) {
_214
return _totalReleased;
_214
}
_214
_214
/**
_214
* @dev Getter for the total amount of `token` already released. `token` should be the address of an IERC20
_214
* contract.
_214
*/
_214
function totalReleased(IERC20 token) public view returns (uint256) {
_214
return _erc20TotalReleased[token];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of shares held by an account.
_214
*/
_214
function shares(address account) public view returns (uint256) {
_214
return _shares[account];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of Ether already released to a payee.
_214
*/
_214
function released(address account) public view returns (uint256) {
_214
return _released[account];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of `token` tokens already released to a payee. `token` should be the address of an
_214
* IERC20 contract.
_214
*/
_214
function released(IERC20 token, address account) public view returns (uint256) {
_214
return _erc20Released[token][account];
_214
}
_214
_214
/**
_214
* @dev Getter for the address of the payee number `index`.
_214
*/
_214
function payee(uint256 index) public view returns (address) {
_214
return _payees[index];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of payee's releasable Ether.
_214
*/
_214
function releasable(address account) public view returns (uint256) {
_214
uint256 totalReceived = address(this).balance + totalReleased();
_214
return _pendingPayment(account, totalReceived, released(account));
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of payee's releasable `token` tokens. `token` should be the address of an
_214
* IERC20 contract.
_214
*/
_214
function releasable(IERC20 token, address account) public view returns (uint256) {
_214
uint256 totalReceived = token.balanceOf(address(this)) + totalReleased(token);
_214
return _pendingPayment(account, totalReceived, released(token, account));
_214
}
_214
_214
/**
_214
* @dev Triggers a transfer to `account` of the amount of Ether they are owed, according to their percentage of the
_214
* total shares and their previous withdrawals.
_214
*/
_214
function release(address payable account) public virtual {
_214
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
_214
_214
uint256 payment = releasable(account);
_214
_214
require(payment != 0, "PaymentSplitter: account is not due payment");
_214
_214
// _totalReleased is the sum of all values in _released.
_214
// If "_totalReleased += payment" does not overflow, then "_released[account] += payment" cannot overflow.
_214
_totalReleased += payment;
_214
unchecked {
_214
_released[account] += payment;
_214
}
_214
_214
Address.sendValue(account, payment);
_214
emit PaymentReleased(account, payment);
_214
}
_214
_214
/**
_214
* @dev Triggers a transfer to `account` of the amount of `token` tokens they are owed, according to their
_214
* percentage of the total shares and their previous withdrawals. `token` must be the address of an IERC20
_214
* contract.
_214
*/
_214
function release(IERC20 token, address account) public virtual {
_214
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
_214
_214
uint256 payment = releasable(token, account);
_214
_214
require(payment != 0, "PaymentSplitter: account is not due payment");
_214
_214
// _erc20TotalReleased[token] is the sum of all values in _erc20Released[token].
_214
// If "_erc20TotalReleased[token] += payment" does not overflow, then "_erc20Released[token][account] += payment"
_214
// cannot overflow.
_214
_erc20TotalReleased[token] += payment;
_214
unchecked {
_214
_erc20Released[token][account] += payment;
_214
}
_214
_214
SafeERC20.safeTransfer(token, account, payment);
_214
emit ERC20PaymentReleased(token, account, payment);
_214
}
_214
_214
/**
_214
* @dev internal logic for computing the pending payment of an `account` given the token historical balances and
_214
* already released amounts.
_214
*/
_214
function _pendingPayment(
_214
address account,
_214
uint256 totalReceived,
_214
uint256 alreadyReleased
_214
) private view returns (uint256) {
_214
return (totalReceived * _shares[account]) / _totalShares - alreadyReleased;
_214
}
_214
_214
/**
_214
* @dev Add a new payee to the contract.
_214
* @param account The address of the payee to add.
_214
* @param shares_ The number of shares owned by the payee.
_214
*/
_214
function _addPayee(address account, uint256 shares_) private {
_214
require(account != address(0), "PaymentSplitter: account is the zero address");
_214
require(shares_ > 0, "PaymentSplitter: shares are 0");
_214
require(_shares[account] == 0, "PaymentSplitter: account already has shares");
_214
_214
_payees.push(account);
_214
_shares[account] = shares_;
_214
_totalShares = _totalShares + shares_;
_214
emit PayeeAdded(account, shares_);
_214
}
_214
}

Storage Variables

Variables can be placed in the storage, thus making their state persistent.

There are several types that can be used:

  • Unsigned integers (multiple lengths)
  • Mappings (maps)
  • Addresses
  • Arrays (fixed and variable length)
  • Bytes
  • Strings (fixed and variable length)
  • Structs
  • Booleans
  • Contracts
  • Enums

Visibility (public, private, internal)

️ Even if something is private in the context of the EVM it will be public for somebody else observing the blockchain.

PaymentSplitter.sol

_214
// SPDX-License-Identifier: MIT
_214
// OpenZeppelin Contracts (last updated v4.7.0) (finance/PaymentSplitter.sol)
_214
_214
pragma solidity ^0.8.0;
_214
_214
import "../token/ERC20/utils/SafeERC20.sol";
_214
import "../utils/Address.sol";
_214
import "../utils/Context.sol";
_214
_214
/**
_214
* @title PaymentSplitter
_214
* @dev This contract allows to split Ether payments among a group of accounts. The sender does not need to be aware
_214
* that the Ether will be split in this way, since it is handled transparently by the contract.
_214
*
_214
* The split can be in equal parts or in any other arbitrary proportion. The way this is specified is by assigning each
_214
* account to a number of shares. Of all the Ether that this contract receives, each account will then be able to claim
_214
* an amount proportional to the percentage of total shares they were assigned. The distribution of shares is set at the
_214
* time of contract deployment and can't be updated thereafter.
_214
*
_214
* `PaymentSplitter` follows a _pull payment_ model. This means that payments are not automatically forwarded to the
_214
* accounts but kept in this contract, and the actual transfer is triggered as a separate step by calling the {release}
_214
* function.
_214
*
_214
* NOTE: This contract assumes that ERC20 tokens will behave similarly to native tokens (Ether). Rebasing tokens, and
_214
* tokens that apply fees during transfers, are likely to not be supported as expected. If in doubt, we encourage you
_214
* to run tests before sending real value to this contract.
_214
*/
_214
contract PaymentSplitter is Context {
_214
event PayeeAdded(address account, uint256 shares);
_214
event PaymentReleased(address to, uint256 amount);
_214
event ERC20PaymentReleased(IERC20 indexed token, address to, uint256 amount);
_214
event PaymentReceived(address from, uint256 amount);
_214
_214
uint256 private _totalShares;
_214
uint256 private _totalReleased;
_214
_214
mapping(address => uint256) private _shares;
_214
mapping(address => uint256) private _released;
_214
address[] private _payees;
_214
_214
mapping(IERC20 => uint256) private _erc20TotalReleased;
_214
mapping(IERC20 => mapping(address => uint256)) private _erc20Released;
_214
_214
/**
_214
* @dev Creates an instance of `PaymentSplitter` where each account in `payees` is assigned the number of shares at
_214
* the matching position in the `shares` array.
_214
*
_214
* All addresses in `payees` must be non-zero. Both arrays must have the same non-zero length, and there must be no
_214
* duplicates in `payees`.
_214
*/
_214
constructor(address[] memory payees, uint256[] memory shares_) payable {
_214
require(payees.length == shares_.length, "PaymentSplitter: payees and shares length mismatch");
_214
require(payees.length > 0, "PaymentSplitter: no payees");
_214
_214
for (uint256 i = 0; i < payees.length; i++) {
_214
_addPayee(payees[i], shares_[i]);
_214
}
_214
}
_214
_214
/**
_214
* @dev The Ether received will be logged with {PaymentReceived} events. Note that these events are not fully
_214
* reliable: it's possible for a contract to receive Ether without triggering this function. This only affects the
_214
* reliability of the events, and not the actual splitting of Ether.
_214
*
_214
* To learn more about this see the Solidity documentation for
_214
* https://solidity.readthedocs.io/en/latest/contracts.html#fallback-function[fallback
_214
* functions].
_214
*/
_214
receive() external payable virtual {
_214
emit PaymentReceived(_msgSender(), msg.value);
_214
}
_214
_214
/**
_214
* @dev Getter for the total shares held by payees.
_214
*/
_214
function totalShares() public view returns (uint256) {
_214
return _totalShares;
_214
}
_214
_214
/**
_214
* @dev Getter for the total amount of Ether already released.
_214
*/
_214
function totalReleased() public view returns (uint256) {
_214
return _totalReleased;
_214
}
_214
_214
/**
_214
* @dev Getter for the total amount of `token` already released. `token` should be the address of an IERC20
_214
* contract.
_214
*/
_214
function totalReleased(IERC20 token) public view returns (uint256) {
_214
return _erc20TotalReleased[token];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of shares held by an account.
_214
*/
_214
function shares(address account) public view returns (uint256) {
_214
return _shares[account];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of Ether already released to a payee.
_214
*/
_214
function released(address account) public view returns (uint256) {
_214
return _released[account];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of `token` tokens already released to a payee. `token` should be the address of an
_214
* IERC20 contract.
_214
*/
_214
function released(IERC20 token, address account) public view returns (uint256) {
_214
return _erc20Released[token][account];
_214
}
_214
_214
/**
_214
* @dev Getter for the address of the payee number `index`.
_214
*/
_214
function payee(uint256 index) public view returns (address) {
_214
return _payees[index];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of payee's releasable Ether.
_214
*/
_214
function releasable(address account) public view returns (uint256) {
_214
uint256 totalReceived = address(this).balance + totalReleased();
_214
return _pendingPayment(account, totalReceived, released(account));
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of payee's releasable `token` tokens. `token` should be the address of an
_214
* IERC20 contract.
_214
*/
_214
function releasable(IERC20 token, address account) public view returns (uint256) {
_214
uint256 totalReceived = token.balanceOf(address(this)) + totalReleased(token);
_214
return _pendingPayment(account, totalReceived, released(token, account));
_214
}
_214
_214
/**
_214
* @dev Triggers a transfer to `account` of the amount of Ether they are owed, according to their percentage of the
_214
* total shares and their previous withdrawals.
_214
*/
_214
function release(address payable account) public virtual {
_214
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
_214
_214
uint256 payment = releasable(account);
_214
_214
require(payment != 0, "PaymentSplitter: account is not due payment");
_214
_214
// _totalReleased is the sum of all values in _released.
_214
// If "_totalReleased += payment" does not overflow, then "_released[account] += payment" cannot overflow.
_214
_totalReleased += payment;
_214
unchecked {
_214
_released[account] += payment;
_214
}
_214
_214
Address.sendValue(account, payment);
_214
emit PaymentReleased(account, payment);
_214
}
_214
_214
/**
_214
* @dev Triggers a transfer to `account` of the amount of `token` tokens they are owed, according to their
_214
* percentage of the total shares and their previous withdrawals. `token` must be the address of an IERC20
_214
* contract.
_214
*/
_214
function release(IERC20 token, address account) public virtual {
_214
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
_214
_214
uint256 payment = releasable(token, account);
_214
_214
require(payment != 0, "PaymentSplitter: account is not due payment");
_214
_214
// _erc20TotalReleased[token] is the sum of all values in _erc20Released[token].
_214
// If "_erc20TotalReleased[token] += payment" does not overflow, then "_erc20Released[token][account] += payment"
_214
// cannot overflow.
_214
_erc20TotalReleased[token] += payment;
_214
unchecked {
_214
_erc20Released[token][account] += payment;
_214
}
_214
_214
SafeERC20.safeTransfer(token, account, payment);
_214
emit ERC20PaymentReleased(token, account, payment);
_214
}
_214
_214
/**
_214
* @dev internal logic for computing the pending payment of an `account` given the token historical balances and
_214
* already released amounts.
_214
*/
_214
function _pendingPayment(
_214
address account,
_214
uint256 totalReceived,
_214
uint256 alreadyReleased
_214
) private view returns (uint256) {
_214
return (totalReceived * _shares[account]) / _totalShares - alreadyReleased;
_214
}
_214
_214
/**
_214
* @dev Add a new payee to the contract.
_214
* @param account The address of the payee to add.
_214
* @param shares_ The number of shares owned by the payee.
_214
*/
_214
function _addPayee(address account, uint256 shares_) private {
_214
require(account != address(0), "PaymentSplitter: account is the zero address");
_214
require(shares_ > 0, "PaymentSplitter: shares are 0");
_214
require(_shares[account] == 0, "PaymentSplitter: account already has shares");
_214
_214
_payees.push(account);
_214
_shares[account] = shares_;
_214
_totalShares = _totalShares + shares_;
_214
emit PayeeAdded(account, shares_);
_214
}
_214
}

Constructor

Can be used to set some storage values when deploying the contracts

PaymentSplitter.sol

_214
// SPDX-License-Identifier: MIT
_214
// OpenZeppelin Contracts (last updated v4.7.0) (finance/PaymentSplitter.sol)
_214
_214
pragma solidity ^0.8.0;
_214
_214
import "../token/ERC20/utils/SafeERC20.sol";
_214
import "../utils/Address.sol";
_214
import "../utils/Context.sol";
_214
_214
/**
_214
* @title PaymentSplitter
_214
* @dev This contract allows to split Ether payments among a group of accounts. The sender does not need to be aware
_214
* that the Ether will be split in this way, since it is handled transparently by the contract.
_214
*
_214
* The split can be in equal parts or in any other arbitrary proportion. The way this is specified is by assigning each
_214
* account to a number of shares. Of all the Ether that this contract receives, each account will then be able to claim
_214
* an amount proportional to the percentage of total shares they were assigned. The distribution of shares is set at the
_214
* time of contract deployment and can't be updated thereafter.
_214
*
_214
* `PaymentSplitter` follows a _pull payment_ model. This means that payments are not automatically forwarded to the
_214
* accounts but kept in this contract, and the actual transfer is triggered as a separate step by calling the {release}
_214
* function.
_214
*
_214
* NOTE: This contract assumes that ERC20 tokens will behave similarly to native tokens (Ether). Rebasing tokens, and
_214
* tokens that apply fees during transfers, are likely to not be supported as expected. If in doubt, we encourage you
_214
* to run tests before sending real value to this contract.
_214
*/
_214
contract PaymentSplitter is Context {
_214
event PayeeAdded(address account, uint256 shares);
_214
event PaymentReleased(address to, uint256 amount);
_214
event ERC20PaymentReleased(IERC20 indexed token, address to, uint256 amount);
_214
event PaymentReceived(address from, uint256 amount);
_214
_214
uint256 private _totalShares;
_214
uint256 private _totalReleased;
_214
_214
mapping(address => uint256) private _shares;
_214
mapping(address => uint256) private _released;
_214
address[] private _payees;
_214
_214
mapping(IERC20 => uint256) private _erc20TotalReleased;
_214
mapping(IERC20 => mapping(address => uint256)) private _erc20Released;
_214
_214
/**
_214
* @dev Creates an instance of `PaymentSplitter` where each account in `payees` is assigned the number of shares at
_214
* the matching position in the `shares` array.
_214
*
_214
* All addresses in `payees` must be non-zero. Both arrays must have the same non-zero length, and there must be no
_214
* duplicates in `payees`.
_214
*/
_214
constructor(address[] memory payees, uint256[] memory shares_) payable {
_214
require(payees.length == shares_.length, "PaymentSplitter: payees and shares length mismatch");
_214
require(payees.length > 0, "PaymentSplitter: no payees");
_214
_214
for (uint256 i = 0; i < payees.length; i++) {
_214
_addPayee(payees[i], shares_[i]);
_214
}
_214
}
_214
_214
/**
_214
* @dev The Ether received will be logged with {PaymentReceived} events. Note that these events are not fully
_214
* reliable: it's possible for a contract to receive Ether without triggering this function. This only affects the
_214
* reliability of the events, and not the actual splitting of Ether.
_214
*
_214
* To learn more about this see the Solidity documentation for
_214
* https://solidity.readthedocs.io/en/latest/contracts.html#fallback-function[fallback
_214
* functions].
_214
*/
_214
receive() external payable virtual {
_214
emit PaymentReceived(_msgSender(), msg.value);
_214
}
_214
_214
/**
_214
* @dev Getter for the total shares held by payees.
_214
*/
_214
function totalShares() public view returns (uint256) {
_214
return _totalShares;
_214
}
_214
_214
/**
_214
* @dev Getter for the total amount of Ether already released.
_214
*/
_214
function totalReleased() public view returns (uint256) {
_214
return _totalReleased;
_214
}
_214
_214
/**
_214
* @dev Getter for the total amount of `token` already released. `token` should be the address of an IERC20
_214
* contract.
_214
*/
_214
function totalReleased(IERC20 token) public view returns (uint256) {
_214
return _erc20TotalReleased[token];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of shares held by an account.
_214
*/
_214
function shares(address account) public view returns (uint256) {
_214
return _shares[account];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of Ether already released to a payee.
_214
*/
_214
function released(address account) public view returns (uint256) {
_214
return _released[account];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of `token` tokens already released to a payee. `token` should be the address of an
_214
* IERC20 contract.
_214
*/
_214
function released(IERC20 token, address account) public view returns (uint256) {
_214
return _erc20Released[token][account];
_214
}
_214
_214
/**
_214
* @dev Getter for the address of the payee number `index`.
_214
*/
_214
function payee(uint256 index) public view returns (address) {
_214
return _payees[index];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of payee's releasable Ether.
_214
*/
_214
function releasable(address account) public view returns (uint256) {
_214
uint256 totalReceived = address(this).balance + totalReleased();
_214
return _pendingPayment(account, totalReceived, released(account));
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of payee's releasable `token` tokens. `token` should be the address of an
_214
* IERC20 contract.
_214
*/
_214
function releasable(IERC20 token, address account) public view returns (uint256) {
_214
uint256 totalReceived = token.balanceOf(address(this)) + totalReleased(token);
_214
return _pendingPayment(account, totalReceived, released(token, account));
_214
}
_214
_214
/**
_214
* @dev Triggers a transfer to `account` of the amount of Ether they are owed, according to their percentage of the
_214
* total shares and their previous withdrawals.
_214
*/
_214
function release(address payable account) public virtual {
_214
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
_214
_214
uint256 payment = releasable(account);
_214
_214
require(payment != 0, "PaymentSplitter: account is not due payment");
_214
_214
// _totalReleased is the sum of all values in _released.
_214
// If "_totalReleased += payment" does not overflow, then "_released[account] += payment" cannot overflow.
_214
_totalReleased += payment;
_214
unchecked {
_214
_released[account] += payment;
_214
}
_214
_214
Address.sendValue(account, payment);
_214
emit PaymentReleased(account, payment);
_214
}
_214
_214
/**
_214
* @dev Triggers a transfer to `account` of the amount of `token` tokens they are owed, according to their
_214
* percentage of the total shares and their previous withdrawals. `token` must be the address of an IERC20
_214
* contract.
_214
*/
_214
function release(IERC20 token, address account) public virtual {
_214
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
_214
_214
uint256 payment = releasable(token, account);
_214
_214
require(payment != 0, "PaymentSplitter: account is not due payment");
_214
_214
// _erc20TotalReleased[token] is the sum of all values in _erc20Released[token].
_214
// If "_erc20TotalReleased[token] += payment" does not overflow, then "_erc20Released[token][account] += payment"
_214
// cannot overflow.
_214
_erc20TotalReleased[token] += payment;
_214
unchecked {
_214
_erc20Released[token][account] += payment;
_214
}
_214
_214
SafeERC20.safeTransfer(token, account, payment);
_214
emit ERC20PaymentReleased(token, account, payment);
_214
}
_214
_214
/**
_214
* @dev internal logic for computing the pending payment of an `account` given the token historical balances and
_214
* already released amounts.
_214
*/
_214
function _pendingPayment(
_214
address account,
_214
uint256 totalReceived,
_214
uint256 alreadyReleased
_214
) private view returns (uint256) {
_214
return (totalReceived * _shares[account]) / _totalShares - alreadyReleased;
_214
}
_214
_214
/**
_214
* @dev Add a new payee to the contract.
_214
* @param account The address of the payee to add.
_214
* @param shares_ The number of shares owned by the payee.
_214
*/
_214
function _addPayee(address account, uint256 shares_) private {
_214
require(account != address(0), "PaymentSplitter: account is the zero address");
_214
require(shares_ > 0, "PaymentSplitter: shares are 0");
_214
require(_shares[account] == 0, "PaymentSplitter: account already has shares");
_214
_214
_payees.push(account);
_214
_shares[account] = shares_;
_214
_totalShares = _totalShares + shares_;
_214
emit PayeeAdded(account, shares_);
_214
}
_214
}

Error handling

Solidity uses state-reverting exceptions to handle errors. Such an exception undoes all changes made to the state in the current call (and all its sub-calls) and flags an error to the caller.

PaymentSplitter.sol

_214
// SPDX-License-Identifier: MIT
_214
// OpenZeppelin Contracts (last updated v4.7.0) (finance/PaymentSplitter.sol)
_214
_214
pragma solidity ^0.8.0;
_214
_214
import "../token/ERC20/utils/SafeERC20.sol";
_214
import "../utils/Address.sol";
_214
import "../utils/Context.sol";
_214
_214
/**
_214
* @title PaymentSplitter
_214
* @dev This contract allows to split Ether payments among a group of accounts. The sender does not need to be aware
_214
* that the Ether will be split in this way, since it is handled transparently by the contract.
_214
*
_214
* The split can be in equal parts or in any other arbitrary proportion. The way this is specified is by assigning each
_214
* account to a number of shares. Of all the Ether that this contract receives, each account will then be able to claim
_214
* an amount proportional to the percentage of total shares they were assigned. The distribution of shares is set at the
_214
* time of contract deployment and can't be updated thereafter.
_214
*
_214
* `PaymentSplitter` follows a _pull payment_ model. This means that payments are not automatically forwarded to the
_214
* accounts but kept in this contract, and the actual transfer is triggered as a separate step by calling the {release}
_214
* function.
_214
*
_214
* NOTE: This contract assumes that ERC20 tokens will behave similarly to native tokens (Ether). Rebasing tokens, and
_214
* tokens that apply fees during transfers, are likely to not be supported as expected. If in doubt, we encourage you
_214
* to run tests before sending real value to this contract.
_214
*/
_214
contract PaymentSplitter is Context {
_214
event PayeeAdded(address account, uint256 shares);
_214
event PaymentReleased(address to, uint256 amount);
_214
event ERC20PaymentReleased(IERC20 indexed token, address to, uint256 amount);
_214
event PaymentReceived(address from, uint256 amount);
_214
_214
uint256 private _totalShares;
_214
uint256 private _totalReleased;
_214
_214
mapping(address => uint256) private _shares;
_214
mapping(address => uint256) private _released;
_214
address[] private _payees;
_214
_214
mapping(IERC20 => uint256) private _erc20TotalReleased;
_214
mapping(IERC20 => mapping(address => uint256)) private _erc20Released;
_214
_214
/**
_214
* @dev Creates an instance of `PaymentSplitter` where each account in `payees` is assigned the number of shares at
_214
* the matching position in the `shares` array.
_214
*
_214
* All addresses in `payees` must be non-zero. Both arrays must have the same non-zero length, and there must be no
_214
* duplicates in `payees`.
_214
*/
_214
constructor(address[] memory payees, uint256[] memory shares_) payable {
_214
require(payees.length == shares_.length, "PaymentSplitter: payees and shares length mismatch");
_214
require(payees.length > 0, "PaymentSplitter: no payees");
_214
_214
for (uint256 i = 0; i < payees.length; i++) {
_214
_addPayee(payees[i], shares_[i]);
_214
}
_214
}
_214
_214
/**
_214
* @dev The Ether received will be logged with {PaymentReceived} events. Note that these events are not fully
_214
* reliable: it's possible for a contract to receive Ether without triggering this function. This only affects the
_214
* reliability of the events, and not the actual splitting of Ether.
_214
*
_214
* To learn more about this see the Solidity documentation for
_214
* https://solidity.readthedocs.io/en/latest/contracts.html#fallback-function[fallback
_214
* functions].
_214
*/
_214
receive() external payable virtual {
_214
emit PaymentReceived(_msgSender(), msg.value);
_214
}
_214
_214
/**
_214
* @dev Getter for the total shares held by payees.
_214
*/
_214
function totalShares() public view returns (uint256) {
_214
return _totalShares;
_214
}
_214
_214
/**
_214
* @dev Getter for the total amount of Ether already released.
_214
*/
_214
function totalReleased() public view returns (uint256) {
_214
return _totalReleased;
_214
}
_214
_214
/**
_214
* @dev Getter for the total amount of `token` already released. `token` should be the address of an IERC20
_214
* contract.
_214
*/
_214
function totalReleased(IERC20 token) public view returns (uint256) {
_214
return _erc20TotalReleased[token];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of shares held by an account.
_214
*/
_214
function shares(address account) public view returns (uint256) {
_214
return _shares[account];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of Ether already released to a payee.
_214
*/
_214
function released(address account) public view returns (uint256) {
_214
return _released[account];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of `token` tokens already released to a payee. `token` should be the address of an
_214
* IERC20 contract.
_214
*/
_214
function released(IERC20 token, address account) public view returns (uint256) {
_214
return _erc20Released[token][account];
_214
}
_214
_214
/**
_214
* @dev Getter for the address of the payee number `index`.
_214
*/
_214
function payee(uint256 index) public view returns (address) {
_214
return _payees[index];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of payee's releasable Ether.
_214
*/
_214
function releasable(address account) public view returns (uint256) {
_214
uint256 totalReceived = address(this).balance + totalReleased();
_214
return _pendingPayment(account, totalReceived, released(account));
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of payee's releasable `token` tokens. `token` should be the address of an
_214
* IERC20 contract.
_214
*/
_214
function releasable(IERC20 token, address account) public view returns (uint256) {
_214
uint256 totalReceived = token.balanceOf(address(this)) + totalReleased(token);
_214
return _pendingPayment(account, totalReceived, released(token, account));
_214
}
_214
_214
/**
_214
* @dev Triggers a transfer to `account` of the amount of Ether they are owed, according to their percentage of the
_214
* total shares and their previous withdrawals.
_214
*/
_214
function release(address payable account) public virtual {
_214
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
_214
_214
uint256 payment = releasable(account);
_214
_214
require(payment != 0, "PaymentSplitter: account is not due payment");
_214
_214
// _totalReleased is the sum of all values in _released.
_214
// If "_totalReleased += payment" does not overflow, then "_released[account] += payment" cannot overflow.
_214
_totalReleased += payment;
_214
unchecked {
_214
_released[account] += payment;
_214
}
_214
_214
Address.sendValue(account, payment);
_214
emit PaymentReleased(account, payment);
_214
}
_214
_214
/**
_214
* @dev Triggers a transfer to `account` of the amount of `token` tokens they are owed, according to their
_214
* percentage of the total shares and their previous withdrawals. `token` must be the address of an IERC20
_214
* contract.
_214
*/
_214
function release(IERC20 token, address account) public virtual {
_214
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
_214
_214
uint256 payment = releasable(token, account);
_214
_214
require(payment != 0, "PaymentSplitter: account is not due payment");
_214
_214
// _erc20TotalReleased[token] is the sum of all values in _erc20Released[token].
_214
// If "_erc20TotalReleased[token] += payment" does not overflow, then "_erc20Released[token][account] += payment"
_214
// cannot overflow.
_214
_erc20TotalReleased[token] += payment;
_214
unchecked {
_214
_erc20Released[token][account] += payment;
_214
}
_214
_214
SafeERC20.safeTransfer(token, account, payment);
_214
emit ERC20PaymentReleased(token, account, payment);
_214
}
_214
_214
/**
_214
* @dev internal logic for computing the pending payment of an `account` given the token historical balances and
_214
* already released amounts.
_214
*/
_214
function _pendingPayment(
_214
address account,
_214
uint256 totalReceived,
_214
uint256 alreadyReleased
_214
) private view returns (uint256) {
_214
return (totalReceived * _shares[account]) / _totalShares - alreadyReleased;
_214
}
_214
_214
/**
_214
* @dev Add a new payee to the contract.
_214
* @param account The address of the payee to add.
_214
* @param shares_ The number of shares owned by the payee.
_214
*/
_214
function _addPayee(address account, uint256 shares_) private {
_214
require(account != address(0), "PaymentSplitter: account is the zero address");
_214
require(shares_ > 0, "PaymentSplitter: shares are 0");
_214
require(_shares[account] == 0, "PaymentSplitter: account already has shares");
_214
_214
_payees.push(account);
_214
_shares[account] = shares_;
_214
_totalShares = _totalShares + shares_;
_214
emit PayeeAdded(account, shares_);
_214
}
_214
}

Control structures

There is: if, else, while, do, for, break, continue, return, with the usual semantics known from C or JavaScript.

Solidity also supports exception handling in the form of try/catch-statements.

PaymentSplitter.sol

_214
// SPDX-License-Identifier: MIT
_214
// OpenZeppelin Contracts (last updated v4.7.0) (finance/PaymentSplitter.sol)
_214
_214
pragma solidity ^0.8.0;
_214
_214
import "../token/ERC20/utils/SafeERC20.sol";
_214
import "../utils/Address.sol";
_214
import "../utils/Context.sol";
_214
_214
/**
_214
* @title PaymentSplitter
_214
* @dev This contract allows to split Ether payments among a group of accounts. The sender does not need to be aware
_214
* that the Ether will be split in this way, since it is handled transparently by the contract.
_214
*
_214
* The split can be in equal parts or in any other arbitrary proportion. The way this is specified is by assigning each
_214
* account to a number of shares. Of all the Ether that this contract receives, each account will then be able to claim
_214
* an amount proportional to the percentage of total shares they were assigned. The distribution of shares is set at the
_214
* time of contract deployment and can't be updated thereafter.
_214
*
_214
* `PaymentSplitter` follows a _pull payment_ model. This means that payments are not automatically forwarded to the
_214
* accounts but kept in this contract, and the actual transfer is triggered as a separate step by calling the {release}
_214
* function.
_214
*
_214
* NOTE: This contract assumes that ERC20 tokens will behave similarly to native tokens (Ether). Rebasing tokens, and
_214
* tokens that apply fees during transfers, are likely to not be supported as expected. If in doubt, we encourage you
_214
* to run tests before sending real value to this contract.
_214
*/
_214
contract PaymentSplitter is Context {
_214
event PayeeAdded(address account, uint256 shares);
_214
event PaymentReleased(address to, uint256 amount);
_214
event ERC20PaymentReleased(IERC20 indexed token, address to, uint256 amount);
_214
event PaymentReceived(address from, uint256 amount);
_214
_214
uint256 private _totalShares;
_214
uint256 private _totalReleased;
_214
_214
mapping(address => uint256) private _shares;
_214
mapping(address => uint256) private _released;
_214
address[] private _payees;
_214
_214
mapping(IERC20 => uint256) private _erc20TotalReleased;
_214
mapping(IERC20 => mapping(address => uint256)) private _erc20Released;
_214
_214
/**
_214
* @dev Creates an instance of `PaymentSplitter` where each account in `payees` is assigned the number of shares at
_214
* the matching position in the `shares` array.
_214
*
_214
* All addresses in `payees` must be non-zero. Both arrays must have the same non-zero length, and there must be no
_214
* duplicates in `payees`.
_214
*/
_214
constructor(address[] memory payees, uint256[] memory shares_) payable {
_214
require(payees.length == shares_.length, "PaymentSplitter: payees and shares length mismatch");
_214
require(payees.length > 0, "PaymentSplitter: no payees");
_214
_214
for (uint256 i = 0; i < payees.length; i++) {
_214
_addPayee(payees[i], shares_[i]);
_214
}
_214
}
_214
_214
/**
_214
* @dev The Ether received will be logged with {PaymentReceived} events. Note that these events are not fully
_214
* reliable: it's possible for a contract to receive Ether without triggering this function. This only affects the
_214
* reliability of the events, and not the actual splitting of Ether.
_214
*
_214
* To learn more about this see the Solidity documentation for
_214
* https://solidity.readthedocs.io/en/latest/contracts.html#fallback-function[fallback
_214
* functions].
_214
*/
_214
receive() external payable virtual {
_214
emit PaymentReceived(_msgSender(), msg.value);
_214
}
_214
_214
/**
_214
* @dev Getter for the total shares held by payees.
_214
*/
_214
function totalShares() public view returns (uint256) {
_214
return _totalShares;
_214
}
_214
_214
/**
_214
* @dev Getter for the total amount of Ether already released.
_214
*/
_214
function totalReleased() public view returns (uint256) {
_214
return _totalReleased;
_214
}
_214
_214
/**
_214
* @dev Getter for the total amount of `token` already released. `token` should be the address of an IERC20
_214
* contract.
_214
*/
_214
function totalReleased(IERC20 token) public view returns (uint256) {
_214
return _erc20TotalReleased[token];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of shares held by an account.
_214
*/
_214
function shares(address account) public view returns (uint256) {
_214
return _shares[account];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of Ether already released to a payee.
_214
*/
_214
function released(address account) public view returns (uint256) {
_214
return _released[account];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of `token` tokens already released to a payee. `token` should be the address of an
_214
* IERC20 contract.
_214
*/
_214
function released(IERC20 token, address account) public view returns (uint256) {
_214
return _erc20Released[token][account];
_214
}
_214
_214
/**
_214
* @dev Getter for the address of the payee number `index`.
_214
*/
_214
function payee(uint256 index) public view returns (address) {
_214
return _payees[index];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of payee's releasable Ether.
_214
*/
_214
function releasable(address account) public view returns (uint256) {
_214
uint256 totalReceived = address(this).balance + totalReleased();
_214
return _pendingPayment(account, totalReceived, released(account));
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of payee's releasable `token` tokens. `token` should be the address of an
_214
* IERC20 contract.
_214
*/
_214
function releasable(IERC20 token, address account) public view returns (uint256) {
_214
uint256 totalReceived = token.balanceOf(address(this)) + totalReleased(token);
_214
return _pendingPayment(account, totalReceived, released(token, account));
_214
}
_214
_214
/**
_214
* @dev Triggers a transfer to `account` of the amount of Ether they are owed, according to their percentage of the
_214
* total shares and their previous withdrawals.
_214
*/
_214
function release(address payable account) public virtual {
_214
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
_214
_214
uint256 payment = releasable(account);
_214
_214
require(payment != 0, "PaymentSplitter: account is not due payment");
_214
_214
// _totalReleased is the sum of all values in _released.
_214
// If "_totalReleased += payment" does not overflow, then "_released[account] += payment" cannot overflow.
_214
_totalReleased += payment;
_214
unchecked {
_214
_released[account] += payment;
_214
}
_214
_214
Address.sendValue(account, payment);
_214
emit PaymentReleased(account, payment);
_214
}
_214
_214
/**
_214
* @dev Triggers a transfer to `account` of the amount of `token` tokens they are owed, according to their
_214
* percentage of the total shares and their previous withdrawals. `token` must be the address of an IERC20
_214
* contract.
_214
*/
_214
function release(IERC20 token, address account) public virtual {
_214
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
_214
_214
uint256 payment = releasable(token, account);
_214
_214
require(payment != 0, "PaymentSplitter: account is not due payment");
_214
_214
// _erc20TotalReleased[token] is the sum of all values in _erc20Released[token].
_214
// If "_erc20TotalReleased[token] += payment" does not overflow, then "_erc20Released[token][account] += payment"
_214
// cannot overflow.
_214
_erc20TotalReleased[token] += payment;
_214
unchecked {
_214
_erc20Released[token][account] += payment;
_214
}
_214
_214
SafeERC20.safeTransfer(token, account, payment);
_214
emit ERC20PaymentReleased(token, account, payment);
_214
}
_214
_214
/**
_214
* @dev internal logic for computing the pending payment of an `account` given the token historical balances and
_214
* already released amounts.
_214
*/
_214
function _pendingPayment(
_214
address account,
_214
uint256 totalReceived,
_214
uint256 alreadyReleased
_214
) private view returns (uint256) {
_214
return (totalReceived * _shares[account]) / _totalShares - alreadyReleased;
_214
}
_214
_214
/**
_214
* @dev Add a new payee to the contract.
_214
* @param account The address of the payee to add.
_214
* @param shares_ The number of shares owned by the payee.
_214
*/
_214
function _addPayee(address account, uint256 shares_) private {
_214
require(account != address(0), "PaymentSplitter: account is the zero address");
_214
require(shares_ > 0, "PaymentSplitter: shares are 0");
_214
require(_shares[account] == 0, "PaymentSplitter: account already has shares");
_214
_214
_payees.push(account);
_214
_shares[account] = shares_;
_214
_totalShares = _totalShares + shares_;
_214
emit PayeeAdded(account, shares_);
_214
}
_214
}

Functions

Functions can be declared:

  • public: can be invoked by users, other contracts and from other functions in the same contract
  • external: only by users or another contract
  • internal: same or child contracts
  • private: only the owning contract

Functions can also be:

  • view: reads the state but cannot change it
  • pure: does not involve any state change
  • by default they can read and write the state

Remember that:

  1. Just reading the blockchain (without a transaction) is free.
  2. Invoking a transaction costs ETH.
  3. Different operations have different cost.
PaymentSplitter.sol

_214
// SPDX-License-Identifier: MIT
_214
// OpenZeppelin Contracts (last updated v4.7.0) (finance/PaymentSplitter.sol)
_214
_214
pragma solidity ^0.8.0;
_214
_214
import "../token/ERC20/utils/SafeERC20.sol";
_214
import "../utils/Address.sol";
_214
import "../utils/Context.sol";
_214
_214
/**
_214
* @title PaymentSplitter
_214
* @dev This contract allows to split Ether payments among a group of accounts. The sender does not need to be aware
_214
* that the Ether will be split in this way, since it is handled transparently by the contract.
_214
*
_214
* The split can be in equal parts or in any other arbitrary proportion. The way this is specified is by assigning each
_214
* account to a number of shares. Of all the Ether that this contract receives, each account will then be able to claim
_214
* an amount proportional to the percentage of total shares they were assigned. The distribution of shares is set at the
_214
* time of contract deployment and can't be updated thereafter.
_214
*
_214
* `PaymentSplitter` follows a _pull payment_ model. This means that payments are not automatically forwarded to the
_214
* accounts but kept in this contract, and the actual transfer is triggered as a separate step by calling the {release}
_214
* function.
_214
*
_214
* NOTE: This contract assumes that ERC20 tokens will behave similarly to native tokens (Ether). Rebasing tokens, and
_214
* tokens that apply fees during transfers, are likely to not be supported as expected. If in doubt, we encourage you
_214
* to run tests before sending real value to this contract.
_214
*/
_214
contract PaymentSplitter is Context {
_214
event PayeeAdded(address account, uint256 shares);
_214
event PaymentReleased(address to, uint256 amount);
_214
event ERC20PaymentReleased(IERC20 indexed token, address to, uint256 amount);
_214
event PaymentReceived(address from, uint256 amount);
_214
_214
uint256 private _totalShares;
_214
uint256 private _totalReleased;
_214
_214
mapping(address => uint256) private _shares;
_214
mapping(address => uint256) private _released;
_214
address[] private _payees;
_214
_214
mapping(IERC20 => uint256) private _erc20TotalReleased;
_214
mapping(IERC20 => mapping(address => uint256)) private _erc20Released;
_214
_214
/**
_214
* @dev Creates an instance of `PaymentSplitter` where each account in `payees` is assigned the number of shares at
_214
* the matching position in the `shares` array.
_214
*
_214
* All addresses in `payees` must be non-zero. Both arrays must have the same non-zero length, and there must be no
_214
* duplicates in `payees`.
_214
*/
_214
constructor(address[] memory payees, uint256[] memory shares_) payable {
_214
require(payees.length == shares_.length, "PaymentSplitter: payees and shares length mismatch");
_214
require(payees.length > 0, "PaymentSplitter: no payees");
_214
_214
for (uint256 i = 0; i < payees.length; i++) {
_214
_addPayee(payees[i], shares_[i]);
_214
}
_214
}
_214
_214
/**
_214
* @dev The Ether received will be logged with {PaymentReceived} events. Note that these events are not fully
_214
* reliable: it's possible for a contract to receive Ether without triggering this function. This only affects the
_214
* reliability of the events, and not the actual splitting of Ether.
_214
*
_214
* To learn more about this see the Solidity documentation for
_214
* https://solidity.readthedocs.io/en/latest/contracts.html#fallback-function[fallback
_214
* functions].
_214
*/
_214
receive() external payable virtual {
_214
emit PaymentReceived(_msgSender(), msg.value);
_214
}
_214
_214
/**
_214
* @dev Getter for the total shares held by payees.
_214
*/
_214
function totalShares() public view returns (uint256) {
_214
return _totalShares;
_214
}
_214
_214
/**
_214
* @dev Getter for the total amount of Ether already released.
_214
*/
_214
function totalReleased() public view returns (uint256) {
_214
return _totalReleased;
_214
}
_214
_214
/**
_214
* @dev Getter for the total amount of `token` already released. `token` should be the address of an IERC20
_214
* contract.
_214
*/
_214
function totalReleased(IERC20 token) public view returns (uint256) {
_214
return _erc20TotalReleased[token];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of shares held by an account.
_214
*/
_214
function shares(address account) public view returns (uint256) {
_214
return _shares[account];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of Ether already released to a payee.
_214
*/
_214
function released(address account) public view returns (uint256) {
_214
return _released[account];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of `token` tokens already released to a payee. `token` should be the address of an
_214
* IERC20 contract.
_214
*/
_214
function released(IERC20 token, address account) public view returns (uint256) {
_214
return _erc20Released[token][account];
_214
}
_214
_214
/**
_214
* @dev Getter for the address of the payee number `index`.
_214
*/
_214
function payee(uint256 index) public view returns (address) {
_214
return _payees[index];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of payee's releasable Ether.
_214
*/
_214
function releasable(address account) public view returns (uint256) {
_214
uint256 totalReceived = address(this).balance + totalReleased();
_214
return _pendingPayment(account, totalReceived, released(account));
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of payee's releasable `token` tokens. `token` should be the address of an
_214
* IERC20 contract.
_214
*/
_214
function releasable(IERC20 token, address account) public view returns (uint256) {
_214
uint256 totalReceived = token.balanceOf(address(this)) + totalReleased(token);
_214
return _pendingPayment(account, totalReceived, released(token, account));
_214
}
_214
_214
/**
_214
* @dev Triggers a transfer to `account` of the amount of Ether they are owed, according to their percentage of the
_214
* total shares and their previous withdrawals.
_214
*/
_214
function release(address payable account) public virtual {
_214
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
_214
_214
uint256 payment = releasable(account);
_214
_214
require(payment != 0, "PaymentSplitter: account is not due payment");
_214
_214
// _totalReleased is the sum of all values in _released.
_214
// If "_totalReleased += payment" does not overflow, then "_released[account] += payment" cannot overflow.
_214
_totalReleased += payment;
_214
unchecked {
_214
_released[account] += payment;
_214
}
_214
_214
Address.sendValue(account, payment);
_214
emit PaymentReleased(account, payment);
_214
}
_214
_214
/**
_214
* @dev Triggers a transfer to `account` of the amount of `token` tokens they are owed, according to their
_214
* percentage of the total shares and their previous withdrawals. `token` must be the address of an IERC20
_214
* contract.
_214
*/
_214
function release(IERC20 token, address account) public virtual {
_214
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
_214
_214
uint256 payment = releasable(token, account);
_214
_214
require(payment != 0, "PaymentSplitter: account is not due payment");
_214
_214
// _erc20TotalReleased[token] is the sum of all values in _erc20Released[token].
_214
// If "_erc20TotalReleased[token] += payment" does not overflow, then "_erc20Released[token][account] += payment"
_214
// cannot overflow.
_214
_erc20TotalReleased[token] += payment;
_214
unchecked {
_214
_erc20Released[token][account] += payment;
_214
}
_214
_214
SafeERC20.safeTransfer(token, account, payment);
_214
emit ERC20PaymentReleased(token, account, payment);
_214
}
_214
_214
/**
_214
* @dev internal logic for computing the pending payment of an `account` given the token historical balances and
_214
* already released amounts.
_214
*/
_214
function _pendingPayment(
_214
address account,
_214
uint256 totalReceived,
_214
uint256 alreadyReleased
_214
) private view returns (uint256) {
_214
return (totalReceived * _shares[account]) / _totalShares - alreadyReleased;
_214
}
_214
_214
/**
_214
* @dev Add a new payee to the contract.
_214
* @param account The address of the payee to add.
_214
* @param shares_ The number of shares owned by the payee.
_214
*/
_214
function _addPayee(address account, uint256 shares_) private {
_214
require(account != address(0), "PaymentSplitter: account is the zero address");
_214
require(shares_ > 0, "PaymentSplitter: shares are 0");
_214
require(_shares[account] == 0, "PaymentSplitter: account already has shares");
_214
_214
_payees.push(account);
_214
_shares[account] = shares_;
_214
_totalShares = _totalShares + shares_;
_214
emit PayeeAdded(account, shares_);
_214
}
_214
}

Receiving ETH

Functions must be declared payable to be able to receive ETH. The amount transferred can be accessed by a global object named msg

PaymentSplitter.sol

_214
// SPDX-License-Identifier: MIT
_214
// OpenZeppelin Contracts (last updated v4.7.0) (finance/PaymentSplitter.sol)
_214
_214
pragma solidity ^0.8.0;
_214
_214
import "../token/ERC20/utils/SafeERC20.sol";
_214
import "../utils/Address.sol";
_214
import "../utils/Context.sol";
_214
_214
/**
_214
* @title PaymentSplitter
_214
* @dev This contract allows to split Ether payments among a group of accounts. The sender does not need to be aware
_214
* that the Ether will be split in this way, since it is handled transparently by the contract.
_214
*
_214
* The split can be in equal parts or in any other arbitrary proportion. The way this is specified is by assigning each
_214
* account to a number of shares. Of all the Ether that this contract receives, each account will then be able to claim
_214
* an amount proportional to the percentage of total shares they were assigned. The distribution of shares is set at the
_214
* time of contract deployment and can't be updated thereafter.
_214
*
_214
* `PaymentSplitter` follows a _pull payment_ model. This means that payments are not automatically forwarded to the
_214
* accounts but kept in this contract, and the actual transfer is triggered as a separate step by calling the {release}
_214
* function.
_214
*
_214
* NOTE: This contract assumes that ERC20 tokens will behave similarly to native tokens (Ether). Rebasing tokens, and
_214
* tokens that apply fees during transfers, are likely to not be supported as expected. If in doubt, we encourage you
_214
* to run tests before sending real value to this contract.
_214
*/
_214
contract PaymentSplitter is Context {
_214
event PayeeAdded(address account, uint256 shares);
_214
event PaymentReleased(address to, uint256 amount);
_214
event ERC20PaymentReleased(IERC20 indexed token, address to, uint256 amount);
_214
event PaymentReceived(address from, uint256 amount);
_214
_214
uint256 private _totalShares;
_214
uint256 private _totalReleased;
_214
_214
mapping(address => uint256) private _shares;
_214
mapping(address => uint256) private _released;
_214
address[] private _payees;
_214
_214
mapping(IERC20 => uint256) private _erc20TotalReleased;
_214
mapping(IERC20 => mapping(address => uint256)) private _erc20Released;
_214
_214
/**
_214
* @dev Creates an instance of `PaymentSplitter` where each account in `payees` is assigned the number of shares at
_214
* the matching position in the `shares` array.
_214
*
_214
* All addresses in `payees` must be non-zero. Both arrays must have the same non-zero length, and there must be no
_214
* duplicates in `payees`.
_214
*/
_214
constructor(address[] memory payees, uint256[] memory shares_) payable {
_214
require(payees.length == shares_.length, "PaymentSplitter: payees and shares length mismatch");
_214
require(payees.length > 0, "PaymentSplitter: no payees");
_214
_214
for (uint256 i = 0; i < payees.length; i++) {
_214
_addPayee(payees[i], shares_[i]);
_214
}
_214
}
_214
_214
/**
_214
* @dev The Ether received will be logged with {PaymentReceived} events. Note that these events are not fully
_214
* reliable: it's possible for a contract to receive Ether without triggering this function. This only affects the
_214
* reliability of the events, and not the actual splitting of Ether.
_214
*
_214
* To learn more about this see the Solidity documentation for
_214
* https://solidity.readthedocs.io/en/latest/contracts.html#fallback-function[fallback
_214
* functions].
_214
*/
_214
receive() external payable virtual {
_214
emit PaymentReceived(_msgSender(), msg.value);
_214
}
_214
_214
/**
_214
* @dev Getter for the total shares held by payees.
_214
*/
_214
function totalShares() public view returns (uint256) {
_214
return _totalShares;
_214
}
_214
_214
/**
_214
* @dev Getter for the total amount of Ether already released.
_214
*/
_214
function totalReleased() public view returns (uint256) {
_214
return _totalReleased;
_214
}
_214
_214
/**
_214
* @dev Getter for the total amount of `token` already released. `token` should be the address of an IERC20
_214
* contract.
_214
*/
_214
function totalReleased(IERC20 token) public view returns (uint256) {
_214
return _erc20TotalReleased[token];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of shares held by an account.
_214
*/
_214
function shares(address account) public view returns (uint256) {
_214
return _shares[account];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of Ether already released to a payee.
_214
*/
_214
function released(address account) public view returns (uint256) {
_214
return _released[account];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of `token` tokens already released to a payee. `token` should be the address of an
_214
* IERC20 contract.
_214
*/
_214
function released(IERC20 token, address account) public view returns (uint256) {
_214
return _erc20Released[token][account];
_214
}
_214
_214
/**
_214
* @dev Getter for the address of the payee number `index`.
_214
*/
_214
function payee(uint256 index) public view returns (address) {
_214
return _payees[index];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of payee's releasable Ether.
_214
*/
_214
function releasable(address account) public view returns (uint256) {
_214
uint256 totalReceived = address(this).balance + totalReleased();
_214
return _pendingPayment(account, totalReceived, released(account));
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of payee's releasable `token` tokens. `token` should be the address of an
_214
* IERC20 contract.
_214
*/
_214
function releasable(IERC20 token, address account) public view returns (uint256) {
_214
uint256 totalReceived = token.balanceOf(address(this)) + totalReleased(token);
_214
return _pendingPayment(account, totalReceived, released(token, account));
_214
}
_214
_214
/**
_214
* @dev Triggers a transfer to `account` of the amount of Ether they are owed, according to their percentage of the
_214
* total shares and their previous withdrawals.
_214
*/
_214
function release(address payable account) public virtual {
_214
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
_214
_214
uint256 payment = releasable(account);
_214
_214
require(payment != 0, "PaymentSplitter: account is not due payment");
_214
_214
// _totalReleased is the sum of all values in _released.
_214
// If "_totalReleased += payment" does not overflow, then "_released[account] += payment" cannot overflow.
_214
_totalReleased += payment;
_214
unchecked {
_214
_released[account] += payment;
_214
}
_214
_214
Address.sendValue(account, payment);
_214
emit PaymentReleased(account, payment);
_214
}
_214
_214
/**
_214
* @dev Triggers a transfer to `account` of the amount of `token` tokens they are owed, according to their
_214
* percentage of the total shares and their previous withdrawals. `token` must be the address of an IERC20
_214
* contract.
_214
*/
_214
function release(IERC20 token, address account) public virtual {
_214
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
_214
_214
uint256 payment = releasable(token, account);
_214
_214
require(payment != 0, "PaymentSplitter: account is not due payment");
_214
_214
// _erc20TotalReleased[token] is the sum of all values in _erc20Released[token].
_214
// If "_erc20TotalReleased[token] += payment" does not overflow, then "_erc20Released[token][account] += payment"
_214
// cannot overflow.
_214
_erc20TotalReleased[token] += payment;
_214
unchecked {
_214
_erc20Released[token][account] += payment;
_214
}
_214
_214
SafeERC20.safeTransfer(token, account, payment);
_214
emit ERC20PaymentReleased(token, account, payment);
_214
}
_214
_214
/**
_214
* @dev internal logic for computing the pending payment of an `account` given the token historical balances and
_214
* already released amounts.
_214
*/
_214
function _pendingPayment(
_214
address account,
_214
uint256 totalReceived,
_214
uint256 alreadyReleased
_214
) private view returns (uint256) {
_214
return (totalReceived * _shares[account]) / _totalShares - alreadyReleased;
_214
}
_214
_214
/**
_214
* @dev Add a new payee to the contract.
_214
* @param account The address of the payee to add.
_214
* @param shares_ The number of shares owned by the payee.
_214
*/
_214
function _addPayee(address account, uint256 shares_) private {
_214
require(account != address(0), "PaymentSplitter: account is the zero address");
_214
require(shares_ > 0, "PaymentSplitter: shares are 0");
_214
require(_shares[account] == 0, "PaymentSplitter: account already has shares");
_214
_214
_payees.push(account);
_214
_shares[account] = shares_;
_214
_totalShares = _totalShares + shares_;
_214
emit PayeeAdded(account, shares_);
_214
}
_214
}

Contracts interaction

Contracts can call other contracts

PaymentSplitter.sol

_214
// SPDX-License-Identifier: MIT
_214
// OpenZeppelin Contracts (last updated v4.7.0) (finance/PaymentSplitter.sol)
_214
_214
pragma solidity ^0.8.0;
_214
_214
import "../token/ERC20/utils/SafeERC20.sol";
_214
import "../utils/Address.sol";
_214
import "../utils/Context.sol";
_214
_214
/**
_214
* @title PaymentSplitter
_214
* @dev This contract allows to split Ether payments among a group of accounts. The sender does not need to be aware
_214
* that the Ether will be split in this way, since it is handled transparently by the contract.
_214
*
_214
* The split can be in equal parts or in any other arbitrary proportion. The way this is specified is by assigning each
_214
* account to a number of shares. Of all the Ether that this contract receives, each account will then be able to claim
_214
* an amount proportional to the percentage of total shares they were assigned. The distribution of shares is set at the
_214
* time of contract deployment and can't be updated thereafter.
_214
*
_214
* `PaymentSplitter` follows a _pull payment_ model. This means that payments are not automatically forwarded to the
_214
* accounts but kept in this contract, and the actual transfer is triggered as a separate step by calling the {release}
_214
* function.
_214
*
_214
* NOTE: This contract assumes that ERC20 tokens will behave similarly to native tokens (Ether). Rebasing tokens, and
_214
* tokens that apply fees during transfers, are likely to not be supported as expected. If in doubt, we encourage you
_214
* to run tests before sending real value to this contract.
_214
*/
_214
contract PaymentSplitter is Context {
_214
event PayeeAdded(address account, uint256 shares);
_214
event PaymentReleased(address to, uint256 amount);
_214
event ERC20PaymentReleased(IERC20 indexed token, address to, uint256 amount);
_214
event PaymentReceived(address from, uint256 amount);
_214
_214
uint256 private _totalShares;
_214
uint256 private _totalReleased;
_214
_214
mapping(address => uint256) private _shares;
_214
mapping(address => uint256) private _released;
_214
address[] private _payees;
_214
_214
mapping(IERC20 => uint256) private _erc20TotalReleased;
_214
mapping(IERC20 => mapping(address => uint256)) private _erc20Released;
_214
_214
/**
_214
* @dev Creates an instance of `PaymentSplitter` where each account in `payees` is assigned the number of shares at
_214
* the matching position in the `shares` array.
_214
*
_214
* All addresses in `payees` must be non-zero. Both arrays must have the same non-zero length, and there must be no
_214
* duplicates in `payees`.
_214
*/
_214
constructor(address[] memory payees, uint256[] memory shares_) payable {
_214
require(payees.length == shares_.length, "PaymentSplitter: payees and shares length mismatch");
_214
require(payees.length > 0, "PaymentSplitter: no payees");
_214
_214
for (uint256 i = 0; i < payees.length; i++) {
_214
_addPayee(payees[i], shares_[i]);
_214
}
_214
}
_214
_214
/**
_214
* @dev The Ether received will be logged with {PaymentReceived} events. Note that these events are not fully
_214
* reliable: it's possible for a contract to receive Ether without triggering this function. This only affects the
_214
* reliability of the events, and not the actual splitting of Ether.
_214
*
_214
* To learn more about this see the Solidity documentation for
_214
* https://solidity.readthedocs.io/en/latest/contracts.html#fallback-function[fallback
_214
* functions].
_214
*/
_214
receive() external payable virtual {
_214
emit PaymentReceived(_msgSender(), msg.value);
_214
}
_214
_214
/**
_214
* @dev Getter for the total shares held by payees.
_214
*/
_214
function totalShares() public view returns (uint256) {
_214
return _totalShares;
_214
}
_214
_214
/**
_214
* @dev Getter for the total amount of Ether already released.
_214
*/
_214
function totalReleased() public view returns (uint256) {
_214
return _totalReleased;
_214
}
_214
_214
/**
_214
* @dev Getter for the total amount of `token` already released. `token` should be the address of an IERC20
_214
* contract.
_214
*/
_214
function totalReleased(IERC20 token) public view returns (uint256) {
_214
return _erc20TotalReleased[token];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of shares held by an account.
_214
*/
_214
function shares(address account) public view returns (uint256) {
_214
return _shares[account];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of Ether already released to a payee.
_214
*/
_214
function released(address account) public view returns (uint256) {
_214
return _released[account];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of `token` tokens already released to a payee. `token` should be the address of an
_214
* IERC20 contract.
_214
*/
_214
function released(IERC20 token, address account) public view returns (uint256) {
_214
return _erc20Released[token][account];
_214
}
_214
_214
/**
_214
* @dev Getter for the address of the payee number `index`.
_214
*/
_214
function payee(uint256 index) public view returns (address) {
_214
return _payees[index];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of payee's releasable Ether.
_214
*/
_214
function releasable(address account) public view returns (uint256) {
_214
uint256 totalReceived = address(this).balance + totalReleased();
_214
return _pendingPayment(account, totalReceived, released(account));
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of payee's releasable `token` tokens. `token` should be the address of an
_214
* IERC20 contract.
_214
*/
_214
function releasable(IERC20 token, address account) public view returns (uint256) {
_214
uint256 totalReceived = token.balanceOf(address(this)) + totalReleased(token);
_214
return _pendingPayment(account, totalReceived, released(token, account));
_214
}
_214
_214
/**
_214
* @dev Triggers a transfer to `account` of the amount of Ether they are owed, according to their percentage of the
_214
* total shares and their previous withdrawals.
_214
*/
_214
function release(address payable account) public virtual {
_214
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
_214
_214
uint256 payment = releasable(account);
_214
_214
require(payment != 0, "PaymentSplitter: account is not due payment");
_214
_214
// _totalReleased is the sum of all values in _released.
_214
// If "_totalReleased += payment" does not overflow, then "_released[account] += payment" cannot overflow.
_214
_totalReleased += payment;
_214
unchecked {
_214
_released[account] += payment;
_214
}
_214
_214
Address.sendValue(account, payment);
_214
emit PaymentReleased(account, payment);
_214
}
_214
_214
/**
_214
* @dev Triggers a transfer to `account` of the amount of `token` tokens they are owed, according to their
_214
* percentage of the total shares and their previous withdrawals. `token` must be the address of an IERC20
_214
* contract.
_214
*/
_214
function release(IERC20 token, address account) public virtual {
_214
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
_214
_214
uint256 payment = releasable(token, account);
_214
_214
require(payment != 0, "PaymentSplitter: account is not due payment");
_214
_214
// _erc20TotalReleased[token] is the sum of all values in _erc20Released[token].
_214
// If "_erc20TotalReleased[token] += payment" does not overflow, then "_erc20Released[token][account] += payment"
_214
// cannot overflow.
_214
_erc20TotalReleased[token] += payment;
_214
unchecked {
_214
_erc20Released[token][account] += payment;
_214
}
_214
_214
SafeERC20.safeTransfer(token, account, payment);
_214
emit ERC20PaymentReleased(token, account, payment);
_214
}
_214
_214
/**
_214
* @dev internal logic for computing the pending payment of an `account` given the token historical balances and
_214
* already released amounts.
_214
*/
_214
function _pendingPayment(
_214
address account,
_214
uint256 totalReceived,
_214
uint256 alreadyReleased
_214
) private view returns (uint256) {
_214
return (totalReceived * _shares[account]) / _totalShares - alreadyReleased;
_214
}
_214
_214
/**
_214
* @dev Add a new payee to the contract.
_214
* @param account The address of the payee to add.
_214
* @param shares_ The number of shares owned by the payee.
_214
*/
_214
function _addPayee(address account, uint256 shares_) private {
_214
require(account != address(0), "PaymentSplitter: account is the zero address");
_214
require(shares_ > 0, "PaymentSplitter: shares are 0");
_214
require(_shares[account] == 0, "PaymentSplitter: account already has shares");
_214
_214
_payees.push(account);
_214
_shares[account] = shares_;
_214
_totalShares = _totalShares + shares_;
_214
emit PayeeAdded(account, shares_);
_214
}
_214
}

Solidity version and imports

Whenever we start a solidity file we need to declare the Solidity version we are going to use:

  • Solidity has evolved quite fast so new features were added making previous versions not to be compatible
  • Remember everything is compiled to EVM bytecode so unless there is an upgrade, the compile code is compatible.

Also libraries (like OZ) can be used helping quite a lot the development process.

Comments

Developing easy to follow, well tested and with proper comments is extremely important as most of the time code that can be abused is written in solidity.

Comments should be written in Ethereum Natural Language Specification Format (NatSpec)

Contract declaration

Contracts resemble to OOP:

  • Multiple inheritance
  • Interfaces, Abstract Contracts

Events

Events can be emitted when executing transactions.

Storage Variables

Variables can be placed in the storage, thus making their state persistent.

There are several types that can be used:

  • Unsigned integers (multiple lengths)
  • Mappings (maps)
  • Addresses
  • Arrays (fixed and variable length)
  • Bytes
  • Strings (fixed and variable length)
  • Structs
  • Booleans
  • Contracts
  • Enums

Visibility (public, private, internal)

️ Even if something is private in the context of the EVM it will be public for somebody else observing the blockchain.

Constructor

Can be used to set some storage values when deploying the contracts

Error handling

Solidity uses state-reverting exceptions to handle errors. Such an exception undoes all changes made to the state in the current call (and all its sub-calls) and flags an error to the caller.

Control structures

There is: if, else, while, do, for, break, continue, return, with the usual semantics known from C or JavaScript.

Solidity also supports exception handling in the form of try/catch-statements.

Functions

Functions can be declared:

  • public: can be invoked by users, other contracts and from other functions in the same contract
  • external: only by users or another contract
  • internal: same or child contracts
  • private: only the owning contract

Functions can also be:

  • view: reads the state but cannot change it
  • pure: does not involve any state change
  • by default they can read and write the state

Remember that:

  1. Just reading the blockchain (without a transaction) is free.
  2. Invoking a transaction costs ETH.
  3. Different operations have different cost.

Receiving ETH

Functions must be declared payable to be able to receive ETH. The amount transferred can be accessed by a global object named msg

Contracts interaction

Contracts can call other contracts

PaymentSplitter.sol
ExpandClose

_214
// SPDX-License-Identifier: MIT
_214
// OpenZeppelin Contracts (last updated v4.7.0) (finance/PaymentSplitter.sol)
_214
_214
pragma solidity ^0.8.0;
_214
_214
import "../token/ERC20/utils/SafeERC20.sol";
_214
import "../utils/Address.sol";
_214
import "../utils/Context.sol";
_214
_214
/**
_214
* @title PaymentSplitter
_214
* @dev This contract allows to split Ether payments among a group of accounts. The sender does not need to be aware
_214
* that the Ether will be split in this way, since it is handled transparently by the contract.
_214
*
_214
* The split can be in equal parts or in any other arbitrary proportion. The way this is specified is by assigning each
_214
* account to a number of shares. Of all the Ether that this contract receives, each account will then be able to claim
_214
* an amount proportional to the percentage of total shares they were assigned. The distribution of shares is set at the
_214
* time of contract deployment and can't be updated thereafter.
_214
*
_214
* `PaymentSplitter` follows a _pull payment_ model. This means that payments are not automatically forwarded to the
_214
* accounts but kept in this contract, and the actual transfer is triggered as a separate step by calling the {release}
_214
* function.
_214
*
_214
* NOTE: This contract assumes that ERC20 tokens will behave similarly to native tokens (Ether). Rebasing tokens, and
_214
* tokens that apply fees during transfers, are likely to not be supported as expected. If in doubt, we encourage you
_214
* to run tests before sending real value to this contract.
_214
*/
_214
contract PaymentSplitter is Context {
_214
event PayeeAdded(address account, uint256 shares);
_214
event PaymentReleased(address to, uint256 amount);
_214
event ERC20PaymentReleased(IERC20 indexed token, address to, uint256 amount);
_214
event PaymentReceived(address from, uint256 amount);
_214
_214
uint256 private _totalShares;
_214
uint256 private _totalReleased;
_214
_214
mapping(address => uint256) private _shares;
_214
mapping(address => uint256) private _released;
_214
address[] private _payees;
_214
_214
mapping(IERC20 => uint256) private _erc20TotalReleased;
_214
mapping(IERC20 => mapping(address => uint256)) private _erc20Released;
_214
_214
/**
_214
* @dev Creates an instance of `PaymentSplitter` where each account in `payees` is assigned the number of shares at
_214
* the matching position in the `shares` array.
_214
*
_214
* All addresses in `payees` must be non-zero. Both arrays must have the same non-zero length, and there must be no
_214
* duplicates in `payees`.
_214
*/
_214
constructor(address[] memory payees, uint256[] memory shares_) payable {
_214
require(payees.length == shares_.length, "PaymentSplitter: payees and shares length mismatch");
_214
require(payees.length > 0, "PaymentSplitter: no payees");
_214
_214
for (uint256 i = 0; i < payees.length; i++) {
_214
_addPayee(payees[i], shares_[i]);
_214
}
_214
}
_214
_214
/**
_214
* @dev The Ether received will be logged with {PaymentReceived} events. Note that these events are not fully
_214
* reliable: it's possible for a contract to receive Ether without triggering this function. This only affects the
_214
* reliability of the events, and not the actual splitting of Ether.
_214
*
_214
* To learn more about this see the Solidity documentation for
_214
* https://solidity.readthedocs.io/en/latest/contracts.html#fallback-function[fallback
_214
* functions].
_214
*/
_214
receive() external payable virtual {
_214
emit PaymentReceived(_msgSender(), msg.value);
_214
}
_214
_214
/**
_214
* @dev Getter for the total shares held by payees.
_214
*/
_214
function totalShares() public view returns (uint256) {
_214
return _totalShares;
_214
}
_214
_214
/**
_214
* @dev Getter for the total amount of Ether already released.
_214
*/
_214
function totalReleased() public view returns (uint256) {
_214
return _totalReleased;
_214
}
_214
_214
/**
_214
* @dev Getter for the total amount of `token` already released. `token` should be the address of an IERC20
_214
* contract.
_214
*/
_214
function totalReleased(IERC20 token) public view returns (uint256) {
_214
return _erc20TotalReleased[token];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of shares held by an account.
_214
*/
_214
function shares(address account) public view returns (uint256) {
_214
return _shares[account];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of Ether already released to a payee.
_214
*/
_214
function released(address account) public view returns (uint256) {
_214
return _released[account];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of `token` tokens already released to a payee. `token` should be the address of an
_214
* IERC20 contract.
_214
*/
_214
function released(IERC20 token, address account) public view returns (uint256) {
_214
return _erc20Released[token][account];
_214
}
_214
_214
/**
_214
* @dev Getter for the address of the payee number `index`.
_214
*/
_214
function payee(uint256 index) public view returns (address) {
_214
return _payees[index];
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of payee's releasable Ether.
_214
*/
_214
function releasable(address account) public view returns (uint256) {
_214
uint256 totalReceived = address(this).balance + totalReleased();
_214
return _pendingPayment(account, totalReceived, released(account));
_214
}
_214
_214
/**
_214
* @dev Getter for the amount of payee's releasable `token` tokens. `token` should be the address of an
_214
* IERC20 contract.
_214
*/
_214
function releasable(IERC20 token, address account) public view returns (uint256) {
_214
uint256 totalReceived = token.balanceOf(address(this)) + totalReleased(token);
_214
return _pendingPayment(account, totalReceived, released(token, account));
_214
}
_214
_214
/**
_214
* @dev Triggers a transfer to `account` of the amount of Ether they are owed, according to their percentage of the
_214
* total shares and their previous withdrawals.
_214
*/
_214
function release(address payable account) public virtual {
_214
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
_214
_214
uint256 payment = releasable(account);
_214
_214
require(payment != 0, "PaymentSplitter: account is not due payment");
_214
_214
// _totalReleased is the sum of all values in _released.
_214
// If "_totalReleased += payment" does not overflow, then "_released[account] += payment" cannot overflow.
_214
_totalReleased += payment;
_214
unchecked {
_214
_released[account] += payment;
_214
}
_214
_214
Address.sendValue(account, payment);
_214
emit PaymentReleased(account, payment);
_214
}
_214
_214
/**
_214
* @dev Triggers a transfer to `account` of the amount of `token` tokens they are owed, according to their
_214
* percentage of the total shares and their previous withdrawals. `token` must be the address of an IERC20
_214
* contract.
_214
*/
_214
function release(IERC20 token, address account) public virtual {
_214
require(_shares[account] > 0, "PaymentSplitter: account has no shares");
_214
_214
uint256 payment = releasable(token, account);
_214
_214
require(payment != 0, "PaymentSplitter: account is not due payment");
_214
_214
// _erc20TotalReleased[token] is the sum of all values in _erc20Released[token].
_214
// If "_erc20TotalReleased[token] += payment" does not overflow, then "_erc20Released[token][account] += payment"
_214
// cannot overflow.
_214
_erc20TotalReleased[token] += payment;
_214
unchecked {
_214
_erc20Released[token][account] += payment;
_214
}
_214
_214
SafeERC20.safeTransfer(token, account, payment);
_214
emit ERC20PaymentReleased(token, account, payment);
_214
}
_214
_214
/**
_214
* @dev internal logic for computing the pending payment of an `account` given the token historical balances and
_214
* already released amounts.
_214
*/
_214
function _pendingPayment(
_214
address account,
_214
uint256 totalReceived,
_214
uint256 alreadyReleased
_214
) private view returns (uint256) {
_214
return (totalReceived * _shares[account]) / _totalShares - alreadyReleased;
_214
}
_214
_214
/**
_214
* @dev Add a new payee to the contract.
_214
* @param account The address of the payee to add.
_214
* @param shares_ The number of shares owned by the payee.
_214
*/
_214
function _addPayee(address account, uint256 shares_) private {
_214
require(account != address(0), "PaymentSplitter: account is the zero address");
_214
require(shares_ > 0, "PaymentSplitter: shares are 0");
_214
require(_shares[account] == 0, "PaymentSplitter: account already has shares");
_214
_214
_payees.push(account);
_214
_shares[account] = shares_;
_214
_totalShares = _totalShares + shares_;
_214
emit PayeeAdded(account, shares_);
_214
}
_214
}

Where to go next?

Again, Solidity is a complex language that requires attention to details:

The following links might be a good start for you:

  1. SpeedRunEthereum.
  2. Solidity documentation.
  3. Solidity by example.