Skip to content
Merged
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
1 change: 1 addition & 0 deletions cloakpay/src/base/errors.cairo
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod CloakPayErrors {
pub const UNSUPPORTED_TOKEN: felt252 = 'UNSUPPORTED TOKEN';
pub const COMMITMENT_ALREADY_USED: felt252 = 'COMMITMENT ALREADY USED';
pub const ERROR_ZERO_ADDRESS: felt252 = 'ERROR ZERO ADDRESS';
}

pub mod payment_errors {
Expand Down
23 changes: 23 additions & 0 deletions cloakpay/src/base/events.cairo
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use starknet::ContractAddress;
use crate::base::types::DepositDetails;

pub mod Events {
Expand All @@ -8,4 +9,26 @@ pub mod Events {
pub deposit_id: u256,
pub details: DepositDetails,
}

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

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

#[derive(Drop, starknet::Event)]
pub struct Paused {
pub account: ContractAddress,
pub timestamp: u64,
}

#[derive(Drop, starknet::Event)]
pub struct Unpaused {
pub account: ContractAddress,
pub timestamp: u64,
}
}
163 changes: 158 additions & 5 deletions cloakpay/src/cloakpay.cairo
Original file line number Diff line number Diff line change
@@ -1,35 +1,87 @@
#[starknet::contract]
pub mod cloakpay {
use core::num::traits::Zero;
use openzeppelin::access::accesscontrol::AccessControlComponent;
use openzeppelin::access::ownable::OwnableComponent;
use openzeppelin::introspection::src5::SRC5Component;
use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait};
use starknet::storage::{
Map, StorageMapReadAccess, StoragePathEntry, StoragePointerReadAccess,
StoragePointerWriteAccess,
Map, MutableVecTrait, StorageMapReadAccess, StoragePathEntry, StoragePointerReadAccess,
StoragePointerWriteAccess, Vec, VecTrait,
};
use starknet::{ContractAddress, get_block_timestamp, get_caller_address, get_contract_address};
use crate::base::errors::{CloakPayErrors, payment_errors};
use crate::base::events::Events::DepositEvent;
use crate::base::events::Events::*;
use crate::base::types::DepositDetails;
use crate::interfaces::ICloakpay::ICloakPay;
const ADMIN_ROLE: felt252 = selector!("ADMIN");
const OVERALL_ADMIN_ROLE: felt252 = selector!("OVERALL_ADMIN_ROLE");

component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);
component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent);
component!(path: SRC5Component, storage: src5, event: SRC5Event);


#[abi(embed_v0)]
impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl<ContractState>;
impl InternalImpl = OwnableComponent::InternalImpl<ContractState>;
// AccessControl
#[abi(embed_v0)]
impl AccessControlImpl =
AccessControlComponent::AccessControlImpl<ContractState>;
impl AccessControlInternalImpl = AccessControlComponent::InternalImpl<ContractState>;

// SRC5
#[abi(embed_v0)]
impl SRC5Impl = SRC5Component::SRC5Impl<ContractState>;
#[storage]
struct Storage {
deposit: Map<u256, DepositDetails>, // deposit id to deposit details
commitments: Map<felt252, bool>, // commitment to use status
commitment_to_deposit_id: Map<felt252, u256>, // commitment to deposit id
total_deposits: u256,
supported_tokens: Map<u256, ContractAddress> // supported token_id to contract address
supported_tokens: Map<u256, ContractAddress>, // supported token_id to contract address
supported_tokens_status: Map<ContractAddress, bool>, // token address to supported status
supported_tokens_list: Vec<ContractAddress>, // list of supported token addresses
paused: bool, // contract paused status
#[substorage(v0)]
ownable: OwnableComponent::Storage,
#[substorage(v0)]
accesscontrol: AccessControlComponent::Storage,
#[substorage(v0)]
src5: SRC5Component::Storage,
}

#[event]
#[derive(Drop, starknet::Event)]
pub enum Event {
DepositEvent: DepositEvent,
TokenAdded: TokenAdded,
TokenRemoved: TokenRemoved,
Paused: Paused,
Unpaused: Unpaused,
#[flat]
OwnableEvent: OwnableComponent::Event,
#[flat]
AccessControlEvent: AccessControlComponent::Event,
#[flat]
SRC5Event: SRC5Component::Event,
}

