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
235 changes: 235 additions & 0 deletions starknet_contracts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
# StarkNet Staking Contract

A secure and gas-efficient staking smart contract implemented in Cairo for StarkNet. Users can stake the native STRK token and earn rewards in Reward tokens based on their stake share and time.

## Features

### Core Functionality

- **Staking**: Users can stake native STRK tokens to earn rewards
- **Unstaking**: Users can withdraw their staked STRK tokens
- **Reward Distribution**: Fair reward calculation based on stake share and time
- **Reward Claiming**: Users can claim accumulated rewards

### Owner Functions

- **Fund Rewards**: Owner can add rewards to the pool with specified duration
- **Pause/Unpause**: Emergency pause functionality
- **Recover ERC20**: Recover accidentally sent tokens (with restrictions)

### Security Features

- **Pausable**: Contract can be paused in emergencies
- **Access Control**: Owner-only functions
- **Input Validation**: Comprehensive input validation
- **Reentrancy Protection**: Built-in protection against reentrancy attacks

## Architecture

### Reward Calculation

The contract implements a "reward per token stored" mechanism:

- **Reward Rate**: Rewards distributed per second across all stakers
- **Reward Per Token**: Cumulative rewards per staked token
- **User Rewards**: Calculated as: `balance * (rewardPerToken - userRewardPerTokenPaid) + pendingRewards`

### Contracts

- **StakingContract**: Main staking contract
- **STRK Token**: Native StarkNet token for staking (0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d)
- **RewardToken**: ERC20 token for rewards (RWT)

## Installation & Setup

### Prerequisites

