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
5 changes: 5 additions & 0 deletions staking_contract/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
target
.snfoundry_cache/
snfoundry_trace/
coverage/
profile/
1 change: 1 addition & 0 deletions staking_contract/.tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
scarb 2.11.4
14 changes: 14 additions & 0 deletions staking_contract/Scarb.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Code generated by scarb DO NOT EDIT.
version = 1

[[package]]
name = "snforge_std"
version = "0.22.0"
source = "git+https://github.com/foundry-rs/starknet-foundry?tag=v0.22.0#9b215944c6c5871c738381b4ded61bbf06e7ba35"

[[package]]
name = "staking_contract"
version = "0.1.0"
dependencies = [
"snforge_std",
]
17 changes: 17 additions & 0 deletions staking_contract/Scarb.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "staking_contract"
version = "0.1.0"
edition = "2024_07"

[dependencies]
starknet = "2.11.4"

[dev-dependencies]
snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", rev = "f994c8b" }
assert_macros = "2.11.4"

[[target.starknet-contract]]
sierra = true

[scripts]
test = "snforge test"
Empty file.
11 changes: 11 additions & 0 deletions staking_contract/snfoundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Visit https://foundry-rs.github.io/starknet-foundry/appendix/snfoundry-toml.html
# and https://foundry-rs.github.io/starknet-foundry/projects/configuration.html for more information

# [sncast.default] # Define a profile name
# url = "https://starknet-sepolia.public.blastapi.io/rpc/v0_9" # Url of the RPC provider
# accounts-file = "../account-file" # Path to the file with the account data
# account = "mainuser" # Account from `accounts_file` or default account file that will be used for the transactions
# keystore = "~/keystore" # Path to the keystore file
# wait-params = { timeout = 300, retry-interval = 10 } # Wait for submitted transaction parameters
# block-explorer = "StarkScan" # Block explorer service used to display links to transaction details
# show-explorer-links = true # Print links pointing to pages with transaction details in the chosen block explorer
3 changes: 3 additions & 0 deletions staking_contract/src/lib.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod staking;
mod stark_token;
mod reward_token;
78 changes: 78 additions & 0 deletions staking_contract/src/reward_token.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// reward_token.cairo
%lang starknet

use starknet::ContractAddress;
use core::integer::u256;
use starknet::storage::LegacyMap;
use starknet::event;

#[storage]
struct Storage {
balances: LegacyMap<ContractAddress, u256>,
total_supply: u256,
name: felt252,
symbol: felt252
}

#[event]
struct Transfer {
from: ContractAddress,
to: ContractAddress,
value: u256
}

#[contract]
mod RewardToken {
use super::{Storage, Transfer};
use starknet::ContractAddress;
use core::integer::u256;

#[constructor]
fn constructor(
ref self: Storage,
name: felt252,
symbol: felt252,
initial_supply: u256,
owner: ContractAddress
) {
self.name = name;
self.symbol = symbol;
self.total_supply = initial_supply;
self.balances.write(owner, initial_supply);
emit!(Transfer { from: ContractAddress::from(0), to: owner, value: initial_supply });
}

#[external(v0)]
fn name(self: @Storage) -> felt252 {
self.name
}

#[external(v0)]
fn symbol(self: @Storage) -> felt252 {
self.symbol
}

#[external(v0)]
fn totalSupply(self: @Storage) -> u256 {
self.total_supply
}

#[external(v0)]
fn balanceOf(self: @Storage, owner: ContractAddress) -> u256 {
self.balances.read(owner)
}

#[external(v0)]
fn transfer(ref self: Storage, to: ContractAddress, amount: u256) {
let sender = starknet::get_caller_address();
let sender_balance = self.balances.read(sender);
assert(sender_balance >= amount, 'Insufficient balance');

self.balances.write(sender, sender_balance - amount);

let receiver_balance = self.balances.read(to);
self.balances.write(to, receiver_balance + amount);

emit!(Transfer { from: sender, to, value: amount });
}
}
93 changes: 93 additions & 0 deletions staking_contract/src/staking.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
%lang starknet

use starknet::ContractAddress;
use core::integer::u256;
use starknet::storage::LegacyMap;

#[storage]
struct Storage {
staking_token: ContractAddress,
reward_token: ContractAddress,
stakes: LegacyMap<ContractAddress, u256>,
rewards: LegacyMap<ContractAddress, u256>,
}

