Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion contracts/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion contracts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ members = [
"workspace_booking",
"payment_escrow",
"resource_credits",
"staking_rewards",
"subscription_tier",
"attendance_batch",
"token_upgrade",
"pause_control",
]

[workspace.dependencies]
Expand All @@ -28,4 +33,4 @@ lto = true
# For more information about this profile see https://soroban.stellar.org/docs/basic-tutorials/logging#cargotoml-profile
[profile.release-with-logs]
inherits = "release"
debug-assertions = true
debug-assertions = true
149 changes: 149 additions & 0 deletions contracts/EVENT_MIGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# Event Migration Audit

## Overview

This document audits all `env.events().publish()` usage across the ManageHub contracts
and identifies migration targets for the `#[contractevent]` attribute macro.

## Migration Pattern

The deprecated pattern:
```rust
env.events().publish(
(symbol_short!("event_name"), key1, key2),
(data1, data2),
);
```

Should be migrated to:
```rust
#[contractevent]
pub struct EventName {
pub key1: Address,
pub key2: BytesN<32>,
pub data1: u64,
pub data2: i128,
}
```

## Event Inventory

### `manage_hub` Contract

| Location | Event Symbol | Keys | Data | Migration Status |
|----------|-------------|------|------|-----------------|
| `membership_token.rs:122` | `token_iss` | id, user | admin, timestamp, expiry, status | Candidate |
| `membership_token.rs:198` | `token_xfr` | id, new_user | old_user, timestamp | Candidate |
| `membership_token.rs:225` | `tok_sale` | id, new_user | sale_price, timestamp | Candidate |
| `membership_token.rs:320-327` | `token_xfr`/`token_dlg` | id, to/spender | old_user, timestamp, allowance | Candidate |
| `membership_token.rs:390` | `admin_set` | admin | timestamp | Candidate |
| `membership_token.rs:604` | `meta_set` | token_id, version | caller, timestamp | Candidate |
| `membership_token.rs:743` | `meta_upd` | token_id, version | caller, timestamp | Candidate |
| `membership_token.rs:819` | `meta_rmv` | token_id, version | caller, timestamp | Candidate |
| `membership_token.rs:915` | `rnw_cfg` | admin | grace_period, notice_days, enabled | Candidate |
| `membership_token.rs:1064` | `token_rnw` | id, user | payment_token, amount, old_expiry, new_expiry | Candidate |
| `membership_token.rs:1145` | `grace_in` | id, user | entered_at, expires_at | Candidate |
| `membership_token.rs:1198` | `auto_rnw` | token_id, user | enabled, payment_token | Candidate |
| `membership_token.rs:1343` | `auto_ok` | id, user | payment_token, amount, old_expiry, new_expiry | Candidate |
| `membership_token.rs:1401` | `emg_pause` | admin | timestamp, reason, auto_unpause_at, time_lock_until | Candidate |
| `membership_token.rs:1450` | `emg_unp` | admin | timestamp | Candidate |
| `membership_token.rs:1520` | `tok_pause` | token_id, admin | timestamp, reason | Candidate |
| `membership_token.rs:1568` | `tok_unp` | token_id, admin | timestamp | Candidate |
| `membership_token.rs:1603` | `grace_ar` | id, user | entered_at, expires_at, reason | Candidate |
| `allowance.rs:49` | `Approval` | token_id, owner, spender | amount, expires_at, timestamp | Candidate |
| `allowance.rs:71` | `AllowanceRevoked` | token_id, owner, spender | timestamp | Candidate |
| `allowance.rs:141` | `AllowanceUsed` | token_id, owner, spender | amount, remaining, timestamp | Candidate |
| `fractionalization.rs:65` | `Fractionalized` | token_id, user | total_shares, min_fraction, timestamp | Candidate |
| `fractionalization.rs:126` | `FractionTransferred` | token_id, from | to, share_amount, timestamp | Candidate |
| `fractionalization.rs:172` | `Recombined` | token_id, holder | timestamp | Candidate |
| `fractionalization.rs:289` | `DividendDistributed` | token_id, admin | total_amount, recipients, timestamp | Candidate |
| `royalty.rs:46` | `roy_set` | token_id | recipient_count, timestamp | Candidate |
| `royalty.rs:92` | `roy_paid` | token_id, recipient | payment_token, amount, timestamp | Candidate |
| `subscription.rs:172` | `sub_creat` | id, user | payment_token, amount, created_at, expires_at | Candidate |
| `subscription.rs:272` | `subscr` | id, user | PauseHistoryEntry | Candidate |
| `subscription.rs:372` | `subscr` | id, user | PauseHistoryEntry | Candidate |
| `subscription.rs:427` | `usdc_set` | usdc_address | admin, timestamp | Candidate |
| `subscription.rs:463` | `sub_cancl` | id, user | timestamp, old_status, new_status | Candidate |
| `subscription.rs:535` | `sub_renew` | id, user | payment_token, amount, old_expiry, new_expiry | Candidate |
| `subscription.rs:693` | `tier_crt` | tier_id, admin | name, level, price, timestamp | Candidate |
| `subscription.rs:747` | `tier_upd` | tier_id, admin | timestamp | Candidate |
| `subscription.rs:814` | `tier_dea` | tier_id, admin | timestamp | Candidate |
| `subscription.rs:902` | `sub_creat` | id, user | tier_id, final_price, created_at, expires_at | Candidate |
| `subscription.rs:1011` | `tier_chg` | change_id, user | from_tier, to_tier, change_type, prorated_amount | Candidate |
| `subscription.rs:1090` | `tier_cmp` | change_id, user | old_tier, new_tier, prorated_amount | Candidate |
| `subscription.rs:1135` | `tier_cnc` | change_id, user | timestamp | Candidate |
| `subscription.rs:1198` | `promo_cr` | promo_id, admin | tier_id, discount, start_date, end_date | Candidate |
| `batch.rs:25` | `bat_mint` | (none) | count, timestamp | Candidate |
| `batch.rs:44` | `bat_xfr` | (none) | count, timestamp | Candidate |
| `batch.rs:63` | `bat_upd` | (none) | count, timestamp | Candidate |
| `upgrade.rs:161` | `TokenUpgraded` | token_id, caller | from_version, to_version | Candidate |
| `attendance_log.rs:84` | `attend` | id, user_id | action | Candidate |

