Skip to content
Open
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
12 changes: 12 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,15 @@ jobs:
- name: Run tests
run: cargo test
working-directory: contract

- name: Install Stellar CLI
run: cargo install --locked stellar-cli

- name: Deploy and verify on Futurenet
run: |
./scripts/deploy.sh \
--network futurenet \
--source "ci-deployer-${{ github.run_id }}-${{ github.run_attempt }}" \
--out ./deployed-ci.json \
--env-out ./.env.deployed-ci
working-directory: contract
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ node_modules/

# Contract (Rust / Soroban)
contract/target/
contract/deployed.json
contract/.env.deployed
contract/deployed-ci.json
contract/.env.deployed-ci
contract/.stellar/

# Environment
.env
Expand Down
52 changes: 52 additions & 0 deletions contract/Cargo.lock

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

13 changes: 13 additions & 0 deletions contract/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@ soroban-sdk = { version = "21.7.0", features = ["testutils"] }
[workspace.dependencies]
soroban-sdk = "21.7.0"

[workspace]
resolver = "2"
members = [
".",
"contracts/content-access",
"contracts/creator-registry",
"contracts/earnings",
"contracts/myfans-lib",
"contracts/myfans-token",
"contracts/subscription",
"contracts/test-consumer",
]

[profile.release]
opt-level = "z"
overflow-checks = true
Expand Down
107 changes: 79 additions & 28 deletions contract/README.md
Original file line number Diff line number Diff line change
@@ -1,51 +1,102 @@
# MyFans Soroban Contracts

Soroban smart contracts for the MyFans decentralized content subscription platform.
Smart contracts and deployment automation for MyFans on Stellar/Soroban.

## Prerequisites
## Contracts deployed by script

- Rust 1.70+
- `soroban-cli` (install: `cargo install soroban-cli`)
1. `myfans-token`
2. `creator-registry`
3. `subscription`
4. `content-access`
5. `earnings`

## Structure
The deploy script applies this order to keep initialization/dependency flow deterministic.

```
contract/
├── Cargo.toml # Workspace root
└── contracts/
└── myfans-token/ # Stub contract
```
## Prerequisites

## Build
- Rust stable
- `stellar-cli` installed:

```bash
cd contract
cargo build
cargo install --locked stellar-cli
```

For optimized WASM:
## Network configuration

`contract/scripts/deploy.sh` supports:

- `--network futurenet`
- RPC: `https://rpc-futurenet.stellar.org:443`
- Passphrase: `Test SDF Future Network ; October 2022`
- `--network testnet`
- RPC: `https://rpc-testnet.stellar.org:443`
- Passphrase: `Test SDF Network ; September 2015`
- `--network mainnet`
- RPC: `https://rpc-mainnet.stellar.org:443`
- Passphrase: `Public Global Stellar Network ; September 2015`

You can override either value with:

- `--rpc-url <url>`
- `--network-passphrase <passphrase>`

## Funding account setup

The deploy script requires a Stellar identity (`--source`).
CLI state is stored locally in `contract/.stellar` by default (override with `STELLAR_STATE_DIR`).

- Futurenet/testnet:
- If the identity does not exist, the script generates it.
- The script funds it automatically (friendbot) unless `--no-fund` is set.
- Mainnet:
- Auto-generation/funding is disabled.
- Provide an existing funded source identity.

Useful commands:

```bash
cargo build --release --target wasm32-unknown-unknown
stellar keys generate myfans-deployer --network futurenet --fund
stellar keys public-key myfans-deployer
stellar keys fund myfans-deployer --network testnet
```

## Test
## One-command deploy

From repository root:

```bash
cd contract
cargo test
./contract/scripts/deploy.sh --network futurenet
```

## Deploy (Testnet)
Deploy to testnet:

```bash
soroban contract build
soroban contract deploy \
--wasm target/wasm32-unknown-unknown/release/myfans_token.wasm \
--network testnet
./contract/scripts/deploy.sh --network testnet --source myfans-testnet-deployer
```

## Next Steps
The script deploys all five contracts, initializes required contracts, then verifies each one by invoking a view method (`version`, `admin`, `has-access`).

## Output files

By default, deployment outputs are written to:

- `contract/deployed.json`
- `contract/.env.deployed`

Both include contract addresses/IDs and network metadata.

Override paths with:

- `--out <path>`
- `--env-out <path>`

## CI verification

GitHub Actions `contracts` job now includes:

1. Build (`cargo build --target wasm32-unknown-unknown --release`)
2. Test (`cargo test`)
3. Deploy on Futurenet (`./scripts/deploy.sh --network futurenet ...`)
4. Verify contract responses during deploy

- Implement subscription lifecycle contract
- Add payment routing and fee logic
- Add access control functions
If contract deploy or verification fails, CI fails.
13 changes: 13 additions & 0 deletions contract/contracts/creator-registry/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "creator-registry"
version = "0.1.0"
edition = "2021"

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

[dependencies]
soroban-sdk = { workspace = true }

[dev-dependencies]
soroban-sdk = { workspace = true, features = ["testutils"] }
41 changes: 41 additions & 0 deletions contract/contracts/creator-registry/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#![no_std]

use soroban_sdk::{contract, contractimpl, contracttype, Address, Env};

#[contracttype]
enum DataKey {
Admin,
Creator(Address),
}

#[contract]
pub struct CreatorRegistry;

#[contractimpl]
impl CreatorRegistry {
pub fn init(env: Env, admin: Address) {
if env.storage().instance().has(&DataKey::Admin) {
panic!("already initialized");
}

admin.require_auth();
env.storage().instance().set(&DataKey::Admin, &admin);
}

pub fn admin(env: Env) -> Address {
env.storage().instance().get(&DataKey::Admin).unwrap()
}

pub fn register(env: Env, creator: Address) {
let admin = Self::admin(env.clone());
admin.require_auth();
env.storage().instance().set(&DataKey::Creator(creator), &true);
}

pub fn is_registered(env: Env, creator: Address) -> bool {
env.storage()
.instance()
.get(&DataKey::Creator(creator))
.unwrap_or(false)
}
}
13 changes: 13 additions & 0 deletions contract/contracts/earnings/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "earnings"
version = "0.1.0"
edition = "2021"

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

[dependencies]
soroban-sdk = { workspace = true }

[dev-dependencies]
soroban-sdk = { workspace = true, features = ["testutils"] }
49 changes: 49 additions & 0 deletions contract/contracts/earnings/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#![no_std]

use soroban_sdk::{contract, contractimpl, contracttype, Address, Env};

#[contracttype]
enum DataKey {
Admin,
Earnings(Address),
}

#[contract]
pub struct Earnings;

#[contractimpl]
impl Earnings {
pub fn init(env: Env, admin: Address) {
if env.storage().instance().has(&DataKey::Admin) {
panic!("already initialized");
}

admin.require_auth();
env.storage().instance().set(&DataKey::Admin, &admin);
}

pub fn admin(env: Env) -> Address {
env.storage().instance().get(&DataKey::Admin).unwrap()
}

pub fn record(env: Env, creator: Address, amount: i128) {
let admin = Self::admin(env.clone());
admin.require_auth();

let current: i128 = env
.storage()
.instance()
.get(&DataKey::Earnings(creator.clone()))
.unwrap_or(0);
env.storage()
.instance()
.set(&DataKey::Earnings(creator), &(current + amount));
}

pub fn get_earnings(env: Env, creator: Address) -> i128 {
env.storage()
.instance()
.get(&DataKey::Earnings(creator))
.unwrap_or(0)
}
}
Loading
Loading