This repository has been archived by the owner on Jun 20, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 488
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #284 from Nonnyjoe/main
Added piggyBank contract example
- Loading branch information
Showing
10 changed files
with
857 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
target | ||
.snfoundry_cache/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 = "piggy_bank" | ||
version = "0.1.0" | ||
dependencies = [ | ||
"snforge_std", | ||
] | ||
|
||
[[package]] | ||
name = "snforge_std" | ||
version = "0.1.0" | ||
source = "git+https://github.com/foundry-rs/starknet-foundry?tag=v0.13.1#e1412ed040d10e66be0fd84115f72a667b57a116" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
[package] | ||
name = "piggy_bank" | ||
version = "0.1.0" | ||
edition = "2023_10" | ||
|
||
# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html | ||
|
||
[dependencies] | ||
snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry", tag = "v0.13.1" } | ||
starknet = "2.4.1" | ||
|
||
[[target.starknet-contract]] | ||
casm = true | ||
|
||
[[tool.snforge.fork]] | ||
name = "GoerliFork" | ||
url = "https://starknet-testnet.public.blastapi.io/rpc/v0_6" | ||
block_id.tag = "Latest" | ||
|
||
[[tool.snforge.fork]] | ||
name = "SepoliaFork" | ||
url = "https://starknet-sepolia.public.blastapi.io/rpc/v0_6" | ||
block_id.tag = "Latest" | ||
|
||
RUST_BACKTRACE=1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
mod piggy_bank; | ||
mod ownership_component; | ||
mod piggy_factory; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
use starknet::ContractAddress; | ||
#[starknet::interface] | ||
trait IOwnable<TContractState> { | ||
fn owner(self: @TContractState) -> ContractAddress; | ||
fn transfer_ownership(ref self: TContractState, new_owner: ContractAddress); | ||
fn renounce_ownership(ref self: TContractState); | ||
} | ||
|
||
#[starknet::component] | ||
mod ownable_component { | ||
use starknet::{get_caller_address, ContractAddress, get_contract_address, Zeroable, get_block_timestamp}; | ||
use super::Errors; | ||
|
||
#[storage] | ||
struct Storage { | ||
owner: ContractAddress | ||
} | ||
|
||
#[event] | ||
#[derive(Drop, starknet::Event)] | ||
enum Event { | ||
OwnershipTransferred: OwnershipTransferred | ||
} | ||
|
||
#[derive(Drop, starknet::Event)] | ||
struct OwnershipTransferred { | ||
previous_owner: ContractAddress, | ||
new_owner: ContractAddress, | ||
} | ||
|
||
#[embeddable_as(Ownable)] | ||
impl OwnableImpl< | ||
TContractState, +HasComponent<TContractState>> of super::IOwnable<ComponentState<TContractState>> { | ||
fn owner(self: @ComponentState<TContractState>) -> ContractAddress { | ||
self.owner.read() | ||
} | ||
|
||
fn transfer_ownership( | ||
ref self: ComponentState<TContractState>, new_owner: ContractAddress | ||
) { | ||
assert(!new_owner.is_zero(), Errors::ZERO_ADDRESS_OWNER); | ||
self.assert_only_owner(); | ||
self._transfer_ownership(new_owner); | ||
} | ||
|
||
fn renounce_ownership(ref self: ComponentState<TContractState>) { | ||
self.assert_only_owner(); | ||
self._transfer_ownership(Zeroable::zero()); | ||
} | ||
} | ||
|
||
#[generate_trait] | ||
impl InternalImpl<TContractState, +HasComponent<TContractState>> of InternalTrait<TContractState> { | ||
fn initializer(ref self: ComponentState<TContractState>, owner: ContractAddress) { | ||
self._transfer_ownership(owner); | ||
} | ||
|
||
fn assert_only_owner(self: @ComponentState<TContractState>) { | ||
let owner: ContractAddress = self.owner.read(); | ||
let caller: ContractAddress = get_caller_address(); | ||
assert(!caller.is_zero(), Errors::ZERO_ADDRESS_CALLER); | ||
assert(caller == owner, Errors::NOT_OWNER); | ||
} | ||
|
||
fn _transfer_ownership( | ||
ref self: ComponentState<TContractState>, new_owner: ContractAddress | ||
) { | ||
let previous_owner: ContractAddress = self.owner.read(); | ||
self.owner.write(new_owner); | ||
self | ||
.emit( | ||
OwnershipTransferred { previous_owner: previous_owner, new_owner: new_owner } | ||
); | ||
} | ||
} | ||
} | ||
|
||
mod Errors { | ||
const NOT_OWNER: felt252 = 'Caller is not the owner'; | ||
const ZERO_ADDRESS_CALLER: felt252 = 'Caller is the zero address'; | ||
const ZERO_ADDRESS_OWNER: felt252 = 'New owner is the zero address'; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
use starknet::ContractAddress; | ||
|
||
#[derive(Drop, Serde, starknet::Store)] | ||
enum target { | ||
blockTime: u128, | ||
amount: u128, | ||
} | ||
|
||
#[starknet::interface] | ||
trait IERC20<TContractState> { | ||
fn name(self: @TContractState) -> felt252; | ||
fn symbol(self: @TContractState) -> felt252; | ||
fn decimals(self: @TContractState) -> u8; | ||
fn total_supply(self: @TContractState) -> u256; | ||
fn balanceOf(self: @TContractState, account: ContractAddress) -> u256; | ||
fn allowance(self: @TContractState, owner: ContractAddress, spender: ContractAddress) -> u256; | ||
fn transfer(ref self: TContractState, recipient: ContractAddress, amount: u256) -> bool; | ||
fn transferFrom( | ||
ref self: TContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256 | ||
) -> bool; | ||
fn approve(ref self: TContractState, spender: ContractAddress, amount: u256) -> bool; | ||
} | ||
|
||
#[starknet::interface] | ||
trait piggyBankTrait<TContractState> { | ||
fn deposit(ref self: TContractState, _amount: u128); | ||
fn withdraw(ref self: TContractState, _amount: u128); | ||
fn get_balance(self: @TContractState) -> u128; | ||
fn get_Target(self: @TContractState) -> (u128 , piggyBank::targetOption) ; | ||
fn get_owner(self: @TContractState) -> ContractAddress; | ||
fn viewTarget(self: @TContractState) -> target; | ||
} | ||
|
||
#[starknet::contract] | ||
mod piggyBank { | ||
use core::option::OptionTrait; | ||
use core::traits::TryInto; | ||
use starknet::{get_caller_address, ContractAddress, get_contract_address, Zeroable, get_block_timestamp}; | ||
use super::{IERC20Dispatcher, IERC20DispatcherTrait, target}; | ||
use core::traits::Into; | ||
use piggy_bank::ownership_component::ownable_component; | ||
component!(path: ownable_component, storage: ownable, event: OwnableEvent); | ||
|
||
|
||
#[abi(embed_v0)] | ||
impl OwnableImpl = ownable_component::Ownable<ContractState>; | ||
impl OwnableInternalImpl = ownable_component::InternalImpl<ContractState>; | ||
|
||
#[storage] | ||
struct Storage { | ||
token: IERC20Dispatcher, | ||
manager: ContractAddress, | ||
balance: u128, | ||
withdrawalCondition: target, | ||
#[substorage(v0)] | ||
ownable: ownable_component::Storage | ||
} | ||
|
||
#[derive(Drop, Serde)] | ||
enum targetOption { | ||
targetTime, | ||
targetAmount, | ||
} | ||
|
||
#[event] | ||
#[derive(Drop, starknet::Event)] | ||
enum Event { | ||
Deposit: Deposit, | ||
Withdraw: Withdraw, | ||
PaidProcessingFee: PaidProcessingFee, | ||
OwnableEvent: ownable_component::Event | ||
} | ||
|
||
#[derive(Drop, starknet::Event)] | ||
struct Deposit { | ||
#[key] | ||
from: ContractAddress, | ||
#[key] | ||
Amount: u128, | ||
} | ||
|
||
#[derive(Drop, starknet::Event)] | ||
struct Withdraw { | ||
#[key] | ||
to: ContractAddress, | ||
#[key] | ||
Amount: u128, | ||
#[key] | ||
ActualAmount: u128, | ||
} | ||
|
||
#[derive(Drop, starknet::Event)] | ||
struct PaidProcessingFee { | ||
#[key] | ||
from: ContractAddress, | ||
#[key] | ||
Amount: u128, | ||
} | ||
|
||
mod Errors { | ||
const Address_Zero_Owner: felt252 = 'Invalid owner'; | ||
const Address_Zero_Token: felt252 = 'Invalid Token'; | ||
const UnAuthorized_Caller: felt252 = 'UnAuthorized caller'; | ||
const Insufficient_Balance: felt252 = 'Insufficient balance'; | ||
} | ||
|
||
#[constructor] | ||
fn constructor(ref self: ContractState, _owner: ContractAddress, _token: ContractAddress, _manager: ContractAddress, target: targetOption, targetDetails: u128) { | ||
assert(!_owner.is_zero(), Errors::Address_Zero_Owner); | ||
assert(!_token.is_zero(), Errors::Address_Zero_Token); | ||
self.ownable.owner.write(_owner); | ||
self.token.write(super::IERC20Dispatcher{contract_address: _token}); | ||
self.manager.write(_manager); | ||
match target { | ||
targetOption::targetTime => self.withdrawalCondition.write(target::blockTime(targetDetails.into())), | ||
targetOption::targetAmount => self.withdrawalCondition.write(target::amount(targetDetails)), | ||
} | ||
} | ||
|
||
#[external(v0)] | ||
impl piggyBankImpl of super::piggyBankTrait<ContractState> { | ||
fn deposit(ref self: ContractState, _amount: u128) { | ||
let (caller, this, currentBalance) = self.getImportantAddresses(); | ||
self.balance.write(currentBalance + _amount); | ||
|
||
self.token.read().transferFrom(caller, this, _amount.into()); | ||
|
||
self.emit(Deposit { from: caller, Amount: _amount}); | ||
} | ||
|
||
fn withdraw(ref self: ContractState, _amount: u128) { | ||
self.ownable.assert_only_owner(); | ||
let (caller, this, currentBalance) = self.getImportantAddresses(); | ||
assert(self.balance.read() >= _amount, Errors::Insufficient_Balance); | ||
|
||
let mut new_amount: u128 = 0; | ||
match self.withdrawalCondition.read() { | ||
target::blockTime(x) => new_amount = self.verifyBlockTime(x, _amount), | ||
target::amount(x) => new_amount = self.verifyTargetAmount(x, _amount), | ||
}; | ||
|
||
self.balance.write(currentBalance - _amount); | ||
self.token.read().transfer(caller, new_amount.into()); | ||
|
||
self.emit(Withdraw { to: caller, Amount: _amount, ActualAmount: new_amount}); | ||
} | ||
|
||
fn get_balance(self: @ContractState) -> u128 { | ||
self.balance.read() | ||
} | ||
|
||
fn get_Target(self: @ContractState) -> (u128 , targetOption) { | ||
let condition = self.withdrawalCondition.read(); | ||
match condition { | ||
target::blockTime(x) => {return (x, targetOption::targetTime);}, | ||
target::amount(x) => {return (x, targetOption::targetAmount);}, | ||
} | ||
} | ||
|
||
fn get_owner(self: @ContractState) -> ContractAddress { | ||
self.ownable.owner() | ||
} | ||
|
||
fn viewTarget(self: @ContractState) -> target { | ||
self.withdrawalCondition.read() | ||
} | ||
|
||
} | ||
|
||
#[generate_trait] | ||
impl Private of PrivateTrait { | ||
fn verifyBlockTime(ref self: ContractState, blockTime: u128, withdrawalAmount: u128) -> u128 { | ||
if (blockTime <= get_block_timestamp().into()) { | ||
return withdrawalAmount; | ||
} else { | ||
return self.processWithdrawalFee(withdrawalAmount); | ||
} | ||
} | ||
|
||
fn verifyTargetAmount(ref self: ContractState, targetAmount: u128, withdrawalAmount: u128) -> u128 { | ||
if (self.balance.read() < targetAmount) { | ||
return self.processWithdrawalFee(withdrawalAmount); | ||
} else { | ||
return withdrawalAmount; | ||
} | ||
} | ||
|
||
fn processWithdrawalFee(ref self: ContractState, withdrawalAmount: u128) -> u128 { | ||
let withdrawalCharge: u128 = ((withdrawalAmount * 10) / 100); | ||
self.balance.write(self.balance.read() - withdrawalCharge); | ||
self.token.read().transfer(self.manager.read(), withdrawalCharge.into()); | ||
self.emit(PaidProcessingFee{from: get_caller_address(), Amount: withdrawalCharge}); | ||
return withdrawalAmount - withdrawalCharge; | ||
} | ||
|
||
fn getImportantAddresses(self: @ContractState) -> (ContractAddress, ContractAddress, u128) { | ||
let caller: ContractAddress = get_caller_address(); | ||
let this: ContractAddress = get_contract_address(); | ||
let currentBalance: u128 = self.balance.read(); | ||
(caller, this, currentBalance) | ||
} | ||
} | ||
} |
Oops, something went wrong.