Skip to content

Commit

Permalink
Feat(contracts) fee mecanism (#4)
Browse files Browse the repository at this point in the history
* feat(contracts): fee mecanism init

* feat(contracts): refactor

* feat(address_whitelist): generalize address implementation

* feat(oo): add whitelist user verification

* fix(format): format code
  • Loading branch information
JordyRo1 authored Aug 22, 2024
1 parent 5493ef1 commit 8d8439c
Show file tree
Hide file tree
Showing 13 changed files with 434 additions and 97 deletions.
6 changes: 6 additions & 0 deletions optimistic_oracle/Scarb.lock
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,15 @@ version = "0.1.0"
dependencies = [
"alexandria_bytes",
"openzeppelin",
"pragma_lib",
"snforge_std",
]

[[package]]
name = "pragma_lib"
version = "1.0.0"
source = "git+https://github.com/astraly-labs/pragma-lib#fe9a46743254182ac331da488ccfc05e0185cdd0"

[[package]]
name = "snforge_std"
version = "0.22.0"
Expand Down
3 changes: 2 additions & 1 deletion optimistic_oracle/Scarb.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ edition = "2023_11"
starknet = "2.6.3"
openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git"}
alexandria_bytes = { git = "https://github.com/keep-starknet-strange/alexandria.git" }
pragma_lib = { git = "https://github.com/astraly-labs/pragma-lib" }


[dev-dependencies]
Expand All @@ -21,4 +22,4 @@ sierra = true
# Enable CASM codegen.
casm = true
# Emit Python-powered hints in order to run compiled CASM class with legacy Cairo VM.
casm-add-pythonic-hints = false
casm-add-pythonic-hints = false
110 changes: 77 additions & 33 deletions optimistic_oracle/src/contracts/common/address_whitelist.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod address_whitelist {
};
use optimistic_oracle::contracts::interfaces::IAddressWhitelist;
use openzeppelin::access::ownable::OwnableComponent;
use core::hash::{LegacyHash, HashStateTrait};
component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);
#[abi(embed_v0)]
impl OwnableImpl = OwnableComponent::OwnableImpl<ContractState>;
Expand All @@ -23,6 +24,25 @@ pub mod address_whitelist {
In,
}

#[derive(PartialEq, Drop, Serde, starknet::Store, Copy)]
pub enum WhitelistType {
Currency,
User,
}

// Implement LegacyHash for (WhitelistType, ContractAddress)
impl WhitelistKeyHash of LegacyHash<(WhitelistType, ContractAddress)> {
fn hash(state: felt252, value: (WhitelistType, ContractAddress)) -> felt252 {
let (whitelist_type, address) = value;
let whitelist_type_felt252 = match whitelist_type {
WhitelistType::Currency => { 0 },
WhitelistType::User => { 1 }
};
let mut state = LegacyHash::<felt252>::hash(state, whitelist_type_felt252);
LegacyHash::<ContractAddress>::hash(state, address)
}
}


