1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
//! Contains Bid Escrow Contract definition and related abstractions.
//!
//! # Definitions
//! * Job Offer - A description of a Job posted by JobPoster
//! * Bid - on offer that can be accepted by the Job Poster
//! * JobPoster - user of the system that posts a Job Offer; it has to be KYC’d
//! * Worker - the user who does a job
//! * Internal Worker - a Worker who completed the KYC and was voted to be a VotingAssociate
//! * External Worker - a Worker who completed the KYC and is not a Voting Associate
//! * Voting Associate (or VA) - users of the system with Reputation and permissions to vote
//! * KYC - Know Your Customer, a process that validates that the user can be the user of the system
//! * Bid Escrow Voting - Mints reputation
//!
//! # Posting
//! The first step of the `Bid Escrow` process is `Posting` a [`Job Offer`](JobOffer).
//! It is done by `JobPoster` by sending a query to a `BidEscrow` contract containing:
//! * Expected timeframe for completing a `Job`
//! * Maximum budget for a `Job`
//! With the query, the `JobPoster` sends a `DOS fee` in `CSPR`. The minimum amount of a `DOS fee` is defined in
//! [`Variable Repository Contract`] under the key`PostJobDOSFee`.
//! This action creates a new object in the contract called `Job Offer` and starts the `Bidding process`.
//!
//! # Bidding
//! The `Bidding` process allows `Workers` to post [`Bids`](Bid) with the offer of completing a job.
//! It is divided into two main parts.
//!
//! ### Internal Auction
//! During this part of the Bidding process only the `VAs` can bid. As the `VAs` have `Reputation`, they are bidding using
//! [`Reputation`] as a stake. The `Bid` query to the contract consists of:
//! * Proposed timeframe for completing the `Job`.
//! * Proposed payment for the `Job`.
//! * The amount of `Reputation` the `Internal Worker` stakes on this `Job`.
//!
//! The Bid is then added to a list of available bids in the contract storage and is available for picking by the `Job Poster`.
//! The time of an `Internal Auction` is defined in a [`Governance Variable`] `InternalAuctionTime`.
//! The bidding process can already be completed here, if the `JobPoster` decides to chose one of the posted Bids before `Internal Auction` ends.
//! However, if no `Bid` is picked during this time, the process becomes a `Public Auction`.
//!
//! ### Public Auction
//! If no `Internal Worker` decides to post a `Bid` on a `Job Offer`, or the `Job Poster` did not pick any bid during `Internal Auction`,
//! the `External Workers` have a chance of submitting their bids during `Public Auction` time. As `External Workers` do not have any
//! `Reputation` to stake, they are staking `CSPR`.
//!
//! A query to the contract in case of `External Workers` consists of:
//! * Proposed timeframe for completing the `Job`.
//! * Proposed payment for the `Job`.
//! * Decision if the `Worker` wants to become a `Voting Associate` if the `Job` is completed.
//! * `CSPR` stake sent alongside the query
//! `Internal Workers` by default cannot to submit their bids during `Public Auction`, however this behavior is configurable using
//! `VACanBidOnPublicAuction` [`Governance Variable`].
//!
//! The time of a `Public Auction` is defined in a [`Governance Variable`] `PublicAuctionTime`.
//!
//! # Picking a Bid
//! During the `Auction` process the `Job Poster` can pick a `Bid`.
//! When no `Bid` is posted or selected by `Job Poster` during both auctions, the `Job` is cancelled,
//! `DOS Fee` is returned to the `Job Poster` and stakes sent by the `Bidders` are returned to them.
//!
//! # Submitting a Job Proof
//! Now the `Worker` has the time to complete the `Job` and submit its proof to the contract.
//! After the works have been completed, the `Worker` sends a query to the contract containing
//! the cryptographic hash of a document being a proof of `Work` done for a `Job Poster`.
//!
//! When no `Bid` is posted or selected by the `Job Poster` during both auctions, the `Job` is cancelled,
//! `DOS Fee` is returned to the `Job Poster` and stakes sent by the `Bidders` are returned to them.
//!
//! # Voting
//! The Voting process is managed by [`VotingEngine`].
//!
//! # Voting passed
//! Besides yielding a positive result, the `Voting` passed means that the `Reputation` staked by the losing side is
//! redistributed between the winning side, depending on the type of `Worker`.
//! ### External Worker who wanted to become VA
//! * The `External Worker` becomes VA.
//! * The `CSPR` that were sent by the `External Worker` as a stake is returned to the `External Worker`.
//! * Reputation of the voters who voted `yes` is returned to them.
//! * Reputation of the voters who voted `no` is redistributed between the voters who voted `yes` proportional to the amount of
//! reputation staked in the voting.
//! * Reputation minted for the `External Worker` and used in the voting process is burned.
//!
//! ### Internal Worker
//! * Reputation of the voters who voted `yes` is returned to them.
//! * Reputation of the voters who voted `no` is redistributed between the voters who voted `yes` proportional to the amount of
//! reputation staked in the voting.
//!
//! ### External Worker
//! * The `CSPR` that were sent by the `External Worker` as a stake is returned to the `External Worker`.
//! * Reputation minted for the `External Worker` and used in the voting process is burned.
//! * Reputation of the voters who voted `yes` is returned to them, except for the Reputation minted for the Worker using `CSPR` stake.
//! * Reputation of the voters who voted `no` is redistributed between the voters who voted `yes` proportional to the amount of
//! reputation staked in the voting (External Worker does not receive Reputation in this step).
//!
//! ### Payment CSPR Redistribution
//! Reputation used for the Voting and minted after a successful `Job` has been redistributed during the above process,
//! but there is `CSPR` to redistribute that was allocated to the `Job`. How much resources is redistributed and to whom
//! depends on the type of `Worker` and whether it wanted to become a `VA`.
//!
//! ### Payment pool
//! The `CSPR` to redistribute is calculated using a formula:
//! `payment pool = job price`
//!
//! ### External Worker who wants to become VA
//! As the External Worker now is the VA, it is considered to be an `Internal Worker` in this scenario.
//!
//! ### Internal Worker
//! Firstly the Governance Payment is calculated using a formula:
//!
//! `governance payment = payment pool * BidEscrowPaymentRatio` [Read more](crate::core_contracts::VariableRepositoryContract#available-keys).
//!
//! The `Governance Payment` is then transferred to a multisig wallet, which address is held in the [`Variable Repository Contract`]
//! called [`BidEscrowWalletAddress`](crate::core_contracts::VariableRepositoryContract#available-keys).
//! The rest of the payment is redistributed between all of the `VAs'`.
//!
//! `remaining amount = payment pool - governance payment`
//!
//! ### External Worker
//! If the `Job` was done by an `External Worker` who didn't want to become a `VA`, the first step is the same
//! as in the case of `Internal Worker` - Governance Payment is being made. However the rest is then divided between
//! the `External Worker` and the `VAs’`.
//!
//! Firstly to get the amount that VA’s receive we use a formula:
//!
//! `VA payment amount = remaining amount * DefaultPolicingRate` [Read more](crate::core_contracts::VariableRepositoryContract#available-keys)
//!
//! Then, the rest is transferred to the `External Worker`:
//!
//! `External Worker payment amount = payment pool- governance payment - VA payment amount`
//!
//! # Voting failed
//! Besides yielding a negative result, the Voting passed means that the Reputation staked by the losing side is
//! redistributed between the winning side, depending on the type of Worker.
//!
//! ### External Worker who wanted to become VA
//! * The External Worked DOES NOT become a VA.
//! * The `CSPR` that were sent by the `External Worker` as a stake is redistributed between the `VA`’s.
//! * The Reputation minted for the `External Worker` using `CSPR` stake is burned.
//! * Reputation of the voters who voted `no` is returned to them.
//! * Reputation of the voters who voted `yes` is redistributed between the voters who voted `no` proportional to the amount of
//! reputation staked in the voting.
//!
//! ### Internal Worker
//! * Reputation of the voters who voted `no` is returned to them.
//! * Reputation of the voters who voted `yes` is redistributed between the voters who voted `no` proportional to the amount of
//! reputation staked in the voting.
//!
//! ### External Worker
//! The `CSPR` that were sent by the `External Worker` as a stake is redistributed between the `VA`’s.
//! The Reputation minted for the `External Worker` using `CSPR` stake is burned.
//! Reputation of the voters who voted `no` is returned to them.
//! Reputation of the voters who voted `yes` is redistributed between the voters who voted `no` proportional to the amount of
//! reputation staked in the voting.
//!
//! ### CSPR
//! If the `Voting` fails, the `CSPR` sent to the contract as a payment for `Job` is returned to the `Job Poster`. If the work
//! has been attempted to do by an `External Worker` the `CSPR` that the `Worker` staked during the `Bid` process
//! is redistributed between all `VA`’s.
//!
//! # Quorum not reached
//! When the `Quorum` is not reached during the `Formal Voting`, following things happen:
//! * The process ends here.
//! * `VA’s` stakes are returned to them.
//! * `Job` Poster payment and `DOS fee is returned.
//! * `Internal Worker`’s Reputation and `External Worker`’s `CSPR` stake is returned.
//! * `External Worker`’s Reputation that was minted using `CSPR` stake is burned.
//!
//! # Returning DOS Fee
//! The final step of the process is returning the `CSPR` `DOS Fee` to the `Job Poster`.
//!
//! # Grace Period
//! However, if `External Worker` do not post a `Job Proof` in time, his `CSPR` stake is redistributed
//! between all `VA’s`.
//! In case of `Internal Worker`, his staked `Reputation` gets burned and it undergoes the
//! `Automated Reputation slashing` see [`Slashing Voter`].
//! Then the process enters a `Grace period` (with the timeframe the same as the timeframe of the work for `Worker`).
//! During this period, anyone (`VA`, `External Worker`, even the original `Worker`) can submit the `Job Proof`,
//! becoming the new `Worker` and participating in the reward mechanism. Alongside the `Job Proof`,
//! a `Worker` needs to send a stake in form of `Reputation` (or `CSPR` for `External Worker`).
//! This stake will behave in the same manner as stake sent by the original `Worker`.
//! If nobody submits the `Job Proof` during the grace period, the whole process ends.
//! The `CSPR` paid by the `Job Poster` is returned along with the `DOS Fee`.
//! This is a special implementation of [positional parameters].
//!
//! [`Variable Repository Contract`]: crate::core_contracts::VariableRepositoryContract
//! [`VotingEngine`]: VotingEngine
//! [`Slashing Voter`]: crate::voting_contracts::SlashingVoterContract
//! [`Reputation`]: crate::core_contracts::ReputationContract
//! [`Governance Variable`]: crate::core_contracts::VariableRepositoryContract#available-keys