#[contract]
mod StakingContract {
use super::{Storage};
use starknet::ContractAddress;
use core::integer::u256;

// --------------------------
// Constructor
// --------------------------
#[constructor]
fn constructor(
ref self: Storage,
staking_token: ContractAddress,
reward_token: ContractAddress
) {
self.staking_token = staking_token;
self.reward_token = reward_token;
}

// --------------------------
// Public Functions
// --------------------------

/// Stake tokens (user should have approved this contract beforehand if needed).
#[external(v0)]
fn stake(ref self: Storage, amount: u256) {
let caller = starknet::get_caller_address();
assert(amount > 0, 'Amount must be > 0');

// Update stake balance
let current_stake = self.stakes.read(caller);
self.stakes.write(caller, current_stake + amount);

// For simplicity, reward = 10% of staked amount
let reward = amount / 10;
let current_reward = self.rewards.read(caller);
self.rewards.write(caller, current_reward + reward);
}

/// Withdraw staked tokens
#[external(v0)]
fn withdraw(ref self: Storage, amount: u256) {
let caller = starknet::get_caller_address();
let current_stake = self.stakes.read(caller);
assert(current_stake >= amount, 'Not enough staked');

self.stakes.write(caller, current_stake - amount);
}

/// Claim rewards (distributes RewardToken)
#[external(v0)]
fn claim_rewards(ref self: Storage) {
let caller = starknet::get_caller_address();
let reward_amount = self.rewards.read(caller);
assert(reward_amount > 0, 'No rewards available');

// Reset rewards before transferring
self.rewards.write(caller, 0);

// Call into RewardToken contract to transfer rewards
starknet::call_contract_syscall(
self.reward_token,
'transfer',
(caller, reward_amount)
).unwrap();
}

/// View: check stake balance
#[external(v0)]
fn get_stake(self: @Storage, user: ContractAddress) -> u256 {
self.stakes.read(user)
}

/// View: check pending rewards
#[external(v0)]
fn get_rewards(self: @Storage, user: ContractAddress) -> u256 {
self.rewards.read(user)
}
}
72 changes: 72 additions & 0 deletions staking_contract/src/stark_token.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// stark_token.cairo
%lang starknet

use starknet::ContractAddress;
use core::integer::u256;
use starknet::storage::LegacyMap;
use starknet::event;

#[storage]
struct Storage {
balances: LegacyMap<ContractAddress, u256>,
total_supply: u256,
name: felt252,
symbol: felt252
}

#[event]
struct Transfer {
from: ContractAddress,
to: ContractAddress,
value: u256
}

#[contract]
mod StarkToken {
use super::{Storage, Transfer};
use starknet::ContractAddress;
use core::integer::u256;

#[constructor]
fn constructor(ref self: Storage, name: felt252, symbol: felt252, initial_supply: u256, owner: ContractAddress) {
self.name = name;
self.symbol = symbol;
self.total_supply = initial_supply;
self.balances.write(owner, initial_supply);
emit!(Transfer { from: ContractAddress::from(0), to: owner, value: initial_supply });
}

#[external(v0)]
fn name(self: @Storage) -> felt252 {
self.name
}

#[external(v0)]
fn symbol(self: @Storage) -> felt252 {
self.symbol
}

#[external(v0)]
fn totalSupply(self: @Storage) -> u256 {
self.total_supply
}

#[external(v0)]
fn balanceOf(self: @Storage, owner: ContractAddress) -> u256 {
self.balances.read(owner)
}

#[external(v0)]
fn transfer(ref self: Storage, to: ContractAddress, amount: u256) {
let sender = starknet::get_caller_address();
let sender_balance = self.balances.read(sender);
assert(sender_balance >= amount, 'Insufficient balance');

self.balances.write(sender, sender_balance - amount);

let receiver_balance = self.balances.read(to);
self.balances.write(to, receiver_balance + amount);

emit!(Transfer { from: sender, to, value: amount });
}
}
47 changes: 47 additions & 0 deletions staking_contract/tests/test_contract.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use starknet::ContractAddress;

use snforge_std_deprecated::{declare, ContractClassTrait, DeclareResultTrait};

use staking_contract::IHelloStarknetSafeDispatcher;
use staking_contract::IHelloStarknetSafeDispatcherTrait;
use staking_contract::IHelloStarknetDispatcher;
use staking_contract::IHelloStarknetDispatcherTrait;

fn deploy_contract(name: ByteArray) -> ContractAddress {
let contract = declare(name).unwrap().contract_class();
let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap();
contract_address
}

#[test]
fn test_increase_balance() {
let contract_address = deploy_contract("HelloStarknet");

let dispatcher = IHelloStarknetDispatcher { contract_address };

let balance_before = dispatcher.get_balance();
assert(balance_before == 0, 'Invalid balance');

dispatcher.increase_balance(42);

let balance_after = dispatcher.get_balance();
assert(balance_after == 42, 'Invalid balance');
}

#[test]
#[feature("safe_dispatcher")]
fn test_cannot_increase_balance_with_zero_value() {
let contract_address = deploy_contract("HelloStarknet");

let safe_dispatcher = IHelloStarknetSafeDispatcher { contract_address };

let balance_before = safe_dispatcher.get_balance().unwrap();
assert(balance_before == 0, 'Invalid balance');

match safe_dispatcher.increase_balance(0) {
Result::Ok(_) => core::panic_with_felt252('Should have panicked'),
Result::Err(panic_data) => {
assert(*panic_data.at(0) == 'Amount cannot be 0', *panic_data.at(0));
}
};
}
1 change: 1 addition & 0 deletions starknet_contracts/.tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
scarb 2.11.4
2 changes: 1 addition & 1 deletion starknet_contracts/Scarb.lock
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ dependencies = [
]

[[package]]
name = "Starknet_contracts"
name = "starknet_contracts"
version = "0.1.0"
dependencies = [
"snforge_std",
Expand Down
6 changes: 3 additions & 3 deletions starknet_contracts/Scarb.toml
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
[package]
name = "Starknet_contracts"
name = "starknet_contracts"
version = "0.1.0"
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"

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

[[target.Starknet-contract]]
[[target.starknet-contract]]
sierra = true

[scripts]
Expand Down
Loading