To maintain the stability and security of the MyFans Soroban smart contracts, the following branch protection rules must be applied to the main and master branches in GitHub.
- Branch name patterns:
main,master
- Require a pull request before merging
- Require approvals:
1(Minimum) - Dismiss stale pull request approvals when new commits are pushed
- Require review from Code Owners: This ensures that any changes to the
contract/directory are approved by the@MyFanss/contractteam (if applicable).
- Require approvals:
- Require status checks to pass before merging
- Require branches to be up to date before merging
- Required Status Checks:
contract— Soroban contract formatting, linting (clippy), unit tests, and WASM build verification
- Require linear history: Prevent merge commits; use Squash and merge or Rebase and merge.
- Require signed commits: All commits should be verified with a GPG or SSH key to ensure authenticity (recommended for production contracts).
- Restrict pushes: Only designated maintainers or automated bots should be allowed to push directly to protected branches.
- Include administrators: All of the above rules apply to administrators as well.
The contract CI workflow (contract-ci.yml) performs the following checks:
-
Formatting Check (
cargo fmt)- Ensures code follows Rust style conventions
- Automatically formatters may be applied locally before committing
-
Linting (
cargo clippy)- Static analysis to catch common mistakes and improve code quality
- Warnings are treated as errors
-
Unit Tests (
cargo test --all-features)- All Soroban contract tests run with all feature flags enabled
- Covers individual contract logic and cross-contract interactions
-
WASM Build & Verification
- Builds the optimized WASM targets for Soroban deployment
- Verifies all expected contract artifacts are produced:
subscription.wasmmyfans_token.wasmcontent_access.wasmcreator_registry.wasmearnings.wasm
If you have the GitHub CLI installed, you can apply these rules using the following command:
gh api -X PUT /repos/MyFanss/MyFans/branches/main/protection \
-H "Accept: application/vnd.github+json" \
-f required_status_checks='{"strict":true,"contexts":["contract"]}' \
-f enforce_admins=true \
-f required_pull_request_reviews='{"dismiss_stale_reviews":true,"require_code_owner_reviews":false,"required_approving_review_count":1}' \
-f restrictions=null \
-f required_linear_history=true \
-f required_signatures=falseBefore pushing to a protected branch, run the same checks locally:
cd contract
# Format check
cargo fmt --all --check
# Linting
cargo clippy --all-targets --all-features -- -D warnings
# Unit tests
cargo test --all-features
# WASM build
cargo build --release --target wasm32-unknown-unknownOr run all checks at once:
cd contract && cargo fmt --all --check && \
cargo clippy --all-targets --all-features -- -D warnings && \
cargo test --all-features && \
cargo build --release --target wasm32-unknown-unknownWhen adding a new contract or modifying existing ones:
- Add comprehensive unit tests in the contract's
mod testsection - Use
Env::default()for isolated test environments - Test both happy path and error cases
- Use descriptive test names that indicate what is being tested
- Mock external dependencies (other contracts, ledger state) as needed
Example test structure:
#[test]
fn test_happy_path_scenario() {
let env = Env::default();
env.mock_all_auths();
// Setup
let contract_id = env.register_contract(None, MyContract);
let client = MyContractClient::new(&env, &contract_id);
// Exercise
let result = client.some_method(&arg);
// Verify
assert_eq!(result, expected_value);
}
#[test]
fn test_error_condition() {
let env = Env::default();
env.mock_all_auths();
let contract_id = env.register_contract(None, MyContract);
let client = MyContractClient::new(&env, &contract_id);
let result = client.try_invalid_method(&bad_arg);
assert_eq!(result, Err(Ok(Error::InvalidInput)));
}For cross-contract interactions, set up multiple contract instances and test call chains:
#[test]
fn test_cross_contract_call() {
let env = Env::default();
env.mock_all_auths();
let token_id = env.register_contract(None, TokenContract);
let token_client = TokenContractClient::new(&env, &token_id);
let subscription_id = env.register_contract(None, SubscriptionContract);
let subscription_client = SubscriptionContractClient::new(&env, &subscription_id);
// Initialize and test interaction
token_client.initialize(&admin, &String::from_str(&env, "Token"));
subscription_client.initialize(&admin, &token_id);
// Test the interaction
let result = subscription_client.subscribe(&subscriber, &amount);
assert!(result.is_ok());
}These rules and tests should be reviewed:
- Quarterly: Ensure rules align with the team's evolving workflow
- Per PR: When adding new contracts or modifying interfaces
- After Security Reviews: When addressing security findings or audit recommendations