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
246 changes: 66 additions & 180 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,219 +1,105 @@
# Contributing to StellarForge

Thank you for your interest in contributing to StellarForge! This document provides guidelines for contributing to our collection of Soroban smart contracts.
Thank you for your interest in contributing to StellarForge! We welcome contributions that help make this collection of Soroban smart contract primitives more robust and easier to use.

## 🚀 Quick Start
## 🛠️ Prerequisites

1. **Fork the repository**
2. **Create a feature branch**: `git checkout -b feature/your-feature-name`
3. **Make your changes**
4. **Run tests**: `make test` or `cargo test --workspace`
5. **Submit a pull request**
To contribute to this project, you will need:
- **Rust:** Latest stable version (2021 edition)
- **Target:** `wasm32v1-none`
- **Stellar CLI:** v25.2.0 or higher
- **Make:** Optional, but recommended for running development commands

## 📦 Shared Error Crate (forge-errors)
## 🚀 Getting Started

When adding new common error variants to `forge-errors`:
1. **Fork the repository** on GitHub.
2. **Clone your fork** locally:
```bash
git clone https://github.com/YOUR_USERNAME/stellarforge.git
cd stellarforge
```
3. **Set up the pre-commit hook** (recommended):
```bash
cp src/scripts/pre-commit .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
```

1. **Consider if the error is truly common** across multiple contracts
2. **Add descriptive documentation** to the variant in `crates/forge-errors/src/lib.rs`
3. **Update error codes** to avoid conflicts with existing variants
4. **Test the change** across all affected contracts
### Pre-commit Hook (Optional but Recommended)

### Adding New Common Errors
We provide a git pre-commit hook that automatically checks code formatting and linting before each commit. This helps catch issues early.

If you identify an error pattern that appears in 3+ contracts, consider adding it to `CommonError`:
By default, the hook runs `cargo fmt` and `cargo clippy`. To also run the full test suite before each commit, set the `FORGE_PRECOMMIT_TESTS` environment variable to `1`:

```rust
#[contracterror]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum CommonError {
// Existing variants...

/// New error description
NewError = NEXT_AVAILABLE_CODE,
}
```bash
# Run tests on this commit only
FORGE_PRECOMMIT_TESTS=1 git commit -m "your message"
```

**Process:**
1. Add the variant to `CommonError` with next available error code
2. Update any contracts that should use this new shared variant
3. Add tests to verify the error behavior
4. Update documentation

## 🏗 Development Setup
## 📦 Shared Error Crate (forge-errors)

### Prerequisites
When adding new common error variants to `forge-errors`:

- **Rust**: 2021 edition with `wasm32v1-none` target
- **Stellar CLI**: v25.2.0 or higher
- **Make**: (optional) for convenience commands
1. **Consider if the error is truly common** across multiple contracts.
2. **Add descriptive documentation** to the variant in `crates/forge-errors/src/lib.rs`.
3. **Update error codes** to avoid conflicts with existing variants.
4. **Test the change** across all affected contracts.

### Installation
## 📜 Development Workflow

### Building
Build all contracts in the workspace:
```bash
# Install Rust
rustup target add wasm32v1-none

# Install Stellar CLI
cargo install --locked stellar-cli

# Verify installation
stellar --version
make build
# or
cargo build --workspace
```

## 🧪 Testing

### Running Tests

### Testing
Run the full test suite:
```bash
# Test all contracts
make test

# Test specific contract
cargo test -p forge-governor
cargo test -p forge-multisig
cargo test -p forge-oracle
cargo test -p forge-stream
cargo test -p forge-vesting
cargo test -p forge-vesting-factory
# or
cargo test --workspace
```

### Test Coverage

We aim for high test coverage. When adding new features:
- Write unit tests for new functionality
- Test error paths exhaustively
- Include integration tests for contract interactions
- Verify all error variants are tested

## 📝 Code Style

Follow these conventions:

### Rust Style

- Use `rustfmt` for formatting: `make fmt`
- Use `clippy` for linting: `make lint`
- Follow Rust idioms and Soroban best practices
- Use `#![no_std]` for all contracts
- Prefer `require_auth()` over manual auth checks where possible

