Source: src/dividend_distributor.rs
The DividendDistributor contract manages yield distributions across multiple supported currencies. It supports:
- Multi-Currency Distributions — Create and claim dividends in various currencies (USDC, EURC, etc.)
- Configurable Fee Rates — Protocol fee taken from each claim (in basis points)
- Auto-Yield Distribution — Optional automatic dividend creation on a configurable cadence
- Claim Tracking — Per-user, per-distribution claim records prevent double-claims
- Distribution Lifecycle — Active/inactive states, deadlines, and deactivation
┌──────────────────────┐
│ DividendDistributor │
└──────────┬───────────┘
│
├──create_distribution()──▶ Stores DividendDistribution
│
├──claim_dividend()──▶ Checks RWAToken balance
│ Transfers currency token (minus fee)
│
└──auto_yield_distribute()──▶ Timer-based distribution creation
| Variant | Code | Description |
|---|---|---|
AlreadyInitialized |
401 | Distributor already initialized |
NotInitialized |
402 | Not yet initialized |
ConfigNotFound |
403 | Configuration not found |
InsufficientFunds |
404 | Not enough funds for distribution |
InvalidAmount |
405 | Zero or negative amount |
DistributionNotFound |
406 | Distribution ID not found |
AlreadyClaimed |
407 | User already claimed |
UnsupportedCurrency |
408 | Currency not supported |
DistributionNotActive |
409 | Distribution is inactive |
AutoDistributionDisabled |
410 | Auto-distribution not enabled |
YieldCadenceNotReached |
411 | Too soon since last distribution |
ZeroTotalSupply |
412 | Token supply is zero |
NoTokensToClaim |
413 | User holds no tokens |
NoDividendAvailable |
414 | No dividend to distribute |
Unauthorized |
415 | Caller not authorized |
| Field | Type | Description |
|---|---|---|
distribution_id |
u64 |
Unique distribution ID |
token_address |
Address |
Associated RWA token |
currency |
Symbol |
Distribution currency |
total_amount |
i128 |
Total distribution amount |
remaining_amount |
i128 |
Amount not yet claimed |
claim_deadline |
u64 |
Deadline timestamp |
is_active |
bool |
Active status |
created_at |
u64 |
Creation timestamp |
metadata |
Map<Symbol, Symbol> |
Arbitrary metadata |
| Field | Type | Description |
|---|---|---|
supported_currencies |
Vec<Symbol> |
Active currencies |
auto_distribute |
bool |
Enable auto-distribution |
min_distribution_amount |
i128 |
Minimum distribution amount |
max_distribution_frequency |
u64 |
Minimum seconds between distributions |
fee_rate |
i64 |
Protocol fee in bps (e.g., 50 = 0.5%) |
fee_recipient |
Address |
Fee collection address |
| Field | Type | Description |
|---|---|---|
claimed |
bool |
Whether claimed |
amount |
i128 |
Amount claimed |
claimed_at |
u64 |
Claim timestamp |
Initializes the dividend distributor with default config.
| Parameter | Type | Description |
|---|---|---|
env |
Env |
Soroban environment |
auth |
Address |
Authorizing address |
admin |
Address |
Admin address |
supported_currencies |
Vec<Symbol> |
Initial supported currencies |
Auth: auth.require_auth()
Returns: Nothing
Errors:
AlreadyInitialized— Already initialized
Example:
let mut currencies = Vec::new(&env);
currencies.push_back(Symbol::new(&env, "USDC"));
currencies.push_back(Symbol::new(&env, "EURC"));
let dist_id = env.register_contract(None, DividendDistributor);
let distributor = DividendDistributorClient::new(&env, &dist_id);
distributor.initialize(&admin, &admin, ¤cies);Admin storage migration.
| Parameter | Type | Description |
|---|---|---|
env |
Env |
Soroban environment |
auth |
Address |
Admin address |
Auth: Admin check
Maps a currency symbol to its Stellar token contract address.
| Parameter | Type | Description |
|---|---|---|
env |
Env |
Soroban environment |
auth |
Address |
Admin address |
currency |
Symbol |
Currency symbol (e.g., "USDC") |
token_address |
Address |
Stellar asset contract address |
Auth: Admin check
Example:
distributor.register_currency_token(
&admin,
&Symbol::new(&env, "USDC"),
&usdc_token_address,
);Creates a single dividend distribution for a token in a specific currency.
| Parameter | Type | Description |
|---|---|---|
env |
Env |
Soroban environment |
auth |
Address |
Admin address |
token_address |
Address |
RWA token address |
currency |
Symbol |
Distribution currency |
amount |
i128 |
Total distribution amount (> 0) |
claim_deadline |
u64 |
Deadline timestamp |
metadata |
Map<Symbol, Symbol> |
Arbitrary metadata |
Auth: Admin check
Returns: u64 — New distribution ID
Errors:
InvalidAmount—amount <= 0UnsupportedCurrency— Currency not registered
Events:
distribution_createdwith topics(distribution_id, token_address, currency, amount)
Example:
let deadline = env.ledger().timestamp() + 86400 * 30; // 30 days
let id = distributor.create_distribution(
&admin,
&token_address,
&Symbol::new(&env, "USDC"),
&10_000i128,
&deadline,
&Map::new(&env),
);multi_ccy_distributions(env, auth, token_address, currencies, amounts, claim_deadline, metadata) -> Vec<u64>
Creates multiple distributions across different currencies in one call.
| Parameter | Type | Description |
|---|---|---|
currencies |
Vec<Symbol> |
Currency symbols |
amounts |
Vec<i128> |
Corresponding amounts |
Returns: Vec<u64> — Distribution IDs
Creates distributions only if auto-distribute is enabled and cadence is met.
Errors:
AutoDistributionDisabled— Not enabled in configYieldCadenceNotReached— Too soon since last distribution
Claims a dividend for a specific distribution. Amount is proportional to the claimer's token balance at claim time, minus protocol fee.
| Parameter | Type | Description |
|---|---|---|
env |
Env |
Soroban environment |
distribution_id |
u64 |
Distribution to claim from |
claimer |
Address |
Claimer address |
Returns: i128 — Net amount claimed (after fee)
Errors:
DistributionNotFound— Invalid IDAlreadyClaimed— Already claimedDistributionNotActive— DeactivatedNoTokensToClaim— Zero token balance- Claim deadline passed
Events:
dividend_claimedwith topics(distribution_id, claimer, amount, fee)
Example:
let claimed_amount = distributor.claim_dividend(&distribution_id, &claimer);
// claimed_amount = gross * holder_percentage * (1 - fee_rate/10000)Claims all eligible dividends for a claimer across all active distributions.
| Parameter | Type | Description |
|---|---|---|
env |
Env |
Soroban environment |
claimer |
Address |
Claimer address |
Returns: Vec<i128> — Amounts claimed per distribution
Returns distribution metadata.
| Parameter | Type | Description |
|---|---|---|
env |
Env |
Soroban environment |
distribution_id |
u64 |
Distribution ID |
Returns: DividendDistribution
Errors:
DistributionNotFound
Returns all active distributions (not expired, not deactivated) for a given token.
| Parameter | Type | Description |
|---|---|---|
env |
Env |
Soroban environment |
token_address |
Address |
RWA token address |
Returns: Vec<DividendDistribution>
Returns claim state for a user-distribution pair, if claimed.
| Parameter | Type | Description |
|---|---|---|
distribution_id |
u64 |
Distribution ID |
claimer |
Address |
Claimer address |
Returns: Option<ClaimInfo>
Computes the claimable amount for a user without actually claiming.
| Parameter | Type | Description |
|---|---|---|
distribution_id |
u64 |
Distribution ID |
claimer |
Address |
Claimer address |
Returns: i128 — Available dividend (0 if already claimed or no tokens)
Updates the distribution configuration.
| Parameter | Type | Description |
|---|---|---|
env |
Env |
Soroban environment |
auth |
Address |
Admin address |
config |
DividendConfig |
New configuration |
Auth: Admin check
Admin deactivates a distribution, preventing further claims.
| Parameter | Type | Description |
|---|---|---|
env |
Env |
Soroban environment |
auth |
Address |
Admin address |
distribution_id |
u64 |
Distribution to deactivate |
Auth: Admin check
| Event | Topics | Emitted By |
|---|---|---|
initialized |
admin |
initialize() |
migrated |
old_version, new_version |
migrate() |
distribution_created |
distribution_id, token_address, currency, amount |
create_distribution() |
dividend_claimed |
distribution_id, claimer, amount, fee |
claim_dividend() |
config_updated |
fee_rate, auto_distribute |
update_config() |
distribution_deactivated |
distribution_id |
deactivate_distribution() |
gross_amount = total_distribution_amount × (claimer_balance / total_supply)
fee = gross_amount × (fee_rate_bps / 10000)
net_amount = gross_amount - fee
The protocol fee is deducted from each claim and sent to fee_recipient.