Skip to content

Commit 0a35fe2

Browse files
authored
Merge pull request #282 from Edoscoba/176-upgradeable-proxy
feat(contracts): proxy-based contract upgradability (#176)
2 parents 178873d + 0abe19b commit 0a35fe2

29 files changed

Lines changed: 35614 additions & 1258 deletions

README.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ cp .env.example .env
128128
| Variable | Description | Example Value |
129129
| -------------------- | ----------------------------------------- | ----------------------------------------------------------------- |
130130
| `STELLAR_NETWORK` | `testnet` or `public` Stellar network | `testnet` |
131-
| `CONTRACT_ID` | Deployed Soroban subscription contract ID | `CB64...` (your deployed contract address) |
131+
| `CONTRACT_ID` | Deployed SubTrackr proxy contract ID (stable) | `CB64...` (your deployed proxy contract address) |
132132
| `WEB3AUTH_CLIENT_ID` | Web3Auth client ID for social login | Get one from [Web3Auth Dashboard](https://dashboard.web3auth.io/) |
133133

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

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

160-
# Build the contract
161-
cargo build --target wasm32-unknown-unknown --release
162-
163-
# Deploy to Stellar testnet
164-
soroban contract deploy --wasm target/wasm32-unknown-unknown/release/subtrackr.wasm --network testnet
160+
# Testnet
161+
export SOROBAN_ACCOUNT="your-testnet-identity"
162+
export ADMIN_ADDRESS="GB..."
163+
./scripts/deploy-testnet.sh
165164
```
166165

166+
SubTrackr uses an upgradeable architecture (proxy + storage + implementation). Use the deployed `PROXY_ID` (saved to `contracts/.env.<network>`) as the stable contract ID.
167+
167168
### 6. Run Tests
168169

169170
Run the test suite to ensure everything is working correctly:

contracts/Cargo.toml

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,11 @@
1-
[package]
2-
name = "subtrackr"
3-
version = "0.1.0"
4-
edition = "2021"
5-
authors = ["SubTrackr Team"]
6-
description = "On-chain subscription management on Stellar/Soroban"
7-
8-
[lib]
9-
crate-type = ["cdylib", "rlib"]
10-
11-
[dependencies]
12-
soroban-sdk = "21.0.0"
13-
14-
[dev-dependencies]
15-
soroban-sdk = { version = "21.0.0", features = ["testutils"] }
1+
[workspace]
2+
resolver = "2"
3+
members = [
4+
"proxy",
5+
"storage",
6+
"subscription",
7+
"types",
8+
]
169

1710
[profile.release]
1811
opt-level = "z"

contracts/DEPLOYMENT.md

Lines changed: 73 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22

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

5+
## Contract Architecture (Upgradeable)
6+
7+
SubTrackr is deployed as three Soroban contracts:
8+
9+
- **Proxy** (`subtrackr-proxy` / `UpgradeableProxy`): Stable contract ID used by the app. Manages upgrades (timelock, history, rollback scheduling) and forwards calls to the current implementation.
10+
- **Storage** (`subtrackr-storage` / `SubTrackrStorage`): Holds all subscription state (plans, subscriptions, indexes). Only the currently-authorized implementation can write to it.
11+
- **Implementation** (`subtrackr-subscription` / `SubTrackrSubscription`): Business logic. Can be upgraded by deploying a new implementation contract and updating the proxy.
12+
13+
The **proxy contract ID never changes** during upgrades, so subscribers and integrators don’t need to update addresses.
14+
515
## Prerequisites
616

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

5466
## Verification
5567

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

5870
```bash
59-
./scripts/verify.sh <CONTRACT_ID> <NETWORK>
71+
./scripts/verify.sh <PROXY_ID> <NETWORK> [SOURCE]
6072
```
6173

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

6476
### Explorer Source Verification
6577

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

8092
This generates a tar.gz in `dist/` containing:
8193
- `contracts/Cargo.toml`
82-
- `contracts/src/**`
94+
- `contracts/proxy/**`
95+
- `contracts/storage/**`
96+
- `contracts/subscription/**`
97+
- `contracts/types/**`
8398
- `WASM_SHA256.txt` (if a compiled WASM was found)
8499

85-
3) Upload the tar.gz bundle to your chosen explorer’s contract page (or submit via their form/API), referencing your deployed `CONTRACT_ID`.
100+
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).
86101

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

91-
## Rollback Procedure
106+
## Upgrade Procedure
107+
108+
### 1) Deploy a new implementation
109+
110+
Build and deploy the updated `subtrackr-subscription` contract.
111+
112+
You can use the helper script (deploy + schedule):
92113

93-
Since smart contracts on Soroban are immutable (unless explicitly designed otherwise), a "rollback" typically involves:
114+
```bash
115+
export SOROBAN_ACCOUNT="your-network-identity"
116+
export ADMIN_ADDRESS="G..."
117+
./scripts/upgrade-deploy-and-schedule.sh <PROXY_ID> <NETWORK>
118+
```
119+
120+
This deploys a new implementation and schedules the upgrade via `authorize_upgrade`.
121+
122+
### 2) Wait for the timelock
94123

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

99-
Ensure you keep track of the `CONTRACT_ID` for each deployment (these are automatically saved to `contracts/.env.<network>`).
127+
### 3) Execute the upgrade
128+
129+
```bash
130+
./scripts/upgrade-execute.sh <PROXY_ID> <IMPLEMENTATION_ID> <NETWORK>
131+
```
132+
133+
Execution calls `upgrade_to(implementation)` which:
134+
- Updates the storage contract to authorize writes from the new implementation
135+
- Runs `validate_upgrade(...)` and `migrate(...)` when needed
136+
- Updates `get_version()` (storage schema version)
137+
- Appends to upgrade history
138+
139+
### Storage Migrations & Versions
140+
141+
`get_version()` on the proxy represents the **storage schema version**.
142+
143+
When changing storage layout between versions:
144+
- Bump the implementation’s `STORAGE_VERSION`
145+
- Implement `migrate(proxy, storage, from_version)`
146+
- Keep migrations **forward-only** and deterministic
147+
148+
## Rollback Procedure (Timelocked)
149+
150+
If the latest implementation is faulty, the proxy can schedule a rollback to the immediately-previous implementation:
151+
152+
1) Schedule rollback:
153+
154+
```bash
155+
./scripts/rollback-schedule.sh <PROXY_ID> <NETWORK>
156+
```
157+
158+
2) After the rollback delay elapses, execute the scheduled rollback with `upgrade_to(...)`.
159+
160+
Notes:
161+
- Rollback changes the **implementation**, not the already-applied storage schema.
162+
- Keep older implementations forward-compatible when possible (e.g., additive storage changes).

contracts/proxy/Cargo.toml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[package]
2+
name = "subtrackr-proxy"
3+
version = "0.2.0"
4+
edition = "2021"
5+
authors = ["SubTrackr Team"]
6+
description = "Upgradeable proxy for SubTrackr (Soroban)"
7+
8+
[lib]
9+
crate-type = ["cdylib", "rlib"]
10+
11+
[dependencies]
12+
soroban-sdk = "21.0.0"
13+
subtrackr-types = { path = "../types" }
14+
15+
[dev-dependencies]
16+
soroban-sdk = { version = "21.0.0", features = ["testutils"] }
17+
subtrackr-subscription = { path = "../subscription" }
18+
subtrackr-storage = { path = "../storage" }

0 commit comments

Comments
 (0)