### `resource_credits` Contract

| Location | Event Symbol | Keys | Data | Migration Status |
|----------|-------------|------|------|-----------------|
| `lib.rs:80` | `mint` | recipient | amount | **Migrated below** |
| `lib.rs:121` | `transfer` | from, to | amount | **Migrated below** |
| `lib.rs:157` | `spend` | member | amount | **Migrated below** |

### `workspace_booking` Contract

| Location | Event Symbol | Keys | Data | Migration Status |
|----------|-------------|------|------|-----------------|
| `lib.rs:114` | `init` | (none) | admin, payment_token | **Migrated below** |
| `lib.rs:175` | `ws_reg` | id | name, type, capacity, hourly_rate | **Migrated below** |
| `lib.rs:207` | `ws_avail` | workspace_id | is_available | **Migrated below** |
| `lib.rs:236` | `ws_rate` | workspace_id | hourly_rate | **Migrated below** |
| `lib.rs:344` | `booked` | booking_id | member, workspace_id, start, end, amount | **Migrated below** |
| `lib.rs:385` | `cancel` | booking_id | caller, refund_amount | **Migrated below** |
| `lib.rs:414` | `complete` | booking_id | workspace_id, member | **Migrated below** |

### `payment_escrow` Contract

| Location | Event Symbol | Keys | Data | Migration Status |
|----------|-------------|------|------|-----------------|
| `lib.rs:114` | `init` | (none) | admin, payment_token, dispute_window | Candidate |
| `lib.rs:131` | `dw_set` | (none) | window_secs | Candidate |
| `lib.rs:218` | `created` | escrow_id | depositor, beneficiary, amount, release_after | Candidate |
| `lib.rs:247` | `released` | escrow_id | beneficiary, amount | Candidate |
| `lib.rs:274` | `refunded` | escrow_id | depositor, amount | Candidate |
| `lib.rs:313` | `disputed` | escrow_id | depositor, timestamp | Candidate |
| `lib.rs:358` | `resolved` | escrow_id | recipient, amount, release_to_beneficiary | Candidate |
| `lib.rs:400` | `claimed` | escrow_id | beneficiary, amount | Candidate |

## Migration Recommendations

### High Priority (value-handling contracts)

1. **`resource_credits`** - Credit operations (mint, transfer, spend) should use
`#[contractevent]` for reliable off-chain indexing. These events carry financial
data that indexers must not miss.

2. **`workspace_booking`** - Booking events (booked, cancel, complete) should use
`#[contractevent]` for reliable booking lifecycle tracking.

3. **`manage_hub` subscription events** - Payment-related events (`sub_creat`,
`sub_renew`, `sub_cancl`, `tier_crt`) should be migrated early.

### Medium Priority (operational events)

4. **`manage_hub` token events** - Token lifecycle events (`token_iss`, `token_xfr`,
`token_rnw`) are important but less time-sensitive.

5. **`payment_escrow`** - Escrow events are important for fund tracking.

### Low Priority (diagnostic events)

6. **Analytics/admin events** - Tier analytics updates, metadata changes, and
configuration events can be migrated later.

## Migration Pattern for Remaining Contracts

For contracts not yet migrated, maintain consistency by keeping `#[allow(deprecated)]`
at the top of the file and documenting the planned migration timeline. When migrating:

1. Define the event struct with `#[contractevent]` (must be in a `#[contract]` module)
2. Use typed fields instead of tuples
3. The event name defaults to the struct name (PascalCase → SnakeCase for topic)
4. Ensure all fields are `IntoVal`/`FromVal` compatible with Soroban
123 changes: 123 additions & 0 deletions contracts/MONOLITH_SPLIT_PLAN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# ManageHub Monolith Split Plan

## Current State