use crate::bid_escrow::bid::Bid;
use crate::bid_escrow::bid_engine::BidEngine;
use crate::bid_escrow::events::BidEscrowSlashResults;
use crate::bid_escrow::job::Job;
use crate::bid_escrow::job_engine::JobEngine;
use crate::bid_escrow::job_offer::JobOffer;
use crate::bid_escrow::types::{BidId, JobId, JobOfferId};
use crate::modules::refs::ContractRefs;
use crate::modules::AccessControl;
use crate::utils::types::DocumentHash;
use crate::voting::ballot::{Ballot, Choice};
use crate::voting::types::VotingId;
use crate::voting::voting_engine::voting_state_machine::{
    VotingStateMachine, VotingSummary, VotingType,
};
use crate::voting::voting_engine::VotingEngine;
use odra::contract_env::{caller, self_balance};
use odra::types::{event::OdraEvent, Address, Balance, BlockTime};

use super::storage::{BidStorage, JobStorage};
use crate::voting_contracts::SlashedVotings;

/// A contract that manages the full `Bid Escrow` process.
/// Uses [`VotingEngine`](crate::voting::voting_engine::VotingEngine) to conduct the voting process.
///
/// For details see [BidEscrowContract](BidEscrowContract).
#[odra::module]
#[allow(dead_code)]
pub struct BidEscrowContract {
    refs: ContractRefs,
    access_control: AccessControl,
    #[odra(using = "refs, voting_engine, job_storage, bid_storage")]
    job_engine: JobEngine,
    #[odra(using = "refs, job_storage, bid_storage")]
    bid_engine: BidEngine,
    #[odra(using = "refs")]
    voting_engine: VotingEngine,
    job_storage: JobStorage,
    bid_storage: BidStorage,
}

