diff --git a/fastx_programmability/adapter/src/adapter.rs b/fastx_programmability/adapter/src/adapter.rs index 38732fbbcd041..547fbc72280df 100644 --- a/fastx_programmability/adapter/src/adapter.rs +++ b/fastx_programmability/adapter/src/adapter.rs @@ -431,7 +431,14 @@ fn resolve_and_type_check( // check that m.type_ matches the parameter types of the function match ¶m_type { Type::MutableReference(inner_t) => { - // (https://github.com/MystenLabs/fastnft/issues/96): check m.mutability + if m.is_read_only() { + return Err(FastPayError::TypeError { + error: format!( + "Argument {} is expected to be mutable, immutable object found", + idx + ), + }); + } type_check_struct(&m.type_, inner_t)?; mutable_ref_objects.push(object); } @@ -443,7 +450,14 @@ fn resolve_and_type_check( } } Type::Struct { .. } => { - // TODO(https://github.com/MystenLabs/fastnft/issues/96): check m.mutability + if m.is_read_only() { + return Err(FastPayError::TypeError { + error: format!( + "Argument {} is expected to be mutable, immutable object found", + idx + ), + }); + } type_check_struct(&m.type_, ¶m_type)?; let res = by_value_objects.insert(object.id(), object); // should always pass due to earlier "no duplicate ID's" check diff --git a/fastx_programmability/adapter/src/unit_tests/adapter_tests.rs b/fastx_programmability/adapter/src/unit_tests/adapter_tests.rs index a408cd63611e0..40ce4b0a2bd7e 100644 --- a/fastx_programmability/adapter/src/unit_tests/adapter_tests.rs +++ b/fastx_programmability/adapter/src/unit_tests/adapter_tests.rs @@ -521,7 +521,7 @@ fn test_transfer_and_freeze() { &mut storage, &native_functions, "transfer_and_freeze", - gas_object, + gas_object.clone(), MAX_GAS, vec![obj1], pure_args, @@ -532,6 +532,39 @@ fn test_transfer_and_freeze() { let obj1 = storage.read_object(&id1).unwrap(); assert!(obj1.is_read_only()); assert_eq!(obj1.owner, addr2); + + // 3. Call transfer again and it should fail. + let pure_args = vec![bcs::to_bytes(&addr1.to_vec()).unwrap()]; + let result = call( + &mut storage, + &native_functions, + "transfer", + gas_object.clone(), + MAX_GAS, + vec![obj1], + pure_args, + ); + assert!(result + .unwrap_err() + .to_string() + .contains("Argument 0 is expected to be mutable, immutable object found")); + + // 4. Call set_value (pass as mutable reference) should fail as well. + let obj1 = storage.read_object(&id1).unwrap(); + let pure_args = vec![bcs::to_bytes(&1u64).unwrap()]; + let result = call( + &mut storage, + &native_functions, + "set_value", + gas_object.clone(), + MAX_GAS, + vec![obj1], + pure_args, + ); + assert!(result + .unwrap_err() + .to_string() + .contains("Argument 0 is expected to be mutable, immutable object found")); } // TODO(https://github.com/MystenLabs/fastnft/issues/92): tests that exercise all the error codes of the adapter diff --git a/fastx_programmability/framework/sources/ObjectBasics.move b/fastx_programmability/framework/sources/ObjectBasics.move index 1804c66cc5760..b292fd6a277dd 100644 --- a/fastx_programmability/framework/sources/ObjectBasics.move +++ b/fastx_programmability/framework/sources/ObjectBasics.move @@ -30,6 +30,10 @@ module FastX::ObjectBasics { Transfer::transfer_and_freeze(o, Address::new(recipient)) } + public fun set_value(o: &mut Object, value: u64, _ctx: &mut TxContext) { + o.value = value; + } + // test that reading o2 and updating o1 works public fun update(o1: &mut Object, o2: &Object, _ctx: &mut TxContext) { o1.value = o2.value