The `manage_hub` contract is a monolith with **11,018 lines** across 20 modules.
All logic lives in a single `#[contract]` with a single `#[contractimpl]` block.

## Proposed Crate Extraction

### Group 1: `membership_token` (expand existing crate)
**Modules:** `membership_token.rs` (1,614 lines), `allowance.rs` (160 lines), `fractionalization.rs` (337 lines)
**New crate:** `contracts/membership_token/` (expand existing)

**Rationale:** Core token logic (issue, transfer, metadata, renewal, allowance, fractionalization)
forms a cohesive unit. The existing `membership_token` crate already provides a basic
standalone contract; this expansion brings in the full feature set from the monolith.

**Dependencies on manage_hub:** Uses `errors::Error`, `guards::PauseGuard`, `types::*`.
After extraction, these will be local to the crate.

### Group 2: `staking_rewards` (new crate)
**Modules:** `staking.rs` (396 lines), `rewards.rs` (65 lines), `staking_errors.rs` (43 lines)
**New crate:** `contracts/staking_rewards/`

**Rationale:** Staking and rewards form a self-contained subsystem. They share storage keys,
types, and error handling but have minimal coupling to the rest of the monolith.

**Dependencies on manage_hub:** Uses `membership_token::DataKey::Admin` for admin checks,
`types::StakeInfo`, `types::StakingConfig`, `types::StakingTier`. These will be moved to
the new crate.

### Group 3: `subscription_tier` (stub)
**Modules:** `subscription.rs` (1,489 lines)
**Proposed crate:** `contracts/subscription_tier/`

**Rationale:** Subscription management with tier support is large enough to warrant
its own crate. It includes tier CRUD, promotions, feature access, analytics, and pause/resume.

**Key dependencies:** `membership_token::DataKey::Admin`, `attendance_log::AttendanceLogModule`,
`types::*`. Would need shared types in `common_types`.

### Group 4: `attendance_batch` (stub)
**Modules:** `attendance_log.rs` (531 lines), `batch.rs` (72 lines), `validation.rs` (22 lines)
**Proposed crate:** `contracts/attendance_batch/`

**Rationale:** Attendance logging with batch operations and analytics is a natural group.
Batch operations call into membership_token, so the interface boundary would need
cross-contract calls or trait abstractions.

### Group 5: `token_upgrade` (stub)
**Modules:** `upgrade.rs` (430 lines), `migration.rs` (159 lines), `upgrade_errors.rs` (43 lines)
**Proposed crate:** `contracts/token_upgrade/`

**Rationale:** Token versioning, snapshots, and rollback logic is self-contained except
for its dependency on `MembershipToken` type and `DataKey::Admin`.

### Group 6: `pause_control` (stub)
**Modules:** `guards.rs` (130 lines), `pause_errors.rs` (32 lines)
**Proposed crate:** `contracts/pause_control/`

**Rationale:** Pause state management is small but conceptually independent. Could be
shared across multiple contracts.

## Dependency Graph

```
common_types
|
+-------+-------+-------+-------+
| | | | |
pause_control | staking_rewards token_upgrade
| | | | |
v v v v v
membership_token <--- subscription_tier
| ^ ^
| | |
v | |
attendance_batch royalty
```

## Extraction Phases

### Phase 1 (This PR)
- [x] Expand `membership_token` crate with full types from monolith
- [x] Create `staking_rewards` crate
- [x] Create stub crates for remaining groups
- [x] Update workspace `Cargo.toml`
- [x] Document inter-module dependencies

### Phase 2 (Future)
- [ ] Extract `subscription_tier` crate with full subscription logic
- [ ] Extract `attendance_batch` crate
- [ ] Extract `token_upgrade` crate
- [ ] Extract `pause_control` crate
- [ ] Convert monolith to a thin facade that calls extracted crates

### Phase 3 (Future)
- [ ] Replace cross-crate type references with shared `common_types`
- [ ] Add integration tests across extracted crates
- [ ] Remove monolith facade entirely

## Shared Types (move to `common_types`)

Types that are used across multiple proposed crates should live in `common_types`:
- `MembershipStatus` (already in common_types)
- `StakeInfo`, `StakingConfig`, `StakingTier`
- `Subscription`, `SubscriptionTier`, `BillingCycle`
- `AttendanceAction`, `AttendanceSummary`
- `EmergencyPauseState`, `TokenPauseState`
- `UpgradeConfig`, `UpgradeRecord`, `TokenVersionSnapshot`
- `RenewalConfig`, `RenewalHistory`, `AutoRenewalSettings`
- `TokenAllowance`, `RoyaltyConfig`, `RoyaltyInfo`
- `FractionalTokenInfo`, `FractionHolder`, `DividendDistribution`
- All error enums

## Risks and Mitigations

| Risk | Mitigation |
|------|-----------|
| Breaking existing deployments | Keep monolith as facade; new crates are additive |
| Circular dependencies | Phase extraction carefully; use shared types crate |
| Test coverage gaps | Run existing test suite after each extraction |
| Storage key conflicts | Use crate-prefixed storage keys in new crates |
Loading
Loading