#[odra::module]
impl BidEscrowContract {
    delegate! {
        to self.voting_engine {
            /// Checks if voting of a given type and id exists.
            pub fn voting_exists(&self, voting_id: VotingId, voting_type: VotingType) -> bool;

            /// Returns the Voter's [`Ballot`].
            pub fn get_ballot(
                &self,
                voting_id: VotingId,
                voting_type: VotingType,
                address: Address,
            ) -> Option<Ballot>;

            /// Returns the address of nth voter who voted on Voting with `voting_id`.
            pub fn get_voter(&self, voting_id: VotingId, voting_type: VotingType, at: u32) -> Option<Address>;

            /// Returns [Voting](VotingStateMachine) for given id.
            pub fn get_voting(&self, voting_id: VotingId) -> Option<VotingStateMachine>;
        }


        to self.bid_engine {
            /// Job Poster post a new Job Offer.
            ///
            /// # Errors
            /// * [`NotKyced`](crate::utils::Error::NotKyced) - if the caller is not KYCed
            /// * [`DosFeeTooLow`](crate::utils::Error::DosFeeTooLow) - if the caller has not sent enough DOS Fee
            ///
            /// # Events
            /// * [`JobOfferCreated`](crate::bid_escrow::events::JobOfferCreated)
            #[odra(payable)]
            pub fn post_job_offer(&mut self, expected_timeframe: BlockTime, budget: Balance, dos_fee: Balance);

            /// Job poster picks a bid. This creates a new Job object and saves it in a storage.
            ///
            /// # Events
            /// * [`JobCreated`](crate::bid_escrow::events::JobCreated)
            /// * [`Unstake`](crate::core_contracts::Unstake) - in case there were other bids on the same Job Offer
            ///
            /// # Errors
            /// * [`OnlyJobPosterCanPickABid`](crate::utils::Error::OnlyJobPosterCanPickABid) - if the caller is not the Job Poster
            /// * [`PurseBalanceMismatch`](crate::utils::Error::PurseBalanceMismatch) - if the purse balance does not match the bid amount
            #[odra(payable)]
            pub fn pick_bid(&mut self, job_offer_id: JobOfferId, bid_id: BidId, cspr_amount: Balance);

            /// Worker submits a [Bid] for a [Job].
            ///
            /// # Events
            /// * [`BidSubmitted`](crate::bid_escrow::events::BidSubmitted)
            /// * [`Stake`](crate::core_contracts::Stake)
            ///
            /// # Errors
            /// * [`NotKyced`](crate::utils::Error::NotKyced) - if the caller is not KYCed
            /// * [`CannotBidOnOwnJob`](crate::utils::Error::CannotBidOnOwnJob) - if the caller is the Job Poster
            /// * [`VaOnboardedAlready`](crate::utils::Error::VaOnboardedAlready) - if the caller has already been onboarded, but is trying to
            /// onboard again
            /// * [`PaymentExceedsMaxBudget`](crate::utils::Error::PaymentExceedsMaxBudget) - if the proposed payment exceeds the maximum budget
            /// * [`AuctionNotRunning`](crate::utils::Error::AuctionNotRunning) - if the auction is not running
            /// * [`OnlyOnboardedWorkerCanBid`](crate::utils::Error::OnlyOnboardedWorkerCanBid) - if the Worker is not onboarded and the
            /// auction is internal
            /// * [`OnboardedWorkerCannotBid`](crate::utils::Error::OnboardedWorkerCannotBid) - if the Worker is onboarded, auction is public
            /// and the configuration forbids bidding by onboarded Workers on such auctions
            /// * [`InsufficientBalance`](crate::utils::Error::InsufficientBalance) - if the Worker has not enough balance to pay for the
            /// bid
            /// * [`ZeroStake`](crate::utils::Error::ZeroStake) - if the Worker tries to stake 0 reputation
            /// * [`NotWhitelisted`](crate::utils::Error::NotWhitelisted) - if the contract is not whitelisted for Reputation Staking
            #[odra(payable)]
            pub fn submit_bid(
                &mut self,
                job_offer_id: JobOfferId,
                time: BlockTime,
                payment: Balance,
                reputation_stake: Balance,
                onboard: bool,
                cspr_stake: Option<Balance>
            );

            /// Worker cancels a [Bid] for a [Job].
            ///
            /// Bid can be cancelled only after VABidAcceptanceTimeout time has passed after submitting a Bid.
            ///
            /// # Events
            /// * [`BidCancelled`](crate::bid_escrow::events::BidCancelled)
            /// * [`Unstake`](crate::core_contracts::Unstake)
            ///
            /// # Errors:
            /// * [`CannotCancelNotOwnedBid`](crate::utils::Error::CannotCancelNotOwnedBid) when trying to cancel a Bid
            /// that is not owned by the Worker
            /// * [`CannotCancelBidOnCompletedJobOffer`](crate::utils::Error::CannotCancelBidOnCompletedJobOffer) when
            /// trying to cancel a Bid on a Job Offer that is already completed
            /// * [`CannotCancelBidBeforeAcceptanceTimeout`](crate::utils::Error::CannotCancelBidBeforeAcceptanceTimeout)
            /// when trying to cancel a Bid before VABidAcceptanceTimeout time has passed
            pub fn cancel_bid(&mut self, bid_id: BidId);

            /// Invalidates the [`Job Offer`](JobOffer), returns `DOS Fee` to the `Job Poster`, returns funds to `Bidders`.
            /// [`Read more`](BidEngine::cancel_job_offer()).
            pub fn cancel_job_offer(&mut self, job_offer_id: JobOfferId);

            /// Returns the total number of job offers.
            pub fn job_offers_count(&self) -> u32;

            /// Returns the total number of bids.
            pub fn bids_count(&self) -> u32;

            /// Returns a JobOffer with given [JobOfferId].
            pub fn get_job_offer(&self, job_offer_id: JobOfferId) -> Option<JobOffer>;

            /// Returns a Bid with given [BidId].
            pub fn get_bid(&self, bid_id: BidId) -> Option<Bid>;
        }

        to self.job_engine {
            /// Submits a job proof. This is called by a `Worker` or any KYC'd user during Grace Period.
            /// This starts a new voting over the result.
            ///
            /// # Events
            /// * [`JobSubmitted`](crate::bid_escrow::events::JobSubmitted)
            /// * [`Unstake`](crate::core_contracts::Unstake) - Stake is used in the voting
            /// * [`BallotCast`](crate::voting::voting_engine::events::BallotCast) - first vote is cast by the Worker
            ///
            /// # Errors
            /// Throws [`JobAlreadySubmitted`](crate::utils::Error::JobAlreadySubmitted) if job was already submitted.
            /// Throws [`OnlyWorkerCanSubmitProof`](crate::utils::Error::OnlyWorkerCanSubmitProof) if the caller is not the Worker
            /// and the grace period is not ongoing.
            pub fn submit_job_proof(&mut self, job_id: JobId, proof: DocumentHash);

            /// Updates the old [`Bid`] and [`Job`], the job is assigned to a new `Worker`. The rest goes the same
            /// as regular proof submission. See [submit_job_proof()][Self::submit_job_proof].
            /// The old `Worker` who didn't submit the proof in time, is getting slashed.
            #[odra(payable)]
            pub fn submit_job_proof_during_grace_period(
                &mut self,
                job_id: JobId,
                proof: DocumentHash,
                reputation_stake: Balance,
                onboard: bool,
            );

            pub fn cancel_job(&mut self, job_id: JobId);

            /// Casts a vote over a job.
            ///
            /// # Events
            /// * [`BallotCast`](crate::voting::voting_engine::events::BallotCast)
            ///
            /// # Errors
            /// * [`CannotVoteOnOwnJob`](crate::utils::Error::CannotVoteOnOwnJob) if the voter is either of Job Poster or Worker
            /// * [`VotingNotStarted`](crate::utils::Error::VotingNotStarted) if the voting was not yet started for this job
            pub fn vote(&mut self, voting_id: VotingId, voting_type: VotingType, choice: Choice, stake: Balance);

            /// Finishes voting. Depending on type of voting, different actions are performed.
            /// [Read more](VotingEngine::finish_voting())
            ///
            /// # Events
            /// * [`VotingEnded`](crate::voting::voting_engine::events::VotingEnded)
            /// * [`BallotCast`](crate::voting::voting_engine::events::BallotCast) - when formal voting starts
            /// * [`Unstake`](crate::core_contracts::Unstake)
            /// * [`Stake`](crate::core_contracts::Stake)
            /// * [`Mint`](crate::core_contracts::Mint)
            ///
            /// # Errors
            /// * [`BidNotFound`](crate::utils::Error::BidNotFound) if the bid was not found
            /// * [`VotingDoesNotExist`](crate::utils::Error::VotingDoesNotExist) if the voting does not exist
            /// * [`VotingWithGivenTypeNotInProgress`](crate::utils::Error::VotingWithGivenTypeNotInProgress) if the voting
            /// is not in progress
            /// * [`FinishingCompletedVotingNotAllowed`](crate::utils::Error::FinishingCompletedVotingNotAllowed) if the
            /// voting is already completed
            pub fn finish_voting(&mut self, voting_id: VotingId, voting_type: VotingType) -> VotingSummary;

            /// Returns the total number of jobs.
            pub fn jobs_count(&self) -> u32;

            /// Returns a job with given [JobId].
            pub fn get_job(&self, job_id: JobId) -> Option<Job>;
        }

        to self.access_control {
            /// Changes the ownership of the contract. Transfers ownership to the `owner`.
            /// Only the current owner is permitted to call this method.
            /// [`Read more`](AccessControl::propose_new_owner())
            pub fn propose_new_owner(&mut self, owner: Address);

            /// Accepts the new owner proposition. This can be called only by the proposed owner.
            /// [`Read more`](AccessControl::accept_new_owner())
            pub fn accept_new_owner(&mut self);

            /// Adds a new address to the whitelist.
            /// [`Read more`](AccessControl::add_to_whitelist())
            pub fn add_to_whitelist(&mut self, address: Address);

            /// Remove address from the whitelist.
            /// [`Read more`](AccessControl::remove_from_whitelist());
            pub fn remove_from_whitelist(&mut self, address: Address);

            /// Checks whether the given address is added to the whitelist.
            /// [`Read more`](AccessControl::is_whitelisted()).
            pub fn is_whitelisted(&self, address: Address) -> bool;

            /// Returns the address of the current owner.
            /// [`Read more`](AccessControl::get_owner()).
            pub fn get_owner(&self) -> Option<Address>;
        }
    }

