From 9571dac9a0a80da034224a7313bfdde16f3187fc Mon Sep 17 00:00:00 2001 From: shaaibu7 Date: Fri, 21 Mar 2025 07:49:57 +0100 Subject: [PATCH 1/6] chore: add struct and mapping for milestone --- .tool-versions | 11 ++++++++++- src/escrow/escrow_contract.cairo | 13 +++++++++++++ src/interface/iescrow.cairo | 1 + 3 files changed, 24 insertions(+), 1 deletion(-) 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..a730c0a 100644 --- a/src/escrow/escrow_contract.cairo +++ b/src/escrow/escrow_contract.cairo @@ -28,6 +28,19 @@ 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: u256, + // mapping to track milestones + milestones: Map::, + } + + #[derive(Drop)] + struct Milestone { + id: u256, + description: ByteArray, + amount: u256, + dueDate: u256, + isCompleted: bool, + isApproved: bool } #[event] diff --git a/src/interface/iescrow.cairo b/src/interface/iescrow.cairo index c33abe5..e463dcd 100644 --- a/src/interface/iescrow.cairo +++ b/src/interface/iescrow.cairo @@ -10,6 +10,7 @@ pub trait IEscrow { provider_address: ContractAddress, amount: u256, ); + fn get_escrow_details(ref self: TContractState, escrow_id: u64) -> Escrow; fn get_depositor(self: @TContractState) -> ContractAddress; fn get_beneficiary(self: @TContractState) -> ContractAddress; From 8391c1212ce8dd7d784d5daba67731c9315ecbb1 Mon Sep 17 00:00:00 2001 From: shaaibu7 Date: Fri, 21 Mar 2025 08:24:30 +0100 Subject: [PATCH 2/6] chore: implement functionality for adding milestone --- src/escrow/escrow_contract.cairo | 39 ++++++++++++++++++++++---------- src/escrow/types.cairo | 10 ++++++++ src/interface/iescrow.cairo | 9 ++++++-- 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/src/escrow/escrow_contract.cairo b/src/escrow/escrow_contract.cairo index a730c0a..9de7e67 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,20 +28,11 @@ 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: u256, + milestone_count: u64, // mapping to track milestones - milestones: Map::, + milestones: Map::, } - #[derive(Drop)] - struct Milestone { - id: u256, - description: ByteArray, - amount: u256, - dueDate: u256, - isCompleted: bool, - isApproved: bool - } #[event] #[derive(Drop, starknet::Event)] @@ -295,6 +286,30 @@ 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, + isApproved: false + }; + + self.milestones.write(milestone_id, milestone) + } + fn fund_escrow( ref self: ContractState, escrow_id: u64, amount: u256, token_address: ContractAddress, ) { diff --git a/src/escrow/types.cairo b/src/escrow/types.cairo index 3b35a15..3dc352c 100644 --- a/src/escrow/types.cairo +++ b/src/escrow/types.cairo @@ -8,6 +8,16 @@ 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 isApproved: 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 e463dcd..e7ddaec 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,7 +10,12 @@ pub trait IEscrow { provider_address: ContractAddress, amount: u256, ); - + fn add_milestone( + ref self: TContractState, + description: ByteArray, + amount: u256, + dueDate: u256, + ); fn get_escrow_details(ref self: TContractState, escrow_id: u64) -> Escrow; fn get_depositor(self: @TContractState) -> ContractAddress; fn get_beneficiary(self: @TContractState) -> ContractAddress; From 7c6891cdc219ca85751fbc50a53610711dc35786 Mon Sep 17 00:00:00 2001 From: shaaibu7 Date: Fri, 21 Mar 2025 10:28:25 +0100 Subject: [PATCH 3/6] chore: implement request milestone payment --- src/escrow/escrow_contract.cairo | 22 +++++++++++++++++++++- src/escrow/types.cairo | 3 ++- src/interface/iescrow.cairo | 1 + 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/escrow/escrow_contract.cairo b/src/escrow/escrow_contract.cairo index 9de7e67..bb9c8ca 100644 --- a/src/escrow/escrow_contract.cairo +++ b/src/escrow/escrow_contract.cairo @@ -304,12 +304,32 @@ mod EscrowContract { amount: amount, dueDate: dueDate, isCompleted: false, - isApproved: false + isApproved: 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.isApproved, '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/types.cairo b/src/escrow/types.cairo index 3dc352c..c5998b8 100644 --- a/src/escrow/types.cairo +++ b/src/escrow/types.cairo @@ -15,7 +15,8 @@ pub struct Milestone { pub amount: u256, pub dueDate: u256, pub isCompleted: bool, - pub isApproved: bool + pub isApproved: bool, + pub isPaid: bool, } #[starknet::interface] diff --git a/src/interface/iescrow.cairo b/src/interface/iescrow.cairo index e7ddaec..d98425f 100644 --- a/src/interface/iescrow.cairo +++ b/src/interface/iescrow.cairo @@ -16,6 +16,7 @@ pub trait IEscrow { 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; From 5e50af556265c011af2e36cfa6f1c7c6490549ad Mon Sep 17 00:00:00 2001 From: shaaibu7 Date: Fri, 21 Mar 2025 10:53:50 +0100 Subject: [PATCH 4/6] chore: add milestone on deploying an escrow --- src/escrow/escrow_factory.cairo | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/escrow/escrow_factory.cairo b/src/escrow/escrow_factory.cairo index d40c5ce..e8f5a32 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,7 +22,9 @@ pub trait IEscrowFactory { #[starknet::component] pub mod EscrowFactory { - use super::IEscrowFactory; + use super::IEscrowDispatcherTrait; +use super::IEscrowDispatcher; +use super::IEscrowFactory; use starknet::{ ContractAddress, class_hash::ClassHash, syscalls::deploy_syscall, SyscallResultTrait, storage::{Map}, @@ -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,15 @@ 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 } From 797dc299066fee24fe880be9e77bdb92b797159d Mon Sep 17 00:00:00 2001 From: shaaibu7 Date: Fri, 21 Mar 2025 11:06:21 +0100 Subject: [PATCH 5/6] chore: fix code formatting --- src/escrow/escrow_contract.cairo | 16 ++++++---------- src/escrow/escrow_factory.cairo | 15 ++++++++------- src/interface/iescrow.cairo | 9 ++++----- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/escrow/escrow_contract.cairo b/src/escrow/escrow_contract.cairo index bb9c8ca..5366ac8 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, types::Milestone ,errors::Errors}; + use crate::escrow::{types::Escrow, types::Milestone, errors::Errors}; use crate::interface::iescrow::{IEscrow}; use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; @@ -28,7 +28,7 @@ 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, + milestone_count: u64, // mapping to track milestones milestones: Map::, } @@ -288,16 +288,11 @@ mod EscrowContract { // add milestone for escrows to help in tracking fn add_milestone( - ref self: ContractState, - description: ByteArray, - amount: u256, - dueDate: u256, + 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, @@ -311,7 +306,9 @@ mod EscrowContract { self.milestones.write(milestone_id, milestone) } - fn request_milestone_payment(ref self: ContractState, id: u64, token_address: ContractAddress) -> bool { + 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'); @@ -327,7 +324,6 @@ mod EscrowContract { token.transfer(caller_address, milestone_amount); return true; - } fn fund_escrow( diff --git a/src/escrow/escrow_factory.cairo b/src/escrow/escrow_factory.cairo index e8f5a32..78a3ec7 100644 --- a/src/escrow/escrow_factory.cairo +++ b/src/escrow/escrow_factory.cairo @@ -23,8 +23,8 @@ pub trait IEscrowFactory { #[starknet::component] pub mod EscrowFactory { use super::IEscrowDispatcherTrait; -use super::IEscrowDispatcher; -use super::IEscrowFactory; + use super::IEscrowDispatcher; + use super::IEscrowFactory; use starknet::{ ContractAddress, class_hash::ClassHash, syscalls::deploy_syscall, SyscallResultTrait, storage::{Map}, @@ -72,11 +72,12 @@ use super::IEscrowFactory; // 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_contract + .add_milestone( + description: milestone_description, + amount: milestone_amount, + dueDate: milestone_dueDate + ); escrow_address } diff --git a/src/interface/iescrow.cairo b/src/interface/iescrow.cairo index d98425f..dfca26a 100644 --- a/src/interface/iescrow.cairo +++ b/src/interface/iescrow.cairo @@ -11,12 +11,11 @@ pub trait IEscrow { amount: u256, ); fn add_milestone( - ref self: TContractState, - description: ByteArray, - amount: u256, - dueDate: u256, + ref self: TContractState, description: ByteArray, amount: u256, dueDate: u256, ); - fn request_milestone_payment(ref self: TContractState, id: u64, token_address: ContractAddress) -> bool; + 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; From 90dcdd6313b823cb58badab09d18b65779fca0f9 Mon Sep 17 00:00:00 2001 From: shaaibu7 Date: Fri, 21 Mar 2025 11:35:53 +0100 Subject: [PATCH 6/6] chore: update milestone struct --- src/escrow/escrow_contract.cairo | 6 ++++-- src/escrow/types.cairo | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/escrow/escrow_contract.cairo b/src/escrow/escrow_contract.cairo index 5366ac8..9c4eb44 100644 --- a/src/escrow/escrow_contract.cairo +++ b/src/escrow/escrow_contract.cairo @@ -299,7 +299,8 @@ mod EscrowContract { amount: amount, dueDate: dueDate, isCompleted: false, - isApproved: false, + isApprovedDepositor: false, + isApprovedBeneficiary: false, isPaid: false }; @@ -312,7 +313,8 @@ mod EscrowContract { let mut milestone: Milestone = self.milestones.read(id); assert(!milestone.isPaid, 'Milestone has been paid'); - assert(milestone.isApproved, 'Milestone must be approved'); + 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; diff --git a/src/escrow/types.cairo b/src/escrow/types.cairo index c5998b8..f62a13b 100644 --- a/src/escrow/types.cairo +++ b/src/escrow/types.cairo @@ -15,7 +15,8 @@ pub struct Milestone { pub amount: u256, pub dueDate: u256, pub isCompleted: bool, - pub isApproved: bool, + pub isApprovedDepositor: bool, + pub isApprovedBeneficiary: bool, pub isPaid: bool, }