- [Scarb](https://docs.swmansion.com/scarb/) - Cairo package manager
- [StarkNet Foundry](https://foundry-rs.github.io/starknet-foundry/) - Testing framework

### Build

```bash
scarb build
```

### Test

```bash
snforge test
```

## Usage

### Deploy Contracts

1. Deploy RewardToken contract
2. Deploy StakingContract with RewardToken address (STRK token address is hardcoded)

### Staking Flow

```cairo
// Approve staking contract to spend STRK tokens
strk_token.approve(staking_contract_address, amount);

// Stake STRK tokens
staking_contract.stake(amount);

// Check earned rewards
let rewards = staking_contract.earned(user_address);

// Claim rewards
staking_contract.claim_rewards();

// Unstake tokens
staking_contract.unstake(amount);
```

### Owner Functions

```cairo
// Fund rewards for distribution
staking_contract.fund_rewards(amount, duration);

// Pause staking (emergency)
staking_contract.pause();

// Unpause staking
staking_contract.unpause();

// Recover accidentally sent tokens
staking_contract.recover_erc20(token_address, amount);
```

## API Reference

### IStaking Interface

#### `stake(amount: u256)`

Stake tokens to earn rewards.

#### `unstake(amount: u256)`

Unstake tokens from the contract.

#### `claim_rewards()`

Claim accumulated reward tokens.

#### `earned(account: ContractAddress) -> u256`

Get earned rewards for an account.

#### `balance_of(account: ContractAddress) -> u256`

Get staked balance for an account.

#### `total_supply() -> u256`

Get total staked tokens.

### IOwnerFunctions Interface

#### `fund_rewards(amount: u256, duration: u64)`

Fund reward pool for distribution over specified duration.

#### `pause()`

Pause staking operations.

#### `unpause()`

Resume staking operations.

#### `recover_erc20(token: ContractAddress, amount: u256)`

Recover accidentally sent ERC20 tokens.

## Events

- **Staked**: Emitted when tokens are staked
- **Unstaked**: Emitted when tokens are unstaked
- **RewardPaid**: Emitted when rewards are claimed
- **RewardsFunded**: Emitted when rewards are funded
- **Paused**: Emitted when contract is paused
- **Unpaused**: Emitted when contract is unpaused
- **RecoveredTokens**: Emitted when tokens are recovered

## Security Considerations

### Reward Calculation Security

- Uses checked arithmetic to prevent overflow
- Updates reward calculations before state changes
- Implements reward per token stored pattern for gas efficiency

### Access Control

- Owner-only functions use OpenZeppelin's Ownable component
- Input validation on all public functions
- Pausable functionality for emergency stops

### Token Recovery

- Cannot recover staked STRK tokens while active distribution
- Cannot recover reward tokens
- Only owner can recover tokens

### Gas Optimization

- Efficient reward calculation using cumulative approach
- Minimal storage reads/writes
- Optimized for batch operations

## Testing

The contract includes comprehensive unit tests covering:

- Basic staking/unstaking functionality
- Reward calculation and claiming
- Owner functions (pause, fund rewards, recover tokens)
- Edge cases and error conditions
- Security scenarios

Run tests with:

```bash
snforge test
```

## Deployment

### Local Development

```bash
# Start local StarkNet node
starknet-devnet

# Deploy contracts
# Use deployment script or manual deployment
```

### Testnet Deployment

```bash
# Deploy to Sepolia testnet
# Use StarkNet CLI or wallet interface
```

## License

This project is licensed under the MIT License.

## Contributing

1. Fork the repository
2. Create a feature branch
3. Add tests for new functionality
4. Ensure all tests pass
5. Submit a pull request

## Disclaimer

This contract is provided as-is for educational and demonstration purposes. Always conduct thorough security audits before deploying to production networks.
127 changes: 123 additions & 4 deletions starknet_contracts/Scarb.lock
Original file line number Diff line number Diff line change
@@ -1,17 +1,135 @@
# Code generated by scarb DO NOT EDIT.
version = 1

[[package]]
name = "openzeppelin"
version = "0.20.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:05fd9365be85a4a3e878135d5c52229f760b3861ce4ed314cb1e75b178b553da"
dependencies = [
"openzeppelin_access",
"openzeppelin_account",
"openzeppelin_finance",
"openzeppelin_governance",
"openzeppelin_introspection",
"openzeppelin_merkle_tree",
"openzeppelin_presets",
"openzeppelin_security",
"openzeppelin_token",
"openzeppelin_upgrades",
"openzeppelin_utils",
]

[[package]]
name = "openzeppelin_access"
version = "0.20.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:7734901a0ca7a7065e69416fea615dd1dc586c8dc9e76c032f25ee62e8b2a06c"
dependencies = [
"openzeppelin_introspection",
]

[[package]]
name = "openzeppelin_account"
version = "0.20.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:1aa3a71e2f40f66f98d96aa9bf9f361f53db0fd20fa83ef7df04426a3c3a926a"
dependencies = [
"openzeppelin_introspection",
"openzeppelin_utils",
]

[[package]]
name = "openzeppelin_finance"
version = "0.20.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:f0c507fbff955e4180ea3fa17949c0ff85518c40101f4948948d9d9a74143d6c"
dependencies = [
"openzeppelin_access",
"openzeppelin_token",
]

[[package]]
name = "openzeppelin_governance"
version = "0.20.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:c0fb60fad716413d537fabd5fcbb2c499ca6beb95af5f0d1699955ecec4c6f63"
dependencies = [
"openzeppelin_access",
"openzeppelin_account",
"openzeppelin_introspection",
"openzeppelin_token",
"openzeppelin_utils",
]

[[package]]
name = "openzeppelin_introspection"
version = "0.20.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:13e04a2190684e6804229a77a6c56de7d033db8b9ef519e5e8dee400a70d8a3d"

[[package]]
name = "openzeppelin_merkle_tree"
version = "0.20.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:039608900e92f3dcf479bf53a49a1fd76452acd97eb86e390d1eb92cacdaf3af"

[[package]]
name = "openzeppelin_presets"
version = "0.20.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:5c07a8de32e5d9abe33988c7927eaa8b5f83bc29dc77302d9c8c44c898611042"
dependencies = [
"openzeppelin_access",
"openzeppelin_account",
"openzeppelin_finance",
"openzeppelin_introspection",
"openzeppelin_token",
"openzeppelin_upgrades",
"openzeppelin_utils",
]

[[package]]
name = "openzeppelin_security"
version = "0.20.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:27155597019ecf971c48d7bfb07fa58cdc146d5297745570071732abca17f19f"

[[package]]
name = "openzeppelin_token"
version = "0.20.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:4452f449dc6c1ea97cf69d1d9182749abd40e85bd826cd79652c06a627eafd91"
dependencies = [
"openzeppelin_access",
"openzeppelin_account",
"openzeppelin_introspection",
"openzeppelin_utils",
]

[[package]]
name = "openzeppelin_upgrades"
version = "0.20.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:15fdd63f6b50a0fda7b3f8f434120aaf7637bcdfe6fd8d275ad57343d5ede5e1"

[[package]]
name = "openzeppelin_utils"
version = "0.20.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:44f32d242af1e43982decc49c563e613a9b67ade552f5c3d5cde504e92f74607"

[[package]]
name = "snforge_scarb_plugin"
version = "0.43.1"
version = "0.44.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:178e1e2081003ae5e40b5a8574654bed15acbd31cce651d4e74fe2f009bc0122"
checksum = "sha256:ec8c7637b33392a53153c1e5b87a4617ddcb1981951b233ea043cad5136697e2"

[[package]]
name = "snforge_std"
version = "0.43.1"
version = "0.44.0"
source = "registry+https://scarbs.xyz/"
checksum = "sha256:17bc65b0abfb9b174784835df173f9c81c9ad39523dba760f30589ef53cf8bb5"
checksum = "sha256:d4affedfb90715b1ac417b915c0a63377ae6dd69432040e5d933130d65114702"
dependencies = [
"snforge_scarb_plugin",
]
Expand All @@ -20,5 +138,6 @@ dependencies = [
name = "Starknet_contracts"
version = "0.1.0"
dependencies = [
"openzeppelin",
"snforge_std",
]
5 changes: 3 additions & 2 deletions starknet_contracts/Scarb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ edition = "2024_07"
# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html

[dependencies]
Starknet = "2.11.4"
starknet = "2.11.4"
openzeppelin = "0.20.0"

[dev-dependencies]
snforge_std = "0.43.1"
snforge_std = "0.44.0"
assert_macros = "2.11.4"

[[target.Starknet-contract]]
Expand Down
Loading