diff --git a/crates/sui-graphql-e2e-tests/tests/consistency/dynamic_fields/dynamic_fields.exp b/crates/sui-graphql-e2e-tests/tests/consistency/dynamic_fields/dynamic_fields.exp
index a5130682191d9..4cde77acbb206 100644
--- a/crates/sui-graphql-e2e-tests/tests/consistency/dynamic_fields/dynamic_fields.exp
+++ b/crates/sui-graphql-e2e-tests/tests/consistency/dynamic_fields/dynamic_fields.exp
@@ -193,7 +193,7 @@ Response: {
"contents": {
"json": {
"id": "0x844185d7b145f503838a1d509845a40ee249534f723dd2f003d9efdfc581000d",
- "count": "1"
+ "count": "2"
}
}
}
@@ -313,7 +313,7 @@ Response: {
"contents": {
"json": {
"id": "0x844185d7b145f503838a1d509845a40ee249534f723dd2f003d9efdfc581000d",
- "count": "1"
+ "count": "2"
}
}
}
@@ -411,7 +411,7 @@ Response: {
"contents": {
"json": {
"id": "0x844185d7b145f503838a1d509845a40ee249534f723dd2f003d9efdfc581000d",
- "count": "1"
+ "count": "2"
}
}
}
@@ -540,7 +540,7 @@ Response: {
"contents": {
"json": {
"id": "0x844185d7b145f503838a1d509845a40ee249534f723dd2f003d9efdfc581000d",
- "count": "1"
+ "count": "2"
}
}
}
@@ -664,7 +664,7 @@ Response: {
"contents": {
"json": {
"id": "0x844185d7b145f503838a1d509845a40ee249534f723dd2f003d9efdfc581000d",
- "count": "1"
+ "count": "2"
}
}
}
@@ -765,7 +765,7 @@ Response: {
"contents": {
"json": {
"id": "0x844185d7b145f503838a1d509845a40ee249534f723dd2f003d9efdfc581000d",
- "count": "1"
+ "count": "2"
}
}
}
@@ -831,7 +831,14 @@ Response: {
"repr": "u64"
}
},
- "value": null
+ "value": {
+ "contents": {
+ "json": {
+ "id": "0x844185d7b145f503838a1d509845a40ee249534f723dd2f003d9efdfc581000d",
+ "count": "2"
+ }
+ }
+ }
}
}
]
@@ -892,7 +899,14 @@ Response: {
"repr": "u64"
}
},
- "value": null
+ "value": {
+ "contents": {
+ "json": {
+ "id": "0x844185d7b145f503838a1d509845a40ee249534f723dd2f003d9efdfc581000d",
+ "count": "2"
+ }
+ }
+ }
}
}
]
diff --git a/crates/sui-graphql-e2e-tests/tests/consistency/dynamic_fields/immutable_dof.exp b/crates/sui-graphql-e2e-tests/tests/consistency/dynamic_fields/immutable_dof.exp
new file mode 100644
index 0000000000000..04c4c2558280f
--- /dev/null
+++ b/crates/sui-graphql-e2e-tests/tests/consistency/dynamic_fields/immutable_dof.exp
@@ -0,0 +1,204 @@
+processed 17 tasks
+
+init:
+A: object(0,0)
+
+task 1 'publish'. lines 20-68:
+created: object(1,0)
+mutated: object(0,1)
+gas summary: computation_cost: 1000000, storage_cost: 8770400, storage_rebate: 0, non_refundable_storage_fee: 0
+
+task 2 'run'. lines 70-70:
+created: object(2,0)
+mutated: object(0,0)
+gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 0, non_refundable_storage_fee: 0
+
+task 3 'run'. lines 72-72:
+created: object(3,0)
+mutated: object(0,0)
+gas summary: computation_cost: 1000000, storage_cost: 2295200, storage_rebate: 978120, non_refundable_storage_fee: 9880
+
+task 4 'run'. lines 74-74:
+created: object(4,0)
+mutated: object(0,0)
+gas summary: computation_cost: 1000000, storage_cost: 2295200, storage_rebate: 978120, non_refundable_storage_fee: 9880
+
+task 5 'run'. lines 76-76:
+created: object(5,0)
+mutated: object(0,0), object(2,0), object(3,0)
+gas summary: computation_cost: 1000000, storage_cost: 6064800, storage_rebate: 3573900, non_refundable_storage_fee: 36100
+
+task 6 'run'. lines 78-78:
+created: object(6,0)
+mutated: object(0,0), object(2,0), object(4,0)
+gas summary: computation_cost: 1000000, storage_cost: 6064800, storage_rebate: 3573900, non_refundable_storage_fee: 36100
+
+task 7 'run'. lines 80-80:
+mutated: object(0,0), object(2,0), object(3,0)
+deleted: object(5,0)
+gas summary: computation_cost: 1000000, storage_cost: 3610000, storage_rebate: 6004152, non_refundable_storage_fee: 60648
+
+task 8 'create-checkpoint'. lines 82-82:
+Checkpoint created: 1
+
+task 9 'run-graphql'. lines 84-114:
+Response: {
+ "data": {
+ "object": {
+ "dynamicFields": {
+ "nodes": [
+ {
+ "value": {
+ "address": "0x0ff6cdb59f761881b9ad479af854ca71c4f560a229795178771459a6522c57f0",
+ "version": 5,
+ "contents": {
+ "json": {
+ "id": "0x0ff6cdb59f761881b9ad479af854ca71c4f560a229795178771459a6522c57f0",
+ "count": "0"
+ }
+ },
+ "dynamicFields": {
+ "nodes": []
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+}
+
+task 10 'run-graphql'. lines 116-146:
+Response: {
+ "data": {
+ "object": {
+ "dynamicFields": {
+ "nodes": [
+ {
+ "value": {
+ "address": "0x0ff6cdb59f761881b9ad479af854ca71c4f560a229795178771459a6522c57f0",
+ "version": 5,
+ "contents": {
+ "json": {
+ "id": "0x0ff6cdb59f761881b9ad479af854ca71c4f560a229795178771459a6522c57f0",
+ "count": "0"
+ }
+ },
+ "dynamicFields": {
+ "nodes": [
+ {
+ "value": {
+ "address": "0xb1585cc21bb75b2e89747d4db64e0d73b227084d33b12f5715d1764a94c6b601",
+ "version": 6,
+ "contents": {
+ "json": {
+ "id": "0xb1585cc21bb75b2e89747d4db64e0d73b227084d33b12f5715d1764a94c6b601",
+ "count": "0"
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+}
+
+task 11 'run-graphql'. lines 148-178:
+Response: {
+ "data": {
+ "object": {
+ "dynamicFields": {
+ "nodes": []
+ }
+ }
+ }
+}
+
+task 12 'run-graphql'. lines 180-210:
+Response: {
+ "data": {
+ "object": null
+ }
+}
+
+task 13 'run-graphql'. lines 212-239:
+Response: {
+ "data": {
+ "object": {
+ "owner": {
+ "parent": {
+ "address": "0x6642719611d6990381dc19edcc37a7d353a1ea55281bf8702d0ad8bf3cfb7ea4"
+ }
+ },
+ "dynamicFields": {
+ "nodes": []
+ }
+ }
+ }
+}
+
+task 14 'run-graphql'. lines 241-258:
+Response: {
+ "data": {
+ "object": null
+ }
+}
+
+task 15 'run-graphql'. lines 260-287:
+Response: {
+ "data": {
+ "object": {
+ "owner": {
+ "_": null
+ },
+ "dynamicFields": {
+ "nodes": [
+ {
+ "value": {
+ "address": "0xb1585cc21bb75b2e89747d4db64e0d73b227084d33b12f5715d1764a94c6b601",
+ "version": 6,
+ "contents": {
+ "json": {
+ "id": "0xb1585cc21bb75b2e89747d4db64e0d73b227084d33b12f5715d1764a94c6b601",
+ "count": "0"
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+}
+
+task 16 'run-graphql'. lines 289-316:
+Response: {
+ "data": {
+ "object": {
+ "owner": {
+ "_": null
+ },
+ "dynamicFields": {
+ "nodes": [
+ {
+ "value": {
+ "address": "0xb1585cc21bb75b2e89747d4db64e0d73b227084d33b12f5715d1764a94c6b601",
+ "version": 6,
+ "contents": {
+ "json": {
+ "id": "0xb1585cc21bb75b2e89747d4db64e0d73b227084d33b12f5715d1764a94c6b601",
+ "count": "0"
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+}
diff --git a/crates/sui-graphql-e2e-tests/tests/consistency/dynamic_fields/immutable_dof.move b/crates/sui-graphql-e2e-tests/tests/consistency/dynamic_fields/immutable_dof.move
new file mode 100644
index 0000000000000..05f1f5ffe0b49
--- /dev/null
+++ b/crates/sui-graphql-e2e-tests/tests/consistency/dynamic_fields/immutable_dof.move
@@ -0,0 +1,316 @@
+// Copyright (c) Mysten Labs, Inc.
+// SPDX-License-Identifier: Apache-2.0
+
+// 1. create Parent1 (2)
+// 2. create Child1 (3)
+// 3. create Child2 (4)
+// 4. add Child1 to Parent1 -> Parent1 (5), Child1 (5)
+// 5. add Child2 as a nested child object by borrowing Parent1.Child1 -> Parent1 (6), Child1 (5), Child2 (6)
+// 6. make Child1 immutable
+
+// dynamic fields rooted on parent
+// Child1 (5) should have a parent
+// Child1 (6) does not exist
+// Child1 (7) should show as an Immutable object
+
+// Verify that Parent1 (6) -> Child1 (5) -> Child2 (6)
+
+//# init --protocol-version 44 --addresses Test=0x0 --accounts A --simulator
+
+//# publish
+module Test::M1 {
+ use sui::dynamic_object_field as ofield;
+
+ public struct Parent has key, store {
+ id: UID,
+ count: u64
+ }
+
+ public struct Child has key, store {
+ id: UID,
+ count: u64,
+ }
+
+ public entry fun parent(recipient: address, ctx: &mut TxContext) {
+ transfer::public_transfer(
+ Parent { id: object::new(ctx), count: 0 },
+ recipient
+ )
+ }
+
+ public entry fun mutate_parent(parent: &mut Parent) {
+ parent.count = parent.count + 42;
+ }
+
+ public entry fun child(recipient: address, ctx: &mut TxContext) {
+ transfer::public_transfer(
+ Child { id: object::new(ctx), count: 0 },
+ recipient
+ )
+ }
+
+ public fun add_child(parent: &mut Parent, child: Child, name: u64) {
+ ofield::add(&mut parent.id, name, child);
+ }
+
+ public fun add_nested_child(parent: &mut Parent, child_name: u64, nested_child: Child, nested_child_name: u64) {
+ let child: &mut Child = ofield::borrow_mut(&mut parent.id, child_name);
+ ofield::add(&mut child.id, nested_child_name, nested_child);
+ }
+
+ public fun reclaim_child(parent: &mut Parent, name: u64): Child {
+ ofield::remove(&mut parent.id, name)
+ }
+
+ public fun reclaim_and_freeze_child(parent: &mut Parent, name: u64) {
+ transfer::public_freeze_object(reclaim_child(parent, name))
+ }
+}
+
+//# run Test::M1::parent --sender A --args @A
+
+//# run Test::M1::child --sender A --args @A
+
+//# run Test::M1::child --sender A --args @A
+
+//# run Test::M1::add_child --sender A --args object(2,0) object(3,0) 42
+
+//# run Test::M1::add_nested_child --sender A --args object(2,0) 42 object(4,0) 420
+
+//# run Test::M1::reclaim_and_freeze_child --sender A --args object(2,0) 42
+
+//# create-checkpoint
+
+//# run-graphql
+{
+ object(address: "@{obj_2_0}", version: 5) {
+ dynamicFields {
+ nodes {
+ value {
+ ... on MoveObject {
+ address
+ version
+ contents {
+ json
+ }
+ dynamicFields {
+ nodes {
+ value {
+ ... on MoveObject {
+ address
+ version
+ contents {
+ json
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+//# run-graphql
+{
+ object(address: "@{obj_2_0}", version: 6) {
+ dynamicFields {
+ nodes {
+ value {
+ ... on MoveObject {
+ address
+ version
+ contents {
+ json
+ }
+ dynamicFields {
+ nodes {
+ value {
+ ... on MoveObject {
+ address
+ version
+ contents {
+ json
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+//# run-graphql
+{
+ object(address: "@{obj_2_0}", version: 7) {
+ dynamicFields {
+ nodes {
+ value {
+ ... on MoveObject {
+ address
+ version
+ contents {
+ json
+ }
+ dynamicFields {
+ nodes {
+ value {
+ ... on MoveObject {
+ address
+ version
+ contents {
+ json
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+//# run-graphql
+{
+ object(address: "@{obj_2_0}", version: 8) {
+ dynamicFields {
+ nodes {
+ value {
+ ... on MoveObject {
+ address
+ version
+ contents {
+ json
+ }
+ dynamicFields {
+ nodes {
+ value {
+ ... on MoveObject {
+ address
+ version
+ contents {
+ json
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+//# run-graphql
+{
+ object(address: "@{obj_3_0}", version: 5) {
+ owner {
+ ... on Immutable {
+ _
+ }
+ ... on Parent {
+ parent {
+ address
+ }
+ }
+ }
+ dynamicFields {
+ nodes {
+ value {
+ ... on MoveObject {
+ address
+ version
+ contents {
+ json
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+//# run-graphql
+{
+ object(address: "@{obj_3_0}", version: 6) {
+ dynamicFields {
+ nodes {
+ value {
+ ... on MoveObject {
+ address
+ version
+ contents {
+ json
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+//# run-graphql
+{
+ object(address: "@{obj_3_0}", version: 7) {
+ owner {
+ ... on Immutable {
+ _
+ }
+ ... on Parent {
+ parent {
+ address
+ }
+ }
+ }
+ dynamicFields {
+ nodes {
+ value {
+ ... on MoveObject {
+ address
+ version
+ contents {
+ json
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+//# run-graphql
+{
+ object(address: "@{obj_3_0}") {
+ owner {
+ ... on Immutable {
+ _
+ }
+ ... on Parent {
+ parent {
+ address
+ }
+ }
+ }
+ dynamicFields {
+ nodes {
+ value {
+ ... on MoveObject {
+ address
+ version
+ contents {
+ json
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/crates/sui-graphql-e2e-tests/tests/consistency/dynamic_fields/nested_dof.exp b/crates/sui-graphql-e2e-tests/tests/consistency/dynamic_fields/nested_dof.exp
new file mode 100644
index 0000000000000..13a83492198a8
--- /dev/null
+++ b/crates/sui-graphql-e2e-tests/tests/consistency/dynamic_fields/nested_dof.exp
@@ -0,0 +1,234 @@
+processed 17 tasks
+
+init:
+A: object(0,0)
+
+task 1 'publish'. lines 24-75:
+created: object(1,0)
+mutated: object(0,1)
+gas summary: computation_cost: 1000000, storage_cost: 8960400, storage_rebate: 0, non_refundable_storage_fee: 0
+
+task 2 'run'. lines 77-77:
+created: object(2,0)
+mutated: object(0,0)
+gas summary: computation_cost: 1000000, storage_cost: 2302800, storage_rebate: 0, non_refundable_storage_fee: 0
+
+task 3 'run'. lines 79-79:
+created: object(3,0)
+mutated: object(0,0)
+gas summary: computation_cost: 1000000, storage_cost: 2295200, storage_rebate: 978120, non_refundable_storage_fee: 9880
+
+task 4 'run'. lines 81-81:
+created: object(4,0)
+mutated: object(0,0)
+gas summary: computation_cost: 1000000, storage_cost: 2295200, storage_rebate: 978120, non_refundable_storage_fee: 9880
+
+task 5 'run'. lines 83-83:
+created: object(5,0)
+mutated: object(0,0), object(2,0), object(3,0)
+gas summary: computation_cost: 1000000, storage_cost: 6064800, storage_rebate: 3573900, non_refundable_storage_fee: 36100
+
+task 6 'run'. lines 85-85:
+created: object(6,0)
+mutated: object(0,0), object(2,0), object(4,0)
+gas summary: computation_cost: 1000000, storage_cost: 6064800, storage_rebate: 3573900, non_refundable_storage_fee: 36100
+
+task 7 'run'. lines 87-87:
+mutated: object(0,0), object(2,0), object(3,0)
+gas summary: computation_cost: 1000000, storage_cost: 3610000, storage_rebate: 3573900, non_refundable_storage_fee: 36100
+
+task 8 'run'. lines 89-89:
+mutated: object(0,0), object(2,0), object(4,0)
+gas summary: computation_cost: 1000000, storage_cost: 3610000, storage_rebate: 3573900, non_refundable_storage_fee: 36100
+
+task 9 'create-checkpoint'. lines 91-91:
+Checkpoint created: 1
+
+task 10 'run-graphql'. lines 93-123:
+Response: {
+ "data": {
+ "object": {
+ "dynamicFields": {
+ "nodes": [
+ {
+ "value": {
+ "address": "0x7cfc2f4e6743c28eee330e37ae5293166f2e9e397e38cda3964d50abf3efb626",
+ "version": 5,
+ "contents": {
+ "json": {
+ "id": "0x7cfc2f4e6743c28eee330e37ae5293166f2e9e397e38cda3964d50abf3efb626",
+ "count": "0"
+ }
+ },
+ "dynamicFields": {
+ "nodes": []
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+}
+
+task 11 'run-graphql'. lines 125-155:
+Response: {
+ "data": {
+ "object": {
+ "dynamicFields": {
+ "nodes": [
+ {
+ "value": {
+ "address": "0x7cfc2f4e6743c28eee330e37ae5293166f2e9e397e38cda3964d50abf3efb626",
+ "version": 5,
+ "contents": {
+ "json": {
+ "id": "0x7cfc2f4e6743c28eee330e37ae5293166f2e9e397e38cda3964d50abf3efb626",
+ "count": "0"
+ }
+ },
+ "dynamicFields": {
+ "nodes": [
+ {
+ "value": {
+ "address": "0x09b23abe081f13c3e7e5ee8064d75b9b04144ab6b0a45a490afd1720f5c57c07",
+ "version": 6,
+ "contents": {
+ "json": {
+ "id": "0x09b23abe081f13c3e7e5ee8064d75b9b04144ab6b0a45a490afd1720f5c57c07",
+ "count": "0"
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+}
+
+task 12 'run-graphql'. lines 157-187:
+Response: {
+ "data": {
+ "object": {
+ "dynamicFields": {
+ "nodes": [
+ {
+ "value": {
+ "address": "0x7cfc2f4e6743c28eee330e37ae5293166f2e9e397e38cda3964d50abf3efb626",
+ "version": 7,
+ "contents": {
+ "json": {
+ "id": "0x7cfc2f4e6743c28eee330e37ae5293166f2e9e397e38cda3964d50abf3efb626",
+ "count": "1"
+ }
+ },
+ "dynamicFields": {
+ "nodes": [
+ {
+ "value": {
+ "address": "0x09b23abe081f13c3e7e5ee8064d75b9b04144ab6b0a45a490afd1720f5c57c07",
+ "version": 6,
+ "contents": {
+ "json": {
+ "id": "0x09b23abe081f13c3e7e5ee8064d75b9b04144ab6b0a45a490afd1720f5c57c07",
+ "count": "0"
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+}
+
+task 13 'run-graphql'. lines 189-219:
+Response: {
+ "data": {
+ "object": {
+ "dynamicFields": {
+ "nodes": [
+ {
+ "value": {
+ "address": "0x7cfc2f4e6743c28eee330e37ae5293166f2e9e397e38cda3964d50abf3efb626",
+ "version": 7,
+ "contents": {
+ "json": {
+ "id": "0x7cfc2f4e6743c28eee330e37ae5293166f2e9e397e38cda3964d50abf3efb626",
+ "count": "1"
+ }
+ },
+ "dynamicFields": {
+ "nodes": [
+ {
+ "value": {
+ "address": "0x09b23abe081f13c3e7e5ee8064d75b9b04144ab6b0a45a490afd1720f5c57c07",
+ "version": 8,
+ "contents": {
+ "json": {
+ "id": "0x09b23abe081f13c3e7e5ee8064d75b9b04144ab6b0a45a490afd1720f5c57c07",
+ "count": "1"
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+}
+
+task 14 'run-graphql'. lines 221-238:
+Response: {
+ "data": {
+ "object": {
+ "dynamicFields": {
+ "nodes": []
+ }
+ }
+ }
+}
+
+task 15 'run-graphql'. lines 240-257:
+Response: {
+ "data": {
+ "object": null
+ }
+}
+
+task 16 'run-graphql'. lines 259-276:
+Response: {
+ "data": {
+ "object": {
+ "dynamicFields": {
+ "nodes": [
+ {
+ "value": {
+ "address": "0x09b23abe081f13c3e7e5ee8064d75b9b04144ab6b0a45a490afd1720f5c57c07",
+ "version": 6,
+ "contents": {
+ "json": {
+ "id": "0x09b23abe081f13c3e7e5ee8064d75b9b04144ab6b0a45a490afd1720f5c57c07",
+ "count": "0"
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+}
diff --git a/crates/sui-graphql-e2e-tests/tests/consistency/dynamic_fields/nested_dof.move b/crates/sui-graphql-e2e-tests/tests/consistency/dynamic_fields/nested_dof.move
new file mode 100644
index 0000000000000..98216bc6e4083
--- /dev/null
+++ b/crates/sui-graphql-e2e-tests/tests/consistency/dynamic_fields/nested_dof.move
@@ -0,0 +1,276 @@
+// Copyright (c) Mysten Labs, Inc.
+// SPDX-License-Identifier: Apache-2.0
+
+// 1. create Parent1 (2)
+// 2. create Child1 (3)
+// 3. create Child2 (4)
+// 4. add Child1 to Parent1 -> Parent1 (5), Child1 (5)
+// 5. add Child2 as a nested child object by borrowing Parent1.Child1 -> Parent1 (6), Child1 (5), Child2 (6)
+// 6. mutate child1 -> Parent1 (7), Child1 (7), Child2 (6)
+// 7. mutate child2 through parent -> Parent1 (8), Child1 (7), Child2 (8)
+
+// dynamic fields rooted on parent
+// Parent(5) -> Child1 (5) -> None // add child1 as child to parent1
+// Parent(6) -> Child1 (5) -> Child2 (6) // add child2 as a child to child1 by borrowing child1 from parent
+// Parent(7) -> Child1 (7) -> Child2 (6) // mutate child1
+// Parent(8) -> Child1 (7) -> Child2 (8) // mutate nested child2 by borrowing from child1
+
+// query with Child1 as the root:
+// Child1 (5) -> None
+// Child1 (7) -> Child2 (6)
+
+//# init --protocol-version 44 --addresses Test=0x0 --accounts A --simulator
+
+//# publish
+module Test::M1 {
+ use sui::dynamic_object_field as ofield;
+
+ public struct Parent has key, store {
+ id: UID,
+ count: u64
+ }
+
+ public struct Child has key, store {
+ id: UID,
+ count: u64,
+ }
+
+ public entry fun parent(recipient: address, ctx: &mut TxContext) {
+ transfer::public_transfer(
+ Parent { id: object::new(ctx), count: 0 },
+ recipient
+ )
+ }
+
+ public entry fun mutate_parent(parent: &mut Parent) {
+ parent.count = parent.count + 42;
+ }
+
+ public entry fun child(recipient: address, ctx: &mut TxContext) {
+ transfer::public_transfer(
+ Child { id: object::new(ctx), count: 0 },
+ recipient
+ )
+ }
+
+ public fun add_child(parent: &mut Parent, child: Child, name: u64) {
+ ofield::add(&mut parent.id, name, child);
+ }
+
+ public fun add_nested_child(parent: &mut Parent, child_name: u64, nested_child: Child, nested_child_name: u64) {
+ let child: &mut Child = ofield::borrow_mut(&mut parent.id, child_name);
+ ofield::add(&mut child.id, nested_child_name, nested_child);
+ }
+
+ public fun mutate_child_on_parent(parent: &mut Parent, child_name: u64) {
+ let child: &mut Child = ofield::borrow_mut(&mut parent.id, child_name);
+ child.count = child.count + 1;
+ }
+
+ public fun mutate_nested_child_on_parent(parent: &mut Parent, child_name: u64, nested_child_name: u64) {
+ let child: &mut Child = ofield::borrow_mut(&mut parent.id, child_name);
+ let nested_child: &mut Child = ofield::borrow_mut(&mut child.id, nested_child_name);
+ nested_child.count = nested_child.count + 1;
+ }
+}
+
+//# run Test::M1::parent --sender A --args @A
+
+//# run Test::M1::child --sender A --args @A
+
+//# run Test::M1::child --sender A --args @A
+
+//# run Test::M1::add_child --sender A --args object(2,0) object(3,0) 42
+
+//# run Test::M1::add_nested_child --sender A --args object(2,0) 42 object(4,0) 420
+
+//# run Test::M1::mutate_child_on_parent --sender A --args object(2,0) 42
+
+//# run Test::M1::mutate_nested_child_on_parent --sender A --args object(2,0) 42 420
+
+//# create-checkpoint
+
+//# run-graphql
+{
+ object(address: "@{obj_2_0}", version: 5) {
+ dynamicFields {
+ nodes {
+ value {
+ ... on MoveObject {
+ address
+ version
+ contents {
+ json
+ }
+ dynamicFields {
+ nodes {
+ value {
+ ... on MoveObject {
+ address
+ version
+ contents {
+ json
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+//# run-graphql
+{
+ object(address: "@{obj_2_0}", version: 6) {
+ dynamicFields {
+ nodes {
+ value {
+ ... on MoveObject {
+ address
+ version
+ contents {
+ json
+ }
+ dynamicFields {
+ nodes {
+ value {
+ ... on MoveObject {
+ address
+ version
+ contents {
+ json
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+//# run-graphql
+{
+ object(address: "@{obj_2_0}", version: 7) {
+ dynamicFields {
+ nodes {
+ value {
+ ... on MoveObject {
+ address
+ version
+ contents {
+ json
+ }
+ dynamicFields {
+ nodes {
+ value {
+ ... on MoveObject {
+ address
+ version
+ contents {
+ json
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+//# run-graphql
+{
+ object(address: "@{obj_2_0}", version: 8) {
+ dynamicFields {
+ nodes {
+ value {
+ ... on MoveObject {
+ address
+ version
+ contents {
+ json
+ }
+ dynamicFields {
+ nodes {
+ value {
+ ... on MoveObject {
+ address
+ version
+ contents {
+ json
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+//# run-graphql
+{
+ object(address: "@{obj_3_0}", version: 5) {
+ dynamicFields {
+ nodes {
+ value {
+ ... on MoveObject {
+ address
+ version
+ contents {
+ json
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+//# run-graphql
+{
+ object(address: "@{obj_3_0}", version: 6) {
+ dynamicFields {
+ nodes {
+ value {
+ ... on MoveObject {
+ address
+ version
+ contents {
+ json
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+//# run-graphql
+{
+ object(address: "@{obj_3_0}", version: 7) {
+ dynamicFields {
+ nodes {
+ value {
+ ... on MoveObject {
+ address
+ version
+ contents {
+ json
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/crates/sui-graphql-rpc/schema/current_progress_schema.graphql b/crates/sui-graphql-rpc/schema/current_progress_schema.graphql
index cb7aa0e80470f..277462a920808 100644
--- a/crates/sui-graphql-rpc/schema/current_progress_schema.graphql
+++ b/crates/sui-graphql-rpc/schema/current_progress_schema.graphql
@@ -881,9 +881,10 @@ type DynamicField {
"""
name: MoveValue
"""
- The actual data stored in the dynamic field.
- The returned dynamic field is an object if its return type is MoveObject,
- in which case it is also accessible off-chain via its address.
+ The returned dynamic field is an object if its return type is `MoveObject`,
+ in which case it is also accessible off-chain via its address. Its contents
+ will be from the latest version that is at most equal to its parent object's
+ version
"""
value: DynamicFieldValue
}
diff --git a/crates/sui-graphql-rpc/src/types/coin.rs b/crates/sui-graphql-rpc/src/types/coin.rs
index fd75a0d459b7f..eb9dbd967ce23 100644
--- a/crates/sui-graphql-rpc/src/types/coin.rs
+++ b/crates/sui-graphql-rpc/src/types/coin.rs
@@ -244,7 +244,7 @@ impl Coin {
name: DynamicFieldName,
) -> Result