Thank you for your interest in contributing to StellarForge! This document provides comprehensive guidelines for contributing to the project. Whether you're fixing bugs, adding features, or improving documentation, we appreciate your effort to make StellarForge better.
- Prerequisites
- Local Development Setup
- Development Workflow
- Commit Message Format
- Pull Request Process
- Code Style Guidelines
- Running Tests
- Adding a New Language
- SDK Upgrade Process
- Security
- Getting Help
This project uses Husky and lint-staged to enforce code quality automatically. The hooks are installed when you run npm install at the repo root (via the prepare script).
| Git event | Hook | What it does |
|---|---|---|
git commit |
pre-commit |
Runs lint-staged on staged files: ESLint auto-fix → Prettier format. Blocks the commit if lint errors remain after auto-fix. |
git push |
pre-push |
Runs the full frontend test suite (npm test -- --run). Aborts the push if any tests fail. |
lint-staged runs on staged files matching: .js, .jsx, .ts, .tsx, .json, .css, .md
If hooks stop working (e.g. after a fresh clone):
npm installThe prepare script re-initialises Husky automatically.
git commit --no-verify -m "your message"
git push --no-verifyOnly use this when absolutely necessary and document the reason in your commit message.
Before setting up your development environment, ensure you have the following installed:
- Git (v2.0+): Version control
- Node.js (v18+): JavaScript runtime for frontend development
- npm (v9+): Package manager (comes with Node.js)
- Rust (latest stable): For building Soroban smart contracts
- Stellar CLI: For contract deployment and testing (installed via setup script)
node --version # Should be v18 or higher
npm --version # Should be v9 or higher
git --version # Should be v2.0 or higher
rustc --version # Should be latest stable-
Fork the repository on GitHub
-
Clone your fork locally:
git clone https://github.com/YOUR_USERNAME/Stellar-forge.git cd Stellar-forge -
Add upstream remote to keep your fork in sync:
git remote add upstream https://github.com/stellar-forge/Stellar-forge.git git fetch upstream
Install workspace-level dependencies (husky for Git hooks, lint-staged for pre-commit linting):
npm installThis automatically runs the prepare script which sets up Git hooks via husky.
cd frontend
npm install
cd ..Run the setup script to install Rust toolchain, Stellar CLI, and configure the testnet:
./scripts/setup-soroban.shThis script will:
- Install Rust (if not already installed)
- Add the
wasm32-unknown-unknowntarget for Rust - Install Stellar CLI (replaces the older soroban-cli)
- Configure the testnet network with Soroban RPC endpoint
Verify everything is working:
# Check frontend setup
cd frontend
npm run lint
npm run test -- --run
# Check contract setup
cd ../contracts/token-factory
cargo testIf all commands pass, your environment is ready for development.
Always create a new branch for your work:
git checkout -b feature/your-feature-nameBranch naming conventions:
feature/- New features (e.g.,feature/add-token-burn)fix/- Bug fixes (e.g.,fix/wallet-connection-timeout)docs/- Documentation updates (e.g.,docs/update-readme)refactor/- Code refactoring (e.g.,refactor/simplify-validation)test/- Test additions or updates (e.g.,test/add-mint-tests)chore/- Maintenance tasks (e.g.,chore/update-dependencies)
-
Make your changes in the appropriate directory:
- Frontend changes:
frontend/src/ - Smart contract changes:
contracts/token-factory/src/
- Frontend changes:
-
Write tests for new functionality:
- Frontend: Add tests in
frontend/src/test/or co-locate with components - Contracts: Add tests in
contracts/token-factory/src/test.rs
- Frontend: Add tests in
-
Run tests locally before committing:
# Frontend tests cd frontend npm run test -- --run # Contract tests cd ../contracts/token-factory cargo test
-
Run linting to catch style issues:
cd frontend npm run lint -
Commit your changes (pre-commit hooks will run automatically):
git add . git commit -m "feat: add token burn functionality"
This project uses husky and lint-staged to enforce code quality automatically before commits.
When you commit changes, the pre-commit hook automatically:
- Runs ESLint on staged
.tsand.tsxfiles in thefrontend/directory - Auto-fixes fixable linting errors
- Runs TypeScript type checking (
tsc --noEmit) - Blocks the commit if there are linting or TypeScript errors
If hooks aren't working or you need to reinstall them:
npm run prepareTo manually verify what the hooks will do:
cd frontend
npm run lint
tsc --noEmitIn rare cases where you need to bypass hooks:
git commit --no-verify -m "your message"Note: This is strongly discouraged as it may introduce code quality issues. Use only when absolutely necessary and document why.
We follow the Conventional Commits specification for clear, semantic commit messages. This enables automated changelog generation and makes history easier to navigate.
<type>(<scope>): <subject>
<body>
<footer>
Must be one of the following:
- feat: A new feature
- fix: A bug fix
- docs: Documentation only changes
- style: Changes that don't affect code meaning (formatting, missing semicolons, etc.)
- refactor: Code change that neither fixes a bug nor adds a feature
- perf: Code change that improves performance
- test: Adding missing tests or correcting existing tests
- chore: Changes to build process, dependencies, or tooling
Optional but recommended. Specify what part of the codebase is affected:
frontend- Frontend changescontracts- Smart contract changeswallet- Wallet integrationipfs- IPFS/Pinata integrationui- UI componentshooks- React hooksutils- Utility functionsconfig- Configuration files
- Use imperative mood ("add" not "added" or "adds")
- Don't capitalize first letter
- No period (.) at the end
- Limit to 50 characters
Optional but recommended for non-trivial changes:
- Explain what and why, not how
- Wrap at 72 characters
- Separate from subject with a blank line
Optional. Reference issues and breaking changes:
Closes #123- Auto-closes the issueBREAKING CHANGE: description- For breaking changes
# Simple feature
git commit -m "feat(ui): add mainnet confirmation modal"
# Bug fix with body
git commit -m "fix(wallet): resolve connection timeout issue
The wallet connection was timing out after 5 seconds.
Increased timeout to 15 seconds and added retry logic."
# Breaking change
git commit -m "refactor(contracts): update token factory interface
BREAKING CHANGE: The create_token function signature has changed.
Callers must now provide a metadata_uri parameter."
# Closes an issue
git commit -m "feat(ipfs): add metadata pinning
Closes #42"-
Sync with upstream to avoid conflicts:
git fetch upstream git rebase upstream/main
-
Run all tests locally:
cd frontend npm run test -- --run npm run lint cd ../contracts/token-factory cargo test
-
Update documentation if needed (README, code comments, etc.)
-
Push your branch to your fork:
git push origin feature/your-feature-name
-
Open a Pull Request on GitHub with a clear title and description
-
Fill out the PR template with:
- Description: What changes are being made and why
- Type of Change: feat, fix, docs, refactor, etc.
- Related Issues: Use
Closes #XXto auto-close issues - Testing: Describe how you tested the changes
- Screenshots: Include if UI changes are involved
- Checklist: Confirm you've followed guidelines
Follow the same format as commit messages:
feat(scope): brief description
fix(scope): brief description
docs: update contributing guide
- At least one maintainer review is required
- Address feedback promptly
- Push additional commits to the same branch (don't force push)
- Request re-review after making changes
- Maintainers will merge when approved
-
Delete your feature branch locally and remotely:
git branch -d feature/your-feature-name git push origin --delete feature/your-feature-name
-
Sync your fork with upstream:
git fetch upstream git checkout main git merge upstream/main git push origin main
- Use TypeScript for all new files (no
.jsfiles) - Strict mode enabled:
strict: truein tsconfig.json - No unused variables or parameters
- No implicit
anytypes
- Use functional components with hooks (no class components)
- Prefer composition over inheritance
- Keep components focused and single-responsibility
- Use descriptive component names (PascalCase)
// Good
export function TokenForm({ onSubmit }: { onSubmit: (data: TokenData) => void }) {
const [formData, setFormData] = useState<TokenData>(initialState);
return (
<form onSubmit={() => onSubmit(formData)}>
{/* form content */}
</form>
);
}
// Avoid
export function TF(props: any) {
// ...
}- Custom hooks should start with
useprefix - Extract complex logic into custom hooks
- Use proper dependency arrays in
useEffect
// Good
export function useTokenBalance(tokenId: string) {
const [balance, setBalance] = useState<number>(0);
useEffect(() => {
fetchBalance(tokenId).then(setBalance);
}, [tokenId]);
return balance;
}- Use Tailwind CSS utility classes
- Avoid inline styles
- Create reusable component variants using Tailwind
- Use CSS modules only for complex styling
// Good
<button className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">
Submit
</button>
// Avoid
<button style={{ padding: '8px 16px', backgroundColor: 'blue' }}>
Submit
</button>- Use semantic HTML (
<button>,<nav>,<main>, etc.) - Add ARIA labels for interactive elements
- Ensure keyboard navigation works
- Test with screen readers
// Good
<button
aria-label="Close modal"
onClick={onClose}
>
✕
</button>
// Avoid
<div onClick={onClose}>✕</div>- Write tests for utilities and custom hooks
- Test user interactions, not implementation details
- Use React Testing Library for component tests
- Aim for meaningful coverage, not 100%
// Good
test('submits form with valid data', () => {
render(<TokenForm onSubmit={mockSubmit} />);
userEvent.type(screen.getByLabelText('Token Name'), 'MyToken');
userEvent.click(screen.getByRole('button', { name: /submit/i }));
expect(mockSubmit).toHaveBeenCalledWith(expect.objectContaining({ name: 'MyToken' }));
});- Follow Rust naming conventions (snake_case for functions/variables, PascalCase for types)
- Use meaningful variable and function names
- Add doc comments for all public functions
- Handle errors explicitly (no unwrap in production code)
- Add doc comments to all public functions and types
- Include examples in doc comments where helpful
- Document error conditions
/// Creates a new token in the factory.
///
/// # Arguments
///
/// * `name` - The token name
/// * `symbol` - The token symbol
/// * `decimals` - Number of decimal places
///
/// # Returns
///
/// The address of the newly created token
///
/// # Errors
///
/// Returns an error if the token name is empty or if the factory is paused.
pub fn create_token(
env: &Env,
name: String,
symbol: String,
decimals: u32,
) -> Result<Address, ContractError> {
// implementation
}- Use
Resulttypes for fallible operations - Define custom error types
- Provide meaningful error messages
// Good
pub fn mint_tokens(
env: &Env,
token_id: Address,
amount: i128,
) -> Result<(), ContractError> {
if amount <= 0 {
return Err(ContractError::InvalidAmount);
}
// implementation
}
// Avoid
pub fn mint_tokens(env: &Env, token_id: Address, amount: i128) {
let result = perform_mint(token_id, amount).unwrap(); // Don't unwrap!
}- Write comprehensive tests for all contract functions
- Test both success and error cases
- Use property-based testing with proptest for complex logic
#[test]
fn test_create_token_success() {
let env = Env::default();
let factory = TokenFactory::new(&env);
let token_addr = factory.create_token(
String::from_utf8(b"MyToken".to_vec()).unwrap(),
String::from_utf8(b"MTK".to_vec()).unwrap(),
18,
).unwrap();
assert!(token_addr.is_valid());
}
#[test]
fn test_create_token_empty_name() {
let env = Env::default();
let factory = TokenFactory::new(&env);
let result = factory.create_token(
String::from_utf8(vec![]).unwrap(),
String::from_utf8(b"MTK".to_vec()).unwrap(),
18,
);
assert!(result.is_err());
}cd frontend
# Run all tests once
npm run test -- --run
# Run tests in watch mode (for development)
npm run test
# Run tests with interactive UI
npm run test:ui
# Generate coverage report
npm run test:coveragecd contracts/token-factory
# Run all tests
cargo test
# Run specific test
cargo test test_create_token
# Run tests with output
cargo test -- --nocapture
# Run tests with coverage (requires tarpaulin)
cargo tarpaulin --out HtmlFrom the root directory:
# Frontend
cd frontend && npm run test -- --run && cd ..
# Contracts
cd contracts/token-factory && cargo test && cd ../..StellarForge is designed to support multiple languages for the UI. To add a new language:
Create a new language file in frontend/src/config/i18n/:
// frontend/src/config/i18n/es.ts
export const es = {
common: {
submit: 'Enviar',
cancel: 'Cancelar',
loading: 'Cargando...',
},
tokens: {
create: 'Crear Token',
name: 'Nombre del Token',
},
// ... more translations
};Update the language registry in frontend/src/config/i18n/index.ts:
import { es } from './es';
export const languages = {
en: en,
es: es,
// ... other languages
};
export type Language = keyof typeof languages;Update the language selector component to include the new language:
// frontend/src/components/LanguageSelector.tsx
const AVAILABLE_LANGUAGES = [
{ code: 'en', name: 'English' },
{ code: 'es', name: 'Español' },
// ... add new language
];- Test the language switcher
- Verify all UI text displays correctly
- Check for text overflow issues
- Test with RTL languages if applicable
Include in your PR:
- Complete translation file
- Screenshots showing the new language in use
- Any special considerations (RTL, character encoding, etc.)
When upgrading Soroban SDK or other major dependencies, follow this process:
- Check the changelog for breaking changes
- Review migration guides
- Identify affected code
# For Soroban SDK in contracts
cd contracts/token-factory
cargo update soroban-sdk --aggressive
# For frontend dependencies
cd frontend
npm update- Fix any breaking changes in the contract code
- Update TypeScript types if needed
- Update configuration files
# Contract tests
cd contracts/token-factory
cargo test
# Frontend tests
cd frontend
npm run test -- --run
npm run lint
# Manual testing on testnet
# Deploy to testnet and test all features- Update CHANGELOG.md with upgrade details
- Document any breaking changes
- Update README if needed
Include in your PR:
- Dependency update commits
- Code changes for compatibility
- Updated documentation
- Test results
# 1. Update Cargo.toml
cd contracts/token-factory
cargo update soroban-sdk
# 2. Fix any compilation errors
cargo build --target wasm32-unknown-unknown
# 3. Run tests
cargo test
# 4. Update CHANGELOG.md
# Add entry: "Upgraded Soroban SDK to v21.1.0"
# 5. Commit
git commit -m "chore(contracts): upgrade soroban-sdk to v21.1.0"Please read and follow our Code of Conduct. By participating, you are expected to uphold this code.
- Review our Security Policy
- Understand the security considerations for blockchain applications
- Never commit sensitive data: Private keys, API secrets, or credentials
- Always test on testnet first: Before any mainnet deployment
- Validate user input: Especially for contract interactions
- Use secure random generation: For any cryptographic operations
- Handle errors gracefully: Don't expose sensitive information in error messages
If you discover a security vulnerability:
- Do not open a public issue
- Email security details to the maintainers (see SECURITY.md)
- Include: Description, affected versions, reproduction steps, and potential impact
- Allow time for the team to respond and prepare a fix
See SECURITY.md for detailed vulnerability reporting procedures.
- GitHub Discussions: Ask questions in the Discussions tab
- GitHub Issues: Search existing issues or create a new one
- Documentation: Check README.md and existing docs
- Code Examples: Review existing components and tests
npm run prepare# Clear cache and reinstall
rm -rf node_modules package-lock.json
npm install
npm run test -- --run# Update Rust
rustup update
# Clean build
cd contracts/token-factory
cargo clean
cargo build --target wasm32-unknown-unknown- Ensure Freighter wallet is installed
- Check that you're on the correct network (testnet/mainnet)
- Clear browser cache and reload
We are committed to providing a welcoming and inclusive environment. Please be respectful and constructive in all interactions.
StellarForge uses react-i18next for internationalization. All UI strings live in frontend/src/i18n/en.json.
-
Copy
frontend/src/i18n/en.jsonto a new file named after the BCP 47 language tag, e.g.es.jsonfor Spanish. -
Translate all values in the new file (keep the keys unchanged).
-
Register the new language in
frontend/src/i18n/index.ts:import es from './es.json' i18n.use(initReactI18next).init({ resources: { en: { translation: en }, es: { translation: es }, // add this line }, ... })
-
Add the language option to
frontend/src/components/LanguageSwitcher.tsx:const LANGUAGES = [ { code: 'en', label: 'English' }, { code: 'es', label: 'Español' }, // add this line ]
That's it — no other code changes are needed.
Thank you for contributing to StellarForge! Your efforts help make token deployment on Stellar accessible to everyone.