// Store manual implementation (basic implementation panics if no default enum is defined for a given address, cf: https://github.com/starkware-libs/cairo/blob/b741c26c553fd9fa3246cee91fd5c637f225cdb9/crates/cairo-lang-starknet/src/plugin/derive/store.rs#L263)
impl StatusStoreImpl of Store<Status> {
Expand Down Expand Up @@ -86,16 +106,19 @@ pub mod address_whitelist {
#[derive(starknet::Event, Drop)]
pub struct AddedToWhitelist {
pub added_address: ContractAddress,
pub whitelist_type: WhitelistType,
}

#[derive(starknet::Event, Drop)]
pub struct RemovedFromWhitelist {
pub removed_address: ContractAddress,
pub whitelist_type: WhitelistType,
}

#[storage]
struct Storage {
whitelist_indices: LegacyMap::<ContractAddress, ContractAddress>,
whitelist: LegacyMap::<ContractAddress, Status>,
whitelist_indices: LegacyMap::<(WhitelistType, ContractAddress), ContractAddress>,
whitelist: LegacyMap::<(WhitelistType, ContractAddress), Status>,
#[substorage(v0)]
ownable: OwnableComponent::Storage,
#[substorage(v0)]
Expand All @@ -110,84 +133,103 @@ pub mod address_whitelist {

#[abi(embed_v0)]
impl IAddressWhitelistImpl of IAddressWhitelist<ContractState> {
fn add_to_whitelist(ref self: ContractState, new_element: ContractAddress) {
fn add_to_whitelist(
ref self: ContractState, new_element: ContractAddress, whitelist_type: WhitelistType
) {
self.ownable.assert_only_owner();
self.reentrancy_guard.start();
match self.get_status(new_element) {
match self.get_status(new_element, whitelist_type) {
Option::Some(status) => {
if (status == Status::In) {
self.reentrancy_guard.end();
return;
} else if (status == Status::Out) {
self.whitelist.write(new_element, Status::In);
self.emit(AddedToWhitelist { added_address: new_element });
self.whitelist.write((whitelist_type, new_element), Status::In);
self.emit(AddedToWhitelist { added_address: new_element, whitelist_type });
} else {
self.insert_to_whitelist(new_element);
self.whitelist.write(new_element, Status::In);
self.emit(AddedToWhitelist { added_address: new_element });
self.insert_to_whitelist(new_element, whitelist_type);
self.whitelist.write((whitelist_type, new_element), Status::In);
self.emit(AddedToWhitelist { added_address: new_element, whitelist_type });
}
},
Option::None => {
self.insert_to_whitelist(new_element);
self.whitelist.write(new_element, Status::In);
self.emit(AddedToWhitelist { added_address: new_element });
self.insert_to_whitelist(new_element, whitelist_type);
self.whitelist.write((whitelist_type, new_element), Status::In);
self.emit(AddedToWhitelist { added_address: new_element, whitelist_type });
}
}
self.reentrancy_guard.end();
}

fn remove_from_whitelist(ref self: ContractState, element_to_remove: ContractAddress) {
fn remove_from_whitelist(
ref self: ContractState,
element_to_remove: ContractAddress,
whitelist_type: WhitelistType
) {
self.ownable.assert_only_owner();
self.reentrancy_guard.start();
if (self.whitelist.read(element_to_remove) != Status::Out) {
self.whitelist.write(element_to_remove, Status::Out);
self.emit(RemovedFromWhitelist { removed_address: element_to_remove });
if (self.whitelist.read((whitelist_type, element_to_remove)) != Status::Out) {
self.whitelist.write((whitelist_type, element_to_remove), Status::Out);
self
.emit(
RemovedFromWhitelist { removed_address: element_to_remove, whitelist_type }
);
}
self.reentrancy_guard.end();
}

fn is_on_whitelist(self: @ContractState, element_to_check: ContractAddress) -> bool {
self.whitelist.read(element_to_check) == Status::In
fn is_on_whitelist(
self: @ContractState, element_to_check: ContractAddress, whitelist_type: WhitelistType
) -> bool {
self.whitelist.read((whitelist_type, element_to_check)) == Status::In
}

fn get_whitelist(self: @ContractState) -> Span<ContractAddress> {
self.build_whitelist_indices_array()
fn get_whitelist(
self: @ContractState, whitelist_type: WhitelistType
) -> Span<ContractAddress> {
self.build_whitelist_indices_array(whitelist_type)
}
}

#[generate_trait]
impl InternalTraitImpl of InternalTrait {
fn get_status(self: @ContractState, address: ContractAddress) -> Option<Status> {
match self.whitelist.read(address) {
fn get_status(
self: @ContractState, address: ContractAddress, whitelist_type: WhitelistType
) -> Option<Status> {
match self.whitelist.read((whitelist_type, address)) {
Status::None => Option::Some(Status::None),
Status::In => Option::Some(Status::In),
Status::Out => Option::Some(Status::Out),
_ => Option::None,
}
}


fn find_last_whitelist_indice(self: @ContractState) -> ContractAddress {
let mut current_indice = self.whitelist_indices.read(0.try_into().unwrap());
fn find_last_whitelist_indice(
self: @ContractState, whitelist_type: WhitelistType
) -> ContractAddress {
let mut current_indice = self
.whitelist_indices
.read((whitelist_type, 0.try_into().unwrap()));
loop {
let next_indice = self.whitelist_indices.read(current_indice);
let next_indice = self.whitelist_indices.read((whitelist_type, current_indice));
if next_indice == 0.try_into().unwrap() {
break current_indice;
}
current_indice = next_indice;
}
}

// Helper: builds a span of whitelist_indices from the storage map
fn build_whitelist_indices_array(self: @ContractState) -> Span<ContractAddress> {
fn build_whitelist_indices_array(
self: @ContractState, whitelist_type: WhitelistType
) -> Span<ContractAddress> {
let mut index = 0.try_into().unwrap();
let mut whitelist_indices = array![];
loop {
let indice = self.whitelist_indices.read(index);
let indice = self.whitelist_indices.read((whitelist_type, index));
if (indice == 0.try_into().unwrap()) {
break ();
}
if (self.whitelist.read(indice) == Status::In) {
if (self.whitelist.read((whitelist_type, indice)) == Status::In) {
whitelist_indices.append(indice);
}
index = indice;
Expand All @@ -196,9 +238,11 @@ pub mod address_whitelist {
whitelist_indices.span()
}

fn insert_to_whitelist(ref self: ContractState, new_element: ContractAddress) {
let last_index = self.find_last_whitelist_indice();
self.whitelist_indices.write(last_index, new_element);
fn insert_to_whitelist(
ref self: ContractState, new_element: ContractAddress, whitelist_type: WhitelistType
) {
let last_index = self.find_last_whitelist_indice(whitelist_type);
self.whitelist_indices.write((whitelist_type, last_index), new_element);
}
}
}
8 changes: 4 additions & 4 deletions optimistic_oracle/src/contracts/data_verification/store.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pub mod store {
use optimistic_oracle::contracts::interfaces::{IStoreDispatcher, IStore, IStoreDispatcherTrait};
component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);
use openzeppelin::token::erc20::interface::{ERC20ABIDispatcher, ERC20ABIDispatcherTrait};

use optimistic_oracle::contracts::optimistic_oracle_v1::optimistic_oracle_v1::ETH_ADDRESS;
use openzeppelin::access::ownable::OwnableComponent;
#[abi(embed_v0)]
impl OwnableImpl = OwnableComponent::OwnableImpl<ContractState>;
Expand Down Expand Up @@ -69,12 +69,12 @@ pub mod store {
fn withdraw_funds(ref self: ContractState, receiver: ContractAddress) {
self.ownable.assert_only_owner();
let eth_dispatcher = ERC20ABIDispatcher {
contract_address: 0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7 // ETH Contract Address
.try_into()
.unwrap()
contract_address: ETH_ADDRESS // ETH Contract Address
.try_into().unwrap()
};
let balance = eth_dispatcher.balanceOf(starknet::get_contract_address());
eth_dispatcher.transfer(receiver, balance);
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pub mod base_escalation_manager {
use starknet::ContractAddress;
use optimistic_oracle::contracts::interfaces::{
IOptimisticOracleDispatcher, IOptimisticOracleDispatcherTrait, AssertionPolicy,
IEscalationManager, IOptimisticOracleV3CallbackRecipient
IEscalationManager, IOptimisticOracleCallbackRecipient
};

#[storage]
Expand Down Expand Up @@ -63,7 +63,7 @@ pub mod base_escalation_manager {


#[abi(embed_v0)]
impl IOptimisticOracleV3CallbackRecipientImpl of IOptimisticOracleV3CallbackRecipient<
impl IOptimisticOracleCallbackRecipientImpl of IOptimisticOracleCallbackRecipient<
ContractState
> {
fn assertion_resolved_callback(
Expand Down
17 changes: 12 additions & 5 deletions optimistic_oracle/src/contracts/interfaces.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use optimistic_oracle::contracts::mocks::oracle_ancillary::mock_oracle_ancillary
QueryPoint, QueryIndex
};
use optimistic_oracle::examples::prediction_market::prediction_market::Market;
use optimistic_oracle::contracts::common::address_whitelist::address_whitelist::WhitelistType;

#[derive(starknet::Store, Drop, Serde, Copy)]
pub struct EscalationManagerSettings {
Expand Down Expand Up @@ -113,13 +114,19 @@ pub trait IIdentifierWhitelist<TContractState> {

#[starknet::interface]
pub trait IAddressWhitelist<TContractState> {
fn add_to_whitelist(ref self: TContractState, new_element: ContractAddress);
fn add_to_whitelist(
ref self: TContractState, new_element: ContractAddress, whitelist_type: WhitelistType
);

fn remove_from_whitelist(ref self: TContractState, element_to_remove: ContractAddress);
fn remove_from_whitelist(
ref self: TContractState, element_to_remove: ContractAddress, whitelist_type: WhitelistType
);

fn is_on_whitelist(self: @TContractState, element_to_check: ContractAddress) -> bool;
fn is_on_whitelist(
self: @TContractState, element_to_check: ContractAddress, whitelist_type: WhitelistType
) -> bool;

fn get_whitelist(self: @TContractState) -> Span<ContractAddress>;
fn get_whitelist(self: @TContractState, whitelist_type: WhitelistType) -> Span<ContractAddress>;
}

#[starknet::interface]
Expand Down Expand Up @@ -195,7 +202,7 @@ pub trait IMockOracleAncillaryConfiguration<TContractState> {


#[starknet::interface]
pub trait IOptimisticOracleV3CallbackRecipient<TContractState> {
pub trait IOptimisticOracleCallbackRecipient<TContractState> {
fn assertion_resolved_callback(
ref self: TContractState, assertion_id: felt252, asserted_truthfully: bool
);
Expand Down
Loading

0 comments on commit 8d8439c

Please sign in to comment.