Smart Contracts (snfoundry Package)
The packages/snfoundry directory contains the smart contract development environment. This package is identical to Scaffold Stark 2, ensuring full compatibility between web and mobile frontends.
Overviewโ
packages/snfoundry/
โโโ contracts/ # Cairo smart contracts
โ โโโ src/
โ โโโ YourContract.cairo # Your contracts
โ โโโ lib.cairo # Module declarations
โโโ deployments/ # Deployment artifacts
โโโ scripts-ts/ # Deployment scripts
โโโ Scarb.toml # Cairo package config
โโโ snfoundry.toml # Foundry config
โโโ package.json # Scripts and dependencies
Reference Documentationโ
Since the snfoundry package is identical to Scaffold Stark 2, refer to the main documentation for comprehensive guides:
- Installation & Setup - Setting up the development environment
- Deploy Smart Contracts - Deployment guide
- Environment Configuration - Network and account setup
Available Commandsโ
Run these commands from the project root:
| Command | Description |
|---|---|
yarn chain | Start local Starknet devnet on port 5050 |
yarn compile | Compile Cairo contracts with Scarb |
yarn deploy | Deploy contracts to the active network |
yarn deploy:clear | Deploy with reset (clear previous deployments) |
yarn deploy:no-reset | Deploy without resetting existing contracts |
yarn test | Run contract tests with snforge |
yarn verify | Verify contracts on Starkscan/Voyager |
Starting Local Developmentโ
# Terminal 1: Start local devnet
yarn chain
# Terminal 2: Deploy contracts
yarn deploy
# Terminal 3: Start mobile app
yarn start
Writing Contractsโ
Basic Contract Exampleโ
// contracts/src/YourContract.cairo
#[starknet::interface]
trait IYourContract<TContractState> {
fn set_greeting(ref self: TContractState, new_greeting: felt252);
fn get_greeting(self: @TContractState) -> felt252;
fn get_balance(self: @TContractState, account: ContractAddress) -> u256;
}
#[starknet::contract]
mod YourContract {
use starknet::ContractAddress;
use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};
use starknet::get_caller_address;
#[storage]
struct Storage {
greeting: felt252,
owner: ContractAddress,
balances: LegacyMap<ContractAddress, u256>,
}
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
GreetingChanged: GreetingChanged,
}
#[derive(Drop, starknet::Event)]
struct GreetingChanged {
#[key]
user: ContractAddress,
new_greeting: felt252,
}
#[constructor]
fn constructor(ref self: ContractState, initial_greeting: felt252) {
self.greeting.write(initial_greeting);
self.owner.write(get_caller_address());
}
#[abi(embed_v0)]
impl YourContractImpl of super::IYourContract<ContractState> {
fn set_greeting(ref self: ContractState, new_greeting: felt252) {
let caller = get_caller_address();
self.greeting.write(new_greeting);
self.emit(GreetingChanged { user: caller, new_greeting });
}
fn get_greeting(self: @ContractState) -> felt252 {
self.greeting.read()
}
fn get_balance(self: @ContractState, account: ContractAddress) -> u256 {
self.balances.read(account)
}
}
}
Register Contract Moduleโ
// contracts/src/lib.cairo
mod YourContract;
// Add more modules as needed
mod AnotherContract;
Deployment Configurationโ
Environment Setupโ
Create a .env file in packages/snfoundry/:
# packages/snfoundry/.env
# Local Devnet (default)
# No configuration needed - uses localhost:5050
# Sepolia Testnet
RPC_URL_SEPOLIA=https://starknet-sepolia.public.blastapi.io/rpc/v0_8
ACCOUNT_ADDRESS_SEPOLIA=0x...your_account_address
PRIVATE_KEY_SEPOLIA=0x...your_private_key
# Mainnet
RPC_URL_MAINNET=https://starknet-mainnet.public.blastapi.io/rpc/v0_8
ACCOUNT_ADDRESS_MAINNET=0x...your_account_address
PRIVATE_KEY_MAINNET=0x...your_private_key
Deployment Scriptโ
// scripts-ts/deploy.ts
import { deployContract, deployer } from "./helpers";
async function main() {
// Deploy with constructor arguments
await deployContract({
contract: "YourContract",
constructorArgs: {
initial_greeting: "Hello, Starknet!",
},
});
// Deploy another contract
await deployContract({
contract: "AnotherContract",
constructorArgs: {},
});
}
main();
Deploy to Different Networksโ
# Deploy to local devnet (default)
yarn deploy
# Deploy to Sepolia testnet
yarn deploy --network sepolia
# Deploy to mainnet
yarn deploy --network mainnet
Testing Contractsโ
Writing Testsโ
// tests/test_your_contract.cairo
use snforge_std::{declare, ContractClassTrait};
#[test]
fn test_greeting() {
// Declare and deploy
let contract = declare('YourContract');
let contract_address = contract.deploy(@array!['Hello']).unwrap();
// Create dispatcher
let dispatcher = IYourContractDispatcher { contract_address };
// Test read
let greeting = dispatcher.get_greeting();
assert(greeting == 'Hello', 'Wrong initial greeting');
// Test write
dispatcher.set_greeting('World');
let new_greeting = dispatcher.get_greeting();
assert(new_greeting == 'World', 'Greeting not updated');
}
#[test]
#[should_panic(expected: ('Only owner', ))]
fn test_unauthorized() {
// Test that should panic
}
Running Testsโ
# Run all tests
yarn test
# Run specific test file
yarn test tests/test_your_contract.cairo
# Run with verbose output
yarn test -v
Contract Verificationโ
Verify your contracts on block explorers:
# Verify on Starkscan (Sepolia)
yarn verify --network sepolia
# Verify on Voyager
yarn verify --network sepolia --explorer voyager
Auto-Generated Typesโ
When you run yarn deploy, the deployment script automatically:
- Compiles your contracts
- Deploys to the target network
- Generates TypeScript types in
packages/rn/contracts/deployedContracts.ts
// packages/rn/contracts/deployedContracts.ts (auto-generated)
export const contracts = {
devnet: {
YourContract: {
address: "0x...",
abi: [...],
classHash: "0x...",
},
},
sepolia: {
YourContract: {
address: "0x...",
abi: [...],
classHash: "0x...",
},
},
} as const;
This enables type-safe contract interactions in your React Native app:
// Type-safe hook usage
const { data } = useScaffoldReadContract({
contractName: "YourContract", // Autocomplete from deployedContracts
functionName: "get_greeting", // Autocomplete from ABI
});
Sharing Contracts with Webโ
Since snfoundry is identical between Scaffold Stark 2 and React Native, you can:
- Share contracts between web and mobile projects
- Deploy once, use everywhere
- Maintain consistency across platforms
Example: Shared Contract Repositoryโ
my-starknet-project/
โโโ contracts/ # Shared Cairo contracts
โ โโโ src/
โ โโโ Token.cairo
โโโ web-app/ # Scaffold Stark 2
โ โโโ packages/
โ โโโ nextjs/
โ โโโ snfoundry/ # Symlink to ../contracts
โโโ mobile-app/ # Scaffold Stark RN
โโโ packages/
โโโ rn/
โโโ snfoundry/ # Symlink to ../contracts
Compatible Versionsโ
| Tool | Version |
|---|---|
| Scarb | 2.16.0 |
| Starknet Foundry | 0.57.0 |
| Cairo | 2.16.x |
| starknet.js | 8.5.3 |
| Starknet Devnet | 0.7.2 |
Troubleshootingโ
Compilation Errorsโ
# Check Scarb version
scarb --version
# Should be 2.16.0
# Clean and recompile
yarn compile
Deployment Failuresโ
# Check devnet is running
curl http://127.0.0.1:5050/is_alive
# Check account balance
yarn chain # Devnet provides prefunded accounts
Type Generation Issuesโ
# Regenerate types
yarn deploy:clear
Next Stepsโ
- Hooks Reference - Use contracts in your app
- Recipes - Practical usage recipes
- Mobile Considerations - Mobile-specific tips