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
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ cp .env.example .env
| Variable | Description | Example Value |
| -------------------- | ----------------------------------------- | ----------------------------------------------------------------- |
| `STELLAR_NETWORK` | `testnet` or `public` Stellar network | `testnet` |
| `CONTRACT_ID` | Deployed Soroban subscription contract ID | `CB64...` (your deployed contract address) |
| `CONTRACT_ID` | Deployed SubTrackr proxy contract ID (stable) | `CB64...` (your deployed proxy contract address) |
| `WEB3AUTH_CLIENT_ID` | Web3Auth client ID for social login | Get one from [Web3Auth Dashboard](https://dashboard.web3auth.io/) |

### 4. Run the Mobile App
Expand All @@ -154,16 +154,17 @@ You can then run the app on:
If you want to work on the smart contracts:

```bash
# Navigate to contracts directory
cd contracts
# Local (requires local Soroban network + `alice` identity)
./scripts/deploy-local.sh

# Build the contract
cargo build --target wasm32-unknown-unknown --release

# Deploy to Stellar testnet
soroban contract deploy --wasm target/wasm32-unknown-unknown/release/subtrackr.wasm --network testnet
# Testnet
export SOROBAN_ACCOUNT="your-testnet-identity"
export ADMIN_ADDRESS="GB..."
./scripts/deploy-testnet.sh
```

SubTrackr uses an upgradeable architecture (proxy + storage + implementation). Use the deployed `PROXY_ID` (saved to `contracts/.env.<network>`) as the stable contract ID.

### 6. Run Tests

Run the test suite to ensure everything is working correctly:
Expand Down
23 changes: 8 additions & 15 deletions contracts/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
[package]
name = "subtrackr"
version = "0.1.0"
edition = "2021"
authors = ["SubTrackr Team"]
description = "On-chain subscription management on Stellar/Soroban"

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
soroban-sdk = "21.0.0"

[dev-dependencies]
soroban-sdk = { version = "21.0.0", features = ["testutils"] }
[workspace]
resolver = "2"
members = [
"proxy",
"storage",
"subscription",
"types",
]

[profile.release]
opt-level = "z"
Expand Down
83 changes: 73 additions & 10 deletions contracts/DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

This guide describes how to deploy SubTrackr smart contracts to various Stellar networks using the provided automation scripts.

## Contract Architecture (Upgradeable)

SubTrackr is deployed as three Soroban contracts:

- **Proxy** (`subtrackr-proxy` / `UpgradeableProxy`): Stable contract ID used by the app. Manages upgrades (timelock, history, rollback scheduling) and forwards calls to the current implementation.
- **Storage** (`subtrackr-storage` / `SubTrackrStorage`): Holds all subscription state (plans, subscriptions, indexes). Only the currently-authorized implementation can write to it.
- **Implementation** (`subtrackr-subscription` / `SubTrackrSubscription`): Business logic. Can be upgraded by deploying a new implementation contract and updating the proxy.

The **proxy contract ID never changes** during upgrades, so subscribers and integrators don’t need to update addresses.

## Prerequisites

- [Soroban CLI](https://developers.stellar.org/docs/smart-contracts/getting-started/setup#install-the-soroban-cli) installed.
Expand Down Expand Up @@ -50,16 +60,18 @@ export ADMIN_ADDRESS="GD..."
| ----------------- | ---------------------------------------------------------------------------------- | ---------------- |
| `SOROBAN_ACCOUNT` | The identity name (configured in Soroban CLI) or secret key to use for deployment. | Testnet, Mainnet |
| `ADMIN_ADDRESS` | The Stellar address that will be set as the contract admin during initialization. | Testnet, Mainnet |
| `UPGRADE_DELAY_SECS` | Minimum delay (seconds) between scheduling and executing an upgrade. | Testnet, Mainnet |
| `ROLLBACK_DELAY_SECS` | Delay (seconds) used when scheduling a rollback via `rollback()`. | Testnet, Mainnet |

## Verification

After deployment, you can verify that the contract is active by running:

```bash
./scripts/verify.sh <CONTRACT_ID> <NETWORK>
./scripts/verify.sh <PROXY_ID> <NETWORK> [SOURCE]
```

Replace `<CONTRACT_ID>` with the ID returned by the deployment script and `<NETWORK>` with `local`, `testnet`, or `public`.
Replace `<PROXY_ID>` with the proxy contract ID returned by the deployment script and `<NETWORK>` with `local`, `testnet`, or `public`.

### Explorer Source Verification

Expand All @@ -79,21 +91,72 @@ cargo build --release --target wasm32-unknown-unknown --manifest-path contracts/

This generates a tar.gz in `dist/` containing:
- `contracts/Cargo.toml`
- `contracts/src/**`
- `contracts/proxy/**`
- `contracts/storage/**`
- `contracts/subscription/**`
- `contracts/types/**`
- `WASM_SHA256.txt` (if a compiled WASM was found)

3) Upload the tar.gz bundle to your chosen explorer’s contract page (or submit via their form/API), referencing your deployed `CONTRACT_ID`.
3) Upload the tar.gz bundle to your chosen explorer’s contract page (or submit via their form/API), referencing your deployed `PROXY_ID` (and optionally the storage/implementation IDs).

