@@ -72,6 +72,9 @@ fn test_integration_escrow_lifecycle_happy_path() {
7272 assert_eq ! ( payment_token_client. balance( & buyer) , 0 ) ;
7373 assert_eq ! ( payment_token_client. balance( & escrow_id) , amount) ;
7474
75+ // Verify invoice token is locked while escrow is active
76+ assert ! ( inv_token_client. transfer_locked( ) ) ;
77+
7578 // 9. Record Payment (Payer settles the invoice)
7679 escrow_client. record_payment ( & invoice_id, & payer, & amount) ;
7780
@@ -88,6 +91,15 @@ fn test_integration_escrow_lifecycle_happy_path() {
8891 escrow_client. get_escrow_status( & invoice_id) ,
8992 EscrowStatus :: Settled
9093 ) ;
94+
95+ // Invoice token transfers must be unlocked after settlement
96+ assert ! ( !inv_token_client. transfer_locked( ) ) ;
97+
98+ // Buyer can now transfer their invoice tokens freely
99+ let recipient = Address :: generate ( & env) ;
100+ inv_token_client. transfer ( & buyer, & recipient, & amount) ;
101+ assert_eq ! ( inv_token_client. balance( & buyer) , 0 ) ;
102+ assert_eq ! ( inv_token_client. balance( & recipient) , amount) ;
91103}
92104
93105#[ test]
@@ -155,4 +167,74 @@ fn test_integration_refund_lifecycle() {
155167 escrow_client. get_escrow_status( & invoice_id) ,
156168 EscrowStatus :: Refunded
157169 ) ;
170+
171+ // Invoice token transfers must be unlocked after refund
172+ assert ! ( !inv_token_client. transfer_locked( ) ) ;
173+ }
174+
175+ #[ test]
176+ fn test_integration_token_locked_during_active_escrow ( ) {
177+ let env = Env :: default ( ) ;
178+ env. mock_all_auths ( ) ;
179+
180+ let admin = Address :: generate ( & env) ;
181+ let seller = Address :: generate ( & env) ;
182+ let buyer = Address :: generate ( & env) ;
183+
184+ let escrow_id = env. register_contract ( None , InvoiceEscrow ) ;
185+ let escrow_client = InvoiceEscrowClient :: new ( & env, & escrow_id) ;
186+
187+ let inv_token_id = env. register_contract ( None , InvoiceToken ) ;
188+ let inv_token_client = InvoiceTokenClient :: new ( & env, & inv_token_id) ;
189+
190+ let payment_token_admin = Address :: generate ( & env) ;
191+ let payment_token_id = env. register_stellar_asset_contract_v2 ( payment_token_admin. clone ( ) ) ;
192+ let payment_token_asset = AssetClient :: new ( & env, & payment_token_id. address ( ) ) ;
193+
194+ let invoice_id = Symbol :: new ( & env, "INVLOCK" ) ;
195+ inv_token_client. initialize (
196+ & admin,
197+ & SorobanString :: from_str ( & env, "Lock Test Invoice" ) ,
198+ & SorobanString :: from_str ( & env, "INVLCK" ) ,
199+ & 18 ,
200+ & invoice_id,
201+ & escrow_id,
202+ ) ;
203+
204+ escrow_client. initialize ( & admin, & 300 ) ;
205+
206+ let amount = 500i128 ;
207+ payment_token_asset. mint ( & buyer, & amount) ;
208+
209+ let due_date = 20000u64 ;
210+ escrow_client. create_escrow (
211+ & invoice_id,
212+ & seller,
213+ & amount,
214+ & due_date,
215+ & payment_token_id. address ( ) ,
216+ & inv_token_id,
217+ ) ;
218+
219+ // Token is locked even before funding (initialized locked)
220+ assert ! ( inv_token_client. transfer_locked( ) ) ;
221+
222+ escrow_client. fund_escrow ( & invoice_id, & buyer) ;
223+
224+ // Token is still locked after funding — transfers are blocked while invoice is active
225+ assert ! ( inv_token_client. transfer_locked( ) ) ;
226+ let other = Address :: generate ( & env) ;
227+ let result = inv_token_client. try_transfer ( & buyer, & other, & 100 ) ;
228+ assert ! ( result. is_err( ) ) ;
229+
230+ // After settlement, token unlocks
231+ let payer = Address :: generate ( & env) ;
232+ payment_token_asset. mint ( & payer, & amount) ;
233+ escrow_client. record_payment ( & invoice_id, & payer, & amount) ;
234+
235+ assert ! ( !inv_token_client. transfer_locked( ) ) ;
236+ // Buyer can now freely transfer invoice tokens
237+ inv_token_client. transfer ( & buyer, & other, & 100 ) ;
238+ assert_eq ! ( inv_token_client. balance( & buyer) , amount - 100 ) ;
239+ assert_eq ! ( inv_token_client. balance( & other) , 100 ) ;
158240}
0 commit comments