#[constructor]
fn constructor(ref self: ContractState, default_supported_token: ContractAddress) {
fn constructor(
ref self: ContractState, owner: ContractAddress, default_supported_token: ContractAddress,
) {
assert(!owner.is_zero(), CloakPayErrors::ERROR_ZERO_ADDRESS);
// initialize owner of contract
self.ownable.initializer(owner);
self.accesscontrol.initializer();
self.accesscontrol.set_role_admin(ADMIN_ROLE, OVERALL_ADMIN_ROLE);
self.accesscontrol._grant_role(ADMIN_ROLE, owner);
self.accesscontrol._grant_role(OVERALL_ADMIN_ROLE, owner);
self.supported_tokens.entry(1_u256).write(default_supported_token);
self.supported_tokens_status.entry(default_supported_token).write(true);
self.supported_tokens_list.push(default_supported_token);
self.total_deposits.write(0_u256);
}

Expand All @@ -39,6 +91,7 @@ pub mod cloakpay {
fn deposit(
ref self: ContractState, supported_token: u256, amount: u256, commitment: felt252,
) {
self._assert_not_paused();
assert(!self.commitments.read(commitment), CloakPayErrors::COMMITMENT_ALREADY_USED);
let supported_token = self.supported_tokens.read(supported_token);
assert(!supported_token.is_zero(), CloakPayErrors::UNSUPPORTED_TOKEN);
Expand Down Expand Up @@ -74,6 +127,66 @@ pub mod cloakpay {
fn get_deposit_id_from_commitment(ref self: ContractState, commitment: felt252) -> u256 {
self.commitment_to_deposit_id.read(commitment)
}


fn pause(ref self: ContractState) {
self.accesscontrol.assert_only_role(ADMIN_ROLE);
self.paused.write(true);
self
.emit(
Event::Paused(
Paused { account: get_caller_address(), timestamp: get_block_timestamp() },
),
);
}
fn resume(ref self: ContractState) {
self.accesscontrol.assert_only_role(ADMIN_ROLE);

self.paused.write(false);
self
.emit(
Event::Unpaused(
Unpaused {
account: get_caller_address(), timestamp: get_block_timestamp(),
},
),
);
}
fn get_paused_status(ref self: ContractState) -> bool {
self.paused.read()
}

fn add_supported_token(ref self: ContractState, token_address: ContractAddress) {
self.accesscontrol.assert_only_role(ADMIN_ROLE);
assert(!token_address.is_zero(), CloakPayErrors::ERROR_ZERO_ADDRESS);
self._assert_not_paused();

assert(!self.supported_tokens_status.read(token_address), 'Token already supported');

self.supported_tokens_status.entry(token_address).write(true);
self.supported_tokens_list.push(token_address);
self.emit(Event::TokenAdded(TokenAdded { token_address }));
}

fn remove_supported_token(ref self: ContractState, token_address: ContractAddress) {
self.accesscontrol.assert_only_role(ADMIN_ROLE);
assert(!token_address.is_zero(), CloakPayErrors::ERROR_ZERO_ADDRESS);
self._assert_not_paused();

assert(self.supported_tokens_status.read(token_address), 'Token not supported');

self.supported_tokens_status.entry(token_address).write(false);
self._remove_token_from_list(token_address);
self.emit(Event::TokenRemoved(TokenRemoved { token_address }));
}

fn is_token_supported(ref self: ContractState, token_address: ContractAddress) -> bool {
self.supported_tokens_status.read(token_address)
}

fn get_supported_tokens(ref self: ContractState) -> Array<ContractAddress> {
self._get_supported_tokens_array()
}
}

#[generate_trait]
Expand Down Expand Up @@ -105,5 +218,45 @@ pub mod cloakpay {
let balance = token.balance_of(caller);
assert(balance >= amount, payment_errors::INSUFFICIENT_BALANCE);
}

/// @notice Asserts that the contract is not paused.
fn _assert_not_paused(ref self: ContractState) {
assert(!self.paused.read(), 'CONTRACT IS PAUSED');
}

/// @notice Gets all supported tokens as an array
fn _get_supported_tokens_array(self: @ContractState) -> Array<ContractAddress> {
let mut tokens = ArrayTrait::<ContractAddress>::new();
let list_length = self.supported_tokens_list.len();
for i in 0..list_length {
let token_address = self.supported_tokens_list.at(i).read();
if self.supported_tokens_status.read(token_address) {
tokens.append(token_address);
}
}
tokens
}

/// @notice Removes a token from the supported tokens list
fn _remove_token_from_list(ref self: ContractState, token_address: ContractAddress) {
let mut temp_list = ArrayTrait::<ContractAddress>::new();
let list_length = self.supported_tokens_list.len();

for i in 0..list_length {
let current_token = self.supported_tokens_list.at(i).read();
if current_token != token_address {
temp_list.append(current_token);
}
}

for _ in 0..list_length {
let _ = self.supported_tokens_list.pop();
}

let temp_length = temp_list.len();
for i in 0..temp_length {
self.supported_tokens_list.push(*temp_list.at(i));
}
}
}
}
9 changes: 8 additions & 1 deletion cloakpay/src/interfaces/ICloakpay.cairo
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
use starknet::ContractAddress;
use crate::base::types::DepositDetails;
#[starknet::interface]
pub trait ICloakPay<TContractState> {
/// @notice This function allows users to deposit supported ERC20 tokens into the mixer.
fn deposit(ref self: TContractState, supported_token: u256, amount: u256, commitment: felt252);
fn get_deposit_details(ref self: TContractState, deposit_id: u256) -> DepositDetails;
fn get_total_deposits(ref self: TContractState) -> u256;
fn get_commitment_used_status(ref self: TContractState, commitment: felt252) -> bool;
fn get_deposit_id_from_commitment(ref self: TContractState, commitment: felt252) -> u256;
fn pause(ref self: TContractState);
fn resume(ref self: TContractState);
fn get_paused_status(ref self: TContractState) -> bool;
fn add_supported_token(ref self: TContractState, token_address: ContractAddress);
fn remove_supported_token(ref self: TContractState, token_address: ContractAddress);
fn is_token_supported(ref self: TContractState, token_address: ContractAddress) -> bool;
fn get_supported_tokens(ref self: TContractState) -> Array<ContractAddress>;
}
Loading
Loading