Skip to content

Commit

Permalink
Merge branch 'NethermindEth:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
LamsyA authored Jul 21, 2024
2 parents 408514e + b442f2d commit 0dee037
Show file tree
Hide file tree
Showing 60 changed files with 3,771 additions and 439 deletions.
4 changes: 2 additions & 2 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
scarb 2.6.4
starknet-foundry 0.24.0
scarb 2.6.5
starknet-foundry 0.25.0
34 changes: 30 additions & 4 deletions Scarb.lock
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
# Code generated by scarb DO NOT EDIT.
version = 1

[[package]]
name = "advanced_factory"
version = "0.1.0"
dependencies = [
"alexandria_storage",
"components",
"crowdfunding",
"snforge_std",
]

[[package]]
name = "alexandria_storage"
version = "0.3.0"
Expand Down Expand Up @@ -44,6 +54,15 @@ version = "0.1.0"
name = "counter"
version = "0.1.0"

[[package]]
name = "crowdfunding"
version = "0.1.0"
dependencies = [
"components",
"openzeppelin",
"snforge_std",
]

[[package]]
name = "custom_type_serde"
version = "0.1.0"
Expand Down Expand Up @@ -98,13 +117,20 @@ dependencies = [

[[package]]
name = "openzeppelin"
version = "0.11.0"
source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.11.0#a83f36b23f1af6e160288962be4a2701c3ecbcda"
version = "0.14.0"
source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.14.0#f091c4f51ddeb10297db984acae965328c5a4e5b"

[[package]]
name = "scarb"
version = "0.1.0"

[[package]]
name = "simple_account"
version = "0.1.0"
dependencies = [
"openzeppelin",
]

[[package]]
name = "simple_vault"
version = "0.1.0"
Expand All @@ -114,8 +140,8 @@ dependencies = [

[[package]]
name = "snforge_std"
version = "0.24.0"
source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.24.0#95e9fb09cb91b3c05295915179ee1b55bf923653"
version = "0.25.0"
source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.25.0#5b366e24821e530fea97f11b211d220e8493fbea"

[[package]]
name = "staking"
Expand Down
8 changes: 5 additions & 3 deletions Scarb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ test = "$(git rev-parse --show-toplevel)/scripts/test_resolver.sh"
[workspace.tool.snforge]

[workspace.dependencies]
starknet = ">=2.6.3"
openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag="v0.11.0" }
starknet = ">=2.6.4"
openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag="v0.14.0" }
components = { path = "listings/applications/components" }
snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.24.0" }
snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.25.0" }
# The latest Alexandria release supports only Cairo v2.6.0, so using explicit rev that supports Cairo v2.6.3
alexandria_storage = { git = "https://github.com/keep-starknet-strange/alexandria.git", rev="800f5ad" }

[workspace.package]
description = "Collection of examples of how to use the Cairo programming language to create smart contracts on Starknet."
Expand Down
1 change: 1 addition & 0 deletions listings/advanced-concepts/simple_account/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target
6 changes: 6 additions & 0 deletions listings/advanced-concepts/simple_account/Scarb.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Code generated by scarb DO NOT EDIT.
version = 1

[[package]]
name = "ecdsa_verification"
version = "0.1.0"
14 changes: 14 additions & 0 deletions listings/advanced-concepts/simple_account/Scarb.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "simple_account"
version.workspace = true
edition = '2023_11'


[dependencies]
starknet.workspace = true
openzeppelin.workspace = true

[scripts]
test.workspace = true

[[target.starknet-contract]]
4 changes: 4 additions & 0 deletions listings/advanced-concepts/simple_account/src/lib.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
mod simple_account;

#[cfg(test)]
mod tests;
90 changes: 90 additions & 0 deletions listings/advanced-concepts/simple_account/src/simple_account.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
use starknet::account::Call;

#[starknet::interface]
trait ISRC6<TContractState> {
fn execute_calls(self: @TContractState, calls: Array<Call>) -> Array<Span<felt252>>;
fn validate_calls(self: @TContractState, calls: Array<Call>) -> felt252;
fn is_valid_signature(
self: @TContractState, hash: felt252, signature: Array<felt252>
) -> felt252;
}