    /// Constructor function.
    ///
    /// # Note
    /// Initializes contract elements:
    /// * Sets up the contract by saving addresses of [`Variable Repository`](crate::core_contracts::VariableRepositoryContract),
    /// [`Reputation Token`](crate::core_contracts::ReputationContract), [`VA Token`](crate::core_contracts::VaNftContract), [`KYC Token`](crate::core_contracts::KycNftContract).
    /// * Sets [`caller`] as the owner of the contract.
    /// * Adds [`caller`] to the whitelist.
    ///
    /// # Events
    /// Emits:
    /// * [`OwnerChanged`](crate::modules::owner::events::OwnerChanged),
    /// * [`AddedToWhitelist`](crate::modules::whitelist::events::AddedToWhitelist),
    #[odra(init)]
    pub fn init(
        &mut self,
        variable_repository: Address,
        reputation_token: Address,
        kyc_token: Address,
        va_token: Address,
    ) {
        self.refs.set_variable_repository(variable_repository);
        self.refs.set_reputation_token(reputation_token);
        self.refs.set_kyc_token(kyc_token);
        self.refs.set_va_token(va_token);
        self.access_control.init(caller());
    }

    /// Returns the CSPR balance of the contract.
    pub fn get_cspr_balance(&self) -> Balance {
        self_balance()
    }

