Skip to main content

Contract Overview

Detailed overview of the LifeCycle Cash Flow smart contract architecture and functions.

What the Contract Does​

The LifeCycle Cash Flow contract manages payment distributions for tokenized securities on Hedera. It supports:

  1. Distribution Execution: Execute dividend or coupon payments to token holders
  2. Bond Cash-Outs: Execute bond maturity redemptions
  3. Snapshot Payments: Execute one-time payments based on token holder snapshots
  4. Payment Management: Manage payment tokens (USDC, HBAR, or other HTS tokens)

Architecture​

Upgradeability​

The contract uses OpenZeppelin's Upgradeable Contracts pattern:

  • Proxy Pattern: Allows upgrading contract logic without changing address
  • Storage Layout: Preserves state across upgrades
  • Access Control: Role-based permissions for upgrade operations

Role-Based Access Control​

The contract implements fine-grained access control:

  • DEFAULT_ADMIN_ROLE: Full contract administration
  • PAUSER_ROLE: Pause/unpause contract in emergencies
  • PAYOUT_ROLE: Execute distributions and snapshots
  • CASHOUT_ROLE: Execute bond cash-outs
  • TRANSFERER_ROLE: Transfer payment tokens from contract
  • PAYMENT_TOKEN_MANAGER_ROLE: Update payment token configuration

Contract Functions​

Distribution Operations​

Execute Distribution (Paginated)​

Execute dividend/coupon payments to a page of holders:

function executeDistribution(
address _asset,
uint256 _distributionID,
uint256 _pageIndex,
uint256 _pageLength
) external returns (
address[] memory failed,
address[] memory succeeded,
uint256[] memory paidAmount,
bool hasMore
)

Parameters:

  • _asset: ATS token address
  • _distributionID: Distribution ID from ATS contract
  • _pageIndex: Starting index for pagination
  • _pageLength: Number of holders to process

Returns:

  • failed: Addresses where payment failed
  • succeeded: Addresses where payment succeeded
  • paidAmount: Amounts paid to each succeeded address
  • hasMore: True if more holders remain

Execute Distribution By Addresses​

Retry payments to specific addresses:

function executeDistributionByAddresses(
address _asset,
uint256 _distributionID,
address[] calldata _holders
) external returns (
address[] memory failed,
address[] memory succeeded,
uint256[] memory paidAmount
)

Use case: Retry failed payments from paginated execution

Bond Cash-Out Operations​

Execute Bond Cash-Out (Paginated)​

Execute bond maturity redemption:

function executeBondCashOut(
address _bond,
uint256 _pageIndex,
uint256 _pageLength
) external returns (
address[] memory failed,
address[] memory succeeded,
uint256[] memory paidAmount,
bool hasMore
)

Execute Bond Cash-Out By Addresses​

Cash out bonds for specific holders:

function executeBondCashOutByAddresses(
address _bond,
address[] calldata _holders
) external returns (
address[] memory failed,
address[] memory succeeded,
uint256[] memory paidAmount
)

Snapshot Operations​

Execute Amount Snapshot​

Pay fixed amount distributed proportionally:

function executeAmountSnapshot(
address _asset,
uint256 _snapshotID,
uint256 _pageIndex,
uint256 _pageLength,
uint256 _amount
) external returns (
address[] memory failed,
address[] memory succeeded,
uint256[] memory paidAmount,
bool hasMore
)

Parameters:

  • _amount: Total amount to distribute proportionally based on holdings

Example: Distribute $10,000 proportionally to all holders based on their token balance percentage

Execute Percentage Snapshot​

Pay percentage of contract balance:

function executePercentageSnapshot(
address _asset,
uint256 _snapshotID,
uint256 _pageIndex,
uint256 _pageLength,
uint256 _percentage
) external returns (
address[] memory failed,
address[] memory succeeded,
uint256[] memory paidAmount,
bool hasMore
)

Parameters:

  • _percentage: Percentage of contract balance to distribute (in basis points, e.g., 1000 = 10%)

Example: Distribute 5% of contract's payment token balance to holders

By Addresses Variants​

Both snapshot types also have ByAddresses variants for retrying specific holders.

Payment Token Management​

Get Payment Token​

function getPaymentToken() external view returns (IERC20)

Returns the current payment token address (e.g., USDC).

Get Payment Token Decimals​

function getPaymentTokenDecimals() external view returns (uint8)

Returns decimals of the payment token (e.g., 6 for USDC).

Update Payment Token​

function updatePaymentToken(address paymentToken) external

Requires: PAYMENT_TOKEN_MANAGER_ROLE

Change the payment token used for distributions.

Transfer Payment Token​

function transferPaymentToken(address to, uint256 amount) external

Requires: TRANSFERER_ROLE

Transfer payment tokens from contract to another address.

Events​

DistributionExecuted​

event DistributionExecuted(
uint256 distributionID,
uint256 pageIndex,
uint256 pageLength,
address[] failed,
address[] succeeded,
uint256[] paidAmount
)

Emitted when a distribution page is executed.

CashOutExecuted​

event CashOutExecuted(
uint256 pageIndex,
uint256 pageLength,
address[] failed,
address[] succeeded,
uint256[] paidAmount
)

Emitted when a bond cash-out page is executed.

PaymentTokenChanged​

event PaymentTokenChanged(address paymentToken)

Emitted when payment token is updated.

Pagination Strategy​

For large holder lists, use pagination:

uint256 pageIndex = 0;
uint256 pageLength = 100;
bool hasMore = true;

while (hasMore) {
(
address[] memory failed,
address[] memory succeeded,
uint256[] memory paid,
bool more
) = contract.executeDistribution(asset, distributionID, pageIndex, pageLength);

hasMore = more;
pageIndex += pageLength;

// Process results...
}

Integration with ATS​

The contract integrates with ATS tokens:

  1. Distribution Data: Fetches distribution details from ATS contract
  2. Holder Lists: Retrieves token holders from ATS
  3. Balance Queries: Checks holder balances for proportional distribution

Security Features​

  1. Role-Based Access: Only authorized accounts can execute operations
  2. Pausability: Emergency pause for all operations
  3. Upgradeability: Fix bugs without redeploying
  4. Event Logging: Full audit trail of all operations
  5. Failure Tracking: Failed payments don't block successful ones

Gas Considerations​

  • Page Size: Recommended 50-100 holders per page
  • Large Distributions: Use multiple transactions for >100 holders
  • Failed Payments: Track and retry individually

Best Practices​

  1. Test on Testnet: Always test full distribution flow before mainnet
  2. Monitor Events: Subscribe to contract events for real-time tracking
  3. Handle Failures: Implement retry logic for failed payments
  4. Check Balances: Ensure contract has sufficient payment tokens
  5. Use Pagination: Don't execute >100 holders in single transaction