### Contract Patterns

- **Error Handling**: Use the shared `CommonError` variants when applicable
- **Storage**: Use appropriate storage types (instance vs persistent)
- **Events**: Emit events for all state changes
- **TTL Management**: Extend storage TTLs appropriately
- **Security**: Follow established security patterns from existing contracts

### Documentation

- Document all public functions with examples
- Include error conditions in docstrings
- Update README.md for new features
- Keep CHANGELOG.md updated

## 🐛 Bug Reports

When reporting bugs:

1. **Use the issue template** provided in GitHub Issues
2. **Include reproduction steps** with minimal example
3. **Specify contract name** and affected functions
4. **Include environment details** (OS, Rust version, Stellar CLI version)
5. **Add logs** and error messages when applicable

## 💡 Feature Requests

We welcome feature requests! Please:

1. **Check existing issues** for similar requests
2. **Describe the use case** clearly
3. **Consider impact** on existing contracts and integrators
4. **Propose implementation approach** if you have ideas

## 📄 Pull Request Process

### Before Submitting

- [ ] Tests pass: `make test`
- [ ] Code formatted: `make fmt`
- [ ] Linting clean: `make lint`
- [ ] Documentation updated
- [ ] CHANGELOG.md updated
### Linting & Formatting
Ensure your code follows the project's style:
```bash
make check
# which runs:
# cargo fmt --all -- --check
# cargo clippy --all-targets -- -D warnings
```

### PR Guidelines
## 🏗️ Pull Request Process

- **Small, focused PRs** are preferred
- **One feature per PR** when possible
- **Include tests** for new functionality
- **Update documentation** as needed
- **Link to related issues**
1. Create a new branch for your feature or bug fix.
2. Ensure all tests pass and the code is correctly formatted.
3. Update the documentation (`README.md`, `docs/`) if you've changed contract interfaces or added new features.
4. Submit a Pull Request targeting the `main` branch.
5. Use the provided PR template to describe your changes and testing.

### Review Process
## 🏷️ Issue Labels

Maintainers will review for:
- ✅ Code quality and style
- ✅ Test coverage
- ✅ Security considerations
- ✅ Documentation completeness
- ✅ Breaking changes (if any)
- `good first issue` — Great for newcomers!
- `bug` — Something isn't working correctly.
- `enhancement` — New features or improvements.
- `documentation` — Improvements to the docs.

## 🔒 Security

Security is our top priority. If you discover a security vulnerability:

1. **Do NOT open a public issue**
1. **Do NOT open a public issue.**
2. **Email us privately**: security@stellarforge.org
3. **Include details**: Impact, reproduction steps, affected versions
4. **Allow time for response**: We'll acknowledge within 48 hours

## 📧 Development Tools

### Make Commands

```makefile
build:
cargo build --workspace

test:
cargo test --workspace

fmt:
cargo fmt --all

lint:
cargo clippy --workspace -- -D warnings

check:
cargo fmt --all && cargo clippy --workspace -- -D warnings && cargo test --workspace

clean:
cargo clean --workspace
```

### Workspace Structure

```
stellarforge/
├── crates/
│ └── forge-errors/ # Shared error library
├── contracts/
│ ├── forge-governor/ # Governance contract
│ ├── forge-multisig/ # Multisig treasury
│ ├── forge-oracle/ # Price feed contract
│ ├── forge-stream/ # Token streaming
│ ├── forge-vesting/ # Token vesting
│ └── forge-vesting-factory/ # Multi-beneficiary vesting
├── benches/ # Performance benchmarks
└── scripts/ # Utility scripts
```
3. **Include details**: Impact, reproduction steps, affected versions.
4. **Allow time for response**: We'll acknowledge within 48 hours.

## 🤝 Community