#[starknet::contract]
mod simpleAccount {
use super::ISRC6;
use starknet::account::Call;
use core::num::traits::Zero;
use core::ecdsa::check_ecdsa_signature;

// Implement SRC5 with openzeppelin
use openzeppelin::account::interface;
use openzeppelin::introspection::src5::SRC5Component;
component!(path: SRC5Component, storage: src5, event: SRC5Event);

#[abi(embed_v0)]
impl SRC5Impl = SRC5Component::SRC5Impl<ContractState>;
impl SRC5InternalImpl = SRC5Component::InternalImpl<ContractState>;

#[storage]
struct Storage {
#[substorage(v0)]
src5: SRC5Component::Storage,
public_key: felt252
}

#[constructor]
fn constructor(ref self: ContractState, public_key: felt252) {
self.src5.register_interface(interface::ISRC6_ID);
self.public_key.write(public_key);
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
SRC5Event: SRC5Component::Event
}

#[abi(embed_v0)]
impl SRC6 of ISRC6<ContractState> {
fn execute_calls(self: @ContractState, calls: Array<Call>) -> Array<Span<felt252>> {
assert(starknet::get_caller_address().is_zero(), 'Not Starknet Protocol');
let Call { to, selector, calldata } = calls.at(0);
let res = starknet::syscalls::call_contract_syscall(*to, *selector, *calldata).unwrap();
array![res]
}

fn validate_calls(self: @ContractState, calls: Array<Call>) -> felt252 {
assert(starknet::get_caller_address().is_zero(), 'Not Starknet Protocol');
let tx_info = starknet::get_tx_info().unbox();
let tx_hash = tx_info.transaction_hash;
let signature = tx_info.signature;
if self._is_valid_signature(tx_hash, signature) {
starknet::VALIDATED
} else {
0
}
}

fn is_valid_signature(
self: @ContractState, hash: felt252, signature: Array<felt252>
) -> felt252 {
if self._is_valid_signature(hash, signature.span()) {
starknet::VALIDATED
} else {
0
}
}
}

#[generate_trait]
impl SignatureVerificationImpl of SignatureVerification {
fn _is_valid_signature(
self: @ContractState, hash: felt252, signature: Span<felt252>
) -> bool {
check_ecdsa_signature(
hash, self.public_key.read(), *signature.at(0_u32), *signature.at(1_u32)
)
}
}
}
3 changes: 3 additions & 0 deletions listings/advanced-concepts/simple_account/src/tests.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#[cfg(test)]
mod tests { // TODO
}
2 changes: 1 addition & 1 deletion listings/advanced-concepts/using_lists/Scarb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ edition = '2023_11'

[dependencies]
starknet.workspace = true
alexandria_storage = { git = "https://github.com/keep-starknet-strange/alexandria.git", rev="800f5ad"}
alexandria_storage.workspace = true

[scripts]
test.workspace = true
Expand Down
2 changes: 2 additions & 0 deletions listings/applications/advanced_factory/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
target
.snfoundry_cache/
18 changes: 18 additions & 0 deletions listings/applications/advanced_factory/Scarb.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "advanced_factory"
version.workspace = true
edition = "2023_11"

[dependencies]
starknet.workspace = true
components.workspace = true
alexandria_storage.workspace = true
snforge_std.workspace = true
crowdfunding = { path = "../crowdfunding" }

[scripts]
test.workspace = true

[[target.starknet-contract]]
casm = true
build-external-contracts = ["crowdfunding::campaign::Campaign"]
152 changes: 152 additions & 0 deletions listings/applications/advanced_factory/src/contract.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// ANCHOR: contract
pub use starknet::{ContractAddress, ClassHash};

#[starknet::interface]
pub trait ICampaignFactory<TContractState> {
fn create_campaign(
ref self: TContractState,
title: ByteArray,
description: ByteArray,
goal: u256,
start_time: u64,
end_time: u64,
token_address: ContractAddress
) -> ContractAddress;
fn get_campaign_class_hash(self: @TContractState) -> ClassHash;
fn update_campaign_class_hash(ref self: TContractState, new_class_hash: ClassHash);
fn upgrade_campaign(
ref self: TContractState, campaign_address: ContractAddress, new_end_time: Option<u64>
);
}