    /// Erases the VA from the all bids, offers and jobs.
    /// Only a whitelisted account is permitted to call this method.
    /// Interacts with [Reputation Token Contract](crate::core_contracts::ReputationContract).
    ///
    /// # Errors
    /// * [crate::utils::Error::BidNotFound]
    /// * [crate::utils::Error::JobOfferNotFound]
    /// * [crate::utils::Error::CannotCancelBidOnCompletedJobOffer]
    /// * [crate::utils::Error::NotWhitelisted]
    ///
    /// # Events
    /// * [`BidEscrowSlashResults`](BidEscrowSlashResults)
    pub fn slash_voter(&mut self, voter: Address) -> SlashedVotings {
        self.access_control.ensure_whitelisted();
        let (slashed_job_offers, slashed_bids) = self.bid_engine.slash_voter(voter);
        let (slashed_jobs, cancelled_votings, affected_votings) =
            self.job_engine.slash_voter(voter);

        BidEscrowSlashResults {
            slashed_job_offers,
            slashed_bids,
            slashed_jobs,
            cancelled_votings: cancelled_votings.clone(),
            affected_votings: affected_votings.clone(),
        }
        .emit();

        SlashedVotings {
            cancelled_votings,
            affected_votings,
        }
    }

    /// Cancels a voting that has not been finished in defined time
    ///
    /// # Errors
    /// * [crate::utils::Error::VotingDoesNotExist]
    /// * [crate::utils::Error::VotingWithGivenTypeNotInProgress]
    /// * [crate::utils::Error::FinishingCompletedVotingNotAllowed]
    /// * [crate::utils::Error::VotingNotStarted]
    /// * [crate::utils::Error::VotingAlreadyFinished]
    /// * [crate::utils::Error::VotingAlreadyCancelled]
    pub fn cancel_finished_voting(&mut self, voting_id: VotingId) {
        // cancel voting
        self.voting_engine.cancel_finished_voting(voting_id);

        // return cspr
        let mut job = self.job_storage.get_job_by_voting_id(voting_id);
        self.job_engine.return_job_poster_payment_and_dos_fee(&job);
        if job.external_worker_cspr_stake() > 0.into() {
            self.job_engine.return_external_worker_cspr_stake(&job);
        }

        // cancel and save job
        job.cancel();
        self.job_storage.store_job(job);
    }
}