- **GitHub Discussions**: Use for questions, ideas, and general discussion
- **Issues**: Bug reports and feature requests
- **Discord**: [Join our community](https://discord.gg/stellarforge) for real-time chat
- **GitHub Discussions**: Use for questions, ideas, and general discussion.
- **Issues**: Bug reports and feature requests.
- **Discord**: [Join our community](https://discord.gg/stellarforge) for real-time chat.

## 📜 License

Expand Down
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,6 @@ clean:
cargo clean

# Run contract benchmarks (Soroban budget: CPU instructions + memory bytes)
.PHONY: bench
bench:
cargo run -p forge-benches
# .PHONY: bench
# bench:
# cargo run -p forge-benches
32 changes: 23 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ Developers evaluating StellarForge can use this table to quickly identify the ri

| Contract | Use Case | Admin Required | Events Emitted | Timelock |
| :--- | :--- | :--- | :--- | :--- |
| [`forge-governor`](#forge-governor) | Governance | No (Auth-based) | None | Yes (Voting/Execution delay) |
| [`forge-multisig`](#forge-multisig) | Multisig Treasury | Yes (Owners) | None | Yes (Post-approval delay) |
| [`forge-oracle`](#forge-oracle) | Price Feed | Yes (Admin) | `price_updated` | No |
| [`forge-governor`](#forge-governor) | Governance | No (Auth-based) | `proposal_created`, `vote_cast`, `proposal_finalized` | Yes (Voting/Execution delay) |
| [`forge-multisig`](#forge-multisig) | Multisig Treasury | Yes (Owners) | `proposal_created`, `proposal_approved`, `proposal_executed` | Yes (Post-approval delay) |
| [`forge-oracle`](#forge-oracle) | Price Feed | Yes (Admin) | `price_updated`, `admin_transferred` | No |
| [`forge-stream`](#forge-stream) | Real-time Payments | No (Stream-specific) | `stream_created`, `withdrawn`, `stream_cancelled`, `stream_paused`, `stream_resumed` | No |
| [`forge-vesting`](#forge-vesting) | Token Vesting | Yes (Admin) | `vesting_initialized`, `claimed`, `vesting_cancelled`, `admin_transferred` | Yes (Cliff period) |
| [`forge-vesting`](#forge-vesting) | Token Vesting | Yes (Admin) | `vesting_initialized`, `claimed`, `vesting_cancelled`, `admin_transferred`, `beneficiary_changed` | Yes (Cliff period) |
| [`forge-vesting-factory`](#forge-vesting-factory) | Multi-beneficiary Vesting | Yes (Per-schedule Admin) | `schedule_created`, `claimed`, `schedule_cancelled` | Yes (Cliff period) |
---

Expand Down Expand Up @@ -144,7 +144,7 @@ A single-deployment factory that manages multiple vesting schedules. Eliminates
### forge-stream
Pay-per-second token streams. Ideal for payroll, subscriptions, or real-time contractor payments.

* **Key Function:** `create_stream(sender, token, recipient, rate_per_second, duration_seconds)`
* **Key Function:** `create_stream(sender, token, recipient, rate_per_second, duration_seconds, min_withdrawal_amount)`
* **Action:** `withdraw(stream_id)` allows the recipient to pull accrued tokens at any time.
* **Pause/Resume:** `pause_stream(stream_id)` and `resume_stream(stream_id)` allow senders to temporarily halt or restart token accrual.
* **`is_active` vs `is_claimable`:** `get_stream_status()` returns both fields. A finished stream has `is_active = false` and `is_finished = true`, but may still have `withdrawable > 0`. Always check `is_claimable` (or `withdrawable` directly) to determine whether tokens can be pulled — do not rely on `is_active` alone.
Expand Down Expand Up @@ -180,14 +180,15 @@ The tables below are verified against the current contract code in `contracts/*/
| :--- | :--- | :--- |
| `vesting_initialized` | Emitted by `initialize(...)` after the vesting config and claimed amount are stored. | `total_amount: i128`, `cliff_seconds: u64`, `duration_seconds: u64` |
| `claimed` | Emitted by `claim()` after the beneficiary's claimed amount is updated and vested tokens are transferred. | `beneficiary: Address`, `claimable: i128` |
| `vesting_cancelled` | Emitted by `cancel()` after the vesting is marked cancelled and any unvested tokens are returned to the admin. | `admin: Address`, `returnable: i128` |
| `vesting_cancelled` | Emitted by `cancel()` after the vesting is marked cancelled and any unvested tokens are returned to the admin. | `admin: Address`, `to_admin: i128`, `beneficiary: Address`, `to_beneficiary: i128` |
| `admin_transferred` | Emitted by `transfer_admin(new_admin)` after admin rights move to the new admin address. | `old_admin: Address`, `new_admin: Address` |
| `beneficiary_changed` | Emitted by `change_beneficiary(new_beneficiary)` after beneficiary rights move to the new address. | `old_beneficiary: Address`, `new_beneficiary: Address` |

### forge-stream

| Event Name | Trigger | Fields |
| :--- | :--- | :--- |
| `stream_created` | Emitted by `create_stream(...)` after the stream is stored and the active stream count is incremented. | `stream_id: u64`, `recipient: Address`, `rate_per_second: i128`, `duration_seconds: u64` |
| `stream_created` | Emitted by `create_stream(...)` after the stream is stored and the active stream count is incremented. | `stream_id: u64`, `recipient: Address`, `rate_per_second: i128`, `duration_seconds: u64`, `min_withdrawal_amount: i128` |
| `withdrawn` | Emitted by `withdraw(stream_id)` after the withdrawn amount is updated and accrued tokens are transferred to the recipient. | `stream_id: u64`, `recipient: Address`, `withdrawable: i128` |
| `stream_cancelled` | Emitted by `cancel_stream(stream_id)` after the stream is marked cancelled and funds are paid out/refunded. | `stream_id: u64`, `withdrawable: i128`, `returnable: i128` |
| `stream_paused` | Emitted by `pause_stream(stream_id)` after the stream is marked paused. | `stream_id: u64` |
Expand All @@ -197,19 +198,32 @@ The tables below are verified against the current contract code in `contracts/*/

| Event Name | Trigger | Fields |
| :--- | :--- | :--- |
| None | This contract does not currently emit any events. | None |
| `proposal_created` | Emitted by `propose(...)` after the proposal is stored and the proposer is marked as approved. | `proposal_id: u64`, `proposer: Address`, `to: Address`, `token: Address`, `amount: i128` |
| `proposal_approved` | Emitted by `approve(...)` after an owner approves a proposal. | `proposal_id: u64`, `owner: Address`, `approval_count: u32` |
| `proposal_executed` | Emitted by `execute(...)` after a proposal is successfully executed. | `proposal_id: u64`, `executor: Address`, `to: Address`, `amount: i128` |

### forge-governor

| Event Name | Trigger | Fields |
| :--- | :--- | :--- |
| None | This contract does not currently emit any events. | None |
| `proposal_created` | Emitted by `propose(...)` after the proposal is stored. | `proposal_id: u64`, `proposer: Address`, `vote_end: u64` |
| `vote_cast` | Emitted by `vote(...)` after a vote is successfully cast. | `proposal_id: u64`, `voter: Address`, `direction: VoteDirection`, `weight: i128` |
| `proposal_finalized` | Emitted by `finalize(...)` after a proposal is finalized (Passed or Failed). | `proposal_id: u64`, `votes_for: i128`, `votes_against: i128` |

### forge-oracle

| Event Name | Trigger | Fields |
| :--- | :--- | :--- |
| `price_updated` | Emitted by `submit_price(base, quote, price)` after the submitted price and update timestamp are written to storage. | `base: Symbol`, `quote: Symbol`, `price: i128`, `updated_at: u64` |
| `admin_transferred` | Emitted by `transfer_admin(new_admin)` after admin rights move to the new admin address. | `old_admin: Address`, `new_admin: Address` |

### forge-vesting-factory

| Event Name | Trigger | Fields |
| :--- | :--- | :--- |
| `schedule_created` | Emitted by `create_schedule(...)` after a new schedule is created. | `id: u64`, `total_amount: i128` |
| `claimed` | Emitted by `claim(...)` after tokens are claimed for a schedule. | `schedule_id: u64`, `claimable: i128` |
| `schedule_cancelled` | Emitted by `cancel(...)` after a schedule is cancelled. | `schedule_id: u64` |

---

Expand Down
14 changes: 0 additions & 14 deletions contracts/forge-governor/Cargo.toml

This file was deleted.

Loading