Notes:
- Ensure the license header is present in your sources if required by the explorer.
- Keep optimizer/toolchain settings consistent across builds for reproducibility.

## Rollback Procedure
## Upgrade Procedure

### 1) Deploy a new implementation

Build and deploy the updated `subtrackr-subscription` contract.

You can use the helper script (deploy + schedule):

Since smart contracts on Soroban are immutable (unless explicitly designed otherwise), a "rollback" typically involves:
```bash
export SOROBAN_ACCOUNT="your-network-identity"
export ADMIN_ADDRESS="G..."
./scripts/upgrade-deploy-and-schedule.sh <PROXY_ID> <NETWORK>
```

This deploys a new implementation and schedules the upgrade via `authorize_upgrade`.

### 2) Wait for the timelock

1. Fixing the issue in the contract source code.
2. Deploying a new version of the contract.
3. Updating the application (frontend/backend) to use the new `CONTRACT_ID`.
Upgrades are timelocked. The proxy enforces:
- `execute_after >= now + upgrade_delay_secs`

Ensure you keep track of the `CONTRACT_ID` for each deployment (these are automatically saved to `contracts/.env.<network>`).
### 3) Execute the upgrade

```bash
./scripts/upgrade-execute.sh <PROXY_ID> <IMPLEMENTATION_ID> <NETWORK>
```

Execution calls `upgrade_to(implementation)` which:
- Updates the storage contract to authorize writes from the new implementation
- Runs `validate_upgrade(...)` and `migrate(...)` when needed
- Updates `get_version()` (storage schema version)
- Appends to upgrade history

### Storage Migrations & Versions

`get_version()` on the proxy represents the **storage schema version**.

When changing storage layout between versions:
- Bump the implementation’s `STORAGE_VERSION`
- Implement `migrate(proxy, storage, from_version)`
- Keep migrations **forward-only** and deterministic

## Rollback Procedure (Timelocked)

If the latest implementation is faulty, the proxy can schedule a rollback to the immediately-previous implementation:

1) Schedule rollback:

```bash
./scripts/rollback-schedule.sh <PROXY_ID> <NETWORK>
```

2) After the rollback delay elapses, execute the scheduled rollback with `upgrade_to(...)`.

Notes:
- Rollback changes the **implementation**, not the already-applied storage schema.
- Keep older implementations forward-compatible when possible (e.g., additive storage changes).
18 changes: 18 additions & 0 deletions contracts/proxy/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "subtrackr-proxy"
version = "0.2.0"
edition = "2021"
authors = ["SubTrackr Team"]
description = "Upgradeable proxy for SubTrackr (Soroban)"

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
soroban-sdk = "21.0.0"
subtrackr-types = { path = "../types" }

[dev-dependencies]
soroban-sdk = { version = "21.0.0", features = ["testutils"] }
subtrackr-subscription = { path = "../subscription" }
subtrackr-storage = { path = "../storage" }
Loading