diff --git a/.tool-versions b/.tool-versions index 4084707..87039f6 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,11 @@ scarb 2.8.2 -starknet-foundry 0.31.0 +starknet-foundry + + + + + + + + + diff --git a/src/escrow/escrow_contract.cairo b/src/escrow/escrow_contract.cairo index 0a86256..9c4eb44 100644 --- a/src/escrow/escrow_contract.cairo +++ b/src/escrow/escrow_contract.cairo @@ -5,7 +5,7 @@ mod EscrowContract { use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess, StoragePathEntry}; use starknet::get_block_timestamp; use core::starknet::{get_caller_address, get_contract_address}; - use crate::escrow::{types::Escrow, errors::Errors}; + use crate::escrow::{types::Escrow, types::Milestone, errors::Errors}; use crate::interface::iescrow::{IEscrow}; use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; @@ -28,8 +28,12 @@ mod EscrowContract { deposit_time: Map::, // Track the funded escrows. Start as false and is setted to true when successfully funds. escrow_funded: Map::, + milestone_count: u64, + // mapping to track milestones + milestones: Map::, } + #[event] #[derive(Drop, starknet::Event)] pub enum Event { @@ -282,6 +286,48 @@ mod EscrowContract { ); } + // add milestone for escrows to help in tracking + fn add_milestone( + ref self: ContractState, description: ByteArray, amount: u256, dueDate: u256, + ) { + assert(amount > 0, 'amount too low'); + let milestone_id: u64 = self.milestone_count.read() + 1; + + let milestone: Milestone = Milestone { + id: milestone_id, + description: description, + amount: amount, + dueDate: dueDate, + isCompleted: false, + isApprovedDepositor: false, + isApprovedBeneficiary: false, + isPaid: false + }; + + self.milestones.write(milestone_id, milestone) + } + + fn request_milestone_payment( + ref self: ContractState, id: u64, token_address: ContractAddress + ) -> bool { + let mut milestone: Milestone = self.milestones.read(id); + + assert(!milestone.isPaid, 'Milestone has been paid'); + assert(milestone.isApprovedBeneficiary, 'Milestone must be approved'); + assert(milestone.isApprovedDepositor, 'Milestone must be approved'); + assert(milestone.isCompleted, 'Milestone must be completed'); + + let milestone_amount = milestone.amount; + + let token = IERC20Dispatcher { contract_address: token_address }; + let caller_address = get_caller_address(); + milestone.isPaid = true; + + token.transfer(caller_address, milestone_amount); + + return true; + } + fn fund_escrow( ref self: ContractState, escrow_id: u64, amount: u256, token_address: ContractAddress, ) { diff --git a/src/escrow/escrow_factory.cairo b/src/escrow/escrow_factory.cairo index d40c5ce..78a3ec7 100644 --- a/src/escrow/escrow_factory.cairo +++ b/src/escrow/escrow_factory.cairo @@ -2,6 +2,7 @@ pub use starknet::{ ContractAddress, class_hash::ClassHash, syscalls::deploy_syscall, SyscallResultTrait, }; +use escrownet_contract::interface::iescrow::{IEscrowDispatcher, IEscrowDispatcherTrait}; #[starknet::interface] pub trait IEscrowFactory { @@ -11,6 +12,9 @@ pub trait IEscrowFactory { depositor: ContractAddress, arbiter: ContractAddress, salt: felt252, + milestone_description: ByteArray, + milestone_amount: u256, + milestone_dueDate: u256, ) -> ContractAddress; fn get_escrow_contracts(self: @TContractState) -> Array; @@ -18,6 +22,8 @@ pub trait IEscrowFactory { #[starknet::component] pub mod EscrowFactory { + use super::IEscrowDispatcherTrait; + use super::IEscrowDispatcher; use super::IEscrowFactory; use starknet::{ ContractAddress, class_hash::ClassHash, syscalls::deploy_syscall, SyscallResultTrait, @@ -44,6 +50,9 @@ pub mod EscrowFactory { depositor: ContractAddress, arbiter: ContractAddress, salt: felt252, + milestone_description: ByteArray, + milestone_amount: u256, + milestone_dueDate: u256, ) -> ContractAddress { let escrow_id = self.escrow_count.read() + 1; @@ -60,6 +69,16 @@ pub mod EscrowFactory { self.escrow_addresses.write(escrow_id, escrow_address); self.escrow_count.write(escrow_id); + // Initialize milestone for every deployed escrow + let escrow_contract = IEscrowDispatcher { contract_address: escrow_address }; + + escrow_contract + .add_milestone( + description: milestone_description, + amount: milestone_amount, + dueDate: milestone_dueDate + ); + escrow_address } diff --git a/src/escrow/types.cairo b/src/escrow/types.cairo index 3b35a15..f62a13b 100644 --- a/src/escrow/types.cairo +++ b/src/escrow/types.cairo @@ -8,6 +8,18 @@ pub struct Escrow { pub balance: u256, } +#[derive(Drop, Serde, starknet::Store)] +pub struct Milestone { + pub id: u64, + pub description: ByteArray, + pub amount: u256, + pub dueDate: u256, + pub isCompleted: bool, + pub isApprovedDepositor: bool, + pub isApprovedBeneficiary: bool, + pub isPaid: bool, +} + #[starknet::interface] pub trait IEscrow { fn get_escrow(self: @TContractState, escrow_id: u256) -> Escrow; diff --git a/src/interface/iescrow.cairo b/src/interface/iescrow.cairo index c33abe5..dfca26a 100644 --- a/src/interface/iescrow.cairo +++ b/src/interface/iescrow.cairo @@ -1,5 +1,5 @@ use starknet::ContractAddress; -use crate::escrow::types::Escrow; +use crate::escrow::types::{Escrow, Milestone}; #[starknet::interface] pub trait IEscrow { @@ -10,6 +10,12 @@ pub trait IEscrow { provider_address: ContractAddress, amount: u256, ); + fn add_milestone( + ref self: TContractState, description: ByteArray, amount: u256, dueDate: u256, + ); + fn request_milestone_payment( + ref self: TContractState, id: u64, token_address: ContractAddress + ) -> bool; fn get_escrow_details(ref self: TContractState, escrow_id: u64) -> Escrow; fn get_depositor(self: @TContractState) -> ContractAddress; fn get_beneficiary(self: @TContractState) -> ContractAddress;