Skip to content
Closed
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
63 changes: 59 additions & 4 deletions src/escrow/escrow_contract.cairo
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#[starknet::contract]
mod EscrowContract {
use starknet::event::EventEmitter;
use crate::interface::ierc20::{IERC20Dispatcher, IERC20DispatcherTrait};
use core::num::traits::Zero;
use starknet::{ContractAddress, storage::Map, contract_address_const};
use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess, StoragePathEntry};
Expand All @@ -11,19 +13,17 @@ mod EscrowContract {

#[storage]
struct Storage {
escrow_id: u64,
depositor: ContractAddress,
beneficiary: ContractAddress,
arbiter: ContractAddress,
token_address: ContractAddress,
time_frame: u64,
worth_of_asset: u256,
client_address: ContractAddress,
provider_address: ContractAddress,
balance: u256,
depositor_approve: Map::<ContractAddress, bool>,
arbiter_approve: Map::<ContractAddress, bool>,
// Track whether an escrow ID has been used
escrow_exists: Map::<u64, bool>,
// Store escrow amounts
escrow_amounts: Map::<u64, u256>,
deposit_time: Map::<u64, u64>,
// Track the funded escrows. Start as false and is setted to true when successfully funds.
Expand All @@ -33,8 +33,11 @@ mod EscrowContract {
#[event]
#[derive(Drop, starknet::Event)]
pub enum Event {
ApproveTransaction: ApproveTransaction,
EscrowEarningsDistributed: EscrowEarningsDistributed,
DepositorApproved: DepositorApproved,
ArbiterApproved: ArbiterApproved,

EscrowInitialized: EscrowInitialized,
EscrowFunded: EscrowFunded,
EscrowRefunded: EscrowRefunded,
Expand All @@ -49,6 +52,13 @@ mod EscrowContract {
}

#[derive(Drop, starknet::Event)]
struct EscrowEarningsDistributed {
escrow_id: u64,
release_address: ContractAddress,
amount: u256
}

// New event for escrow initialization
pub struct ArbiterApproved {
arbiter: ContractAddress,
escrow_id: u64,
Expand All @@ -65,6 +75,7 @@ mod EscrowContract {
timestamp: u64,
}


#[derive(Drop, starknet::Event)]
pub struct EscrowRefunded {
escrow_id: u64,
Expand Down Expand Up @@ -109,6 +120,7 @@ mod EscrowContract {

#[abi(embed_v0)]
impl EscrowImpl of IEscrow<ContractState> {

fn get_escrow_details(ref self: ContractState, escrow_id: u64) -> Escrow {
// Validate if the escrow exists
let exists = self.escrow_exists.read(escrow_id);
Expand All @@ -117,6 +129,7 @@ mod EscrowContract {
let client_address = self.client_address.read();
let provider_address = self.provider_address.read();
let amount = self.escrow_amounts.read(escrow_id);

let balance = self.balance.read();

let escrow = Escrow {
Expand Down Expand Up @@ -300,6 +313,7 @@ mod EscrowContract {
// Check that the correct amount was sent.
assert(amount >= expected_amount, 'Amount is less than expected');


// First modify in-contract state to avoid reentrancy attacks
// Set escrow to funded
self.escrow_funded.write(escrow_id, true);
Expand All @@ -310,6 +324,7 @@ mod EscrowContract {

// Use the OpenZeppelin ERC20 contract to transfer the funds from the caller address to
// the escrow contract.

let token = IERC20Dispatcher { contract_address: token_address };
token.transfer_from(caller_address, contract_address, amount);

Expand Down Expand Up @@ -362,6 +377,7 @@ mod EscrowContract {
self.depositor.read()
}


fn get_beneficiary(self: @ContractState) -> ContractAddress {
self.beneficiary.read()
}
Expand All @@ -382,4 +398,43 @@ mod EscrowContract {
(depositor_approved, arbiter_approved)
}
}

#[external(v0)]
fn distribute_escrow_earnings(
ref self: ContractState, escrow_id: u64, release_address: ContractAddress
) {
assert(self.escrow_id.read() == escrow_id, 'Escrow Contract is not valid');

let depositor_approved = self.depositor_approve.entry(self.depositor.read()).read();
let arbiter_approved = self.arbiter_approve.entry(self.arbiter.read()).read();
// Verify both approvals
assert(depositor_approved && arbiter_approved, 'Escrow not approved');

//Verify token validity
let token_address = self.token_address.read();
assert(!token_address.is_zero(), 'Invalid token address');

//Verify if funds were already distributed or there is enough balance
assert(self.balance.read() > 0, 'Funds already distributed');
assert(self.balance.read() >= self.worth_of_asset.read(), 'Insufficient funds');

// Create token dispatcher
let token_contract = IERC20Dispatcher { contract_address: token_address };
let depositor = self.depositor.read();

// Transfer tokens
let transfer_result = token_contract
.transfer_from(depositor, release_address, self.worth_of_asset.read());
assert(transfer_result, 'Token transfer failed');

// Update balance after successful transfer
self.balance.write(0);

self
.emit(
EscrowEarningsDistributed {
escrow_id, release_address, amount: self.worth_of_asset.read()
}
);
}
}
1 change: 1 addition & 0 deletions src/interface.cairo
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod ierc20;
pub mod iescrow;
pub mod ierc20;
3 changes: 3 additions & 0 deletions src/interface/iescrow.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ pub trait IEscrow<TContractState> {
provider_address: ContractAddress,
amount: u256,
);

fn approve(ref self: TContractState, benefeciary: ContractAddress);
fn get_escrow_details(ref self: TContractState, escrow_id: u64) -> Escrow;

fn get_depositor(self: @TContractState) -> ContractAddress;
fn get_beneficiary(self: @TContractState) -> ContractAddress;
fn refundTimer(ref self: TContractState, escrow_id: u64, refund_period: u64);
Expand Down
Loading