#[starknet::contract]
pub mod CampaignFactory {
use core::num::traits::zero::Zero;
use starknet::{
ContractAddress, ClassHash, SyscallResultTrait, syscalls::deploy_syscall,
get_caller_address, get_contract_address
};
use alexandria_storage::list::{List, ListTrait};
use crowdfunding::campaign::{ICampaignDispatcher, ICampaignDispatcherTrait};
use components::ownable::ownable_component;

component!(path: ownable_component, storage: ownable, event: OwnableEvent);

#[abi(embed_v0)]
impl OwnableImpl = ownable_component::Ownable<ContractState>;
impl OwnableInternalImpl = ownable_component::OwnableInternalImpl<ContractState>;

#[storage]
struct Storage {
#[substorage(v0)]
ownable: ownable_component::Storage,
/// Store all of the created campaign instances' addresses and thei class hashes
campaigns: LegacyMap<(ContractAddress, ContractAddress), ClassHash>,
/// Store the class hash of the contract to deploy
campaign_class_hash: ClassHash,
}

#[event]
#[derive(Drop, starknet::Event)]
pub enum Event {
#[flat]
OwnableEvent: ownable_component::Event,
CampaignClassHashUpgraded: CampaignClassHashUpgraded,
CampaignCreated: CampaignCreated,
ClassHashUpdated: ClassHashUpdated,
}

#[derive(Drop, starknet::Event)]
pub struct ClassHashUpdated {
pub new_class_hash: ClassHash,
}

#[derive(Drop, starknet::Event)]
pub struct CampaignClassHashUpgraded {
pub campaign: ContractAddress,
}

#[derive(Drop, starknet::Event)]
pub struct CampaignCreated {
pub creator: ContractAddress,
pub contract_address: ContractAddress
}

pub mod Errors {
pub const CLASS_HASH_ZERO: felt252 = 'Class hash cannot be zero';
pub const ZERO_ADDRESS: felt252 = 'Zero address';
pub const SAME_IMPLEMENTATION: felt252 = 'Implementation is unchanged';
pub const CAMPAIGN_NOT_FOUND: felt252 = 'Campaign not found';
}

#[constructor]
fn constructor(ref self: ContractState, class_hash: ClassHash) {
assert(class_hash.is_non_zero(), Errors::CLASS_HASH_ZERO);
self.campaign_class_hash.write(class_hash);
self.ownable._init(get_caller_address());
}


#[abi(embed_v0)]
impl CampaignFactory of super::ICampaignFactory<ContractState> {
fn create_campaign(
ref self: ContractState,
title: ByteArray,
description: ByteArray,
goal: u256,
start_time: u64,
end_time: u64,
token_address: ContractAddress,
) -> ContractAddress {
let creator = get_caller_address();

// Create contructor arguments
let mut constructor_calldata: Array::<felt252> = array![];
((creator, title, description, goal), start_time, end_time, token_address)
.serialize(ref constructor_calldata);

// Contract deployment
let (contract_address, _) = deploy_syscall(
self.campaign_class_hash.read(), 0, constructor_calldata.span(), false
)
.unwrap_syscall();

// track new campaign instance
self.campaigns.write((creator, contract_address), self.campaign_class_hash.read());

self.emit(Event::CampaignCreated(CampaignCreated { creator, contract_address }));

contract_address
}

fn get_campaign_class_hash(self: @ContractState) -> ClassHash {
self.campaign_class_hash.read()
}

fn update_campaign_class_hash(ref self: ContractState, new_class_hash: ClassHash) {
self.ownable._assert_only_owner();
assert(new_class_hash.is_non_zero(), Errors::CLASS_HASH_ZERO);

self.campaign_class_hash.write(new_class_hash);

self.emit(Event::ClassHashUpdated(ClassHashUpdated { new_class_hash }));
}

fn upgrade_campaign(
ref self: ContractState, campaign_address: ContractAddress, new_end_time: Option<u64>
) {
assert(campaign_address.is_non_zero(), Errors::ZERO_ADDRESS);

let creator = get_caller_address();
let old_class_hash = self.campaigns.read((creator, campaign_address));
assert(old_class_hash.is_non_zero(), Errors::CAMPAIGN_NOT_FOUND);
assert(old_class_hash != self.campaign_class_hash.read(), Errors::SAME_IMPLEMENTATION);

let campaign = ICampaignDispatcher { contract_address: campaign_address };
campaign.upgrade(self.campaign_class_hash.read(), new_end_time);
}
}
}
// ANCHOR_END: contract


Loading

0 comments on commit 0dee037

Please sign in to comment.